天天看點

Mybatis學習系列(三)動态SQL

  在mapper配置檔案中,有時需要根據查詢條件選擇不同的SQL語句,或者将一些使用頻率高的SQL語句單獨配置,在需要使用的地方引用。Mybatis的一個特性:動态SQL,來解決這個問題。

mybatis動态sql語句是基于OGNL表達式的,主要有以下幾類:

1. if 語句 (簡單的條件判斷)

2. choose (when,otherwize) ,相當于java 語言中的 switch ,與 jstl 中的choose 很類似

3. trim (對包含的内容加上 prefix,或者 suffix 等,字首,字尾)

4. where (主要是用來簡化sql語句中where條件判斷的,能智能的處理 and or ,不必擔心多餘導緻文法錯誤)、

5. set (主要用于更新時)

6. foreach (在實作 mybatis in 語句查詢時特别有用)

if标簽語句

  if标簽用來實作根據條件拼接sql語句,下面示例用來判斷參數如果不為null,則拼接sql

示例:

<select id="ifTest" resultType="com.sl.po.Product">
        select * from products where 
        <if test="ProductName!=null">
            name like #{ProductName}
        </if>
        <if test="description!=null">
            and description like CONCAT(CONCAT(\'%\', #{Description, jdbcType=VARCHAR}),\'%\')
        </if>
    </select>      

當參數ProductName和Description不為null,則正常拼接處sql語句:select * from products where name like ? and description like CONCAT(CONCAT(\'%\', ?),\'%\')

但是上面的SQL中如果傳入的參數ProductName為null,則解析出錯誤的語句:select * from products where and description like CONCAT(CONCAT(\'%\', ?),\'%\') ,解決這個問題,需要用到where标簽

where标簽語句

  當 where 中的條件使用的 if 标簽較多時,這樣的組合可能會導緻錯誤, “where”标簽會自動判斷如果它包含的标簽中有傳回值的話,就在sql中插入一個‘where’,如果where标簽最後傳回的内容是以 and 或者or 開頭的,也會被自動移除掉。

上面的示例用where标簽改寫後示例如下:

<select id="whereTest" resultType="com.sl.po.Product">
        select * from products
<!-- where标簽自動移除第一個and-->
        <where>
            <if test="Name!=null">
                and name like #{Name}
                <!--name like #{Name}-->
            </if>
            <if test="description!=null">
                and description like #{Description}
            </if>
        </where>
</select>      

set标簽語句

  set 标簽是用在更新操作的時候,功能和 where 标簽元素差不多,主要是在包含的語句前輸出一個 set,然後如果包含的語句是以逗号結束的話将會把該逗号忽略,如果 set 标簽最終傳回的内容為空的話則可能會出錯(update table where id=1)

 使用set标簽示例:

<!-- if + set 實作按條件更新-->
    <update id="setTest">
        update products
 <!-- set标簽将移除最後一個“,” -->
        <set>
            <if test="cityCode!=null">
              citycode = #{cityCode} ,
            </if>
            <if test="Name!=null">
               name = #{Name} ,
            </if>
            <if test="description!=null">
                description = #{Description} ,
            </if>
        </set>
        where id =#{id}
</update>      

trim标簽語句

  trim 元素的主要功能是可以在自己包含的内容前加上某些字首,也可以在其後加上某些字尾,與之對應的屬性是 prefix 和 suffix;可以把包含内容的首部某些内容覆寫,即忽略,也可以把尾部的某些内容覆寫,對應的屬性是 prefixOverrides 和 suffixOverrides;正因為 trim 有這樣的功能,它可以用來實作 where 和 set 的效果。

前面where标簽示例,此處使用trim代替:

<!-- if+trim 使用trim代理where-->
    <select id="trimwhereTest" resultType="com.sl.po.Product">
        select * from products
       <!--
          <where>
            <if test="Name!=null">
                and name like #{Name}
                <!--name like #{Name}-->
            </if>
            <if test="description!=null">
                and description like #{Description}
            </if>
        </where>
       -->
       <!-- 移除首部所有指定在 prefixOverrides 屬性中的内容,并且插入 prefix 屬性中指定的内容-->
<trim prefix="WHERE" prefixOverrides="AND |OR">
            <if test="Name!=null">
                and name like #{Name}
            </if>
            <if test="description!=null">
                and description like #{Description}
            </if>
        </trim>

    </select>      

前面set标簽示例,此處使用trim代替:

<!--if+trim 代替 使用trime代替set  -->
    <update id="trimsetTest">
      update products
    <!--
     <set>
            <if test="cityCode!=null">
              citycode = #{cityCode} ,
            </if>
            <if test="Name!=null">
               name = #{Name} ,
            </if>
            <if test="description!=null">
                description = #{Description}
            </if>
        </set>
   -->      
<!-- 移除尾部所有指定在 suffixOverrides 屬性中的内容,并且插入 prefix 屬性中指定的内容-->      
<trim prefix="set" suffixOverrides=",">
            <if test="cityCode!=null and cityCode!=\'\'">
              citycode = #{cityCode} ,
            </if>
            <if test="Name!=null">
               name = #{Name} ,
            </if>
            <if test="description!=null">
                description = #{Description}
            </if>
        </trim>
        where id=#{id}
    </update>       

choose (when, otherwise)标簽

  choose标簽是按順序判斷其内部when标簽中的test條件出否成立,如果有一個成立,則 choose 結束。當 choose 中所有 when 的條件都不滿則時,則執行 otherwise 中的sql,類似于sql server語句(case when then)

示例:

<!-- choose + when + otherwise 隻能選擇一個作為查詢條件 作用類似sql case when then -->
    <select id="choosewhenotherwiseTest" resultType="com.sl.po.Product">
        select * from products
     <where>
        <choose>
            <when test="name!=null">
                and name like #{Name}
            </when>
            <when test="description!=null">
                and description like #{Description}
            </when>
            <otherwise>
                and unitprice > #{UnitPrice}
            </otherwise>
        </choose>
     </where>
   </select>      

如果name!=null,則解析出sql: select * from product where name like ?

Name==null&& description!=null,則解析出sql: select * from product where description like ?

否則:select * from product where unitprice >?

foreach标簽語句

  mybatis提供foreach标簽,用來對一個集合進行周遊,通常是用來建構 IN 條件語句,也可用于其他情況下動态拼接sql語句。

foreach标簽有以下幾個屬性collection, item,index,open,separator,close。

1. collection表示需要周遊的集合

2. item 表示每次周遊時生成的對象名

3. index表示在疊代過程中,每次疊代到的位置)

4. open表示開始周遊時要拼接的字元串

5. separator表示在每次周遊時兩個對象直接的連接配接字元串

6. close表示結束周遊時要拼接的字元串

當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。

在使用foreach的時候針對不同的參數類型, collection屬性值要分為以下3種情況:

1.如果傳入的是單參數且參數類型是一個List的時候,collection屬性值為list

2.如果傳入的是單參數且參數類型是一個array數組的時候,collection的屬性值為array

3.如果傳入的參數是多個的時候,我們就需要把它們封裝成一個Map或者Object。

 示例:

<!-- 隻有一個List參數時它的參數名為list,即collection="list" ;  如果參數類型時數組object[],則  collection="array" -->
<select id="foreachTest" resultType="com.sl.po.Product">
      select * from products 
      <where>
        <if test="list!=null">
          <foreach item="id" index="index"  collection="list" open="id in(" separator="," close=")">#{id}</foreach>
        </if>
      </where>
</select>      
<!-- 通過pojo傳遞list, collection值為pojo中對應的屬性名-->
<select id="foreachVoTest" resultType="com.sl.po.Product">
      select * from products 
      <where>
         <if test="name!=null"> and name like #{name} </if>
         <if test="ids!=null">
            <foreach item="item" index="index"  collection="ids" open="and id in(" separator="," close=")">#{item}</foreach>
         </if>
      </where>
 </select>      

測試代碼:

Mybatis學習系列(三)動态SQL
Mybatis學習系列(三)動态SQL
//@Test
        public void testforeachTest() {
            String statement = "com.sl.mapper.ProductMapper.foreachTest";
            List<Integer> list = new ArrayList<Integer>();
            list.add(1);
            list.add(2);
            list.add(3);
            
            List<Product> listProduct = session.selectList(statement, list);
            for (Product pro : listProduct) {
                System.out.println(pro);
            }

            // 關閉會話
            session.close();
        }
        
        //@Test
        public void testforeachVoTest() {
            String statement = "com.sl.mapper.ProductMapper.foreachVoTest";
            ProductVo2 vo2 = new ProductVo2();
            vo2.setName("%國際%");
            List<Integer> ids = new ArrayList<Integer>();
            ids.add(11);
            ids.add(12);
            ids.add(13);
            vo2.setIds(ids);
            
            List<Product> listProduct = session.selectList(statement, vo2);
            for (Product pro : listProduct) {
                System.out.println(pro);
            }

            // 關閉會話
            session.close();
        }      

View Code

Mybatis學習系列(三)動态SQL
Mybatis學習系列(三)動态SQL
package com.sl.po;

import java.util.List;

public class ProductVo2 {

    private String name;
    
    private List<Integer> ids;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
    
}      

View Code

Sql片段

  Sql中可将重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的

示例:

<select id="sqlTest" resultType="com.sl.po.Product">
        select * from products
        <where>
        <!-- 引用sql片段 -->
        <include refid="sqltemp"/>
          <!-- 提取sql片段
            <if test="cityCode!=null">
               and citycode = #{cityCode}
            </if>
            <if test="Name!=null">
                and name like #{Name}
            </if>
            <if test="description!=null">
                and description like #{Description}
            </if>
             -->
        </where>
</select>


    <!-- 定義sql片段 :将where條件提取 -->
    <sql id="sqltemp">
    <if test="cityCode!=null">
               and citycode = #{cityCode}
            </if>
            <if test="Name!=null">
                and name like #{Name}
            </if>
            <if test="description!=null">
                and description like #{Description}
            </if>
    </sql>