動(dòng)態(tài) SQL

MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL。如果你有使用 JDBC 或其它類似框架的經(jīng)驗(yàn)鬼廓,你就能體會(huì)到根據(jù)不同條件拼接 SQL 語句的痛苦。例如拼接時(shí)要確保不能忘記添加必要的空格,還要注意去掉列表最后一個(gè)列名的逗號(hào)猪半。利用動(dòng)態(tài) SQL 這一特性可以徹底擺脫這種痛苦降铸。
雖然在以前使用動(dòng)態(tài) SQL 并非一件易事锉屈,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強(qiáng)大的動(dòng)態(tài) SQL 語言得以改進(jìn)這種情形。
動(dòng)態(tài) SQL 元素和 JSTL 或基于類似 XML 的文本處理器相似垮耳。在 MyBatis 之前的版本中颈渊,有很多元素需要花時(shí)間了解。MyBatis 3 大大精簡(jiǎn)了元素種類终佛,現(xiàn)在只需學(xué)習(xí)原來一半的元素便可俊嗽。MyBatis 采用功能強(qiáng)大的基于 OGNL 的表達(dá)式來淘汰其它大部分元素。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

動(dòng)態(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都會(huì)返回牙捉;反之若傳入了“title”竹揍,那么就會(huì)對(duì)“title”一列進(jìn)行模糊查找并返回 BLOG 結(jié)果(細(xì)心的讀者可能會(huì)發(fā)現(xiàn),“title”參數(shù)值是可以包含一些掩碼或通配符的)邪铲。

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

<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

有時(shí)我們不想應(yīng)用到所有的條件語句,而只想從中擇其一項(xiàng)。針對(duì)這種情況被饿,MyBatis 提供了 choose 元素四康,它有點(diǎn)像 Java 中的 switch 語句。

還是上面的例子狭握,但是這次變?yōu)樘峁┝恕皌itle”就按“title”查找闪金,提供了“author”就按“author”查找的情形,若兩者都沒有提供论颅,就返回所有符合條件的 BLOG(實(shí)際情況可能是由管理員按一定策略選出 BLOG 列表毕泌,而不是返回大量無意義的隨機(jī)結(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

前面幾個(gè)例子已經(jīng)合宜地解決了一個(gè)臭名昭著的動(dòng)態(tài) SQL 問題⌒崂保現(xiàn)在回到“if”示例撼泛,這次我們將“ACTIVE = 1”也設(shè)置成動(dòng)態(tài)的條件,看看會(huì)發(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>

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

SELECT * FROM BLOG
WHERE

這會(huì)導(dǎo)致查詢失敗。如果僅僅第二個(gè)條件匹配又會(huì)怎樣蛙奖?這條 SQL 最終會(huì)是這樣:

SELECT * FROM BLOG
WHERE 
AND title like ‘someTitle’

這個(gè)查詢也會(huì)失敗潘酗。這個(gè)問題不能簡(jiǎn)單地用條件句式來解決,如果你也曾經(jīng)被迫這樣寫過雁仲,那么你很可能從此以后都不會(huì)再寫出這種語句了仔夺。

MyBatis 有一個(gè)簡(jiǎn)單的處理,這在 90% 的情況下都會(huì)有用攒砖。而在不能使用的地方缸兔,你可以自定義處理方式來令其正常工作。一處簡(jiǎn)單的修改就能達(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 元素只會(huì)在至少有一個(gè)子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句吹艇。而且惰蜜,若語句的開頭為“AND”或“OR”,where 元素也會(huì)將它們?nèi)コ?/p>

如果 where 元素沒有按正常套路出牌受神,我們可以通過自定義 trim 元素來定制 where 元素的功能抛猖。比如,和 where 元素等價(jià)的自定義 trim 元素為:

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

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

類似的用于動(dòng)態(tài)更新語句的解決方案叫做 set撑碴。set 元素可以用于動(dòng)態(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 元素會(huì)動(dòng)態(tài)前置 SET 關(guān)鍵字驮履,同時(shí)也會(huì)刪掉無關(guān)的逗號(hào),因?yàn)橛昧藯l件語句之后很可能就會(huì)在生成的 SQL 語句的后面留下這些逗號(hào)廉嚼。(譯者注:因?yàn)橛玫氖恰癷f”元素玫镐,若最后一個(gè)“if”沒有匹配上而前面的匹配上,SQL 語句的最后就會(huì)有一個(gè)逗號(hào)遺留)

若你對(duì) set 元素等價(jià)的自定義 trim 元素的代碼感興趣怠噪,那這就是它的真面目:

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

注意這里我們刪去的是后綴值恐似,同時(shí)添加了前綴值。

foreach

動(dòng)態(tài) SQL 的另外一個(gè)常用的操作需求是對(duì)一個(gè)集合進(jìn)行遍歷傍念,通常是在構(gòu)建 IN 條件語句的時(shí)候矫夷。比如:

<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 元素的功能非常強(qiáng)大,它允許你指定一個(gè)集合憋槐,聲明可以在元素體內(nèi)使用的集合項(xiàng)(item)和索引(index)變量双藕。它也允許你指定開頭與結(jié)尾的字符串以及在迭代結(jié)果之間放置分隔符。這個(gè)元素是很智能的阳仔,因此它不會(huì)偶然地附加多余的分隔符忧陪。

注意 你可以將任何可迭代對(duì)象(如 List、Set 等)近范、Map 對(duì)象或者數(shù)組對(duì)象傳遞給 foreach 作為集合參數(shù)嘶摊。當(dāng)使用可迭代對(duì)象或者數(shù)組時(shí),index 是當(dāng)前迭代的次數(shù)评矩,item 的值是本次迭代獲取的元素叶堆。當(dāng)使用 Map 對(duì)象(或者 Map.Entry 對(duì)象的集合)時(shí),index 是鍵斥杜,item 是值虱颗。

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

bind

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

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

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

一個(gè)配置了“_databaseId”變量的 databaseIdProvider 可用于動(dòng)態(tài)代碼中弱恒,這樣就可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商構(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>

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

MyBatis 從 3.2 開始支持可插拔腳本語言,這允許你插入一種腳本語言驅(qū)動(dòng)返弹,并基于這種語言來編寫動(dòng)態(tài) SQL 查詢語句锈玉。

可以通過實(shí)現(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ū)動(dòng),你就可以在 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)語言义起,你也可以針對(duì)特殊的語句指定特定語言拉背,可以通過如下的lang屬性來完成:

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

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

public interface Mapper {
  @Lang(MyLanguageDriver.class)
  @Select("SELECT * FROM BLOG")
  List<Blog> selectBlog();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椅棺,一起剝皮案震驚了整個(gè)濱河市犁罩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌两疚,老刑警劉巖床估,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異诱渤,居然都是意外死亡丐巫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門勺美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來递胧,“玉大人,你說我怎么就攤上這事赡茸《衅ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我格郁,道長(zhǎng),這世上最難降的妖魔是什么舷蒲? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮友多,結(jié)果婚禮上牲平,老公的妹妹穿的比我還像新娘。我一直安慰自己域滥,他們只是感情好纵柿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著启绰,像睡著了一般昂儒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上委可,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天渊跋,我揣著相機(jī)與錄音,去河邊找鬼着倾。 笑死拾酝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卡者。 我是一名探鬼主播蒿囤,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼崇决!你這毒婦竟也來了材诽?” 一聲冷哼從身側(cè)響起底挫,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脸侥,沒想到半個(gè)月后建邓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湿痢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年涝缝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扑庞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片譬重。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖罐氨,靈堂內(nèi)的尸體忽然破棺而出臀规,到底是詐尸還是另有隱情,我是刑警寧澤栅隐,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布塔嬉,位于F島的核電站,受9級(jí)特大地震影響租悄,放射性物質(zhì)發(fā)生泄漏谨究。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一泣棋、第九天 我趴在偏房一處隱蔽的房頂上張望胶哲。 院中可真熱鬧,春花似錦潭辈、人聲如沸鸯屿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寄摆。三九已至,卻和暖如春修赞,著一層夾襖步出監(jiān)牢的瞬間婶恼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工柏副, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熙尉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓搓扯,卻偏偏與公主長(zhǎng)得像检痰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锨推,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

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

  • 4.動(dòng)態(tài)SQL MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL铅歼。如果你有使用 JDBC 或其他類似框架的經(jīng)驗(yàn)公壤,...
    王偵閱讀 188評(píng)論 0 0
  • MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL。如果你有使用 JDBC 或其他類似框架的經(jīng)驗(yàn)椎椰,你就能體會(huì)到根據(jù)...
    jackcooper閱讀 488評(píng)論 0 1
  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis 厦幅? MyBatis 是支持定制化 SQL、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,464評(píng)論 0 4
  • Mybatis的強(qiáng)大特性之一便是它的動(dòng)態(tài)SQL慨飘。在實(shí)際開發(fā)中确憨,數(shù)據(jù)庫(kù)的查詢比較復(fù)雜,需要在不同的邏輯中執(zhí)行不同的S...
    Michaelhbjian閱讀 4,170評(píng)論 0 2
  • 《對(duì)話尼克松》這部電影是一部我積存已久的存貨瓤的,最近終于得暇把它看完休弃,原本想寫一個(gè)材料充沛的評(píng)論,現(xiàn)在看來時(shí)間上不太...
    白淵閱讀 1,005評(píng)論 0 50