MyBatis——動態SQL、if、where、set、foreach、sql片段

目錄

在這裏插入圖片描述

一、 動態SQL

跳轉到目錄

  • MyBatis的強大特性之一便是它的動態SQL

  • 如果你有使用JDBC或其他類似框架的經驗,你就能體會到根據不同條件拼接SQL語句有多麼痛苦。

  • 拼接的時候要確保不能忘了必要的空格, 還要注意省掉列名列表最後的逗號。利用動態SQL這一特性可以徹底擺脫這種痛苦。

  • 通常使用動態SQL不可能是獨立的一部分, MyBatis當然使用一-種強大的動態SQL語言來改進這種情形,這種語言可以被用在任意的SQL映射語句中。

  • 和標籤庫JSTL很像

1、if

跳轉到目錄
<if test="boolean表達式"></if>

  • if元素用於判斷,一般用作是否應該包含某一個查詢條件

測試類

/**
     * 查詢工資大於等於1000的員工
     */
    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        BigDecimal minSalary = new BigDecimal("1001");
        List<Employee> employees = mapper.query1(minSalary);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        sqlSession.close();
    }

    /**
     * 查詢工資在1000-2000之間的員工
     */
    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        BigDecimal minSalary = new BigDecimal("1000");
        BigDecimal maxSalary = new BigDecimal("2000");
        List<Employee> employees = mapper.query2(minSalary, maxSalary);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        sqlSession.close();
    }

Employee接口

/**
     * 查詢工資大於1000的員工
     */
    List<Employee> query1(@Param("minSalary") BigDecimal minSalary);

    /**
     * 查詢工資在1000-2000之間
     * @param minSalary
     * @param maxSalary
     * @return
     */
    List<Employee> query2(
            @Param("minSalary") BigDecimal minSalary,
            @Param("maxSalary") BigDecimal maxSalary
    );

EmployeeMapper.xml

    <!--查詢工資大於等於1000的員工-->
    <select id="query1" resultType="Employee">
        SELECT * FROM employee
        <if test="minSalary!=null">
            WHERE salary >= #{minSalary}
        </if>
    </select>

    <!--查詢工資在1000-2000之間的員工-->
    <select id="query2" resultType="Employee">
        SELECT * FROM employee WHERE 1 = 1
        <if test="minSalary!=null">
            AND salary >= #{minSalary}
        </if>
        <if test="maxSalary!=null">
            AND salary &lt;= #{maxSalary};
        </if>
    </select>

注意: 如果minSalary和maxSalary條件是可選擇的,也就是說當minSalary傳入null時,SQL就會出現問題.就不確定使用WHERE還是AND來連接查詢條件.

解決方案: 使用WHERE 1 = 1方式,其他的查詢條件都使用AND或OR連接,但是WHERE 1 = 1 會影響查詢性能.

2、choose、when、otherwise

跳轉到目錄

  • 相當於switch判斷

測試方法

    /**
     * 查詢指定部門的員工信息
     */
    @Test
    public void test3(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        BigDecimal minSalary = new BigDecimal("100");
        BigDecimal maxSalary = new BigDecimal("1000");
        List<Employee> employees = mapper.query3(minSalary, maxSalary, 20L);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        sqlSession.close();
    }

Employee接口中的方法

  List<Employee> query3(
          @Param("minSalary") BigDecimal minSalary,
          @Param("maxSalary") BigDecimal maxSalary,
          @Param("deptId") Long deptId
  );

EmployeeMapper.xml

<!--查詢指定部門的員工信息-->
  <select id="query3" resultType="Employee">
      SELECT * FROM employee WHERE 1 = 1
      <if test="minSalary!=null">
          AND salary >= #{minSalary}
      </if>
      <if test="maxSalary!=null">
          AND salary &lt;= #{maxSalary}
      </if>
      <!--假如下拉列表獲取的部門id,"所在部門"這個要排除,設爲-1-->
      <!--<if test="deptId > 0">
          AND deptId = #{deptId}
      </if>-->
      <choose> <!--相當於switch判斷-->
          <when test="deptId > 0">AND deptId = #{deptId}</when>
          <otherwise>AND deptId IS NOT NULL</otherwise>
      </choose>
  </select>

3、trim(where,set)

跳轉到目錄

3.1、where

跳轉到目錄

  • where元素: 判斷查詢條件是否有WHERE關鍵字,如果沒有,則在第一個查詢條件之前,插入一個WHERE, 如果發現查詢條件AND 或者 OR開頭,也會把第一個查詢條件前的AND/OR 替換成WHERE
  • 這種方式避免了 WHERE 1 = 1的形式

查詢指定部門的員工信息

    <select id="query3" resultType="Employee">
        SELECT * FROM employee
        <where>
            <if test="minSalary!=null">
                AND salary >= #{minSalary}
            </if>
            <if test="maxSalary!=null">
                AND salary &lt;= #{maxSalary}
            </if>
            <choose> <!--相當於switch判斷-->
                <when test="deptId > 0">AND deptId = #{deptId}</when>
                <otherwise>AND deptId IS NOT NULL</otherwise>
            </choose>
        </where>
    </select>

3.2、set

跳轉到目錄

  • set元素和where元素相似,也能根據set中的sql動態的去掉最後的逗號,並在前面添加set關鍵字,如過沒有內容,也會選擇忽略set語句.

應用場景
在這裏插入圖片描述
因爲password沒有設置值,所以就要採用if來動態判斷password是否爲空,如果爲空,則不拼接,但是此時會出現問題,上面拼接的語句最後會存在一個,.
在這裏插入圖片描述
這個時候就採用set元素來操作了,可以去掉後面的,.

測試方法

    /**
     * 更新指定id的員工信息
     */
    @Test
    public void test4(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

        Employee employee = new Employee();
        employee.setId(6L);
        employee.setSn("6238");
//        employee.setName("桂陽");
        employee.setSalary(new BigDecimal("8888"));

        int update = mapper.update(employee);
        if (update > 0){
            System.out.println("成功修改"+update+"條用戶信息!");
        }

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

EmployeeMapper接口

int update(Employee employee);

EmployeeMapper.xml

<update id="update">
        UPDATE employee
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="sn!=null">
                sn = #{sn},
            </if>
            <if test="salary!=null">
                salary = #{salary},
            </if>
        </set>
        WHERE id = #{id};
    </update>

3.3、trim

跳轉到目錄
trim是更強大的格式化SQL的標籤:

<trim prefix="" prefixOverrides="" suffix="" suffixOverrides="">
	<!--trim包含的動態SQL-->
</trim>

前提如果trim元素包含內容返回一個字符串,則

  • prefix : 在這個字符串之前插入prefix屬性值

  • prefixOverrides : 字符串內容以prefixOverrides中的內容開頭(可以包含管道符號|),那麼使用prefix屬性值替換內容的開頭.

  • suffix : 在這個字符串之後插入suffix屬性值

  • suffixOverrides : 字符串的內容以suffixOverrides中的內容結尾(可以包含管道符號|),那麼使用suffix屬性值替換內容的結尾;
    在這裏插入圖片描述

  • 使用where等價於

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
    </trim>
    
    <select id="query3" resultType="Employee">
        SELECT * FROM employee
        <!--和使用where標籤效果一樣-->
        <trim prefix="WHERE" prefixOverrides="AND|OR">
            <if test="minSalary!=null">
                AND salary >= #{minSalary}
            </if>
            <if test="maxSalary!=null">
                AND salary &lt;= #{maxSalary}
            </if>
            <choose> <!--相當於switch判斷-->
                <when test="deptId > 0">AND deptId = #{deptId}</when>
                <otherwise>AND deptId IS NOT NULL</otherwise>
            </choose>
        </trim>
    </select>
    

    注意: 此時AND後面有一個空格

  • 使用set等價於

    <trim prefix="WHERE" suffix="" suffixOverrides=",">
    </trim>
    
    trim prefix="SET" suffix="" suffixOverrides=",">
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="sn!=null">
                sn = #{sn},
            </if>
            <if test="salary!=null">
                salary = #{salary},
            </if>
        </trim>
    

4、foreach

跳轉到目錄
SQL中有時候使用IN關鍵字,如WHERE id IN(10,20,30),此時可以使用${ids}直接拼接SQL ,但是會導致SQL注入問題,要避免SQL注入,只能使用#{}方式,此時就可以配合使用foreach元素了。foreach元素用於迭代-個集合/數組, 通常是構建在IN運算符條件中

在這裏插入圖片描述

  • foreach元素:
    • collection屬性:表示對哪一個集合或數組做迭代
      如果參數是數組類型,此時Map的key爲array;
      如果參數是List類型,此時Map的key爲list;
    • open屬性:在迭代集合之前,拼接什麼符號
    • close屬性:在迭代集合之後,拼接什麼符號
    • separactor屬性:在迭代元素時,每一個元素之間使用什麼符號分割開來
    • item屬性:被迭代的每一個元素的變量
    • index屬性:迭代的索引

需求: 刪除ID爲10,20,30的數據
在這裏插入圖片描述

需求: 批量插入語法

在這裏插入圖片描述

注意: 當傳遞一個List對象或數字對象參數給MyBatis時,(這裏可以看前面講MyBatis參數處理的部分),MyBatis會自動把它包裝到一個Map中,當是List對象時會以list作爲key, 數組對象會以array作爲key. 一般使用Param註解設置key名.

EmployeeMapperTest測試類

    /**
     * 批量刪除指定id的員工信息
     */
    @Test
    public void test5(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        mapper.batchDelete(new Long[]{10L,20L,30L});

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

    /**
     * 批量插入員工信息
     */
    @Test
    public void test6(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        List<Employee> list = new ArrayList<Employee>();
        list.add(new Employee(null, "周", "10001", new BigDecimal("5555.00"), 50L));
        list.add(new Employee(null, "吳", "10002", new BigDecimal("6666.00"), 60L));
        list.add(new Employee(null, "鄭", "10003", new BigDecimal("7777.00"), 70L));
        int count = mapper.batchInsert(list);
        if (count > 0){
            System.out.println("成功插入了:"+count+"條用戶信息!");
        }

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

EmployeeMapper接口

    /**
     * 使用foreach元素批量刪除
     * @param ids
     * param註解原理還是Map,Map的key
     */
    void batchDelete(@Param("ids") Long[] ids);

    /**
     * 批量插入用戶信息
     * @param list
     * @return
     * 當參數是數組或集合時,一般要加上@Param註解,寫死
     */
    int batchInsert(@Param("emps") List<Employee> emps);

EmployeeMapper.xml

    <!--使用foreach元素_完成批量刪除-->
    <delete id="batchDelete">
        DELETE FROM employee WHERE id IN
        <!--
            foreach元素:
                collection屬性:表示對哪一個集合或數組做迭代
                       如果參數是數組類型,此時Map的key爲array;
                       如果參數是List類型,此時Map的key爲list;
                open屬性:在迭代集合之前,拼接什麼符號
                close屬性:在迭代集合之後,拼接什麼符號
                separactor屬性:在迭代元素時,每一個元素之間使用什麼符號分割開來
                item屬性:被迭代的每一個元素的變量
                index屬性:迭代的索引
        -->
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>

    <!--使用foreach元素_完成批量插入-->
    <insert id="batchInsert">
        INSERT INTO employee(id, name, sn, salary, deptId) VALUES
        <foreach collection="emps" separator="," item="e">
            (#{e.id}, #{e.name}, #{e.sn}, #{e.salary}, #{e.deptId})
        </foreach>
    </insert>

5、sql、include、bind

跳轉到目錄

  • 使用sql可以把相同的sql片段起一個名字,並使用include在sql任意位置使用.
  • bind: 使用OGNL表達式創建一個變量,並將其綁定在上下文中.

需求: 按照員工的關鍵字、工資範圍、所屬部門來查詢
需求: 按照查詢條件了查詢員工的人數

注意: 要封裝一個查詢條件類,用來設置條件使用

EmployeeQueryObject 封裝查詢條件的

package com.sunny.query;

import lombok.Data;
import java.math.BigDecimal;

/**
 * 封裝員工的高級查詢信息--->封裝查詢條件
 */
@Data
public class EmployeeQueryObject {
    private String keyword; // 根據keyword來查詢,員工名字或編號
    private BigDecimal minSalary; // 最低工資
    private BigDecimal maxSalary; // 最高工資
    private Long deptId = -1L; // 部門ID,缺省爲-1;表示所有部門

    /**
     * 重寫keyword的get方法,如果
     * @return
     */
    public String getKeyword(){
    	// 防止傳的條件是空或空字符
        return empty2null(keyword);
    }

    // 如果字符串爲空串,也應該設置爲null
    private String empty2null(String str){
        return hasLength(str) ? str : null;
    }

    // 判斷這個字符串是否有數據
    private boolean hasLength(String str){
        // str不爲空 並且 str trim()後不和""相等
        /**
         * 判斷非空,假如str爲zy
         * zy!=null ---> true
         * "".equals(zy.trim()) --> false
         * !"".equals(zy.trim()) ---> !false ---> true
         * 所以
         * true && true ---> true 不爲空
         */
        return str!=null && !"".equals(str.trim());
    }
}

EmployeeMapper接口

public interface EmployeeMapper {
    
    /**
     * 根據查詢條件來查詢員工
     * @param qo 封裝查詢條件的類對象
     * @return
     */
    List<Employee> queryForList(EmployeeQueryObject qo);

    /**
     * 根據查詢條件來查詢員工人數
     * @param qo 封裝查詢條件的類對象
     * @return
     */
    int queryForEmpCount(EmployeeQueryObject qo);
}

EmployeeMapper.xml

<?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:綁定一個對應的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.EmployeeMapper">

    <!--多個查詢共同使用的sql-->
    <sql id="Base_where">
        <where>
            <if test="keyword!=null">
                <bind name="keywordLike" value="'%' + keyword +'%'"/>
                AND (name LIKE #{keywordLike} OR sn LIKE #{keywordLike})
              <!--AND (name LIKE concat('%', #{keyword}, '%') OR sn LIKE concat('%', #{keyword}, '%'))-->
            </if>
            <if test="minSalary!=null">
                AND salary >= #{minSalary}
            </if>
            <if test="maxSalary!=null">
                AND salary &lt;=#{maxSalary}
            </if>
            <if test="deptId!=null">
                AND deptId = #{deptId}
            </if>
        </where>
    </sql>

    <!--根據查詢條件來查詢符合條件的查詢-->
    <select id="queryForList" resultType="Employee">
        SELECT * FROM employee
        <include refid="Base_where"></include>
    </select>

    <!--查詢符合條件的員工數量-->
    <select id="queryForEmpCount" resultType="int">
        SELECT count(*) FROM employee
        <include refid="Base_where"></include>
    </select>

</mapper>

EmployeeMapperTest測試類

public class EmployeeMapperTest {
    
    /**
     * 按照員工的關鍵字、工資範圍、所屬部門來查詢
     */
    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        EmployeeQueryObject qo = new EmployeeQueryObject();
        qo.setKeyword("2");
        qo.setMinSalary(new BigDecimal("1000"));
        qo.setMaxSalary(new BigDecimal("9000"));
        qo.setDeptId(30L);
        List<Employee> employees = mapper.queryForList(qo);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        sqlSession.close();
    }

    /**
     * 按照查詢條件了查詢員工的人數
     */
    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        EmployeeQueryObject qo = new EmployeeQueryObject();
        qo.setKeyword("2");
        qo.setMinSalary(new BigDecimal("1000"));
        qo.setMaxSalary(new BigDecimal("9000"));
        qo.setDeptId(30L);
        int i = mapper.queryForEmpCount(qo);
        if (i > 0) {
            System.out.println("符合條件的一共有:"+i+"人!");
        }
        sqlSession.close();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章