Mybatis 動(dòng)態(tài) SQL 詳解


MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL。如果你有使用 JDBC 或其他類似框架的經(jīng)驗(yàn)嘉抒,你就能體會(huì)到根據(jù)不同條件拼接 SQL 語(yǔ)句有多么痛苦零聚。拼接的時(shí)候要確保不能忘了必要的空格,還要注意省掉列名列表最后的逗號(hào)。利用動(dòng)態(tài) SQL 這一特性可以徹底擺脫這種痛苦隶症。

通常使用動(dòng)態(tài) SQL 不可能是獨(dú)立的一部分,MyBatis 當(dāng)然使用一種強(qiáng)大的動(dòng)態(tài) SQL 語(yǔ)言來(lái)改進(jìn)這種情形,這種語(yǔ)言可以被用在任意的 SQL 映射語(yǔ)句中政模。

動(dòng)態(tài) SQL 元素和使用 JSTL 或其他類似基于 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多的元素需要來(lái)了解沿腰。MyBatis 3 大大提升了它們,現(xiàn)在用不到原先一半的元素就可以了览徒。MyBatis 采用功能強(qiáng)大的基于 OGNL 的表達(dá)式來(lái)消除其他元素。

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

if

動(dòng)態(tài) SQL 通常要做的事情是有條件地包含 where 子句的一部分颂龙。比如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG 
  WHERE state = ‘ACTIVE’ 
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

這條語(yǔ)句提供了一個(gè)可選的文本查找類型的功能习蓬。如果沒(méi)有傳入“title”,那么所有處于“ACTIVE”狀態(tài)的BLOG都會(huì)返回措嵌;反之若傳入了“title”躲叼,那么就會(huì)把模糊查找“title”內(nèi)容的BLOG結(jié)果返回(就這個(gè)例子而言,細(xì)心的讀者會(huì)發(fā)現(xiàn)其中的參數(shù)值是可以包含一些掩碼或通配符的)企巢。

如果想可選地通過(guò)“title”和“author”兩個(gè)條件搜索該怎么辦呢枫慷?首先,改變語(yǔ)句的名稱讓它更具實(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ǔ)句笋婿,而只想從中擇其一二誉裆。針對(duì)這種情況,MyBatis 提供了 choose 元素缸濒,它有點(diǎn)像 Java 中的 switch 語(yǔ)句足丢。

還是上面的例子,但是這次變?yōu)樘峁┝恕皌itle”就按“title”查找庇配,提供了“author”就按“author”查找斩跌,若兩者都沒(méi)有提供,就返回所有符合條件的BLOG(實(shí)際情況可能是由管理員按一定策略選出BLOG列表捞慌,而不是返回大量無(wú)意義的隨機(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 問(wèn)題。現(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>

如果這些條件沒(méi)有一個(gè)能匹配上將會(huì)怎樣锻霎?最終這條 SQL 會(huì)變成這樣:

SELECT * FROM BLOG
WHERE

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

SELECT * FROM BLOG
WHERE 
AND title like ‘someTitle’

這個(gè)查詢也會(huì)失敗旋恼。這個(gè)問(wèn)題不能簡(jiǎn)單的用條件句式來(lái)解決吏口,如果你也曾經(jīng)被迫這樣寫過(guò)奄容,那么你很可能從此以后都不想再這樣去寫了。

MyBatis 有一個(gè)簡(jiǎn)單的處理产徊,這在90%的情況下都會(huì)有用昂勒。而在不能使用的地方,你可以自定義處理方式來(lái)令其正常工作舟铜。一處簡(jiǎn)單的修改就能得到想要的效果:

<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 元素知道只有在一個(gè)以上的if條件有值的情況下才去插入“WHERE”子句戈盈。而且,若最后的內(nèi)容是“AND”或“OR”開(kāi)頭的谆刨,where 元素也知道如何將他們?nèi)コ?/p>

如果 where 元素沒(méi)有按正常套路出牌塘娶,我們還是可以通過(guò)自定義 trim 元素來(lái)定制我們想要的功能。比如痊夭,和 where 元素等價(jià)的自定義 trim 元素為:

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

prefixOverrides 屬性會(huì)忽略通過(guò)管道分隔的文本序列(注意此例中的空格也是必要的)刁岸。它帶來(lái)的結(jié)果就是所有在 prefixOverrides 屬性中指定的內(nèi)容將被移除,并且插入 prefix 屬性中指定的內(nèi)容她我。

類似的用于動(dòng)態(tài)更新語(yǔ)句的解決方案叫做 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ì)消除無(wú)關(guān)的逗號(hào)恨狈,因?yàn)橛昧藯l件語(yǔ)句之后很可能就會(huì)在生成的賦值語(yǔ)句的后面留下這些逗號(hào)击敌。

若你對(duì)等價(jià)的自定義 trim 元素的樣子感興趣,那這就應(yīng)該是它的真面目:

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

