1. 程式人生 > >Mybatis---動態SQL詳解(四)

Mybatis---動態SQL詳解(四)

(一)動態SQL簡介

    使用JDBC對資料庫進行操作,通常需要根據需求手動的拼接SQL或重新編寫SQL語句,這是一項非常無聊和麻煩的操作,但是Mybatis提供了對SQL語句動態組裝的功能,恰好解決這一項麻煩的操作。
參考: Mybatis官方文件

Mybatis動態SQL語句中的主要元素
元素 說明
<if> 用於單條件分支判斷
<choose> <when> <otherwise> 用於多條件分支判斷,類似Java中的switch語句
<where> <trim><set> 輔助元素用於處理sql拼裝、特殊字元等問題
<foreach> 迴圈語句用於in關鍵字列舉條件中
<bind> 從OGNL表示式中建立一個變數將其繫結到上下文,用於模糊查詢
專案結構:

這裡寫圖片描述這裡寫圖片描述

1.<if>元素

如果有多個<if test=”表示式”>元素,且test右邊的表示式為true,則SQL語句進行動態組裝。

例項演示

比如查詢客戶資訊,可以按使用者名稱或職位單欄位查詢,也可以使用者名稱和職位組合查詢(SQL動態組裝條件)
這裡寫圖片描述
01.在CustomerMapper.xml中編寫動態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+id確定一條SQL語句 -->
<mapper namespace="com.wang.mapper.CustomerMapper">
    <select id="findCustomerByNameAndJobs"
        parameterType
="com.wang.po.Customer" resultType="com.wang.po.Customer">
select*from t_customer where 1=1 <if test="username!=null and username!=''"> and username like concat('%',#{username},'%') </if> <!--jobs表示Customer物件的屬性不為空--> <if test="jobs!=null and jobs!=''"> and jobs=#{jobs} </if> </select> </mapper>

02.在mybatis-config.xml檔案中引入CustomerMapper.xml檔案

<mappers>
    <mapper resource="com/wang/mapper/CustomerMapper.xml" />
</mappers>

03.建立測試類MybatisTest.java

@Test
    public void findCustomersByNameAndJobs() {
        // 第一次查詢會話
        SqlSession sqlSession = MybatisUtils.getSession();

        Customer customer = new Customer();
        // 為customer物件屬性設值,Mapper.xml使用OGNL可以檢測username是否設值
        customer.setUsername("李四");
        customer.setJobs("程式設計師");
        List<Customer> customers = sqlSession.selectList("com.wang.mapper.CustomerMapper.findCustomerByNameAndJobs", customer);
        for (Customer customer1 : customers) {
            System.out.println(customer1);
        }
        sqlSession.close();

    }

結果

01.當只在使用者名稱輸入框中輸入李四時:SQL語句只組裝username欄位條件

DEBUG [main] - ==>  Preparing: select*from t_customer where 1=1 and
username like concat('%',?,'%') 
DEBUG [main] - ==> Parameters: 李四(String)
DEBUG [main] - <==      Total: 1
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]
02.當只在職業文字框輸入程式設計師時:SQL語句只組裝jobs欄位條件

DEBUG [main] - ==>  Preparing: select*from t_customer where 1=1 and jobs=? 
DEBUG [main] - ==> Parameters: 程式設計師(String)
DEBUG [main] - <==      Total: 1
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]
03.同時在使用者和職業文字框輸入李四和程式設計師時:SQL組裝username和jobs兩個條件

DEBUG [main] - ==>  Preparing: select*from t_customer where 1=1 
and username like concat('%',?,'%') and jobs=? 
DEBUG [main] - ==> Parameters: 李四(String), 程式設計師(String)
DEBUG [main] - <==      Total: 1
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]

2.<choose><when><otherwise>元素

<choose><when><otherwise>元素類似Java中switch語句,滿足其中的一個條件即可實現SQL動態組裝。

例項演示

這裡寫圖片描述
比如:01.使用者名稱不為空只能使用使用者名稱查詢。02.職業不為空只能使用職業來查詢,03.使用者名稱和職業都為空,那麼只能按電話號碼查詢。
01.在CustomerMapper.xml中編寫動態SQL語句

<select id="findCustomerByNameOrJobs"
        parameterType="com.wang.po.Customer"
        resultType="com.wang.po.Customer">
        select*from t_customer where 1=1
        <choose>
            <when test="username!=null and username!=''">
                and username like concat('%',#{username},'%')
            </when>
            <!--jobs表示Customer物件的屬性不為空 -->
            <when test="jobs!=null and jobs!=''">
                and jobs=#{jobs}
            </when>
            <otherwise>
                and phone is not null
            </otherwise>
        </choose>
    </select>

02.在mybatis-config.xml檔案中引入CustomerMapper.xml檔案

<mappers>
    <mapper resource="com/wang/mapper/CustomerMapper.xml" />
</mappers>

03.建立測試類MybatisTest.java


    @Test
    public void findCustomersByNameOrJobs() {
        // 第一次查詢會話
        SqlSession sqlSession = MybatisUtils.getSession();

        Customer customer = new Customer();
        // 為customer物件屬性設值,Mapper.xml使用OGNL可以檢測username是否設值

        customer.setUsername("李四");
        customer.setJobs("程式設計師");
        List<Customer> customers = sqlSession.selectList("com.wang.mapper.CustomerMapper.findCustomerByNameOrJobs", customer);
        for (Customer customer1 : customers) {
            System.out.println(customer1);
        }
        sqlSession.close();

    }

結果:

注意:假設同時輸入李四和程式設計師,SQL語句根據CustomerMapper.xml中
username和jobs配置先後決定動態組裝哪個欄位。

比如CustomerMapper.xml中先配置
<when test="username!=null and username!=''">
        and username like concat('%',#{username},'%')
</when>所以先先判斷username,即先判斷使用者名稱
01.出入使用者名稱為李四,職業為程式設計師SQL語句動態組裝為以下形式:

DEBUG [main] - ==>  Preparing: select*from t_customer 
where 1=1 and username like concat('%',?,'%') 
DEBUG [main] - ==> Parameters: 李四(String)
DEBUG [main] - <==      Total: 1
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]
02.使用者名稱和職業都不輸入SQL語句動態組裝為以下形式:

DEBUG [main] - ==>  Preparing: select*from t_customer where 1=1 
and phone is not null 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 3
Customer [id=1, username=張三, jobs=學生, phone=183999999]
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]
Customer [id=3, username=王五, jobs=醫生, phone=183888888]

3.<where>、<trim> 元素用來代替”where 1=1”

