數據訪問層框架,ORM,Object Relation Mapping,將對象映射到關係,通過操作對象來達到操作關係的目的。
通過hibernate訪問數據層不需要自己編寫sql,簡單智能,調用方法就可完成。ibatis需要自己編寫sql語句,更加靈活,可對sql進行優化。ibatis的最後版本爲2.3.4,從3.x開始,轉投到Google Code門下,並改名爲MyBatis。
===========================================
ibatis的核心類是SqlMapClient,通過其可完成對數據庫的訪問操作。
spring提供的SqlMapClientFactoryBean對SqlMapClient進行了封裝,類內提供對SqlMapClient實例進行初始化的方法,並提供返回SqlMapClient實例的方法getObject。SqlMapClientFactoryBean實現FactoryBean接口,spring規定實現該接口的Bean,由框架實例化時,返回的是getObject返回的實例,這也體現了SqlMapClientFactoryBean是SqlMapClient的實例工廠的意義——配置SqlMapClientFactoryBean,要注入configLocation屬性,對應ibatis的配置文件;datasource屬性,對應數據源bean。
spring提供的SqlMapClientTemplate對SqlMapClient進行了封裝,類內實際是對SqlMapClient的增刪改查方法做了一個外包層,類內SqlMapClient實例的初始化需要外部的注入,本身不提供初始化方法。SqlMapClientTemplate繼承JdbcAccessor,JdbcAccessor內部封裝了DataSource數據源Bean——配置SqlMapClientTemplate,要注入sqlMapClient屬性,對應注入SqlMapClientFactoryBean的實例,因爲其返回的就是SqlMapClient的實例;datasource屬性,對應數據源bean。
spring提供的SqlMapClientDaoSupport對SqlMapClientTemplate進行了封裝,是一個抽象類,必須繼承才能使用,類內以new的方式生成一個SqlMapClientTemplate的實例。
DataSource可在SqlMapClientFactoryBean內注入,也可在SqlMapClientTemplate內注入,放在SqlMapClientTemplate內更合適。
實際項目中肯能會有多個數據源,如mysql、sqlserver、oracle等。
SqlMapClientFactoryBean旨在生成SqlMapClient的實例,與屬性configLocation有關,需要指明映射關係,而SqlMapClient實例的生成與屬性datasource無關,僅在連接數據庫時會用到。
SqlMapClientTemplate內部使用SqlMapClient的實例進行數據庫操作,需要連接數據庫,必須注入屬性datasource。
針對不同的數據源,需要對每個數據源配置一個SqlMapClientFactoryBean用於注入對應數據源的映射文件,需要對每個數據源配置一個SqlMapClientTemplate用於注入數據源bean來連接不同的數據庫。
dao的三種實現方式,根據設計模式能用對象組合不用繼承的原則,不建議使用後兩種方式。
對象組合方式,dao內部定義SqlMapClientTemplate的實例,SqlMapClientTemplate的實例由spring註冊並注入,SqlMapClientTemplate要注入SqlMapClientFactoryBean與DataSource。
繼承方式,dao繼承SqlMapClientTemplate,dao的配置也就包括了SqlMapClientTemplate的配置,dao內部可以直接使用繼承的方法操作數據庫,dao配置要注入SqlMapClientFactoryBean與DataSource——類內屬性不加註解,配置文件內也不給類注入,配置文件頭部<beans default-autowire="byName">
給出default-autowire,spring也能完成注入。
繼承方式,dao繼承SqlMapClientDaoSupport,同樣dao的配置也包括了SqlMapClientTemplate的配置,dao內部可以直接使用繼承的SqlMapClientTemplate實例操作數據庫,dao配置要注入SqlMapClientFactoryBean與DataSource——類內只給出set方法,不給出屬性定義,spring也能完成注入。
===========================================
ibatis中一個pojo對應一個dao,一個dao對應一個映射文件,由此項目中通常會有多個映射文件。由於ibatis最終會將所有的映射文件合併到一起,爲防止id衝突,需要命名空間namespace來唯一標識該映射文件。
以下兩個配置文件在dao層,屬於ibatis的內容。
// Computer.xml
// namespace,通常用pojo名
<sqlMap namespace="Computer">
// alias,類型別名,通常爲首字母小寫的pojo名
<typeAlias alias="computer" type="xx.Computer" />
// 對應一類查詢結果集
// class,指明查詢結果要映射的pojo類
<resultMap id="all" class="computer">
// 指明pojo類的屬性與關係表中字段的對應關係
// jdbcType與javaType的對應關係用時去網上查一下,不要背
<result property="id" column="id" jdbcType="INT" javaType="java.lang.Integer" />
<result property="name" column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<result property="price" column="price" jdbcType="VARCHAR" javaType="java.lang.String"/>
</resultMap>
<resultMap id="part" class="computer">
<result property="name" column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<result property="price" column="price" jdbcType="VARCHAR" javaType="java.lang.String"/>
</resultMap>
// id,指出該sql語句的名字,代碼中會用到,用時必須加命名空間
// parameterClass,指出參數類型
<insert id="insert" parameterClass="computer">
// CDATA中的內容被定義爲純文本,以防與xml標籤混淆
// 在#之間的爲變量
<![CDATA[
insert into computer(name, price) values(#name#, #price#)
]]>
// selectKey,用於在插入時自動生成主鍵
// keyProperty,定義主鍵名字
// resultClass,定義返回類型
<selectKey resultClass="java.lang.Long" keyProperty="id">
// 返回上次插入自動生成的id
select @@IDENTITY as id
</selectKey>
</insert>
// 項目中不要用基本類型,要用類包裝
// 也有將刪除操作放在update標籤內的,沒多大區別
<delete id="delete" parameterClass="java.lang.Long">
delete from computer where id = #id#
</delete>
<update id="update" parameterClass="computer">
update computer set name = #name#, price = #price# where id = #id#
// 或
// dynamic的prepend值會覆蓋條件標籤中第一個條件爲真的prepend的值
// 前提是條件標籤prepend的值被設置且長度 > 0
update computer
<dynamic prepend="set">
<isNotEmpty property="name" prepend=','>
name = #name#
</isNotEmpty>
<isNotEmpty property="price" prepend=','>
price = #price #
</isNotEmpty>
</dynamic>
where id = #id#
</update>
// select查詢出的字段必須和resultMap配置的字段對應,多了、少了或不對應都會拋出異常
// ibatis內部會將查詢結果依次賦值到resultMap配置的pojo實例內對應的屬性上
// resultMap,爲前面定義的resultMap的id
<select id="list" resultMap="all">
select id, name, price from computer
</select>
// 或
// resultMap是顯示映射,pojo屬性與關係字段映射明確,性能更好,推薦使用
// resultClass,指出查詢結果要映射的類,爲隱式映射,若pojo的屬性名與關係的字段名不同,查詢結果就不會映射到類
<select id="list" resultClass="computer">
select id, name, price from computer
</select>
// 查詢結果僅一個字段
<select id="getName" parameterClass="java.lang.Long" resultClass="java.lang.String">
select name from computer where id = #id#
</select>
// sql標籤用來定義sql語句片段
// id,爲sql片段標識,後面可根據此id拼接sql語句
<sql id="list_where">
// isNotNull,指當property設置的變量name不爲空時,執行標籤裏內容
// prepend,表示將其內容and追加在isNotNull標籤的內容之前
<isNotNull property="name" prepend="and">
name = #name#
</isNotNull>
<isNotNull property="price" prepend="and">
price = #price#
</isNotNull>
// 上述打印內容爲 and name = xx and price = xx
// 通過設置dynamic標籤的prepend來覆蓋第一個條件爲真的and
// 打印內容爲where name = xx and price = xx
<dynamic prepend="where">
<isNotNull property="name" prepend="and">
name = #name#
</isNotNull>
<isNotNull property="price" prepend="and">
price = #price#
</isNotNull>
</dynamic>
</sql>
// parameterClass,參數爲map類型
<select id="list_2" parameterClass="map" resultMap="part">
// where 1 = 1,用於巧妙的連接name之前的and,若用dynamic可以不用此
// include,用於包含sql片段
// start,屬性值來自map類型的參數
// limit start, rows 返回查詢結果的第start行,偏移從0開始,共返回rows行
// limit start, -1 返回查詢結果的第start,及其之後的所有行
// limit end 等價於 limit 0, end 返回查詢結果第end行,及其之前的所有行
// #與$的區別,#之間放變量,$用於字符串之間的拼接,$間不要放字符或字符串變量,否則出錯
select name, price from computer
where 1 = 1
<include refid="list_where" />
order by id asc
<isNotNull property="start" prepend=" ">
limit $start$, $rows$
</isNotNull>
</select>
</sqlMap>
// sqlmap-config-mysql.xml
// 用於配置數據源信息,以及整合全部的映射文件
// 若有其它異構數據庫,應單獨建立另一個文件,如sqlmap-config-sqlserver.xml
<sqlMapConfig>
// useStatementNamespaces,設爲true表示開啓映射文件的命名空間
<settings useStatementNamespaces="true"/>
// 配置數據源,與spring整合後,該部分移植到spring配置文件
....
// 以當前目錄爲相對路徑,加載映射文件
<sqlMap resource="xx/mysql/xx.xml" />
</sqlMapConfig>
===========================================
以下配置文件在web層,屬於spring與ibatis的整合內容。
// spring-config-datasource-dbcp.xml
// 所有的數據源在此配置
<beans>
// 將數據源註冊爲bean
<bean id="mysqlDataSource" class="..BasicDataSource">
// 屬性的名字都是在BasicDataSource裏定義好的,不要自定義
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xxdb" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
// 其它的數據源,如sqlserver、oracle、分庫的
</beans>
// spring-dao.xml
// 註冊項目所有的dao
<beans>
// 註冊SqlMapClientFactoryBean
<bean id="sqlMapClient" class="..SqlMapClientFactoryBean">
// SqlMapClientFactoryBean源碼內定義了屬性dataSource和configLocation
// dataSource放在SqlMapClientTemplate內注入
// configLocation在此注入,看爲與ibatis整合的入口
<property name="configLocation" value="classpath:sqlmap-config-mysql.xml" />
</bean>
// 針對不同的數據源,要再次配置新的SqlMapClientFactoryBean
....
// 配置SqlMapClientTemplate
<bean id="mysqlSqlMapClientTemplate" class="..SqlMapClientTemplate">
<property name="dataSource" ref="mysqlDataSource">
// SqlMapClientTemplate源碼內定義了ibatis的SqlMapClient類型的屬性sqlMapClient
<property name="sqlMapClient" ref="sqlMapClient">
</property>
// 針對不同的數據源,要再次配置新的SqlMapClientTemplate
....
// 放到項目用註解完成
<bean id="xxDaoImpl" class="xx.xxDaoImpl">
<property name="myClientTemplate" ref="mysqlSqlMapClientTemplate" />
</bean>
</beans>
// spring-config.xml
// 將上述xml文件整合到一起
@Repository("computerDao ")
public class ComputerDaoImpl implements ComputerDao {
@Resource(name = "mysqlSqlMapClientTemplate")
private SqlMapClientTemplate mysqlTool;
..
public Integer insertComputer(Computer computer) {
// 加命名空間
return (Integer)mysqlTool.insert("Computer.insert", computer);
}
}