Java EE學習筆記(八)

動態SQL

一、動態SQL中的元素

1)、做用:無需手動拼裝SQL,MyBatis已提供的對SQL語句動態組裝的功能,使得數據庫開發效率大大提升!java

2)、動態SQL是MyBatis的強大特性之一,MyBatis3採用了功能強大的基於OGNL的表達式來完成動態SQL。動態SQL主要元素以下表所示:mysql

二、<if>元素

1)、在MyBatis中,<if>元素是最經常使用的判斷語句,它相似於Java中的if語句,主要用於實現某些簡單的條件選擇。其基本使用示例以下:使用<if>元素對username和jobs進行非空判斷,並動態組裝SQLsql

1      select * from t_customer where 1=1 
2      <if test="username !=null and username !=''">
3             and username like concat('%',#{username}, '%')
4      </if>
5      <if test="jobs !=null and jobs !=''">
6     and jobs= #{jobs}
7      </if>  

2)、本章項目文件結構數據庫

①配置文件:src->mybatis-config.xmlapache

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 3                           "http://mybatis.org/dtd/mybatis-3-config.dtd">
 4                           
 5 <configuration>
 6     <!-- 宏定義 -->
 7     <properties resource="db.properties" />
 8     
 9     <!--1.配置環境 ,默認的環境id爲mysql -->
10     <environments default="mysql">
11         
12         <!--1.2.配置id爲mysql的數據庫環境 -->
13         <environment id="mysql">
14             
15             <!-- 使用JDBC的事務管理 -->
16             <transactionManager type="JDBC" />
17             
18             <!--數據庫鏈接池 -->
19             <dataSource type="POOLED">
20                 
21                 <!-- 數據庫驅動 -->
22                 <property name="driver" value="${jdbc.driver}" />
23                 
24                 <!-- 鏈接數據庫的url -->
25                 <property name="url" value="${jdbc.url}" />
26                 
27                 <!-- 鏈接數據庫的用戶名 -->
28                 <property name="username" value="${jdbc.username}" />
29                 
30                 <!-- 鏈接數據庫的密碼 -->
31                 <property name="password" value="${jdbc.password}" />
32                 
33             </dataSource>
34             
35         </environment>
36         
37     </environments>
38     
39     <!--2.配置Mapper的位置 -->
40     <mappers>
41         <mapper resource="com/itheima/mapper/CustomerMapper.xml" />
42     </mappers>
43 </configuration>

②src->db.properties數組

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=******session

#密碼本身設置mybatis

因爲MyBatis默認使用log4j輸出日誌信息,因此若是要查看控制檯的輸出SQL語句,那麼就須要在classpath路徑下配置其日誌文件。在項目的src目錄下建立log4j.properties文件。app

# Global logging configuration,全局的日誌配置,Mybatis的日誌配置和控制檯輸出,其中Mybatis的日誌配置用於將com.itheima包下全部類的日誌記錄級別設置爲DEBUG
log4j.rootLogger=ERROR, stdoutide


# MyBatis logging configuration...
log4j.logger.com.itheima=DEBUG


# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

④修改映射文件:CustomerMapper.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3                    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4   
 5 <mapper namespace="com.itheima.mapper.CustomerMapper">
 6     <!-- <if>元素使用,
 7         test屬性多用於條件判斷語句中,用於判斷真假,大部分進行非空判斷
 8         若傳入的查詢條件非空就進行動態SQL組裝,也就是條件成立時執行查詢語句,注意用and鏈接查詢語句
 9      -->
