Mybatis 動態 SQL 詳解
MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其他類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句有多麼痛苦。拼接的時候要確保不能忘了必要的空格,還要注意省掉列名列表最後的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。
通常使用動態 SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態 SQL 語言來改進這種情形,這種語言可以被用在任意的 SQL 對映語句中。
動態 SQL 元素和使用 JSTL 或其他類似基於 XML 的文字處理器相似。在 MyBatis 之前的版本中,有很多的元素需要來了解。MyBatis 3 大大提升了它們,現在用不到原先一半的元素就可以了。MyBatis 採用功能強大的基於 OGNL 的表示式來消除其他元素。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if
動態 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”和“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>
choose, when, otherwise
有些時候,我們不想用到所有的條件語句,而只想從中擇其一二。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
還是上面的例子,但是這次變為提供了“title”就按“title”查詢,提供了“author”就按“author”查詢,若兩者都沒有提供,就返回所有符合條件的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>
trim, where, set
前面幾個例子已經合宜地解決了一個臭名昭著的動態 SQL 問題。現在考慮回到“if”示例,這次我們將“ACTIVE = 1”也設定成動態的條件,看看會發生什麼。
<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>
</select>
如果這些條件沒有一個能匹配上將會怎樣?最終這條 SQL 會變成這樣:
SELECT * FROM BLOG
WHERE
這會導致查詢失敗。如果僅僅第二個條件匹配又會怎樣?這條 SQL 最終會是這樣:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
這個查詢也會失敗。這個問題不能簡單的用條件句式來解決,如果你也曾經被迫這樣寫過,那麼你很可能從此以後都不想再這樣去寫了。
MyBatis 有一個簡單的處理,這在90%的情況下都會有用。而在不能使用的地方,你可以自定義處理方式來令其正常工作。一處簡單的修改就能得到想要的效果:
<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 元素知道只有在一個以上的if條件有值的情況下才去插入“WHERE”子句。而且,若最後的內容是“AND”或“OR”開頭的,where 元素也知道如何將他們去除。
如果 where 元素沒有按正常套路出牌,我們還是可以通過自定義 trim 元素來定製我們想要的功能。比如,和 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 關鍵字,同時也會消除無關的逗號,因為用了條件語句之後很可能就會在生成的賦值語句的後面留下這些逗號。
若你對等價的自定義 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>
foreach 元素的功能是非常強大的,它允許你指定一個集合,宣告可以用在元素體內的集合項和索引變數。它也允許你指定開閉匹配的字串以及在迭代中間放置分隔符。這個元素是很智慧的,因此它不會偶然地附加多餘的分隔符。
注意 你可以將任何可迭代物件(如列表、集合等)和任何的字典或者陣列物件傳遞給foreach作為集合引數。當使用可迭代物件或者陣列時,index是當前迭代的次數,item的值是本次迭代獲取的元素。當使用字典(或者Map.Entry物件的集合)時,index是鍵,item是值。
到此我們已經完成了涉及 XML 配置檔案和 XML 對映檔案的討論。下一部分將詳細探討 Java API,這樣才能從已建立的對映中獲取最大利益。
bind
bind 元素可以從 OGNL 表示式中建立一個變數並將其繫結到上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
Multi-db vendor support
一個配置了“_databaseId”變數的 databaseIdProvider 對於動態程式碼來說是可用的,這樣就可以根據不同的資料庫廠商構建特定的語句。比如下面的例子:
<insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>
動態 SQL 中可插拔的指令碼語言
MyBatis 從 3.2 開始支援可插拔的指令碼語言,因此你可以在插入一種語言的驅動(language driver)之後來寫基於這種語言的動態 SQL 查詢。
可以通過實現下面介面的方式來插入一種語言:
public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}
一旦有了自定義的語言驅動,你就可以在 mybatis-config.xml 檔案中將它設定為預設語言:
<typeAliases>
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
<setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>
除了設定預設語言,你也可以針對特殊的語句指定特定語言,這可以通過如下的 lang 屬性來完成:
<select id="selectBlog" lang="myLanguage">
SELECT * FROM BLOG
</select>
或者在你正在使用的對映中加上註解 @Lang 來完成:
public interface Mapper {
@Lang(MyLanguageDriver.class)
@Select("SELECT * FROM BLOG")
List<Blog> selectBlog();
}
相關文章
- MyBatis 動態 SQLMyBatisSQL
- mybatis動態SQLMyBatisSQL
- Mybatis深入瞭解(五)----動態SQLMyBatisSQL
- 1 Mybatis動態SQLMyBatisSQL
- Mybatis--動態SQLMyBatisSQL
- MyBatis(七) 動態SQLMyBatisSQL
- MyBatis基礎:MyBatis動態SQL(3)MyBatisSQL
- mybatis動態sql總結MyBatisSQL
- Mybatis-06 動態SqlMyBatisSQL
- MyBatis4:動態SQLMyBatisSQL
- Mybatis介紹之 動態SQLMyBatisSQL
- Java-Mybatis動態SQL整理JavaMyBatisSQL
- MyBatis對動態SQL的支援MyBatisSQL
- mybatis動態sql與分頁MyBatisSQL
- MyBatis框架之SQL對映和動態SQLMyBatis框架SQL
- Mybatis 動態執行SQL語句MyBatisSQL
- 【MyBatis學習總結 (五),動態SQL】MyBatisSQL
- Mybatis where 1=1 動態sql問題MyBatisSQL
- Mybatis學習筆記(6)-動態SQLMyBatis筆記SQL
- mybatis入門基礎(五)----動態SQLMyBatisSQL
- MyBatis學習 之 三、動態SQL語句MyBatisSQL
- 學習MyBatis必知必會(7)~註解開發、動態SQLMyBatisSQL
- 例項詳解如何構建動態SQL語句SQL
- MyBatis系列(七):MyBatis動態Sql之choose,where,set標籤的用法MyBatisSQL
- Mybatis(一)Porxy動態代理和sql解析替換MyBatisSQL
- MyBatis標籤實現的動態SQL語句MyBatisSQL
- Mybatis詳解MyBatis
- MyBatis 動態 SQL 最全教程,這樣寫 SQL 太優雅了!MyBatisSQL
- Mybatis系列全解(八):Mybatis的9大動態SQL標籤你知道幾個?提前致女神!MyBatisSQL
- MyBatis從入門到精通(六):MyBatis動態Sql之if標籤的用法MyBatisSQL
- JDK動態代理詳解JDK
- MyBatis引數傳入集合之foreach動態sqlMyBatisSQL
- MyBatis詳解(一)MyBatis
- MyBatis 配置詳解MyBatis
- 動態sql和利用動態sql解決資料字典的讀取SQL
- 好程式設計師分享MyBatis之動態SQL語句程式設計師MyBatisSQL
- MyBatis進階--介面代理方式實現Dao 和動態SQLMyBatisSQL
- Hive動態分割槽詳解Hive