本文主要介绍 MyBatis 动态SQL,它是MyBatis 的强大特性之一,它可以帮你解决不同条件拼接 SQL 语句,比如空格,逗号,单双引号,各种语句关键字等,利用 MyBatis 动态 SQL,可以轻松处理掉。如果你还对于MyBatis基本使用不了解的可以参考我我之前的文章:
MyBatis笔记(一)Spring Boot整合MyBatis实现增删查改详解(入门版)
MyBatis笔记(二)MyBatis参数传递详解
本文中案例返回结果(com.alian.mybatissql.dto.EmployeeDto)的一个一个映射如下:
EmployeeDto.java
package com.alian.mybatissql.dto;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
@Data
public class EmployeeDto implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 员工编号
*/
private String id;
/**
* 员工姓名
*/
private String name;
/**
* 员工年龄
*/
private int age;
/**
* 工资
*/
private double salary;
/**
* 部门
*/
private String department;
/**
* 入职时间
*/
private LocalDate hireDate;
}
EmployeeMapper.java接口定义如下:
/**
* 根据名字查找员工,如果又部门则部门一起参与查询
*/
List<EmployeeDto> getByNameWithDepartment(@Param("name") String name, @Param("department") String department);
EmployeeMapper.xml里对应的配置如下:
<select id="getByNameWithDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
WHERE
e.emp_name= #{name}
<if test="department != null and department!=''">
AND e.department = #{department}
if>
select>
当传入的department 为空,表示
SELECT
e.id, e.emp_name as name, e.age, e.salary, e.department, e.hire_date as hireDate
FROM tb_inf_employee e
WHERE
e.emp_name= #{name};
当传入的department 不为空,表示
SELECT
e.id, e.emp_name as name, e.age, e.salary, e.department, e.hire_date as hireDate
FROM tb_inf_employee e
WHERE
e.emp_name= #{name} AND e.department = #{department};
EmployeeMapper.java接口定义如下:
/**
* 根据名字查或者部门查询员工信息
*/
List<EmployeeDto> getByNameAndDepartment(@Param("name") String name, @Param("department") String department);
EmployeeMapper.xml里对应的配置如下:
<select id="getByNameAndDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
WHERE
<if test="name != null and name!=''">
e.emp_name= #{name}
if>
<if test="department != null and department!=''">
AND e.department = #{department}
if>
select>
当name不为空时,不管department 有没有值都可以执行,但是当name为空,department 不为空时就有问题了,此时的查询语句变成:
SELECT
e.id, e.emp_name as name, e.age, e.salary, e.department, e.hire_date as hireDate
FROM tb_inf_employee e
WHERE
AND e.department = #{department};
从语句我们也知道这是一个明显的错误语句,有些小伙伴可能会这样做(在where后面添加一个恒为true的条件,之后的
<select id="getByNameAndDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
WHERE 1=1
<if test="name != null and name!=''">
AND e.emp_name= #{name}
if>
<if test="department != null and department!=''">
AND e.department = #{department}
if>
select>
这时不管两个值为为不为空都不会有问题了,当然这不是最优解,MyBaits也想到了,也就是我们接下里要说的 where + if 语句。
EmployeeMapper.java接口定义如下(还是上一个示例):
/**
* 根据名字查或者部门查询员工信息
*/
List<EmployeeDto> getByNameAndDepartment(@Param("name") String name, @Param("department") String department);
EmployeeMapper.xml里对应的配置如下:
<select id="getByNameAndDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
<where>
<if test="name != null and name!=''">
e.emp_name= #{name}
if>
<if test="department != null and department!=''">
AND e.department = #{department}
if>
where>
select>
从上面的xml配置上看到,我们的SQL直接省略了那个恒为true条件,即算第一句加上 AND也能正常运行,因为MyBaits已经帮我们处理好了。
EmployeeMapper.java接口定义如下:
/**
* 根据id更新部门和薪资
*/
int updateDepartmentAndSalaryById(@Param("id") String id, @Param("department") String department,
@Param("salary") double salary);
EmployeeMapper.xml里对应的配置如下:
<update id="updateDepartmentAndSalaryById">
update tb_inf_employee e
<set>
<if test="department != null and department!=''">
e.department = #{department},
if>
<if test="salary > 0">
e.salary= #{salary},
if>
set>
<where>
e.id= #{id}
where>
update>
如果所有的条件同时为空,那么
EmployeeMapper.java接口定义如下:
/**
* 根据id更新部门和薪资
*/
int updateDepartmentAndSalaryById(@Param("id") String id, @Param("department") String department,
@Param("salary") double salary);
EmployeeMapper.xml里对应的配置如下:
<update id="updateDepartmentAndSalaryById">
update tb_inf_employee e
<trim prefix="set" suffixOverrides=",">
<if test="department != null and department!=''">
e.department = #{department},
if>
<if test="salary > 0">
e.salary= #{salary},
if>
trim>
<where>
e.id= #{id}
where>
update>
这么看来,这个
EmployeeMapper.java接口定义如下:
/**
* 根据名字查或者部门查询员工信息
*/
List<EmployeeDto> getByNameAndDepartment(@Param("name") String name, @Param("department") String department);
EmployeeMapper.xml里对应的配置如下:
<select id="getByNameAndDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name!=''">
AND e.emp_name= #{name}
if>
<if test="department != null and department!=''">
AND e.department = #{department}
if>
trim>
select>
EmployeeMapper.java接口定义如下:
/**
* 根据给定id或姓名或部门信息查询员工信息
*/
List<EmployeeDto> getByIdOrNameOrDepartment(@Param("id") String id, @Param("name") String name,
@Param("department") String department);
EmployeeMapper.xml里对应的配置如下:
<select id="getByIdOrNameOrDepartment" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
<where>
<choose>
<when test="id != null and id!=''">
e.id = #{id}
when>
<when test="name != null and name!=''">
e.emp_name = #{name}
when>
<otherwise>
e.department = #{department}
otherwise>
choose>
where>
select>
这段代码的意思是:
实际这种结构和java里的switch结构很像:
switch (条件) {
case 员工id不为空:
//根据id查询员工
break;
case 员工姓名不为空:
//根据姓名查询员工
break;
default: //根据部门查询员工
}
EmployeeMapper.java接口定义如下:
/**
* 根据id列表查询员工
*/
List<EmployeeDto> getByIdIn(@Param("idList") List<String> idList);
EmployeeMapper.xml里对应的配置如下:
<select id="getByIdIn" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
where
<foreach collection="idList" item="item" index="index" open="e.id IN(" separator="," close=")">
#{item}
foreach>
select>
实际执行语句如下:
SELECT e.id, e.emp_name as name, e.age, e.salary, e.department, e.hire_date as hireDate
FROM tb_inf_employee e
where e.id IN( ? , ? , ? )
关于
EmployeeMapper.java接口定义如下:
/**
* 根据id列表查询员工
*/
List<EmployeeDto> getByIdOr(@Param("idList") List<String> idList);
EmployeeMapper.xml里对应的配置如下:
<select id="getByIdOr" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
where
<foreach collection="idList" item="item" index="index" separator="or" >
e.id = #{item}
foreach>
select>
实际执行语句如下:
SELECT e.id, e.emp_name as name, e.age, e.salary, e.department, e.hire_date as hireDate
FROM tb_inf_employee e
where e.id = ? or e.id = ? or e.id = ?
EmployeeMapper.java接口定义如下:
/**
* 查询所有员工列表
*/
List<EmployeeDto> findAll();
EmployeeMapper.xml里对应的配置如下:
<sql id="fieldList">e.id,e.emp_name as name,e.age,e.salary,e.department,e.hire_date as hireDatesql>
<select id="findAll" resultType="EmployeeDto">
SELECT
<include refid="fieldList">include>
FROM
tb_inf_employee e
select>
之前我们示例里的字段一直都在重复的写,这个时候,我们就可以直接调用定义的
EmployeeMapper.java接口定义如下:
/**
* 根据姓名模糊查询员工
*/
List<EmployeeDto> getByNameLikeWithBindSql(@Param("name") String name);
EmployeeMapper.xml里对应的配置如下:
<select id="getByNameLikeWithBindSql" resultType="EmployeeDto">
<bind name="pattern" value="'%' + name + '%'"/>
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
where
e.emp_name like #{pattern}
select>
需要注意的是#{pattern},这里的pattern是上面定义的bind标签的name,而不是接口定义的name。我这里是演示bind标签的使用,实际的模糊查询不会这么写:
<select id="getByNameLike" resultType="EmployeeDto">
SELECT
e.id,
e.emp_name as name,
e.age,
e.salary,
e.department,
e.hire_date as hireDate
FROM
tb_inf_employee e
where
e.emp_name like CONCAT('%',#{name},'%')
select>
MyBatis 动态SQL就说到这里,主要还是要实际使用,加深理解,后续有时间我们继续研究其他方面的。