1. 程式人生 > >(十三)Mybatis動態SQL標籤使用

(十三)Mybatis動態SQL標籤使用

注:程式碼已託管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,專案是mybatis-09-DynamicSQL,需要自取,需要配置maven環境以及mysql環境(sql語句在resource下的test.sql中),覺得有用可以點個小星星,小菜鳥在此Thanks~
mybatis有一個強大的特性,其他框架在拼接sql的時候要特別謹慎,比如哪裡需要空格,還要注意去掉列表最後一個列名的逗號,mybtis的動態sql可以幫助我們逃離這樣的痛苦掙扎,那就是動態SQL.它還可以處理一種情況,當你不確定你的引數不知道是不是為空的時候,我們不需要在業務邏輯中判斷,直接在sql中處理,程式碼無比簡潔。主要的動態sql標籤如下:
-
- (trim,set)
- (when, otherwise)
-

注意事項:
在mapper中如果出現大於號(>),小於號(),大於等於號(),小於等於號()等,最好需要轉換成為實體符號,這是因為mapper是XML檔案,xml檔案本身就含有較多的<>這樣的尖括號,所以解析的時候可能會解析出錯。

原符號 < <= >
=
&
替換符號 &lt; &lt;= &gt;
&gt;= &amp; &apos; &quot;

我們經常需要根據where後面的條件篩選出需要的資料,當多個條件拼接的時候,我們一般使用,如果if裡面的條件成立,那麼就會使用標籤的語句,但是我們可以知道where句子第一個標籤是沒有and的,而後面的條件都需要and,所以有一種做法是第一個使用where 1 = 1,這個條件恆成立,後面的所有子語句都加上and,如果增加判斷,那麼我們只需要加標籤就可以了。

    <!-- 動態sql if標籤-->
    <!-- &可以使用and來代替 ,注意!=需要連在一起寫-->
    <select id="selectStudentByDynamicSQL"
resultType="Student"> <!--最常用的(動態引數) select id,name,age,score from student where name like '%' #{name} '%' --> <!-- 下面的是字串拼接 ,只能寫value,瞭解即可,容易sql注入,執行效率低,不建議使用--> select id,name,age,score from student where 1=1 <if test="name != null and name != ''"> and name like '%' #{name} '%' </if> <if test="age > 0"> and age > #{age} </if> </select>

當有兩個查詢條件的時候,sql語句是:select * from student where 1=1 and name like ‘%’ ? ‘%’ and age > ?
當有一個查詢條件的時候:sql語句就變成:select * from student where 1=1 and name like ‘%’ ? ‘%’
當沒有查詢條件的時候,sql語句是:
select * from student where 1=1
標籤需要手動在where後面新增1=1語句,這是因為如果後面的條件都是false的時候,where後面如果沒有1=1語句,sql就剩下一個空空的where,sql就會報錯。所以在where後面需要加上永真句子1=1,但是這樣有一個問題,當資料量比較大的時候,會嚴重影響sql的查詢效率。

,,

使用標籤,在有查詢語句的時候,自動補上where子句,在沒有查詢條件的時候,不會加上where子句,這也就解決了我們上面所涉及到的問題,剩下的就是標籤的and子句,第一個,片段裡面可以不包含and,也可以包含,系統會自動去掉and,但是其他的片段裡面的and,必須寫上,否則會出錯。下面的寫法中,如果name為null,第二個if標籤中的if也會被去掉,不會報錯。

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        <!--最常用的(動態引數) select id,name,age,score from student where name like '%' #{name} '%' -->
        <!-- 下面的是字串拼接 ,只能寫value,瞭解即可,容易sql注入,執行效率低,不建議使用-->
        select id,name,age,score
        from student
        <where>
            <if test="name != null and name != ''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </where>
    </select>

如果where裡面是不規範的,那我們可以通過來自定義where元素的功能,標籤主要有以下屬性:
- prefix:在包含的內容前加上字首,不是百分之百會加,會根據需要自動加
- suffix:在包含的內容後面加上字尾,不是百分之百會加,會根據需要自動加
- prefixOverrides:可以把包含內容的首部某些內容忽略(不能自己增加),不一定會忽略,根據需要自動忽略
- suffixOverrides:也可以把包含內容的尾部的某些內容忽略(不能自己增加),同上

下面這樣的是錯誤的,當傳入的name不為空,而且age大於0的時候

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        select id,name,age,score
        from student
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null and name != ''">
                name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                age > #{age}
            </if>
        </trim>
    </select>

不會自己增加and在第二個age前面:


下面的是正確的,我們在兩個標籤前面都增加了and,第二個and會自動去掉:

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        select id,name,age,score
        from student
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null and name != ''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </trim>
    </select>

