http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

動態(tài) SQL


MyBatis 的強大特性之一便是它的動態(tài) SQL。如果你有使用 JDBC 或其它類似框架的經(jīng)驗,你就能體會到根據(jù)不同條件拼接 SQL 語句的痛苦沮明。例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態(tài) SQL 這一特性可以徹底擺脫這種痛苦宰僧。


雖然在以前使用動態(tài) SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強大的動態(tài) SQL 語言得以改進(jìn)這種情形观挎。


動態(tài) SQL 元素和 JSTL 或基于類似 XML 的文本處理器相似琴儿。在 MyBatis 之前的版本中,有很多元素需要花時間了解嘁捷。MyBatis 3 大大精簡了元素種類造成,現(xiàn)在只需學(xué)習(xí)原來一半的元素便可。MyBatis 采用功能強大的基于 OGNL 的表達(dá)式來淘汰其它大部分元素雄嚣。

if

choose (when, otherwise)

trim (where, set)

foreach

if

動態(tài) SQL 通常要做的事情是根據(jù)條件包含 where 子句的一部分晒屎。比如:

<select id="findActiveBlogWithTitleLike"? ? resultType="Blog">? SELECT * FROM BLOG

? WHERE state = ‘ACTIVE’

? <if test="title != null">? ? AND title like #{title}

? </if></select>

這條語句提供了一種可選的查找文本功能。如果沒有傳入“title”,那么所有處于“ACTIVE”狀態(tài)的BLOG都會返回鼓鲁;反之若傳入了“title”履肃,那么就會對“title”一列進(jìn)行模糊查找并返回 BLOG 結(jié)果(細(xì)心的讀者可能會發(fā)現(xiàn),“title”參數(shù)值是可以包含一些掩碼或通配符的)坐桩。

如果希望通過“title”和“author”兩個參數(shù)進(jìn)行可選搜索該怎么辦呢尺棋?首先,改變語句的名稱讓它更具實際意義绵跷;然后只要加入另一個條件即可膘螟。

<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

有時我們不想應(yīng)用到所有的條件語句,而只想從中擇其一項碾局。針對這種情況荆残,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句净当。

還是上面的例子内斯,但是這次變?yōu)樘峁┝恕皌itle”就按“title”查找,提供了“author”就按“author”查找的情形像啼,若兩者都沒有提供俘闯,就返回所有符合條件的 BLOG(實際情況可能是由管理員按一定策略選出 BLOG 列表,而不是返回大量無意義的隨機結(jié)果)忽冻。

<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

前面幾個例子已經(jīng)合宜地解決了一個臭名昭著的動態(tài) SQL 問題≌胬剩現(xiàn)在回到“if”示例,這次我們將“ACTIVE = 1”也設(shè)置成動態(tài)的條件僧诚,看看會發(fā)生什么遮婶。

<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>

如果這些條件沒有一個能匹配上會發(fā)生什么?最終這條 SQL 會變成這樣:

SELECT * FROM BLOG

WHERE

這會導(dǎo)致查詢失敗湖笨。如果僅僅第二個條件匹配又會怎樣旗扑?這條 SQL 最終會是這樣:

SELECT * FROM BLOG

WHERE

AND title like ‘someTitle’

這個查詢也會失敗。這個問題不能簡單地用條件句式來解決慈省,如果你也曾經(jīng)被迫這樣寫過臀防,那么你很可能從此以后都不會再寫出這種語句了。

MyBatis 有一個簡單的處理辫呻,這在 90% 的情況下都會有用清钥。而在不能使用的地方,你可以自定義處理方式來令其正常工作放闺。一處簡單的修改就能達(dá)到目的:

<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?元素只會在至少有一個子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句。而且缕坎,若語句的開頭為“AND”或“OR”怖侦,where?元素也會將它們?nèi)コ?/p>

如果?where?元素沒有按正常套路出牌,我們可以通過自定義 trim 元素來定制?where?元素的功能。比如匾寝,和?where?元素等價的自定義 trim 元素為:

<trim prefix="WHERE" prefixOverrides="AND |OR ">? ...</trim>

prefixOverrides?屬性會忽略通過管道分隔的文本序列(注意此例中的空格也是必要的)搬葬。它的作用是移除所有指定在?prefixOverrides?屬性中的內(nèi)容,并且插入?prefix?屬性中指定的內(nèi)容艳悔。

類似的用于動態(tài)更新語句的解決方案叫做?set急凰。set?元素可以用于動態(tài)包含需要更新的列,而舍去其它的猜年。比如:

<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?元素會動態(tài)前置 SET 關(guān)鍵字,同時也會刪掉無關(guān)的逗號乔外,因為用了條件語句之后很可能就會在生成的 SQL 語句的后面留下這些逗號床三。(譯者注:因為用的是“if”元素,若最后一個“if”沒有匹配上而前面的匹配上杨幼,SQL 語句的最后就會有一個逗號遺留)

若你對?set?元素等價的自定義 trim 元素的代碼感興趣撇簿,那這就是它的真面目:

<trim prefix="SET" suffixOverrides=",">? ...</trim>

注意這里我們刪去的是后綴值,同時添加了前綴值差购。

foreach

