1. 程式人生 > >MyBatis(4)動態SQL

MyBatis(4)動態SQL

截圖 轉義字符 ram 分隔符 當前 pup efi Opens rep

MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記添加必要的空格,還要註意去掉列表最後一個列名的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。

雖然在以前使用動態 SQL 並非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強大的動態 SQL 語言得以改進這種情形。

動態 SQL 元素和 JSTL 或基於類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間了解。MyBatis 3 大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis 采用功能強大的基於 OGNL 的表達式來淘汰其它大部分元素。

--->if --->choose(when,otherwise) --->trim(where,set) --->foreach 此文章及以後不帶結果的截圖,影響整體文章的布局美感!!! 其他的一些可以簡單看一下之前的博文! 首先來看看本次工程的目錄吧: 技術分享圖片

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>
     <properties resource="db.properties" ></properties>
     <environments default
="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="DynamicSQL.xml"/> </mappers> </configuration>

Employee.java(getter&setter&toString)

public class Employee {
     private int id;
     private String name;
     private String gender;
     private String email;
     private Department dept;
}

現在基本的布局已經完成!!

1)if

A.在DynamicMapper.java接口中

//攜帶了哪個字段的查詢條件就攜帶這個字段的值
     public List<Employee> getEmpByIf(Employee emp);

在Dynamic‘SQl.xml文件
<!-- if -->
 <!-- 查詢員工,要求,攜帶了那個字段查詢條件就帶上那個字段的字段值 -->
 <!--      public List<Employee> getEmpByIf(Employee emp); -->
 <select id="getEmpByIf" resultType="com.MrChengs.bean.Employee">
     select * from test
     where
          <!-- test:判斷表達式(OGNL) -->
          <!-- OGNL:apache官方文檔有明確的解釋說明 -->
          <!-- 從參數中取值進行判斷不是數據庫中取值 -->
          <!-- 特殊字符應該寫轉義字符 -->
     <if test="id!=null">
           id=#{id}
     </if>
     <if test="name!=null and name!=‘‘">
           and name like #{name}
     </if>
     <if test="email!=null and email.trim()!=‘‘">
           and email like #{email}
     </if>
 </select>
在這個文件的內容簡要的進行說明一下: and name like #{name} 這裏的紅色的name是我們查詢的name值,不是數據庫中的name #{name}是把我們手動輸入的紅色name傳遞過去,進行數據庫的查詢 測試類:
public SqlSessionFactory getSqlSessionFactory() throws IOException{
           String resource = "mybatis-config.xml";
           InputStream inputStream = Resources.getResourceAsStream(resource);   
           return new SqlSessionFactoryBuilder().build(inputStream);
     }
     @Test
     public void test() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                //傳入的紅色name值進行數據庫的查詢
                Employee emp = new Employee(5, "%Mr%", "boy", "%1287%");
                List<Employee> emps = mapper.getEmpByIf(emp);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }

查詢之後的顯示代碼

DEBUG 10-02 12:13:49,806 ==>  Preparing: select * from test where id=? and name like ? and email like ?   (BaseJdbcLogger.java:159)
DEBUG 10-02 12:13:49,843 ==> Parameters: 5(Integer), %Mr%(String), %1287%(String)  (BaseJdbcLogger.java:159)
DEBUG 10-02 12:13:49,873 <==      Total: 1  (BaseJdbcLogger.java:159)
[Employee [id=5, name=MrChengs, gender=boy, [email protected], dept=null]]

B.在查詢的時候,如果某些時候某些條件沒帶可能導致sql拼裝有問題 實例:
<select id="getEmpByIf" resultType="com.MrChengs.bean.Employee">
     select * from test
     where
          <!-- test:判斷表達式(OGNL) -->
          <!-- OGNL:apache官方文檔有明確的解釋說明 -->
          <!-- 從參數中取值進行判斷不是數據庫中取值 -->
          <!-- 特殊字符應該寫轉義字符 -->
     <!--此時我們假設忘記把id傳進來 -->
     <if test="name!=null and name!=‘‘">
           and name like #{name}
     </if>
     <if test="email!=null and email.trim()!=‘‘">
           and email like #{email}
     </if>
 </select>

look:

show message:DEBUG 10-02 12:18:30,831 ==>  Preparing: select * from test where and name like ? and email like ? 
(BaseJdbcLogger.java:159)

solution ①: where 1=1
<select id="getEmpByIf" resultType="com.MrChengs.bean.Employee">
     select * from test
    <!-- 加入固定的條件,怎麽拼裝都行 -->
     where 1=1
          <!-- test:判斷表達式(OGNL) -->
          <!-- OGNL:apache官方文檔有明確的解釋說明 -->
          <!-- 從參數中取值進行判斷不是數據庫中取值 -->
          <!-- 特殊字符應該寫轉義字符 -->
     <if test="name!=null and name!=‘‘">
           and name like #{name}
     </if>
     <if test="email!=null and email.trim()!=‘‘">
           and email like #{email}
     </if>
 </select>