10     <select id="findCustomerByNameAndJobsID" parameterType="com.itheima.po.Customer" 
11              resultType="com.itheima.po.Customer"> 
12         select * from t_customer where 1=1 
13         <if test="username !=null and username !=''">
14             and username like concat('%',#{username},'%') 
15         </if> 
16         
17         <if test="jobs !=null and jobs !=''"> 
18             and jobs= #{jobs} 
19         </if> 
20     </select>
21 </mapper>

⑤單元測試:

 1     /**
 2      * 根據客戶姓名和職業組合條件查詢客戶信息列表
 3      */
 4     @Test
 5     public void findCustomerByNameAndJobsTest(){
 6         
 7         // 一、經過工具類生成SqlSession對象
 8         SqlSession session = MybatisUtils.getSession();
 9         
10         // 二、建立Customer對象,封裝須要組合查詢的條件
11         Customer customer = new Customer();
12         customer.setUsername("jack");
13         customer.setJobs("teacher");
14         
15         // 三、執行SqlSession的查詢方法,返回結果集
16        List<Customer> customers = session.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobsID",customer);
17         
18        // 四、輸出查詢結果信息
19         for (Customer customer2 : customers) {
20             // 打印輸出結果
21             System.out.println(customer2);
22         }
23         
24         // 五、關閉SqlSession
25         session.close();
26     }

⑥運行結果:

⑦若註釋掉封裝對象中的jack和teacher的2行代碼,則執行結果爲查詢整張表:

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

1)、這三個元素的組合至關於java語言中的switch...case...default語句,其XML文件通常爲如下格式:使用<choose>及其子元素依次對條件進行非空判斷,並動態組裝SQL

      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>

①修改映射文件:CustomerMapper.xml

 1 <!--<choose>(<when>、<otherwise>)元素使用 -->
 2     <select id="findCustomerByNameOrJobsID" parameterType="com.itheima.po.Customer"
 3                 resultType="com.itheima.po.Customer">
 4         select * from t_customer where 1=1
 5         <choose>
 6             <!--<choose>元素至關於switch, <when>元素至關於case語句
 7                 語法和switch...case...default同樣
 8              -->
 9             <when test="username !=null and username !=''">
10                 and username like concat('%',#{username}, '%')
11             </when>
12             
13             <when test="jobs !=null and jobs !=''">
14                 and jobs= #{jobs}
15             </when>
16             
17             <!-- <otherwise>元素至關於default關鍵字,若前面的條件都不成立,默認執行下面這一條件 -->
18             <otherwise>
19                 and phone is not null
20             </otherwise>
21             
22         </choose>
23     </select>

②單元測試:

 1     /**
 2      * 根據客戶姓名或職業查詢客戶信息列表
 3      */
 4     @Test
 5     public void findCustomerByNameOrJobsTest(){
 6         
 7         // 一、經過工具類生成SqlSession對象
 8         SqlSession session = MybatisUtils.getSession();
 9         
10         // 二、建立Customer對象,封裝須要組合查詢的條件
11         Customer customer = new Customer();
12         customer.setUsername("jack");
13         customer.setJobs("teacher");
14         
15         // 三、執行SqlSession的查詢方法,返回結果集
16         List<Customer> customers = session.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameOrJobsID",customer);
17         
18         // 四、輸出查詢結果信息
19         for (Customer customer2 : customers) {
20             // 打印輸出結果
21             System.out.println(customer2);
22         }
23         
24         // 五、關閉SqlSession
25         session.close();
26     }

③運行結果:(注意當前查詢語句)

④若註釋掉customer.setUsername("jack");則執行結果以下:(注意當前查詢語句)

⑤若再註釋掉customer.setJobs("teacher");則執行結果以下:(執行XML中<otherwise>元素塊)

四、<when>、<trim>元素

1)、疑惑:在前兩個小節的案例中,映射文件中編寫的SQL後面都加入了「where 1=1」的條件,那麼到底爲何要這麼寫呢?若是將where後「1=1」的條件去掉,那麼MyBatis所拼接出來的SQL將會以下所示:

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

能夠看出上面SQL語句明顯存在SQL語法錯誤,而加入了條件「1=1」後,既保證了where後面的條件成立,又避免了where後面第一個詞是and或者or之類的關鍵詞

2)、針對上述狀況中「where 1=1」,在MyBatis的SQL中就可使用<where><trim>元素進行動態處理。

