動態SQL


1.1動態SQL中的元素

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

它消除了之前版本中需要了解的大多數元素,使用不到原來一半的元素就能完成所需工作。

MyBatis動態SQL中的主要元素,如表所示。

 

1.2<if>元素

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

 

在實際應用中,我們可能會通過多個條件來精確地查詢某個數據。例如,要查找某個客戶的信息,可以通過姓名和職業來查找客戶,

也可以不填寫職業直接通過姓名來查找客戶,還可以都不填寫而查詢出所有客戶,此時姓名和職業就是非必須條件。

類似於這種情況,在MyBatis中就可以通過<if>元素來實現。下面就通過一個具體的案例,來演示這種情況下<if>元素的使用,具體實現步驟如下。

(2)修改映射文件CustomerMapper.xml,在映射文件中使用<if>元素編寫根據客戶姓名和職業組合條件查詢客戶信息列表的動態SQL

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace表示命名空間 -->
<mapper namespace="com.itheima.mapper.CustomerMapper">
<!-- <if>元素使用 -->
  <!-- parameterType屬性用於指定傳入參數的類型 -->
  <!-- resultType屬性用於指定返回結果的類型 -->
  <select id="finCustomerByNameAndJob"
       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="job!=null and job!=''">
          and job=#{job}
       </if>
  </select>
</mapper>

 

使用<if>元素的test屬性分別對username和jobs進行了非空判斷(test屬性多用於條件判斷語句中,

用於判斷真假,大部分的場景中都是進行非空判斷,有時候也需要判斷字符串、數字和枚舉等),

如果傳入的查詢條件非空就進行動態SQL組裝。

 

注意工具類代碼和mybatis-config.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1.配置環境,默認的環境id為mysql -->
 <environments default="mysql">
    <!-- 1.2配置id為mysql的數據庫環境 -->
    <environment id="mysql">
       <!-- 使用JDBC的事務管理 -->
       <transactionManager type="JDBC"/>
       <!-- 數據庫連接池 -->
       <dataSource type="POOLED">
          <!-- 數據庫驅動 -->
           <property name="driver" value="com.mysql.jdbc.Driver"></property>
          <!-- 連接數據庫 -->
          <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
          <!-- 連接數據庫的用戶名 -->
          <property name="username" value="root"></property>
          <!-- 連接數據庫的密碼 -->
         <property name="password" value="root"></property>
       </dataSource>
    </environment>
 </environments>
 <!-- 2.配置Mapper的位置 -->
 <mappers>
   <mapper resource="com/itheima/mapper/CustomerMapper.xml"></mapper>
 </mappers>
</configuration>

 

工具類

import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
    // 初始化SqlSessionFactory對象
    static {
        try {
            // 使用MyBatis提供的Resources類加載MyBatis的配置文件
            Reader reader =
                   Resources.getResourceAsReader("mybatis-config.xml");
            // 構建SqlSessionFactory工廠
             sqlSessionFactory =
                   new SqlSessionFactoryBuilder().build(reader);
           } catch (Exception e) {
                e.printStackTrace();
             }
        }
        // 獲取SqlSession對象的靜態方法
        public static SqlSession getSession() {
            return sqlSessionFactory.openSession();
        }
}

 

 

(3)在測試類MybatisTest中,編寫測試方法findCustomerByNameAndJobsTest(),如文件

/*
 * 根據客戶姓名和職業組合條件查詢客戶信息列表
 */
  public void findCustomerByNameAndJobTest() {
  //通過工具類生成SqlSession對象
  SqlSession sqlSession=MybatisUtils.getSession();
  //創建Customer對象,封裝需要組合查詢的條件
   Customer customer=new Customer();
   //customer.setUsername("jack");
   //customer.setJobs("teacher");
   //執行SqlSession的查詢方法,返回結果集
   List<Customer> customers=sqlSession.selectList("com.itheima.mapper"
            +".CustomerMapper2.finCustomerByNameAndJob",customer);
   //輸出結果
    for(Customer customer2:customers) {
        System.out.println(customer2.toString());
      }
    sqlSession.close();
  }

注意://customer.setUsername("jack");//customer.setJobs("teacher");

這兩行加與不加產生的結果大不相同

 

1.3<choose>、<when>、<otherwise>元素

在使用<if>元素時,只要test屬性中的表達式為true,就會執行元素中的條件語句,但是在實際應用中,有時只需要從多個選項中選擇一個去執行。

 

例如下面的場景:“當客戶名稱不為空,則只根據客戶名稱進行客戶篩選;當客戶名稱為空,而客戶職業不為空,則只根據客戶職業進行客戶篩選。

當客戶名稱和客戶職業都為空,則要求查詢出所有電話不為空的客戶信息。”此種情況下,使用<if>元素進行處理是非常不合適的。

如果使用的是Java語言,這種情況顯然更適合使用switch…case…default語句來處理。那么在MyBatis中有沒有類似的語句呢?答案是肯定的。