注意這里我們忽略的是后綴中的值拴事,而又一次附加了前綴中的值。

foreach

動(dòng)態(tài) SQL 的另外一個(gè)常用的必要操作是需要對(duì)一個(gè)集合進(jìn)行遍歷圣蝎,通常是在構(gòu)建 IN 條件語(yǔ)句的時(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)和索引變量牲证。它也允許你指定開(kāi)閉匹配的字符串以及在迭代中間放置分隔符。這個(gè)元素是很智能的关面,因此它不會(huì)偶然地附加多余的分隔符坦袍。

注意 你可以將任何可迭代對(duì)象(如列表、集合等)和任何的字典或者數(shù)組對(duì)象傳遞給foreach作為集合參數(shù)等太。當(dāng)使用可迭代對(duì)象或者數(shù)組時(shí)捂齐,index是當(dāng)前迭代的次數(shù),item的值是本次迭代獲取的元素缩抡。當(dāng)使用字典(或者M(jìn)ap.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>

Multi-db vendor support

一個(gè)配置了“_databaseId”變量的 databaseIdProvider 對(duì)于動(dòng)態(tài)代碼來(lái)說(shuō)是可用的岳悟,這樣就可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商構(gòu)建特定的語(yǔ)句。比如下面的例子:

<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 中可插拔的腳本語(yǔ)言

MyBatis 從 3.2 開(kāi)始支持可插拔的腳本語(yǔ)言泼差,因此你可以在插入一種語(yǔ)言的驅(qū)動(dòng)(language driver)之后來(lái)寫基于這種語(yǔ)言的動(dòng)態(tài) SQL 查詢贵少。

可以通過(guò)實(shí)現(xiàn)下面接口的方式來(lái)插入一種語(yǔ)言:

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

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

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

除了設(shè)置默認(rèn)語(yǔ)言拴驮,你也可以針對(duì)特殊的語(yǔ)句指定特定語(yǔ)言春瞬,這可以通過(guò)如下的 lang 屬性來(lái)完成:

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

或者在你正在使用的映射中加上注解 @Lang 來(lái)完成:

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


參考文章:http://www.mybatis.org/mybatis-3/zh/index.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市套啤,隨后出現(xiàn)的幾起案子宽气,更是在濱河造成了極大的恐慌,老刑警劉巖潜沦,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萄涯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡唆鸡,警方通過(guò)查閱死者的電腦和手機(jī)涝影,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)争占,“玉大人燃逻,你說(shuō)我怎么就攤上這事”酆郏” “怎么了伯襟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)握童。 經(jīng)常有香客問(wèn)我姆怪,道長(zhǎng),這世上最難降的妖魔是什么澡绩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任稽揭,我火速辦了婚禮,結(jié)果婚禮上肥卡,老公的妹妹穿的比我還像新娘溪掀。我一直安慰自己,他們只是感情好召调,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布膨桥。 她就那樣靜靜地躺著蛮浑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪只嚣。 梳的紋絲不亂的頭發(fā)上沮稚,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音册舞,去河邊找鬼蕴掏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛调鲸,可吹牛的內(nèi)容都是我干的盛杰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼藐石,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼即供!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起于微,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逗嫡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后株依,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體驱证,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年恋腕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抹锄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荠藤,死狀恐怖伙单,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哈肖,我是刑警寧澤车份,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牡彻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏出爹。R本人自食惡果不足惜庄吼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望严就。 院中可真熱鬧总寻,春花似錦、人聲如沸梢为。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祟印,卻和暖如春肴沫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蕴忆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工颤芬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人套鹅。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓站蝠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親卓鹿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菱魔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL吟孙、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥(niǎo)慢飛閱讀 5,520評(píng)論 0 4
  • 1 動(dòng)態(tài)SQL# 那么澜倦,問(wèn)題來(lái)了: 什么是動(dòng)態(tài)SQL? 動(dòng)態(tài)SQL有什么作用? 傳統(tǒng)的使用JDBC的方法拔疚,相信大家...
    七寸知架構(gòu)閱讀 18,643評(píng)論 2 58
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法肥隆,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法稚失,繼承相關(guān)的語(yǔ)法栋艳,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,631評(píng)論 18 399
  • 什么是真正意義上的人?首先TA必須是一個(gè)獨(dú)立的人凿宾,有自己獨(dú)立的思想矾屯,獨(dú)立的精神境界的人;其次初厚,TA必須有一定的文化...
    娛情飯桶說(shuō)閱讀 342評(píng)論 0 4
  • 風(fēng)吹紅葉黃花 細(xì)雨輕灑 憑添寒意幾許 雨落花 驚起歲月漣漪 斑駁陸離 信步青石小路 秋思 染上了眉宇 推杯換盞 酒...
    秦筱千閱讀 237評(píng)論 5 3