①修改映射文件:CustomerMapper.xml

 1     <!-- <where>元素 
 2         只有<where>元素內的條件成立(執行過程和switch-case語句同樣,只要條件一成立直接返回查詢該字段)時,纔會在拼接SQL中加入where關鍵字,不然將不會添加;
 3         即便where以後的內容有多餘的"AND"或者是"OR",<where>元素也會自動將它們刪除
 4         若沒有where關鍵字,即<where>元素內沒有一個條件成立,則將查詢整張表
 5     -->
 6     <select id="findCustomerByNameAndJobsID" parameterType="com.itheima.po.Customer" 
 7                 resultType="com.itheima.po.Customer"> 
 8         select * from t_customer 
 9         <where> 
10             <if test="username !=null and username !=''"> 
11                 and username like concat('%',#{username},'%') 
12             </if> 
13             
14             <if test="jobs !=null and jobs !=''"> 
15                      and jobs= #{jobs} 
16             </if> 
17         </where> 
18     </select>

②執行結果:

③若註釋掉customer.setUsername("jack");則查詢結果以下:注意查詢語句

④若再註釋掉customer.setJobs("teacher");則爲查詢整張表

3)、除了使用<where>元素外,還能夠經過<trim>元素來定製須要的功能。

①修改映射文件:CustomerMapper.xml(運行結果和上面同樣)

 1      <!-- <trim>元素
 2         prefix屬性:語句的前綴,這裏使用where來鏈接後面的SQL片斷
 3             prefixOverrides屬性:須要去除的那些特殊字符串(這裏定義了要去除的SQL中的and)
 4      -->
 5     <select id="findCustomerByNameAndJobsID" parameterType="com.itheima.po.Customer"
 6                     resultType="com.itheima.po.Customer">
 7         select * from t_customer
 8         <trim prefix="where" prefixOverrides="and">
 9             <if test="username !=null and username !=''">
10                 and username like concat('%',#{username}, '%')
11             </if>
12             <if test="jobs !=null and jobs !=''">
13                 and jobs= #{jobs}
14             </if>
15         </trim>
16     </select>

4)、總之,<where>和<trim>元素做用以下:

五、<set>元素

1)、在Hibernate中,想要更新某個對象,就須要發送全部的字段給持久化對象,這種想更新的每一條數據都要將其全部的屬性都更新一遍的方法,其執行效率很是差的。爲此,在MyBatis中能夠使用動態SQL中的<set>元素進行處理:使用<set>和<if>元素對username和jobs進行更新判斷,並動態組裝SQL。這樣就只須要傳入想要更新的字段便可。

①修改映射文件:CustomerMapper.xml

 1      <!-- <set>元素
 2         <set>和<if>來組裝update語句,其中<set>元素會動態前置SET關鍵字,
 3         同時也會消除SQL語句中最後一個多餘的逗號
 4      -->
 5     <update id="updateCustomerID" parameterType="com.itheima.po.Customer">
 6         update t_customer
 7         <set>
 8             <if test="username !=null and username !=''">
 9                 username=#{username},
10             </if>
11             <if test="jobs !=null and jobs !=''">
12                 jobs=#{jobs},
13             </if>
14             <if test="phone !=null and phone !=''">
15                 phone=#{phone},
16             </if>
17         </set>
18         where id=#{id}
19     </update>

②單元測試:

 1     /**
 2      * 更新客戶信息
 3      */
 4     @Test
 5     public void updateCustomerTest() {        
 6         
 7         // 一、經過工具類獲取SqlSession
 8         SqlSession sqlSession = MybatisUtils.getSession();
 9         
10         // 二、建立Customer對象,並向對象中添加數據
11         Customer customer = new Customer();
12         customer.setId(3);
13         customer.setPhone("99999999999");
14         
15         // 三、執行SqlSession的更新方法,返回的是SQL語句影響的行數
16         int rows = sqlSession.update("com.itheima.mapper.CustomerMapper.updateCustomerID", customer);
17         
18         // 四、經過返回結果判斷更新操做是否執行成功
19         if(rows > 0){
20             System.out.println("您成功修改了"+rows+"條數據!");
21         }else{
22             System.out.println("執行修改操做失敗!!!");
23         }
24         
25         // 五、提交事務
26         sqlSession.commit();
27         
28         // 六、關閉SqlSession
29         sqlSession.close();
30     }

③執行結果:

④若註釋掉customer.setId(3);和customer.setPhone("66666666666");這2條語句,則執行時會出錯:

即:若<set>元素內包含的內容都爲空,則會出現SQL語法錯誤。因此在使用<set>元素進行字段信息更新時,要確保傳入的更新字段不能爲空

六、<foreach>元素

1)、需求:提升批量查詢的效率!

①修改映射文件:CustomerMapper.xml

 1      <!--<foreach>元素使用
 2         使用<foreach>元素對傳入的集合進行遍歷並進行了動態SQL組裝
 3         item:配置的是循環中當前的元素,(本次迭代獲取的元素爲屬性id)
 4         index:配置的是當前元素在集合的位置下標(index是當前迭代的次數)
 5         當使用字典(或者Map.Entry對象的集合)時,index是鍵,item是值
 6         
 7         collection(指定輸入對象中的集合屬性):下面栗子中配置的list是傳遞過來的參數類型(首字母小寫),
 8         它能夠是一個array、list(或collection)、Map集合的鍵、
 9         POJO包裝類中數組或集合類型的屬性名等。
10         open和close:配置的是以什麼符號將這些集合元素包裝起來。
11         open:拼接字符串的前綴, close:拼接字符串的後綴
12         separator:配置的是各個元素的間隔符。
13         
14         SQL查詢語句:select * from t_customer where id in (1,2,3,4,5);
15         這個配置其實也就是SQL語句中有哪些屬性值屬於要查詢的集合,用法殊途同歸之妙
16      -->
17     <select id="findCustomerByIds" parameterType="List"
18         resultType="com.itheima.po.Customer">
19         select * from t_customer where id in
20         <foreach item="user_id" index="index" collection="list" open="("
21             separator="," close=")">
22             #{user_id}
23             <!-- 這裏的#{user_id}表示取出集合中每個元素對象,其中的user_id對應屬性item值 -->
24         </foreach>
25     </select>

②測試單元:

 1     /**
 2      * 根據客戶編號批量查詢客戶信息
 3      */
 4     @Test
 5     public void findCustomerByIdsTest(){
 6         
 7         // 一、經過工具類獲取SqlSession
 8         SqlSession session = MybatisUtils.getSession();
 9         
10         // 二、建立List集合,封裝查詢id
11         List<Integer> ids=new ArrayList<Integer>();
12         ids.add(1);
13         ids.add(2);
14         
15         // 三、執行SqlSession的查詢方法,返回結果集
16         List<Customer> customers = session.selectList("com.itheima.mapper.CustomerMapper.findCustomerByIds", ids);
17         
18         // 四、輸出查詢結果信息    
19         for (Customer customer : customers) {
20             // 打印輸出結果
21             System.out.println(customer);
22         }
23         
24         // 五、關閉SqlSession
25         session.close();
26     }

③查詢結果:

2)、在使用<foreach>時最關鍵也是最容易出錯的就是collection屬性,該屬性是必須指定的(按實際狀況進行配置),並且在不一樣狀況下,該屬性的值是不同的。主要有如下3種狀況:

a)、若是傳入的是單參數且參數類型是一個數組或者List的時候,collection屬性值分別爲array和list(或collection)

b)、若是傳入的參數是多個的時候,就須要把它們封裝成一個Map了,固然單參數也能夠封裝成Map集合,這時候collection屬性值就爲Map的鍵

c)、若是傳入的參數是POJO包裝類的時候,collection屬性值就爲該包裝類中須要進行遍歷的數組或集合的屬性名

七、<bind>元素

1)、思考:記得入門案例中模糊查詢的SQL語句:

select * from t_customer where username like '%${value}%'

不妥之處:

a)、若是使用「${}」進行字符串拼接,則沒法防止SQL注入問題;

b)、若是改用concat函數進行拼接,則只針對MySQL數據庫有效;

c)、若是改用「||」進行字符串拼接,則只針對Oracle數據庫有效。

d)、總結:這樣映射文件中的SQL就要根據不一樣的狀況提供不一樣形式的實現,這顯然是比較麻煩的,且不利於項目的移植。爲了減小這種麻煩,就可使用MyBatis的<bind>元素來解決這一問題。

2)、MyBatis的<bind>元素能夠經過OGNL表達式來建立一個上下文變量,其使用方式以下:

①修改映射文件:CustomerMapper.xml

 1     <!--<bind>元素的使用:根據客戶名模糊查詢客戶信息 -->
 2     <select id="findCustomerByName" parameterType="com.itheima.po.Customer"
 3                 resultType="com.itheima.po.Customer">
 4         <!--
 5             _parameter.getUsername()也可直接寫成傳入的字段屬性名,即username
 6             <bind>元素定義了一個name爲pattern_username的變量
 7             <bind>元素中value的屬性值就是拼接的查詢字符串  8             其中_parameter.getUsername()表示傳遞進來的參數  9          -->
10         <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'" />
11         select * from t_customer
12         where
13         username like #{pattern_username}
14     </select>

②單元測試:

 1     /**
 2      * bind元素的使用:根據客戶名模糊查詢客戶信息 
 3      */
 4     @Test
 5     public void findCustomerByNameTest(){
 6         
 7         // 一、經過工具類生成SqlSession對象
 8         SqlSession session = MybatisUtils.getSession();
 9         
10         // 二、建立Customer對象,封裝查詢的條件
11         Customer customer =new Customer();
12         customer.setUsername("j");
13         
14         // 三、執行sqlSession的查詢方法,返回結果集
15         List<Customer> customers = session.selectList("com.itheima.mapper.CustomerMapper.findCustomerByName", customer);
16         
17         // 四、輸出查詢結果信息    
18         for (Customer customer2 : customers) {
19             // 打印輸出結果
20             System.out.println(customer2);
21         }
22         
23         // 五、關閉SqlSession
24         session.close();
25     }

③運行結果:

3)、最後再貼一下工具類和用戶類:

①工具類:MybatisUtils.java

 1 package com.itheima.utils;
 2 import java.io.Reader;
 3 import org.apache.ibatis.io.Resources;
 4 import org.apache.ibatis.session.SqlSession;
 5 import org.apache.ibatis.session.SqlSessionFactory;
 6 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 7 /**
 8  * 工具類
 9  */
10 public class MybatisUtils {
11     private static SqlSessionFactory sqlSessionFactory = null;
12     // 初始化SqlSessionFactory對象
13     static {
14         try {
15             // 使用MyBatis提供的Resources類加載mybatis的配置文件
16             Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
17             // 構建sqlSession的工廠
18             sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
19         } catch (Exception e) {
20             e.printStackTrace();
21         }
22     }
23     // 獲取SqlSession對象的靜態方法
24     public static SqlSession getSession() {
25         return sqlSessionFactory.openSession();
26     }
27 }

②用戶類:src/com/itheima/po/Customer.java

 1 package com.itheima.po;
 2 /**
 3  * 客戶持久化類
 4  */
 5 public class Customer {
 6     private Integer id;       // 主鍵id
 7     private String username; // 客戶名稱
 8     private String jobs;      // 職業
 9     private String phone;     // 電話
10     
11     public Integer getId() {
12         return id;
13     }
14     
15     public void setId(Integer id) {
16         this.id = id;
17     }
18     
19     public String getUsername() {
20         return username;
21     }
22     
23     public void setUsername(String username) {
24         this.username = username;
25     }
26     
27     public String getJobs() {
28         return jobs;
29     }
30     
31     public void setJobs(String jobs) {
32         this.jobs = jobs;
33     }
34     
35     public String getPhone() {
36         return phone;
37     }
38     
39     public void setPhone(String phone) {
40         this.phone = phone;
41     }
42     
43     @Override
44     public String toString() {
45         return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]";
46     }
47 }

我的總結:

動態SQL解決了手動拼接sql語句,爲查詢用戶信息帶來很大便利,只要編寫好XML中須要查詢的動態SQL語句(其用法和sql查詢語句相似),剩下的就交給MyBatis底層去實現,並且還不容易出錯,開發效率獲得極大提升,十分推薦!

相關文章
相關標籤/搜索