針對上面情況,MyBatis中可以使用<choose>、<when>、<otherwise>元素進行處理。下面讓我們看一下如何使用<choose>、<when>、<otherwise>元素組合去實現上面的情況。

 

(1)在映射文件CustomerMapper.xml中,使用<choose>、<when>、<otherwise>元素執行上述情況的動態SQL代碼如下所示。

<!-- <choose>.<when>.<otherwise>元素 -->
 <select id="findCustomerByNameOrJob"
     parameterType="com.itheima.po.Customer"
     resultType="com.itheima.po.Customer">
     <choose>
         <when test="username!=null and username!=''">
            and username like concat('%',#{username},'%')
         </when>
         <when test="job!=null and job!=''">
          and job=#{job}
         </when>
         <otherwise>
             and phone is not null
         </otherwise>
     </choose>
 </select>

在上述代碼中,使用了<choose>元素進行SQL拼接,當第一個<when>元素中的條件為真,

則只動態組裝第一個<when>元素內的SQL片段,

否則就繼續向下判斷第二個<when>元素中的條件是否為真,以此類推。當前面所有when元素中的條件都不為真時,則只組裝<otherwise>元素內的SQL片段。

 

(2)在測試類MybatisTest中,編寫測試方法findCustomerByNameOrJobsTest(),其代碼如下所示。

public void findCustomerByNameOrJobTest() {
       //通過工具類生成SqlSession對象
       SqlSession sqlSession=MybatisUtils.getSession();
       //創建Customer對象,封裝需要組合查詢的條件
       Customer customer=new Customer();
       //customer.setUsername("jack");
       //customer.setJobs("teacher");
       //執行SqlSession的查詢方法,返回結果集
      List<Customer> customers=sqlSession.selectList("com.itheima.mapper"
            +".CustomerMapper2.finCustomerByNameOrJob",customer);
       //輸出結果
       for(Customer customer2:customers) {
              System.out.println(customer2.toString());
       }
    }

 

 

1.4<where>、<trim>元素

在前兩個小節的案例中,映射文件中編寫的SQL后面都加入了“where 1=1”的條件,那么到底為什么要這么寫呢?如果將where后“1=1”的條件去掉,那么MyBatis所拼接出來的SQL將會如下所示。

select *from t_customer where and username like concat('%',?,'%')

 

上面SQL中,where后直接跟的是and,這在運行時肯定會報SQL語法錯誤,而加入了條件“1=1”后,既保證了where后面的條件成立,

又避免了where后面第一個詞是and或者or之類的關鍵詞。那么在MyBatis中,有沒有什么辦法不用加入“1=1”這樣的條件,也能使拼接后的SQL成立呢?

 

針對這種情況,MyBatis提供了<where>元素來處理這樣的問題。

<!-- <where>元素 -->
<select id="findCustomerByNameAndJob"
     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="job!=null and job!=''">
          and job=#{job}
       </if>
   </where>
</select>

 

上述配置代碼中,使用<where>元素對“where 1=1”條件進行了替換,

<where>元素會自動判斷組合條件下拼裝的SQL語句,只有<where>元素內的條件成立時,才會在拼接SQL中加入where關鍵字,

否則將不會添加;即使where之后的內容有多余的“AND”或“OR”,<where>元素也會自動將它們去除。

 

可以和<if>的做一個比較

<!-- <if>元素使用 -->
  <!-- parameterType屬性用於指定傳入參數的類型 -->
  <!-- resultType屬性用於指定返回結果的類型 -->
  <select id="finCustomerByNameAndJob"
       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="job!=null and job!=''">
          and job=#{job}
       </if>
  </select>

 

 

除了使用<where>元素外,還可以通過<trim>元素來定制需要的功能,上述代碼還可以修改為如下形式:

<!-- <trim>元素 -->
<select id="findCustomerByNameAndJob"
     parameterType="com.itheima.po.Customer"
     resultType="com.itheima.po.Customer">
   select *from t_customer
   <trim prefix="where" prefixOverrides="and>
      <if test="username!=null and username!=''">
          and username like concat('%',#{username},'%')
       </if>
       <if test="job!=null and job!=''">
          and job=#{job}
       </if>
   </trim>
</select>

 

上述配置代碼中,同樣使用<trim>元素對“where 1=1”條件進行了替換,<trim>元素的作用是去除一些特殊的字符串,

它的prefix屬性代表的是語句的前綴(這里使用where來連接后面的SQL片段),而prefixOverrides屬性代表的是需要去除的那些特殊字符串

(這里定義了要去除SQL中的and),上面的寫法和使用<where>元素基本是等效的。

 

1.5<set>元素:

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

以入門案例中的更新操作為例,使用<set>元素對映射文件中更新客戶信息的SQL語句進行修改的代碼如下所示。

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

為了驗證上述配置,可以在測試類中編寫測試方法updateCustomerTest(),其代碼如下所示。

/*
   * 更新客戶
   */
  public void updateCustomerTest() {
    //通過工具類生成SqlSession對象
    SqlSession sqlSession=MybatisUtils.getSession();
    //創建Customer對象,封裝需要組合查詢的條件
    Customer customer=new Customer();
    customer.setId(3);
    customer.setPhone("1234564332213");
    //4.SqlSession執行更新操作
    //4.2執行SqlSession的更新方法,返回的是SQL語句影響的行數
    int row=sqlSession.update("com.itheima.mapper"
            +".CustomerMapper.updateCustomer",customer);
    //4.3通過返回值判斷是否更新成功
    if(row>0) {
        System.out.println("您成功修改了"+row+"條數據");
    }else {
        System.out.println("執行修改操作失敗!!!!");
    }
    //4.4提交事務
    sqlSession.commit();
    //5關閉sqlSession
    sqlSession.close();
  }
  

 

 

1.6<foreach>元素

在實際開發中,有時可能會遇到這樣的情況:假設在一個客戶表中有1000條數據,現在需要將id值小於100的客戶信息全部查詢出來,這要怎么做呢?

有人也許會說,“我可以一條一條查出來”,那如果查詢200、300甚至更多也一條一條查嗎?這顯然是不可取的。有的人會想到,可以在Java方法中使用循環,

將查詢方法放在循環語句中,然后通過條件循環的方式查詢出所需的數據。這種查詢方式雖然可行,但每執行一次循環語句,都需要向數據庫中發送一條查詢SQL

,其查詢效率是非常低的。那么還有其他更好的方法嗎?我們能不能通過SQL語句來執行這種查詢呢?

 

其實,MyBatis中已經提供了一種用於數組和集合循環遍歷的方式,那就是使用<foreach>元素,我們完全可以通過<foreach>元素來解決上述類似的問題。

 

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

 

<!-- <foreach>元素使用 -->
<select id="finCustomerByIds" 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>

 

在上述代碼中,使用了<foreach>元素對傳入的集合進行遍歷並進行了動態SQL組裝。關於<foreach>元素中使用的幾種屬性的描述具體如下。

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

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

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

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

· separator:配置的是各個元素的間隔符。

 

為了驗證上述配置,可以在測試類MybatisTest中,編寫測試方法findCustomerByIdsTest(),其代碼如下所示。

 

 /*
   * 根據客戶編號批量查詢客戶信息
   */
  public void findCustomerByIdsTest() {
    //通過工具類生成SqlSession對象
    SqlSession sqlSession=MybatisUtils.getSession();
    //創建List集合,封裝查詢id
    List<Integer> ids=new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    //執行SqlSession的查詢方法,返回結果集
    List<Customer> customers=sqlSession.selectList("com.itheima.mapper"
            + ".CustomerMapper.finCustomerByIds",ids);
    //輸出查詢結果
    for(Customer customer:customers) {
        System.out.println(customer.toString());
    }
    sqlSession.close();
  }

 

 

1.7<bind>元素

<bind>元素在進行模糊查詢編寫SQL語句的時候,如果使用“${}”進行字符串拼接,

則無法防止SQL注入問題;如果使用concat函數進行拼接,則只針對MySQL數據庫有效;

如果使用的是Oracle數據庫,則要使用連接符號“||”。這樣,映射文件中的SQL就要根據不同的情況提供不同形式的實現,

這顯然是比較麻煩的,且不利於項目的移植。為此,MyBatis提供了<bind>元素來解決這一問題,我們完全不必使用數據庫語言,只要使用MyBatis的語言即可與所需參數連接。

 

MyBatis的<bind>元素可以通過OGNL表達式來創建一個上下文變量,其使用方式如下:

 

 

<!-- <bind>元素的使用:根據客戶名模糊查詢客戶信息 -->
<select id="findCustomerByName" parameterType="com.itheima.po.Customer"
       resultType="com.itheima.po.Customer">
      <!-- _parameter.getUsername()也可以直接寫成傳入字段屬性名,username -->
      <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'"/>
         select *from t_customer
         where
         username like #{pattern_username}
</select>

 

上述配置代碼中,使用<bind>元素定義了一個name為pattern_username的變量,

<bind>元素中value的屬性值就是拼接的查詢字符串,其中_parameter.getUsername()表示傳遞進來的參數(也可以直接寫成對應的參數變量名,如username)。

在SQL語句中,直接引用<bind>元素的name屬性值即可進行動態SQL組裝。

 

/*
   * <bind>元素的使用:根據客戶名模糊查詢客戶信息
   */
  public void findCustomerByNameTest() {
    //通過工具類生成SqlSession對象
    SqlSession sqlSession=MybatisUtils.getSession();
    //創建Customer對象,封裝需要組合查詢的條件
    Customer customer=new Customer();
    customer.setUsername("j");
    //執行SqlSession的查詢方法,返回結果集
    List<Customer> customers=sqlSession.selectList("com.itheima.mapper"
            + ".CustomerMapper.findCustomerByName",customer);
    //輸出查詢結果
    for(Customer customer2:customers) {
        System.out.println(customer2.toString());
    }
    sqlSession.close();
  }


免責聲明!

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



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