MyBatis 最強大的特性之一就是它的動態語句功能。如果您以前有使用 JDBC 或者類似框架的經歷,您就會明白把 SQL 語句條件連接在一起是多么的痛苦,要確保不能忘記空格或者不要在 columns 列后面省略一個逗號等。動態語句能夠完全解決掉這些痛苦。
盡管與動態 SQL 一起工作不是在開一個 party,但是 MyBatis 確實能通過在任何映射 SQL 語句中使用強大的動態 SQL 來改進這些狀況。
動態 SQL 元素對於任何使用過 JSTL 或者類似於 XML 之類的文本處理器的人來說,都是非常熟悉的。在上一版本中,需要了解和學習非常多的元素,但在 MyBatis3 中有了許多的改進,現在只剩下差不多二分之一的元素。MyBatis 使用了基於強大的 OGNL 表達式來消除了大部分元素。
• if
• choose(when,otherwise)
• trim(where,set)
• foreach
if 元素
動態 SQL 最常做的事就是有條件地包括 where 子句。例如:
<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state='ACTIVE' <if test="title!=null"> AND title like #{title} </if> </select>
這條語句提供一個帶功能性的可選的文字。如果您沒有傳入標題,那么將返回所有激活的 Blog。如果您傳入了一個標題,那它就會查找與這個標題匹配的 Blog(在這種情況下,您的參數值可能需要包括任何 masking 或者通配符)。
如果我們想要可選地根據標題或者作者查詢怎么辦?首先,我把語句的名稱稍稍改一下,使得看起來更直觀。然后簡單地加上另外一個條件。
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state='ACTIVE' <if test="title!=null"> AND title like #{title} </if> <if test="author!=nullandauthor.name!=null"> AND title like #{author.name} </if> </select>
choose,when,otherwise元素
有時候我們不想應用所有的條件,而是想從多個選項中選擇一個。與 java 中的 switch 語句相似,MyBatis 提供了一個 choose 元素。
讓我們繼續使用上面的例子,但這次我們只搜索有提供查詢標題的,或者只搜索有提供查詢作者的數據。如果兩者都沒有提供,那只返回加精的 Blog(可能是管理員有選擇性的查詢,而不是返回大量無意義的隨機的 Blog)。
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state='ACTIVE' <choose> <when test="title!=null"> AND title like #{title} </when> <when test="author!=nullandauthor.name!=null"> AND title like #{author.name} </when> <otherwise> AND featured=1 </otherwise> </choose> </select>
trim,where,set元素
考慮一下我們上面提到的 'if' 的例子中,如果現在我們把'ACTIVE=1'也做為條件,會發生什么情況。
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE <if test="state!=null"> state = #{state} </if> <if test="title!=null"> AND title like #{title} </if> <if test="author!=null and author.name!=null"> AND title like#{author.name} </if> </select>
如果我們一個條件都不設置,會發生什么呢?語句最終可能會變成這個樣子:
SELECT * FROM BLOG
WHERE
這將會執行失敗。如果只有第二個條件滿足呢?語句最終會變成這樣:
SELECT * FROM BLOG
WHERE
AND title like 'someTitle'
這同樣會執行失敗。這個問題僅用條件很難簡單地解決,如果您已經這么寫了,那您可能以后永遠都不想犯這樣的錯了。
MyBatis 有個簡單的方案能解決這里面 90% 的問題。如果 where 沒有出現的時候,您可以自定一個。修改一下,就能完全解決:
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state!=null"> state=#{state} </if> <if test="title!=null"> AND title like #{title} </if> <if test="author!=nullandauthor.name!=null"> AND title like #{author.name} </if> </where> </select>
where 元素知道插入 "where" 如果它包含的標簽中有內容返回的話。此外,如果返回的內容以 "AND" 或者 "OR" 開頭,它會把 "AND" 或者 "OR" 去掉。
如果 where 元素的行為並沒有完全按您想象的那樣,您還可以使用 trim 元素來自定義。例如,下面的 trim 與 where 元素實現相同功能:
<trim prefix="WHERE" prefixOverrides="AND|OR"> … </trim>
overrides 屬性使用了管道分隔的文本列表來覆寫,而且它的空白也不能忽略的。這樣的結果是移出了指定在 overrides 屬性里字符,而在開頭插入 prefix 屬性中指定的字符。
下面的兩種配置方法效果是一樣的:
<selectid="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG <where> <if test="state!=null"> state=#{state} </if> <if test="title!=null"> AND title like #{title} </if> <if test="author!=null and author.name!=null"> AND title like #{author.name} </if> </where> </select> <selectid="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG <trim prefix="WHERE" prefixOverrides="AND |OR"> <if test="state!=null"> state = #{state} </if> <if test="title!=null"> AND title like #{title} </if> <if test="author!=null and author.name!=null"> AND title like #{author.name} </if> </trim> </select>
下面的使用 SET 元素也類似。
在動態 update 語句里相似的解決方式叫做 set,這個 set 元素能夠動態地更新列。例如:
<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author"> update Author <set> <if test="username!=null">username=#{username},</if> <if test="password!=null">password=#{password},</if> <if test="email!=null">email=#{email},</if> <if test="bio!=null">bio=#{bio}</if> </set> whereid=#{id} </update>
set 元素將動態的配置 SET 關鍵字,也用來剔除追加到條件末尾的任何不相關的逗號。
您想知道等同的 trim 元素該怎么寫吧,它就像這樣:
<trim prefix="SET" suffixOverrides=","> … </trim>
注意這種情況,我們剔除了一個后綴,同時追加了一個前綴。
Foreach 元素
另一個動態 SQL 經常使用到的功能是集合迭代,通常用在 IN 條件句。例如:
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="("separator=","close=")"> #{item} </foreach> </select>
對上面這個映射 SQL 語句的 java 調用代碼示例如下:
List<Integer> authorIdList = new ArrayList<Integer>(); postList.add(2); postList.add(3); postList.add(4); List<Post> postList =(List<Post>)session.selectList("selectPostIn",postList); //將會查詢出ID是2、3、4的文章。
foreach 元素非常強大,允許您指定一個集合,申明能夠用在元素體內的項和索引變量。也允許您指定開始和結束的字符,也可以加入一個分隔符到迭代器之間。這個元素的聰明之處在於它不會意外地追加額外的分隔符。
注意:您可以把一個 List 實例或者一個數組作為一個參數對象傳遞給 MyBatis。如果您這么做,MyBatis 會自動將它包裝成一個 Map,並以名字作為 key。List 實例會以 "list" 作為key,array 實例會以 "array" 作為key。