MyBatis 的強大特性之一即是它的動態 SQL。若是你有使用 JDBC 或其餘相似框架的經驗,你就能體會到根據不一樣條件拼接 SQL 語句有多麼痛苦。拼接的時候要確保不能忘了必要的空格,還要注意省掉列名列表最後的逗號。利用動態 SQL 這一特性能夠完全擺脫這種痛苦。數據庫
一般使用動態 SQL 不多是獨立的一部分,MyBatis 固然使用一種強大的動態 SQL 語言來改進這種情形,這種語言能夠被用在任意的 SQL 映射語句中。apache
動態 SQL 元素和使用 JSTL 或其餘相似基於 XML 的文本處理器類似。在 MyBatis 以前的版本中,有不少的元素須要來了解。MyBatis 3 大大提高了它們,如今用不到原先一半的元素就能夠了。MyBatis 採用功能強大的基於 OGNL 的表達式來消除其餘元素。數組
動態 SQL 一般要作的事情是有條件地包含 where 子句的一部分。好比:mybatis
<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結果返回(就這個例子而言,細心的讀者會發現其中的參數值是能夠包含一些掩碼或通配符的)。oracle
若是想可選地經過「title」和「author」兩個條件搜索該怎麼辦呢?首先,改變語句的名稱讓它更具實際意義;而後只要加入另外一個條件便可。app
<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>
有些時候,咱們不想用到全部的條件語句,而只想從中擇其一二。針對這種狀況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。框架
仍是上面的例子,可是此次變爲提供了「title」就按「title」查找,提供了「author」就按「author」查找,若二者都沒有提供,就返回全部符合條件的BLOG(實際狀況多是由管理員按必定策略選出BLOG列表,而不是返回大量無心義的隨機結果)。dom
<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>
前面幾個例子已經合宜地解決了一個臭名昭著的動態 SQL 問題。如今考慮回到「if」示例,此次咱們將「ACTIVE = 1」也設置成動態的條件,看看會發生什麼。ide
<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 會變成這樣:spa
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>
注意這裏咱們忽略的是後綴中的值,而又一次附加了前綴中的值。
動態 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 元素能夠從 OGNL 表達式中建立一個變量並將其綁定到上下文。好比:
<select id="selectBlogsLike" resultType="Blog"> <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" /> SELECT * FROM BLOG WHERE title LIKE #{pattern} </select>
一個配置了「_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>
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(); }
注意 能夠將 Apache Velocity 做爲動態語言來使用,更多細節請參考 MyBatis-Velocity 項目。
你前面看到的全部 xml 標籤都是默認 MyBatis 語言提供的,它是由別名爲 xml 語言驅動器 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver 驅動的。