下面是字尾模式,prefix="set"表示在整個語句前面加上字首set,suffixoverride=","表示每一個語句後面的字尾”,”可以被忽略,如果是需要的話suffix=" where id = #{id}表示在整個語句後面增加where id = #{id},:

update user
<trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
  <if test="name != null and name.length()>0"> name=#{name} , </if>
  <if test="age != null "> age=#{age} ,  </if>
</trim>

當然,我們對上面的語句還有動態解決的方案,那就是標籤:

    <update id="updateStudent">
        update student
        <set>
            <!-- 第一個if標籤的逗號一定要有,最後一個標籤的逗號可以沒有-->
            <if test="name != null"> name=#{name},</if>
            <if test="age != null">age=#{age},</if>
            <if test="score != null"> score=#{score},</if>
        </set>
         where id=#{id}
    </update>

, ,

有時候,我們只想去匹配第一個條件,或者第一個條件不匹配的時候才會去匹配第二個條件,不像標籤裡面的一樣會去判斷所有的子語句是否可以匹配,而是遇到一個匹配的就會執行跳出

    <!--    selectStudentByDynamicSQLChoose 類似於switch,滿足後就不會判斷後面的了-->
    <!-- 如果名字不為空,那麼按照名字來查詢,如果名字為空,就按照年齡來查詢,如果沒有查詢條件,就沒有查詢條件 -->
    <select id="selectStudentByDynamicSQLChoose" resultType="Student">
        <!--最常用的(動態引數) select id,name,age,score from student where name like '%' #{name} '%' -->
        select id,name,age,score
        from student
        <where>
            <choose>
                <when test="name != null and name != ''">
                    and name like '%' #{name} '%'
                </when>
                <when test="age > 0">
                    and age > #{age}
                </when>
                <otherwise>
                    and 1 != 1
                </otherwise>
            </choose>
        </where>
    </select>

標籤就像是switch語句,每一個都像是case,後面預設跟上break語句,只要滿足一個就不會判斷後面的子語句了,當前面所有的都不執行的時候,就會執行標籤的內容,這個內容也就像是switch語句裡面的default。

foreach

動態SQL要有一個比較多的操作是對一個集合進行遍歷,通常是在構建IN條件語句的時候。需要注意的點:
- collection 表示需要遍歷的集合型別,array表示需要遍歷的陣列
- open,close,separator是對遍歷內容的SQL拼接
- foreach 元素的功能非常強大,它允許你指定一個集合,宣告可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字串以及在迭代結果之間放置分隔符。
- 你可以將任何可迭代物件(如 List、Set 等)、Map 物件或者陣列物件傳遞給 foreach 作為集合引數。當使用可迭代物件或者陣列時,index 是當前迭代的次數,item 的值是本次迭代獲取的元素。當使用 Map 物件(或者 Map.Entry 物件的集合)時,index 是鍵,item 是值。

1.比如我們需要查詢學生的id為1,2,3的學生資訊,我們不希望分開一次査一個,而是希望將陣列id一次傳進去,查出來一個學生的集合。
sql介面可以這樣寫,傳入一個物件的陣列:

public List<Student>selectStudentByDynamicSQLForeachArray(Object[]studentIds);

sql語句如下,遍歷array陣列的時候,指定左邊符號是左括號,右邊是右括號,元素以逗號分隔開:

    <!-- select * from student where id in (1,3) -->
    <select id="selectStudentByDynamicSQLForeachArray" resultType="Student">
        select id,name,age,score
        from student
        <if test="array !=null and array.length > 0 ">
            where id in
            <foreach collection="array" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

2.當遍歷的是一個型別為int的list列表時:

public List<Student>selectStudentByDynamicSQLForeachList(List<Integer>studentIds);

sql語句如下,colleaction指定為list:

    <select id="selectStudentByDynamicSQLForeachList" resultType="Student">
        select id,name,age,score
        from student
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

3.當遍歷的是一個型別為物件的list:

public List<Student>selectStudentByDynamicSQLForeachListStudent(List<Student>students);

sql語句裡面與上面相似,只是在使用屬性的時候不太一樣:

<select id="selectStudentByDynamicSQLForeachListStudent" resultType="Student">
        select id,name,age,score
        from student
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

用於定義sql片段,方便在其他SQL標籤裡面複用,在其他地方複用的時候需要使用子標籤,可以定義sql的任何部分,所以標籤可以放在動態SQL的任何位置。

    <sql id="selectHead">
        select id,name,age,score
         from student
    </sql>
    <!-- 可讀性比較差 -->
    <select id="selectStudentByDynamicSQLfragment" resultType="Student">
        <include refid="selectHead"></include>
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

動態sql讓SQL寫起來更加簡潔,減少了很多重複程式碼,動態sql之間可以相互拼接,只要符合sql語句規範即可。