鍍金池/ 教程/ Java/ MyBatis動態(tài)SQL語句
Mybatis表關(guān)聯(lián)多對一
MyBatis打印輸出SQL語句
Mybatis表關(guān)聯(lián)一對多
mybaits if標簽語句
MyBatis整合Spring MVC
MyBatis動態(tài)SQL語句
MyBatis教程
MyBatis choose(when, otherwise)標簽
Mybatis與Spring集成
MyBatis分頁
MyBatis SqlSessionDaoSupport實例
MyBatis where標簽語句
Mybatis增刪改查(CURD)
Mybatis接口注解
Mybatis trim標簽
Mybatis set標簽
Mybatis 多對多
MyBatis環(huán)境配置及入門

MyBatis動態(tài)SQL語句

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

通常使用動態(tài) SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態(tài) SQL 語言來改進這種情形,這種語言可以被用在任意的 SQL 映射語句中。

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

mybatis 的動態(tài)sql語句是基于OGNL表達式的??梢苑奖愕脑?sql 語句中實現(xiàn)某些邏輯. 總體說來mybatis 動態(tài)SQL 語句主要有以下幾類:
1. if 語句 (簡單的條件判斷)
2. choose (when,otherwize) ,相當于java 語言中的 switch ,與 jstl 中的choose 很類似.
3. trim (對包含的內(nèi)容加上 prefix,或者 suffix 等,前綴,后綴)
4. where (主要是用來簡化sql語句中where條件判斷的,能智能的處理 and or ,不必擔心多余導(dǎo)致語法錯誤)
5. set (主要用于更新時)
6. foreach (在實現(xiàn) mybatis in 語句查詢時特別有用)

if

動態(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>

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

如果想可選地通過“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 語句。

還是上面的例子,但是這次變?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 問題?,F(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>

如果這些條件沒有一個能匹配上將會怎樣?最終這條 SQL 會變成這樣:

SELECT * FROM BLOG
WHERE

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

SELECT * FROM BLOG
WHERE 
AND title like yiibai.com

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

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”子句。而且,若最后的內(nèi)容是“AND”或“OR”開頭的,where 元素也知道如何將他們?nèi)コ?

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

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

prefixOverrides 屬性會忽略通過管道分隔的文本序列(注意此例中的空格也是必要的)。它帶來的結(jié)果就是所有在 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)的逗號,因為用了條件語句之后很可能就會在生成的賦值語句的后面留下這些逗號。

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

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

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

foreach

動態(tài) SQL 的另外一個常用的必要操作是需要對一個集合進行遍歷,通常是在構(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)的集合項和索引變量。它也允許你指定開閉匹配的字符串以及在迭代中間放置分隔符。這個元素是很智能的,因此它不會偶然地附加多余的分隔符。

注意 你可以將一個 List 實例或者數(shù)組作為參數(shù)對象傳給 MyBatis,當你這么做的時候,MyBatis 會自動將它包裝在一個 Map 中并以名稱為鍵。List 實例將會以“l(fā)ist”作為鍵,而數(shù)組實例的鍵將是“array”。

到此我們已經(jīng)完成了涉及 XML 配置文件和 XML 映射文件的討論。下一部分將詳細探討 Java API,這樣才能從已創(chuàng)建的映射中獲取最大利益。

bind

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

<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 對于動態(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ū)動(language driver)之后來寫基于這種語言的動態(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);
}

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

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

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

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

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

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