動態(tài) SQL 的另外一個常用的操作需求是對一個集合進(jìn)行遍歷四瘫,通常是在構(gòu)建 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?元素的功能非常強大欲逃,它允許你指定一個集合莲组,聲明可以在元素體內(nèi)使用的集合項(item)和索引(index)變量。它也允許你指定開頭與結(jié)尾的字符串以及在迭代結(jié)果之間放置分隔符暖夭。這個元素是很智能的锹杈,因此它不會偶然地附加多余的分隔符。

注意?你可以將任何可迭代對象(如 List迈着、Set 等)竭望、Map 對象或者數(shù)組對象傳遞給?foreach?作為集合參數(shù)。當(dāng)使用可迭代對象或者數(shù)組時裕菠,index 是當(dāng)前迭代的次數(shù)咬清,item 的值是本次迭代獲取的元素。當(dāng)使用 Map 對象(或者 Map.Entry 對象的集合)時奴潘,index 是鍵旧烧,item 是值。

到此我們已經(jīng)完成了涉及 XML 配置文件和 XML 映射文件的討論画髓。下一章將詳細(xì)探討 Java API掘剪,這樣就能提高已創(chuàng)建的映射文件的利用效率。

bind

bind?元素可以從 OGNL 表達(dá)式中創(chuàng)建一個變量并將其綁定到上下文奈虾。比如:

<select id="selectBlogsLike" resultType="Blog">? <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />? SELECT * FROM BLOG

? WHERE title LIKE #{pattern}</select>


多數(shù)據(jù)庫支持

一個配置了“_databaseId”變量的 databaseIdProvider 可用于動態(tài)代碼中夺谁,這樣就可以根據(jù)不同的數(shù)據(jù)庫廠商構(gòu)建特定的語句廉赔。比如下面的例子:

<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>

動態(tài) SQL 中的可插拔腳本語言

MyBatis 從 3.2 開始支持可插拔腳本語言,這允許你插入一種腳本語言驅(qū)動匾鸥,并基于這種語言來編寫動態(tài) SQL 查詢語句蜡塌。

可以通過實現(xiàn)以下接口來插入一種語言:

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);}

一旦設(shè)定了自定義語言驅(qū)動,你就可以在 mybatis-config.xml 文件中將它設(shè)置為默認(rèn)語言:

<typeAliases>? <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/></typeAliases><settings>? <setting name="defaultScriptingLanguage" value="myLanguage"/></settings>

除了設(shè)置默認(rèn)語言勿负,你也可以針對特殊的語句指定特定語言馏艾,可以通過如下的?lang?屬性來完成:

<select id="selectBlog" lang="myLanguage">? SELECT * FROM BLOG</select>

或者,如果你使用的是映射器接口類奴愉,在抽象方法上加上?@Lang?注解即可:

public interface Mapper {? @Lang(MyLanguageDriver.class)? @Select("SELECT * FROM BLOG")? List<Blog> selectBlog();}

注意?可以將 Apache Velocity 作為動態(tài)語言來使用琅摩,更多細(xì)節(jié)請參考 MyBatis-Velocity 項目。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躁劣,一起剝皮案震驚了整個濱河市迫吐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌账忘,老刑警劉巖志膀,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鳖擒,居然都是意外死亡溉浙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門蒋荚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來戳稽,“玉大人,你說我怎么就攤上這事期升【妫” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵播赁,是天一觀的道長颂郎。 經(jīng)常有香客問我,道長容为,這世上最難降的妖魔是什么乓序? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮坎背,結(jié)果婚禮上替劈,老公的妹妹穿的比我還像新娘。我一直安慰自己得滤,他們只是感情好陨献,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耿戚,像睡著了一般湿故。 火紅的嫁衣襯著肌膚如雪阿趁。 梳的紋絲不亂的頭發(fā)上膜蛔,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天坛猪,我揣著相機與錄音,去河邊找鬼皂股。 笑死墅茉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呜呐。 我是一名探鬼主播就斤,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蘑辑!你這毒婦竟也來了洋机?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤洋魂,失蹤者是張志新(化名)和其女友劉穎绷旗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體副砍,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡衔肢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了豁翎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片角骤。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖心剥,靈堂內(nèi)的尸體忽然破棺而出邦尊,到底是詐尸還是另有隱情,我是刑警寧澤优烧,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布蝉揍,位于F島的核電站,受9級特大地震影響匙隔,放射性物質(zhì)發(fā)生泄漏疑苫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一纷责、第九天 我趴在偏房一處隱蔽的房頂上張望捍掺。 院中可真熱鬧,春花似錦再膳、人聲如沸挺勿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽不瓶。三九已至禾嫉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚊丐,已是汗流浹背熙参。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留麦备,地道東北人孽椰。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像凛篙,于是被迫代替她去往敵國和親黍匾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis 呛梆? MyBatis 是支持定制化 SQL锐涯、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,527評論 0 4
  • MyBatis 理論篇 [TOC] 什么是MyBatis ?MyBatis是支持普通SQL查詢,存儲過程和高級映射...
    有_味閱讀 2,908評論 0 26
  • 50個常用的sql語句Student(S#,Sname,Sage,Ssex) 學(xué)生表Course(C#,Cname...
    哈哈海閱讀 1,235評論 0 7
  • 什么是SQL數(shù)據(jù)庫: SQL是Structured Query Language(結(jié)構(gòu)化查詢語言)的縮寫。SQL是...
    西貝巴巴閱讀 1,822評論 0 10
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽光小鎮(zhèn)少爺閱讀 3,827評論 0 8