動態SQL(Dynamic SQL)


MyBatis 最強大的特性之一就是它的動態語句功能。如果您以前有使用 JDBC 或者類似框架的經歷,您就會明白把 SQL 語句條件連接在一起是多么的痛苦,要確保不能忘記空格或者不要在 columns 列后面省略一個逗號等。動態語句能夠完全解決掉這些痛苦。

盡管與動態 SQL 一起工作不是在開一個 party,但是 MyBatis 確實能通過在任何映射 SQL 語句中使用強大的動態 SQL 來改進這些狀況。

動態 SQL 元素對於任何使用過 JSTL 或者類似於 XML 之類的文本處理器的人來說,都是非常熟悉的。在上一版本中,需要了解和學習非常多的元素,但在 MyBatis3 中有了許多的改進,現在只剩下差不多二分之一的元素。MyBatis 使用了基於強大的 OGNL 表達式來消除了大部分元素。

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

 

if 元素

動態 SQL 最常做的事就是有條件地包括 where 子句。例如:

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

這條語句提供一個帶功能性的可選的文字。如果您沒有傳入標題,那么將返回所有激活的 Blog。如果您傳入了一個標題,那它就會查找與這個標題匹配的 Blog(在這種情況下,您的參數值可能需要包括任何 masking 或者通配符)。

如果我們想要可選地根據標題或者作者查詢怎么辦?首先,我把語句的名稱稍稍改一下,使得看起來更直觀。然后簡單地加上另外一個條件。

<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG WHERE state='ACTIVE'
    <if test="title!=null">
        AND title like #{title}
    </if>
    <if test="author!=nullandauthor.name!=null">
        AND title like #{author.name}
    </if>
</select>

 

choose,when,otherwise元素

有時候我們不想應用所有的條件,而是想從多個選項中選擇一個。與 java 中的 switch 語句相似,MyBatis 提供了一個 choose 元素。

讓我們繼續使用上面的例子,但這次我們只搜索有提供查詢標題的,或者只搜索有提供查詢作者的數據。如果兩者都沒有提供,那只返回加精的 Blog(可能是管理員有選擇性的查詢,而不是返回大量無意義的隨機的 Blog)。

<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG WHERE state='ACTIVE'
    <choose>
        <when test="title!=null">
            AND title like #{title}
        </when>
        <when test="author!=nullandauthor.name!=null">
            AND title like #{author.name}
        </when>
        <otherwise>
            AND featured=1
        </otherwise>
    </choose>
</select>

 

trim,where,set元素

考慮一下我們上面提到的 'if' 的例子中,如果現在我們把'ACTIVE=1'也做為條件,會發生什么情況。

<select id="findActiveBlogLike" parameterType="Blog" 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 title like#{author.name}
    </if>
</select>

如果我們一個條件都不設置,會發生什么呢?語句最終可能會變成這個樣子:

SELECT * FROM BLOG
WHERE

這將會執行失敗。如果只有第二個條件滿足呢?語句最終會變成這樣:

SELECT * FROM BLOG
WHERE
AND title like 'someTitle'

這同樣會執行失敗。這個問題僅用條件很難簡單地解決,如果您已經這么寫了,那您可能以后永遠都不想犯這樣的錯了。

MyBatis 有個簡單的方案能解決這里面 90% 的問題。如果 where 沒有出現的時候,您可以自定一個。修改一下,就能完全解決:

<select id="findActiveBlogLike" parameterType="Blog" 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!=nullandauthor.name!=null">
            AND title like #{author.name}
        </if>
    </where>
</select>

where 元素知道插入 "where" 如果它包含的標簽中有內容返回的話。此外,如果返回的內容以 "AND" 或者 "OR" 開頭,它會把 "AND" 或者 "OR" 去掉。

如果 where 元素的行為並沒有完全按您想象的那樣,您還可以使用 trim 元素來自定義。例如,下面的 trim 與 where 元素實現相同功能:

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

overrides 屬性使用了管道分隔的文本列表來覆寫,而且它的空白也不能忽略的。這樣的結果是移出了指定在 overrides 屬性里字符,而在開頭插入 prefix 屬性中指定的字符。

下面的兩種配置方法效果是一樣的:

<selectid="findActiveBlogLike" parameterType="Blog" 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 title like #{author.name}
        </if>
    </where>
</select>

<selectid="findActiveBlogLike" parameterType="Blog" resultType="Blog">
    SELECT * FROM BLOG
    <trim prefix="WHERE" prefixOverrides="AND |OR">
        <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 title like #{author.name}
        </if>
    </trim>
</select>

下面的使用 SET 元素也類似。

在動態 update 語句里相似的解決方式叫做 set,這個 set 元素能夠動態地更新列。例如:

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">
    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>
    whereid=#{id}
</update>

set 元素將動態的配置 SET 關鍵字,也用來剔除追加到條件末尾的任何不相關的逗號。

您想知道等同的 trim 元素該怎么寫吧,它就像這樣:

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

注意這種情況,我們剔除了一個后綴,同時追加了一個前綴。

 

Foreach 元素

另一個動態 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>

對上面這個映射 SQL 語句的 java 調用代碼示例如下:

List<Integer> authorIdList = new ArrayList<Integer>();
postList.add(2);
postList.add(3);
postList.add(4);
List<Post> postList =(List<Post>)session.selectList("selectPostIn",postList);
//將會查詢出ID是2、3、4的文章。

foreach 元素非常強大,允許您指定一個集合,申明能夠用在元素體內的項和索引變量。也允許您指定開始和結束的字符,也可以加入一個分隔符到迭代器之間。這個元素的聰明之處在於它不會意外地追加額外的分隔符。

注意:您可以把一個 List 實例或者一個數組作為一個參數對象傳遞給 MyBatis。如果您這么做,MyBatis 會自動將它包裝成一個 Map,並以名字作為 key。List 實例會以 "list" 作為key,array 實例會以 "array" 作為key。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM