目錄
- 動态SQL
- if
- choose、when、otherwise
- trim、where、set
- foreach
- SQL片段
動态 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的架構,你應該能了解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確定不能忘記添加必要的空格,還要注意去掉清單最後一個列名的逗号。利用動态 SQL,可以徹底擺脫這種痛苦。
使用動态 SQL 并非一件易事,但借助可用于任何 SQL 映射語句中的強大的動态 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。
如果你之前用過 JSTL 或任何基于類 XML 語言的文本處理器,你對動态 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間了解大量的元素。借助功能強大的基于 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。
- choose (when, otherwise)
- trim (where, set)
使用動态 SQL 最常見情景是根據條件包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
</select>
這條語句提供了可選的查找文本功能。如果不傳入 “title”,那麼所有處于 “ACTIVE” 狀态的 BLOG 都會傳回;如果傳入了 “title” 參數,那麼就會對 “title” 一列進行模糊查找并傳回對應的 BLOG 結果(細心的讀者可能會發現,“title” 的參數值需要包含查找掩碼或通配符字元)。
如果希望通過 “title” 和 “author” 兩個參數進行可選搜尋該怎麼辦呢?首先,我想先将語句名稱修改成更名副其實的名稱;接下來,隻需要加入另一個條件即可。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
有時候,我們不想使用所有的條件,而隻是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
還是上面的例子,但是政策變為:傳入了 “title” 就按 “title” 查找,傳入了 “author” 就按 “author” 查找的情形。若兩者都沒有傳入,就傳回标記為 featured 的 BLOG(這可能是管理者認為,與其傳回大量的無意義随機 Blog,還不如傳回一些由管理者精選的 Blog)。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
MyBatis 有一個簡單且适合大多數場景的解決辦法。而在其他場景中,可以對其進行自定義以符合需求。而這,隻需要一處簡單的改動:
<select id="findActiveBlogLike" 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 author_name like #{author.name}
</if>
</where>
</select>
where 元素隻會在子元素傳回任何内容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會将它們去除。
如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定制 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的内容,并且插入 prefix 屬性中指定的内容。
用于動态更新語句的類似解決方案叫做 set。set 元素可以用于動态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
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>
where id=#{id}
</update>
這個例子中,set 元素會動态地在行首插入 SET 關鍵字,并會删掉額外的逗号(這些逗号是在使用條件語句給列指派時引入的)。
來看看與 set 元素等價的自定義 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
動态 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>
foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體内使用的集合項(item)和索引(index)變量。它也允許你指定開頭與結尾的字元串以及集合項疊代之間的分隔符。這個元素也不會錯誤地添加多餘的分隔符,看它多智能!
提示 你可以将任何可疊代對象(如 List、Set 等)、Map 對象或者數組對象作為集合參數傳遞給 foreach。當使用可疊代對象或者數組時,index 是目前疊代的序号,item 的值是本次疊代擷取到的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
<select id="selectPostIn" parameterType="map" resultType="domain.blog.Post">
SELECT * FROM POST P
WHERE ID in
<foreach item="id" index="index" collection="ids"
open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!--
id:這個是唯一辨別sql代碼片段
經驗 : 基于單表寫的sql代碼重用性比較高】
: 就是在sql代碼裡不要出現where
-->
<sql id="query_list">
<if test="student!=null and student!=''">
<if test="student.name!=null and student.name!=''">
and name=#{student.name}
</if>
<if test="student.sex!=null and student.sex!=''">
and sex=#{student.sex}
</if>
</if>
</sql>
<!-- 拼接成功的時候,這個where會自動去掉第一個 and -->
<where>
<include refid="query_list"></include>
</where>