solution ②:使用<where></where> 只會去掉一個and 或者or
 <select id="getEmpByIf" resultType="com.MrChengs.bean.Employee">
     select * from test
     <where>
                <!-- test:判斷表達式(OGNL) -->
                <!-- OGNL:apache官方文檔有明確的解釋說明 -->
                <!-- 從參數中取值進行判斷不是數據庫中取值 -->
                <!-- 特殊字符應該寫轉義字符 -->
          <if test="name!=null and name!=‘‘">
                and name like #{name}
          </if>
          <if test="email!=null and email.trim()!=‘‘">
                and email like #{email}
          </if>
     </where>
 </select>

註意使用and

2.使用trim標簽進行,字符串截取

先看一個案例的錯誤代碼展示: DynamicSQLMapper.java
     //測試Trim
     public List<Employee> getEmpByIfTrim(Employee emp);

在DynamicSQL.xml

<!-- 測試Trim() -->
 <!-- public List<Employee> getEmpByIfTrim(Employee emp); -->
 <select id="getEmpByIfTrim" resultType="com.MrChengs.bean.Employee">
    select * from test
    where
     <if test="id!=null">
           id=#{id} and
     </if>
     <if test="name!=null and name!=‘‘">
           name like #{name}    and
     </if>
     <if test="email!=null and email.trim()!=‘‘">
           email like #{email}
     </if>
 </select>

假設我們此時傳參為name屬性一個
@Test
     public void testgetEmpByIfTrim() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                Employee emp = new Employee("%Mr%", null, null);
                List<Employee> emps = mapper.getEmpByIfTrim(emp);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }

拼串結果

DEBUG 10-02 13:31:59,995 ==>  Preparing: select * from test where id=? and name like ? and

開始使用trim標簽:(一些用法都在註釋中,請註意看註釋)

 <!-- 測試Trim() -->
 <!-- public List<Employee> getEmpByIfTrim(Employee emp); -->
 <select id="getEmpByIfTrim" resultType="com.MrChengs.bean.Employee">
    select * from test
<!-- prefix:前綴, trim標簽體中是整個字符串拼串後的結果 給拼串後的整體字符串加一個前綴--> <!-- prefixOverrides:前綴覆蓋, 去點整個前綴前面多余的字符串 --> <!-- suffix:後綴, 給拼串後的整個字符串加一個後綴 --> <!-- suffixOverrides:後綴覆蓋,去掉整個字符串後面多余的字符串 -->

<trim prefix="where" suffixOverrides="and"> <if test="name!=null and name!=‘‘"> name like #{name} and </if> <if test="email!=null and email.trim()!=‘‘"> email like #{email} and </if> <if test="gender!=null"> gender=#{gender} </if> </trim> </select>

測試:

public void testgetEmpByIfTrim() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                Employee emp = new Employee("%Mr%", null, null);
                List<Employee> emps = mapper.getEmpByIfTrim(emp);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }

結果拼串:

DEBUG 10-02 13:43:25,216 ==>  Preparing: select * from test where name like ?   (BaseJdbcLogger.java:159)
DEBUG 10-02 13:43:25,266 ==> Parameters: %Mr%(String)  (BaseJdbcLogger.java:159)

註意:在測試id的時候,不寫則默認為零,博主自己測試的時候遇到的,所以把id的查詢條件拿掉了!

3.choose分支選擇 如果帶了id使用id進行查詢,帶了name就是用name進行查詢 只能使用一個進行查詢 接口類的代碼:
//測試choose
     public List<Employee> getEmpBychoose(Employee emp);

DynamicSQL.xml:

<!-- choose -->
 <!-- 如果帶了id使用id進行查詢,帶了name就是用name進行查詢,只能使用一個進行查詢 -->
 <!-- public List<Employee> getEmpBychoose(Employee emp); -->
 <select id="getEmpBychoose" resultType="com.MrChengs.bean.Employee">
     select * from test
     <where>
           <choose>
                <when test="name!=null">
                     name like #{name}
                </when>    
                <when test="email!=null">
                     email = #{email}
                </when>
                <when test="id!=null">
                     id=#{id}
                </when>    
                <otherwise>
                     d_id=1
                </otherwise>
           </choose>
     </where>
 </select>

測試代碼:

     //測試choose
     @Test
     public void testgetEmpBychoose() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                Employee emp = new Employee("%Mr%", null, null);
                emp.setId(5);
                List<Employee> emps = mapper.getEmpBychoose(emp);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }

結果:

DEBUG 10-02 14:07:35,311 ==>  Preparing: select * from test WHERE name like ?   (BaseJdbcLogger.java:159)
DEBUG 10-02 14:07:35,363 ==> Parameters: %Mr%(String)  (BaseJdbcLogger.java:159)

此時我們不僅傳入了name同時還傳入了id,但是拼串之後是使用name進行查詢的

3.更新

A.<set></set>版本 在接口中:
     //更新方法
     public void updataEmp(Employee emp);

在DynamicSQl.xml文件:

<!-- update更新 -->
  <!-- 更新 -->
 <!-- public void updataEmp(Employee emp); -->
 <update id="updataEmp">
  update test
  <set>
  <if test="name!=null">name=#{name},</if>
  <if test="email!=null">  email=#{email},</if>
  <if test="gender!=null">gender=#{gender},</if>
  </set>
  where  id=#{id}
 </update>

使用<set>標簽,可以自動為我們解決存在的 ”,“ 問題

測試:
     //更新upddate
     @Test
     public void testgetEmpupdate() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                Employee emp = new Employee("MrChengsR", "gril", null);
                emp.setId(7);
                mapper.updataEmp(emp);
                System.out.println(emp);
                session.commit();
           }finally{
                session.close();
           }
     }

此時修改數據成功

B.<trim><trim> version 僅僅是修改xml文件,其余的都不變
<update id="updataEmp">
       update test
           <trim prefix="set" suffixOverrides=",">
                <if test="name!=null">name=#{name},</if>
                <if test="email!=null">  email=#{email},</if>
                <if test="gender!=null">gender=#{gender},</if>
           </trim>
       where  id=#{id}
      </update>

4.foreach

A)foreach: DynamicSQLMapper.java
     //foreach
     public List<Employee> getEmpsByCollection(List<Integer> list);

DynamicSQL.xml

     <!-- foreach: -->
      <!--      public List<Employee> getEmpsByCollection(Employee emp); -->
     <select id="getEmpsByCollection" resultType="com.MrChengs.bean.Employee" >
           select * from test where id in(
           <!-- collection:指定遍歷的集合 -->
           <!-- list類型的參數會做特殊的處理封裝在map中,map的key叫list -->
           <!-- item:將當前遍歷出的元素賦值給指定的變量 -->
           <!-- #{變量名} 就能取出當前遍歷的元素 -->
           <!-- separator:每個元素之間的分隔符    此時是in(a,b,c,d)這裏面的   , -->
           <!-- open:遍歷出所有結果拼接一個開始的字符 -->
           <!-- close:便利的所有結果拼出結尾 -->
           <!-- index:遍歷list是索引,遍歷map就是map的key -->
           <foreach collection="list" item="item_id" separator=",">
                #{item_id}
           </foreach>
           )
     </select>

測試類:
@Test
     public void testgetEmpForeach() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           
           SqlSession session = sessionFactory.openSession();
           
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                
                List<Employee> emps = mapper.getEmpsByCollection(Arrays.asList(5,7,8));
                for(Employee emp : emps){
                     System.out.println(emp);
                }
           }finally{
                session.close();
           }
     }

得到結果:

DEBUG 10-02 19:16:01,838 ==>  Preparing: select * from test where id in( ? , ? , ? )   (BaseJdbcLogger.java:159)
DEBUG 10-02 19:16:01,887 ==> Parameters: 5(Integer), 7(Integer), 8(Integer)  (BaseJdbcLogger.java:159)
DEBUG 10-02 19:16:01,909 <==      Total: 3  (BaseJdbcLogger.java:159)
Employee [id=5, name=MrChengs, gender=boy, [email protected], dept=null]
Employee [id=7, name=MrChengs, gender=gril, [email protected], dept=null]
Employee [id=8, name=MrChen, gender=gril, [email protected], dept=null]

B.批量保存

方法1: 接口類中:
     //批量存取
     public void addEmps(@Param("emps")List<Employee> employee);

xml文件:

     <!-- //批量存取-->
     <!--  public void addEmps(@Param("emps")Employee employee); -->
     <insert id="addEmps">
           insert into test(name,gender,email,d_id)
           values
           <foreach collection="emps" separator="," item="emp">
            <!-- 傳參數之前是我們new的一個對象,傳參數之後是插入數據庫的數據 -->
           (#{emp.name},#{emp.gender},#{emp.email},#{emp.dept.id})
           </foreach>
     </insert>

實現類:

//批量存取
     @Test
     public void testgetEmpaddEmps() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                List<Employee> employee = new ArrayList<Employee>();
                employee.add(new Employee("Ma", "gril", "Ma@Ma", new Department(1)));
                employee.add(new Employee("Mb", "boy", "Mb@Mb", new Department(2)));
                mapper.addEmps(employee);
                session.commit();
           }finally{
                session.close();
           }
     }
此時是成功插入數據 方法二:
<!-- 方法二 -->
     <!-- 需要加上 -->
     <!-- jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true -->
     <insert id="addEmps">
           <foreach collection="emps" separator=";" item="emp">
           insert into test(name,gender,email,d_id)
           values
           (#{emp.name},#{emp.gender},#{emp.email},#{emp.dept.id})
           </foreach>
     
     </insert>

其余不變可以進行測試

c.兩個重要的參數 <!-- 兩個重要的參數 --> <!-- _parameter:代表整個參數,單個參數就是這個參數,多個參數就是封裝成的map --> <!-- _databaseId:配置了databaseIdProvider標簽,就是代表當前數據庫的別名 --> _databaseId: mybatis-config.xml
<databaseIdProvider type="DB_VENDOR">
           <property name="MySQL" value="mysql"/>
           <property name="Oracle" value="oracle"/>
     </databaseIdProvider>

接口類中

//測試兩個屬性
     public List<Employee> getEmpselect();

DynamicMapper.xml

<!-- 兩個重要的參數 -->
     <!-- _parameter:代表整個參數,單個參數就是這個參數,多個參數就是封裝成的map -->
     <!-- _databaseId:配置了databaseIdProvider標簽,就是代表當前數據庫的別名 -->
     
     <!-- public Employee getEmpselect(int id); -->

    <!-- 修改if中的test條件即可實現不同數據庫之間的查詢 -->
     <select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql">
           <if test="_databaseId==‘mysql‘">
                select * from test
           </if>
           <if test="_databaseId==‘oracle‘">
                select * from test
           </if>
     </select>

測試類:

     //兩個重要的參數
     @Test
     public void testgetEmpselect() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                List<Employee> emps= mapper.getEmpselect();
                for(Employee emp : emps){
                     System.out.println();
                }
           }finally{
                session.close();
           }
     }

此時可以成功查詢數據!! _parameter 在接口類中:把剛剛測試代碼加上id
//測試兩個屬性
     public List<Employee> getEmpselect(int id);

在xnl文件中:

<!-- public Employee getEmpselect(int id); -->
     <select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql">
           <if test="_databaseId==‘mysql‘">
                select * from test
                <if test="_parameter!=null">
                     where id=#{id}
                </if>
           </if>
           <if test="_databaseId==‘oracle‘">
                select * from test
           </if>
     </select>

測試類:
@Test
     public void testgetEmpselect() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                List<Employee> emps= mapper.getEmpselect(5);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }
此時的查詢成功!!! D.bind標簽的使用 接口類中:
     //測試兩個屬性
     //public List<Employee> getEmpselect();
     //public List<Employee> getEmpselect(int id);
     public List<Employee> getEmpselect(Employee em);

xml文件:
<select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql"> <!-- bind:可以將OGNL表達式的值綁定到一個變量中,方便引用這個變量的值 --> <!-- name :是我們指定的綁定參數--> <!-- value :指定參數的值 --> <bind name="_name" value="‘%‘+name+‘%‘"/> <if test="_databaseId==‘mysql‘"> select * from test <if test="_parameter!=null"> where name like #{_name} </if> </if> <if test="_databaseId==‘oracle‘"> select * from test </if> </select>

測試類:

     @Test
     public void testgetEmpselect() throws IOException {
           SqlSessionFactory sessionFactory = getSqlSessionFactory();
           SqlSession session = sessionFactory.openSession();
           try{
                DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
                //List<Employee> emps= mapper.getEmpselect();
                //List<Employee> emps= mapper.getEmpselect(5);
                Employee emp = new Employee();
                emp.setName("M");
                List<Employee> emps= mapper.getEmpselect(emp);
                System.out.println(emps);
           }finally{
                session.close();
           }
     }

E.SQL標簽

<!-- <include refid=""></include> --> <!-- SQL:抽取可重用的sql字段,方便後面引用 --> <!-- include:就是引用外部標簽 --> <!-- 1.sql抽取:經常要查詢的列名,或者插入用的列名抽取出來方便引用 2.include來引用已經抽取的sql 3.include還可以自定義一些property,sql標簽內部只能使用自定義的屬性 include-property:取值正確方式 ${prop} #{不可以使用這種方式} --> <sql id=""> <!-- 同時這裏面還可以使用 if進行判斷 --> <if test=""></if> </sql>

MyBatis(4)動態SQL