MyBatis學習——動態SQL


  開發人員在使用JDBC框架或者其他類似的框架進行數據庫開發時,通常都要根據需求去手動拼接SQL,這樣非常麻煩,而myBatis提供了對SQL語句動態組裝的功能,恰好解決了這一問題。

一,動態SQL中的元素

  動態SQL是MyBatis的強大特性之一,MyBatis 3后采用了基於OGNL的表達式來完成動態SQL,

  MyBatis動態SQL中的主要元素,如下:

元素 說明
<if> 判斷語句,用於單條件分支判斷
<choose>(<when>,<otherwise>) 相當於Java中的switch..case...default
<where>,<trim>,<set> 補助元素,用於處理一些SQL拼接,特殊字符問題
<foreach> 循環語句,常用於in語句等例舉條件中
<bind> 從OGNL表達式中創建一個變量,並將其綁定到上下文中,常用於模糊查詢的sql中

 

二.

1.<if>元素:通常用於通過多個條件來精確查詢某個數據

  在MyBatis中,<if>元素是最常用的判斷語句,它類似Java中的if語句,主要用於簡單的條件判斷。

  (1)在映射文件中使用<if>元素來編寫根據客戶名和客戶職業組合信息來查詢客戶信息.

<!-- 使用if元素來進行查詢 -->
    <select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer" 
                                          resultType="com.itheima.po.Customer">
        select * from t_customer where 1=1
        <if test="username != null and username !='' ">
            and username like concat('%',#{username},'%')
        </if>
        <if test="jobs != null and jobs !='' ">
            and jobs=#{jobs}
        </if>                                      
    </select>

  使用<if>元素的test屬性對username和jobs進行了非空判斷(test屬性常用於條件判斷中,用於判斷真假,大部分時候是用於進行非空判斷),如果傳入的條件非空,就進行sql拼接。

  (2)在測試類中,編寫測試方法findCustomerByNameAndJobs:

/**
     * 根據用戶名和用戶的工作來查詢用戶
     */
    @Test
    public void findCustomerByNameAndJobsTest() {
        //通過工具類來生成SqlSession對象
        SqlSession sqlSession=MyBatisUtils.getSession();
        //創建Customer對象,封裝需要組合查詢的條件
        Customer customer=new Customer();
        customer.setJobs("teacher");
        customer.setUsername("jack");
        
        //執行SqlSession對象的查詢方法,返回一個結果集
        List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobs", customer);
        //輸出查詢結果
        for(Customer customer2:customers) {
            System.out.println(customer2);
        }
        
        //關閉sqlSession
        sqlSession.close();
    }

  (3)測試結果:

   

 

 

  (4)如果將上面代碼中的設值注釋掉,如下,就會將所有的信息都輸出來: 

 1     /**
 2      * 根據用戶名和用戶的工作來查詢用戶
 3      */
 4     @Test
 5     public void findCustomerByNameAndJobsTest() {
 6         //通過工具類來生成SqlSession對象
 7         SqlSession sqlSession=MyBatisUtils.getSession();
 8         //創建Customer對象,封裝需要組合查詢的條件
 9         Customer customer=new Customer();
10         //customer.setJobs("teacher");
11         //customer.setUsername("jack");
12         
13         //執行SqlSession對象的查詢方法,返回一個結果集
14         List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobs", customer);
15         //輸出查詢結果
16         for(Customer customer2:customers) {
17             System.out.println(customer2);
18         }
19         
20         //關閉sqlSession
21         sqlSession.close();
22     }

 

     

      

 

 

 

 2.<choose>,<when>,<otherwise>:從多個選項中選一個去執行

  在<if>中,只要test屬性中的條件為真,就去執行元素中的條件語句,但是有時候在實際生活中是需要從多個選項中選擇一個去執行,例如

  當客戶名不可空,則只根據客戶名進行客戶篩選,

  當客戶名為空,職業不為空時就根據客戶職業進行客戶篩選,

  如果兩者都為空,就查詢出所有電話不為空的客戶信息

  在此種情況下,是不能使用<if>元素的,針對如上情況,可以使用<choose>,<when>,<otherwise>元素組合實現上面的情況

  (1).在映射文件中配置如下

<!-- <choose>(<when>,<otherwise>元素的組合使用) -->
    <select id="findCustomerByNameOrJobs" parameterType="com.itheima.po.Customer" 
                                          resultType="com.itheima.po.Customer">
        select * from t_customer where 1=1
        <choose>
        
            <when test="username != null and username != '' ">
                and username like concat('%',#{username},'%')
            </when>
            <when test="jobs != null and jobs !='' ">
                and jobs=#{jobs}
            </when>
            <otherwise>
                and phone is not null
            </otherwise>
            
        </choose>
    </select>

 

  在上述代碼中,使用了<choose>,<when>,<otherwise>組合,當第一個<when>元素中的條件為真時,則只動態地組裝第一個<when>元素中的條件,否則就繼續向下判斷第二個<when>元素中的條件是否為真,依次類推,如果<when>中的條件都不為真時,就會執行<otherwise>內的SQL片段。

  (2)創建測試方法

/**
     * 根據用戶名或者職業來查詢用戶信息
     */
    @Test
    public void findCustomerByNameOrJobs() {
        //通過工具類來生成SqlSession對象
        SqlSession sqlSession=MyBatisUtils.getSession();
        //創建Customer對象,封裝需要組合查詢的條件
        Customer customer=new Customer();
        customer.setJobs("teacher");
        customer.setUsername("jack");
        
        //執行SqlSession對象的查詢方法,返回一個結果集
        List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameOrJobs", customer);
        //輸出查詢結果
        for(Customer customer2:customers) {
            System.out.println(customer2);
        }
        
        //關閉sqlSession
        sqlSession.close();
    }

 

  (3)測試結果

       

  (4)當把customer.setUsername("jack")注釋掉后,結果為:

        

  (5)如果把customer.setJobs(“teacher”)也注釋掉后,結果為:

       

  

 

 

 

3.<where>,<trim>元素

  在前面的小節中編寫的SQL后面都添加了“where 1=1 "這個條件,那么為什么要這么寫呢?是應為如果將后面的”where 1=1“去掉,那么MyBatis拼接出來的SQL語句將會出錯

  SELECT *from t_customer  where and usename like  concat( '%',#{username}, '%');

  在上面的語句中,where后面跟的是and,這樣將出錯,而加入了where 1=1 后,既保證了where后面的條件成立,也保證了避免where后面跟着是and或者or.

那么在Mybatis中有什么辦法是可以不加入”1=1“這個條件也可以呢?

  MyBatis提供了<where>元素來處理這樣的問題。例如

   <select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer" 
                                          resultType="com.itheima.po.Customer">
        select * from t_customer 
     <where> <if test="username != null and username !='' "> and username like concat('%',#{username},'%') </if> <if test="jobs != null and jobs !='' "> and jobs=#{jobs} </if>
    </where> </select>

 

  在上面的語句中,使用<where>元素取代了”where 1=1“, where元素會自動判斷組合條件下的拼接的sql語句,只有當<where>里的條件成立時,才會在拼接sql時,加入where元素,否則不會添加,即使where后面有多余的"and",“or”,<where>元素會自動將它們除去。

 

4.<set>元素

  在Hibernate中,如果要更新一條數據,就需要發送所有的字段給持久化對象,然而在實際應用中,大多數情況下,是只跟新某一個字段或者幾個字段,如果更新的每一條數據都要將所有的屬性都更新一遍,那么其執行效率是非常的低,有沒有辦法使程序只更新需要更新的字段?

  在MyBatis中,提供了<set>元素來完成這一工作,<set>元素的作用用於更新操作,其主要作用是在動態包含的SQL語句前輸出一個SET關鍵字,並且將SQL語句中最后一個多余的逗號去除。

  例如,在入門案例中,使用<set>元素對更新操作的代碼進行修改如下:

<!-- set元素的使用 -->
    <update id="updateCustomer" parameterType="com.itheima.po.Customer">
    
        update t_customer
        <set>
            <if test="username != null and username !='' ">
                username=#{username},
            </if>
            <if test="jobs != null and jobs != ''">
                jobs=#{jobs},
            </if>
            <if test ="phone != null and phone != ''">
                phone=#{phone},
            </if>
        </set>
        where id=#{id}
        
    </update>

 

   在上面的sql語句中,使用了<set>和<if>語句相組合的方法將組裝update語句,其中<set>元素會自動的動態前置SET關鍵字,同時也會消除sql語句的最后一個多余的逗號,<if>元素是用來判斷相應的字段是否傳入值,如果傳入的更新字段非空,就將此字段進行動態的組裝,並且更新此字段,否則此字段不更新。

  在測試類中編寫如下代碼:

    /**
     *使用set 更新客戶
     */
    @Test
    public void updateCustomer1Test() {
        //1.通過工具類來生成SqlSession對象
        SqlSession sqlSession=MyBatisUtils.getSession();
        //2.創建Customer對象,並向對象中添加數據
        Customer customer=new Customer();
        customer.setId(3);
        customer.setPhone("444444");
        //3.執行sqlSession的更新方法,返回的是受影響的行數
        int rows=sqlSession.update("com.itheima.mapper.CustomerMapper.updateCustomer", customer);
        //4.根據返回的結果來判斷是否更新成功
        if(rows>0) {
            System.out.println("更新成功!");
        }else {
            System.out.println("更新失敗!");
        }
        
        //5.提交事務
        sqlSession.commit();
        //6.關閉事務
        sqlSession.close();
    }
    

  測試結果如下:

  

 

 

   注意:使用<set>和<if>元素組合進行update語句動態的SQL組裝時,如果<set>中的內容都為空,則會出現sql語法錯誤,所以在<set>元素進行動態更新時,要確保傳入的更新字段不為空。

 

 

5.<foreach>元素

   該元素是用於數組和集合循環遍歷。

  <foreach>元素是通常在構建IN條件語句時使用的,其使用方法如下

 

<select id="findCustomerByIds" parameterType="List" resultType="com.itheima.po.Customer">
        select * from t_customer where id in
        <foreach item="id" index="index" collection="list" open="("  separator="," close=")">
            #{id}
        </foreach>
    </select>

 

  在上述代碼中,使用了<foreach>元素對傳入的集合進行了遍歷並進行了動態的SQL組轉,其屬性如下:

  item:配置的是循環中當前的元素。

  index:配置的是當前元素在集合的位置下標。

  collection:配置的list是傳遞過來的參數類型(首字母小寫),她可以是一個array,list(或者是collection),Map集合的建,POJO包裝類中數組或者集合類型的屬性名。

  open和close:配置的是以什么符號將這些集合元素包裝起來。

  separate:配置的是各個元素之間的間隔符。

  在測試類中編寫如下代碼,驗證

/**
     * 根據客戶編號批量查詢客戶信息
     */
    @Test
    public void findCustomerByIdsTest() {
        //1.獲取SqlSession對象
        SqlSession sqlSession=MyBatisUtils.getSession();
        //2.創建一個list集合,用來封裝查詢的id值
        List<Integer> ids=new ArrayList<Integer>();
        ids.add(2);
        ids.add(3);
        //3.執行SqlSession的查詢方法,返回結果集
        List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByIds", ids);
        //4.輸出查詢的結果
        for(Customer customer:customers) {
            System.out.println(customer);
        }
        //5.關閉sqlSession
        sqlSession.close();
    }

  測試結果如下:

    

  注意:在使用<foreach>元素時,應注意的事項是collection屬性值,該屬性是必須要指定的,並且在不同的情況下,該屬性的值是不同的,主要三種情況:

  (1)如果在調用該sql語句時,傳入的是一個參數,並且參數的類型是一個數組或者是list的時候,該collection屬性值就是array和list(或者collection)

  (2)如果傳入的是多個參數的時候,就需要把它們封裝成一個Map,單個參數也是可以這樣封裝的,這時候collection屬性值就是Map的鍵

  (3)如果傳進來的是POJO的包裝類的時候,collection屬性值就是該包裝類中需要進行遍歷的數組或者集合的屬性名。

 


免責聲明!

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



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