01.<where> 的使用
<select id="findCustomerByNameOrJobs"
        parameterType="com.wang.po.Customer" 
        resultType="com.wang.po.Customer">
        select*from t_customer
        <where>
            <choose>
                <when test="username!=null and username!=''">
                    and username like concat('%',#{username},'%')
                </when>
                <!--jobs表示Customer物件的屬性不為空 -->
                <when test="jobs!=null and jobs!=''">
                    and jobs=#{jobs}
                </when>

                <otherwise>
                    and phone is not null
                </otherwise>
            </choose>
        </where>
    </select>
02.<trim> 的使用
屬性 說明
prefix 表示使用where連線後面的sql
prefixOverrides 去除特殊字元。比如sql中的and和or等
<select id="findCustomerByNameOrJobs"
        parameterType="com.wang.po.Customer" 
        resultType="com.wang.po.Customer">
        select*from t_customer
        <trim prefix="where" prefixOverrides="and"> 
            <choose>
                <when test="username!=null and username!=''">
                    and username like concat('%',#{username},'%')
                </when>
                <!--jobs表示Customer物件的屬性不為空 -->
                <when test="jobs!=null and jobs!=''">
                    and jobs=#{jobs}
                </when>

                <otherwise>
                    and phone is not null
                </otherwise>
            </choose>
        </trim>
    </select>

4.<set> 元素代替SQL語句中的關鍵字set

    <set> 元素的作用:動態的修改資料表中的欄位。比如Hebernate更新某個物件需要傳送所有欄位給持久層物件,把持久層的所有都更新了一遍。在開發過程中往往不需要把資料庫中的欄位全部更新一遍,因此我們使用Mybatis動態SQL語句更新需要更新的資料,這樣增強了程式執行效率。

01.<set> 的使用(一條動態SQL可以在多個場景引用)
例項演示

這裡寫圖片描述
這裡寫圖片描述

<update id="updateCustomer" parameterType="com.wang.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>
            <if test="address !=null and address!=''">
                address=#{address}
            </if>
        </set>
        where id=#{id}
    </update>
@Test
    public void updateCustomersByNameOrJobsOrAddress() {
        // 第一次查詢會話
        SqlSession sqlSession = MybatisUtils.getSession();

        Customer customer = new Customer();
        // 為customer物件屬性設值,Mapper.xml使用OGNL可以檢測username是否設值
        customer.setId(3);
        //修改id為3的電話號碼
        customer.setPhone("183121212");
        customer.setJobs("程式設計師");
        int rows=sqlSession.update("com.wang.mapper.CustomerMapper.updateCustomer", customer);
        if(rows>0) {
            System.out.println("更新成功!");

        }else {
            System.out.println("更新失敗!");
        }

        sqlSession.commit();
        sqlSession.close();

    }

結果

注:修改兩個欄位

DEBUG [main] - ==>  Preparing: update t_customer SET jobs=?, phone=? 
where id=? 
DEBUG [main] - ==> Parameters: 程式設計師(String), 183121212(String), 3(Integer)
DEBUG [main] - <==    Updates: 1
更新成功!

5.<foreach> 元素用於查詢一個指定範圍類資料

<foreach> 元素主要屬性

屬性 說明
item 表示迴圈獲取的元素
index 表示當前元素在集合中的位置(即集合中的索引)
collection 配置查詢引數的型別(注:引數型別要小寫)
open和close 配置的是以什麼符號將這些集合元素包裝起來
separator 使用逗號分割元素

比如:查詢t_customer表中id小於或等於2的使用者資訊
這裡寫圖片描述
這裡寫圖片描述

01.CustomerMapper.xml

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

02.MybatisTest.java

@Test
    public void findCustomerByIds() {
        SqlSession sqlSession = MybatisUtils.getSession();

        // 建立List集合物件用來儲存id
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);

        List<Customer> customers = sqlSession.selectList("com.wang.mapper.CustomerMapper" + ".findCustomerByIds", ids);
        for (Customer customer : customers) {
            System.out.println(customer);
        }
        sqlSession.close();
    }

結果

DEBUG [main] - ==>  Preparing: select*from t_customer where id in ( ? , ? ) 
DEBUG [main] - ==> Parameters: 1(Integer), 2(Integer)
DEBUG [main] - <==      Total: 2
Customer [id=1, username=張三, jobs=學生, phone=183999999]
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]

6.<bind> 元素等價於concat()函式都是用於模糊查詢

比如:模糊查詢t_customer資料表使用者名稱為“李”的客戶資訊
這裡寫圖片描述這裡寫圖片描述
01.CustomerMapper.xml

<select id="findCustomerByIds" parameterType="com.wang.po.Customer"
        resultType="com.wang.po.Customer">
        <!-- value:填寫Customer中的username屬性 -->
         <bind name="name" value="'%'+username+'%'"/>
         select*from t_customer where username like #{name}
</select>

02.MybatisTest.java


    @Test
    public void findCustomerByName() {
        SqlSession sqlSession = MybatisUtils.getSession();

        Customer customer=new Customer();
        customer.setUsername("李");
        List<Customer> customers = sqlSession.selectList("com.wang.mapper."
                + "CustomerMapper" + ".findCustomerByIds", customer);
        for (Customer customer1 : customers) {
            System.out.println(customer1);
        }
        sqlSession.close();
    }

結果

DEBUG [main] - ==>  Preparing: select*from t_customer where username like ? 
DEBUG [main] - ==> Parameters: %李%(String)
DEBUG [main] - <==      Total: 1
Customer [id=2, username=李四, jobs=程式設計師, phone=183666666]