本來我也不喜歡iBatis,那是由於我當時還不怎么會用它,如今我想說,iBatis是個好東西,不信你試試看。以下是我在項目實踐中對iBatis的一個小總結。希望幫助眾多在疲於iBatis編碼而無暇思考的苦工們找到一些偷懶的機會。
SqlMap的配置是iBatis中應用的核心。這部分任務占領了iBatis開發的70的工作量。
1、命名空間:
<sqlMap namespace="Account">。在此空間外要引用此空間的元素,則須要加上命名空間名。
2、實體的別名:
<typeAlias alias="Account" type="com.lavasoft.ibatissut.simple.domain.entity.Account"/>
假設實用到的全名的地方。能夠用別名取代,受命名空間約束。
3、插入操作
對於自增主鍵的表,插入能夠不配置插入的主鍵列。
否則是必須的。
4、獲取主鍵
插入語句之前配置:主要是針對Sequence主鍵而言,插入前必須指定一個主鍵值給要插入的記錄。
Oracle、DB2亦如此。方法是在插入語句標簽<insert....>之前配置上:
<insert id="insertAccount" parameterClass="Account">
<selectKey resultClass="long" keyProperty="sctId">
SELECT SEQ_TEST.NEXTVAL FROM DUAL
</selectKey>
insert into .... ........
</insert>
插入語句之后配置:蛀牙是針對自增主鍵的表而言,這類表在插入時不須要主鍵。而是在插入過程自己主動獲取一個自增的主鍵。
比方MySQL
<insert id="insertAccount" parameterClass="Account">
<selectKey resultClass="long" keyProperty="sctId">
SELECT LAST_INSERT_ID()
</selectKey>
insert into .... ........
</insert>
當然,是否須要配置<selectKey>依據情況。僅僅要能保證記錄有主鍵就可以。一旦配置了<selectKey>,就能夠在運行插入操作時獲取到新增記錄的主鍵。
6、SQL入參parameterClass
插入語句入參:parameterClass="類別名" 來設定。
查詢語句入參:能夠設定類別名,也能夠設定為map,也能夠設定為iBatis支持的原生類型(比方string、int、long等)。當僅僅有一個原生類型入參時。則在SQL中用valuekeyword來引用。比方:
<select id="getById" parameterClass="long" resultMap="result_base">
select * from customer where id =
#value#
</select>
map是最強大的入參方式。不論什么入參方式都能夠轉換為這樣的入參方式,由於iBatis僅接受一個入參,當幾個參數分布在不同對象中的時候,將這些對象的屬性(或者對象本身put)到map中,然后一次傳遞給sql語句是很有效。
能夠自己寫一個將對象或者對象集合轉換為map的工具(我已經實現一個了)。
另外。map的中的元素(比方pobj)是個復雜對象,則還能夠在SQL中以#pobj.protyename#的格式來引用當中內嵌的屬性。當然不推薦這么干。
7、返回值參數類型
返回值參數也相同有兩種類型,一種是對象類型resultClass="Account"。一種是resultMap="AccountResult"。
這兩種類型的選擇經常會令人迷惑不解,一言明其理:
當結果集列名和類屬性名全然相應的時候,則應該使用resultClass來指定查詢結果類型。當然有些列明不正確應。能夠在sql中使用as重命名達到一致的效果。
當查詢結果列名和類屬性名相應不上的時候。應該選擇resultMap指定查詢結果集類型。否則,則查詢出來填充的對象屬性為空(數字的為0。對象的為null)。
可是實際上resultMap是對一個Java Bean的映射。須要先定義xml的映射后。才干夠引用。比如:
<
resultMap id="AccountResult" class="Account">
<result property="id" column="ACC_ID"/>
<result property="firstName" column="ACC_FIRST_NAME"/>
<result property="lastName" column="ACC_LAST_NAME"/>
<result property="emailAddress" column="ACC_EMAIL"/>
</resultMap>
resultMap映射的結果的目的就是要將查詢的結果集綁定到映射對象的屬性上。
無論使用哪種返回值參數類型。其終於目的就是要把每條記錄映射到一個類的對象或者對象集合上。假設有某個類屬性映射不上。則在得到的這個對象或對象集合中這個屬性為空。映射的屬性能夠是表與實體中的一部分。不要同一時候使用兩種返回值參數類型。這樣僅僅會令人迷惑。
8、查詢結果集分組
查詢結果集排序有兩種方式:一是在結果集映射上定義<resultMap id="result" class="bar" groupBy="id">。還有一種就是在SQL語句中分組。建議在SQL語句中分組,以獲得更大的可控制性。
9、SQL中參數的引用
SQL中引用parameterClass的參數有三種方式:
iBatis內置支持的類型。比方int、string,使用#value#來引用。這個value是keyword,不可變。
map類型的參數,使用#keyName#來引用,keyName為鍵名。
復雜對象的參數,使用#propertyName#來引用。propertyName類屬性的名字。
10、模糊查詢中參數的引用
模糊查詢是針對字符串而言的,假設遇到兩個單引號要包括一個參數。則不能再用#來引用變量了,而應該改為$,比方:'%$varName$%',當然,也能夠使用 '%' || #varname# || '%' 來繞過此問題。
11、SQL片段
能夠通過<sql id="sql_xxx">...</sql>定義SQL片段。然后<include refid="sql_xxx"/>來在各種語句中引用。達到服用目的,
12、動態SQL
能夠通過使用動態SQL來組織靈活性更大的更通過的SQL,這樣極大降低了編碼量,是iBatis應用的第二大亮點。
比方:一個動態的where條件
<dynamic prepend="where">
<isNotEmpty prepend="and" property="$$$$$">
$name like '%'|| #$name# ||'%'
</isNotEmpty>
<isGreaterThan prepend="and" property="$$$$$" compareValue="$$$number">
$code like '%'|| #$code# ||'%'
</isGreaterThan>
</dynamic>
當然,prepend表示鏈接keyword,能夠為不論什么字符串,當為sqlkeyword時,iBatis自己主動推斷是否應該加入該keyword。
該語法也非常easy,關鍵是要會用心思考組織動態SQL。
這里面有一點要注意:差別<isNotEmpty>和<isNotNull>差別,當為空空串時<isNotEmpty>返回true,當為空串時<isNotNull>返回真。哈哈。自己體會吧。說了反而啰嗦。
13、結果集映射繼承
結果集映射的繼承的目的是為了映射定義的復用,比方以下定義了兩個映射。AccountResult繼承了base:
<resultMap id="base" class="Account">
<result property="id" column="ACC_ID"/>
<result property="firstName" column="ACC_FIRST_NAME"/>
<result property="lastName" column="ACC_LAST_NAME"/>
</resultMap>
<resultMap id="AccountResult" class="Account" extends="Account.base">
<result property="emailAddress" column="ACC_EMAIL"/>
</resultMap>
這樣,就非常easy擴展了一個映射策略。
14、查詢注入
查詢注入是在一個查詢中嵌入另外一個查詢,這樣做的目的是為了實現實體對象之間的關聯關聯關系(一對一、一對多、多對多)分單項雙向。有關這些內容,是比較復雜的,筆者對此做了深入研究,並分別寫了三篇來講述。
查詢注入的實現就是在實體屬性為另外一個實體或者實體集合的時候。引入一個相關的查詢來實現。比如,客戶和訂單的映射關系:
public class Customer {
private Long id;
private String name;
private String address;
private String postcode;
private String sex;
private List<Orders> orderlist = new ArrayList<Orders>();
<resultMap id="result" class="customer">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="postcode" column="postcode"/>
<result property="sex" column="sex"/>
<result property="orderlist" column="id" select="orders.findByCustomerId"/>
</resultMap>
在這個映射中,為了查詢客戶的時候,能查詢到相關的訂單,能夠在映射
orderlist屬性的時候,將其指向另外一個查詢
orders.findByCustomerId,這個查詢是以Customer的
id為參數來查詢的。
select="orders.findByCustomerId"這個查詢定義例如以下:
<select id="findByCustomerId" resultMap="result_base" parameterClass="long">
select * from orders where customerId = #value#
</select>
原理就是這么簡單,然后依據實際情況,能夠自由實現實體間的關聯關系。
14、iBatis的分頁查詢
iBatis的分頁有兩種方式。一點都不神奇,不要被網上的流言所迷惑。
第一種方式:結果集篩選分頁。
先運行部分頁的SQL查詢語句,然后得到一個ResultSet。然后依據分頁范圍選擇有效的記錄填充到對象中。終於以集合的形式返回。
對於10w條一下的記錄的表。不存在性能問題。假設存在,你能夠選擇第二中方式。
另外一種方式:SQL分頁,通過組裝分頁類型的SQL來實現分頁。這個關鍵在於分頁參數的傳遞和分頁SQL的構建。分頁SQL構件每種數據庫都不一樣,不說了。分頁參數的傳遞卻能夠通用。
我主張用map分裝入參,連同分頁參數一塊傳遞進來,就搞定了。假設原來沒有考慮到分頁,而用的是對象做參數,則能夠通過apache 的 beanutils組件來實現一個object到map之間的轉換工具,問題迎刃而解。
當然。這還不是分頁查詢應用的最高境地。思考。分頁須要計算一個總記錄數,記錄數運行的sql返回值是count(?),條件是除了分頁以外的條件,因此應該將查詢SQL靜態分開,以MySQL為例,能夠將查詢分為查什么,和什么條件兩部分,在條件部分對分頁參數進行動態推斷,假設分頁參數就不分頁,假設有則分頁。這樣最后僅僅須要兩個組裝的sql就能夠計算總數和分頁查詢了。大大簡化了問題的難度。
Oracle的解決思路也一樣,不一樣的地方就是拼裝分頁SQL改變了。
15、運行存儲過程的配置
SQL Map 通過<procedure>元素支持存儲過程。
以下的樣例說明怎樣使用具有輸出參數
的存儲過程。
<parameterMap id="swapParameters" class="map">
<parameter property="email1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>
<parameter property="email2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>
</parameterMap>
<procedure id="swapEmailAddresses" parameterMap="swapParameters">
{call swap_email_address (?, ?)}
</procedure>
調用上面的存儲過程將同一時候互換兩個字段(數據庫表)和參數對象(Map)中的兩個 email地址。
假設參數的 mode 屬性設為 INOUT 或 OUT,則參數對象的值被改動。否則保持不變。
注意。要確保始終僅僅使用 JDBC 標准的存儲過程語法。參考 JDBC 的 CallableStatement
文檔以獲得更具體的信息。
16、就是iBatis中各種id的命名了,這個看起來小菜一碟,可是搞砸了會非常痛苦。建議假設有DAO層的話。DAO接口的名字和SQL語句id的名字保持一致。同一時候,在DAO中將save和update封裝為一個方法(從Hibernate中學來的),這是非常好的。也能夠直接在SQL層將插入和更新柔和在一塊,太復雜,有點影響效率。這見機行事了。
另外Spring提供了各種數據操作模板。通過模板。擦做數據也就是“一句話”的問題,寫個DAO還有必要么。尤其對iBatis來說,根本沒有必要。
這樣。就須要在領域活動層的設計上下功夫了。
18、偷懶的最高境地,讓程序去干哪里80%的體力活。自己只把把關。不論什么反復的活動都有規律可循的。一旦發現了當中的規律,你就能夠想辦法把自己從中解脫出來。
iBatis也不例外,每一個表都有增刪改查、分頁等操作。相應在每一個DAO方法上亦如此。
能夠通過數據庫生成sqlmap、entity、dao,然后將這些東西改吧改吧就完畢大部分的工作量。本人已經實現過了,當然開發這個工具的前提是你對iBatis有深入研究和理解。
-------------------------------------------------
以下是iBatis開發指南中內容:
附錄:easy出錯的地方
本附錄是譯者加入的,列出了剛開始學習的人easy出錯的地方。作為完畢高速入門課程后的學習
筆記,能夠讓剛開始學習的人少走些彎路。僅供參考。
1) 在 parameterMap 和 resultMap 中。字段數據類型是 java.sql.Types 類定義的常量名
稱。經常使用的數據類型包含 BLOB,CHAR,CLOB,DATE,LONGVARBINARY。
INTEGER,NULL,NUMERIC,TIME,TIMESTAMP 和 VARCHAR 等。
2) 對於數據表中 NULLABLE 的字段,必須在 parameterMap 和 resultMap 中指定字段
的數據類型。
3) 對於數據類型是 DATE,CLOB 或 BLOB 的字段。最好在 parameterMap 和 resultMap中指定數據類型。
4) 對於二進制類型的數據。能夠將 LONGVARBINARY 映射成 byte[]。
5) 對於文本類型較大的數據,能夠將 CLOB 映射成 String。
6) Java Bean 必須擁有缺省的構造器(即無參數的構造器)。
7) Java Bean 最好實現 Serializable 接口,以備應用的進一步擴展。
本人覺得:盡量避免在每一個入參后面附加參數的類型。以保持配置簡潔。而且本人在長期開發中。沒有發現必需要那么做。