2023.11.30 关于 MyBatis 动态 SQL 的使用
目录
引言
if 标签
trim 标签
where 标签
set 标签
foreach 标签
引言
- 动态 sql 是 MyBatis 的强大特性之一
- 允许你根据输入的参数动态地构建 sql 语句
- 从而在运行时根据不同的条件生成不同的 sql
核心思想
- 基于提供的数据和条件,能够修改、增加、删除 sql 语句的部分内容
- 这为编写更通用、可重用的 sql 提供了极大的灵活性
- 以下我们介绍 5 个常用的动态 sql 标签
if 标签
实例理解
- 此处我们想要实现 根据 name 和 age 字段来筛选用户信息 功能
创建数据库
- 在数据库中创建一个如下图所示的 user 表,并插入几条用户数据
- 注意这里的 state 状态
- 值为 1 表示该用户可正常登录
- 值为 0 表示该用户异常,无法登录
实现 UserMapper 接口
- 此处我们实现一个 selectUser 方法
import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; //添加 @Mapper 注解 代表该接口会伴随这 项目的启动而注入到容器中 @Mapper public interface UserMapper { // 根据 name 和 age 字段来筛选用户信息 List<User> selectUser(@Param("user_name") String name, @Param("user_age") Integer age); }
实现 UserMapper XML 文件
- 在与接口相对应的 XML 文件中
- 添加上与 selectUser 方法 相对应的 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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <select id="selectUser" resultType="com.example.demo.entity.User"> select * from user where 1=1 <if test="user_name != null"> and name = #{user_name} </if> <if test="user_age != null"> and age = #{user_age} </if> </select> </mapper>
分析功能 与 select 语句
- 根据 name 和 age 字段筛选用户信息 存在四种情况
情况一:
- name 和 age 均不为空,其所对应的 sql 语句为:
select * from user where name = #{user_name} and age = #{user_age}
情况二:
- name 和 age 均为空,其所对应的 sql 语句为:
select * from user
情况三:
- name 为空, age 不为空,其所对应的 sql 语句为:
select * from user where age = #{user_age}
情况四:
- name 不为空, age 为空,其所对应的 sql 语句为:
select * from user where name = #{user_name}
分析 XML 中的 select 语句
- 加上 if 标签后,该 sql 也将存在四种情况,与上述分析的四种情况相对应
注意:
- 理解此处加上的 绿框部分,即 '1=1'
- 如果删除绿框部分,那么我们再观察下图
- 所以 XML 中的 select 语句必须加上绿框部分,否则将会导致 sql 语法错误
创建 selectUser 的测试方法
- 我们随意选择上述四种情况的任意一种进行传参
- 此处我们选择仅传参 age= 20
@Test void selectUser() { List<User> users = userMapper.selectUser(null,20); users.stream().forEach(System.out::println); }
执行测试方法
- 测试方法成功执行
trim 标签
属性
- prefix:表示整个语句块,以 prefix 的值作为前缀
- suffix:表示整个语句块,以 suffix 的值作为后缀
- prefixOverrides:表示整个语句块,所需要去除的多余前缀
- suffixOverrides:表示整个语句块,所需要去除的多余后缀
实例理解
- 下方的 select 查询语句为了保证 sql 语法的正确性,还必须得添加上 '1=1'
<select id="selectUser" resultType="com.example.demo.entity.User"> select * from user where 1=1 <if test="user_name != null"> and name = #{user_name} </if> <if test="user_age != null"> and age = #{user_age} </if> </select>
- 但是我们可以使用 <trim> 标签来改写上方的 select 语句,使其可以不用添加上 '1=1'的同时保障 sql 语法的正确性
select * from user <trim prefix="where" suffixOverrides="and"> <if test="user_name != null"> name = #{user_name} and </if> <if test="user_age != null"> age =#{user_age} </if> </trim>
分析该 sql 语句
- 此处经存在一种情况会 发生去除多余后缀 'and'
注意:
- 当 <trim> 标签中生成了代码,那么才会添加 <trim> 标签里的前缀和后缀
- 如果 <trim> 标签中未生成代码,则前缀和后缀都会省略
where 标签
实例理解
- 我们同样可以使用 <wehre> 标签来改写上述 根据 name 和 age 字段来筛选用户信息 的 sql 语句
<select id="selectUser" resultType="com.example.demo.entity.User"> select * from user <where> <if test="user_name != null"> name = #{user_name} </if> <if test="user_age != null"> and age =#{user_age} </if> </where> </select>
注意:
- 上述实例为 <where> 标签的正确写法,不能写成下述 sql 语句
<select id="selectUser" resultType="com.example.demo.entity.User"> select * from user <where> <if test="user_name != null"> name = #{user_name} and </if> <if test="user_age != null"> age =#{user_age} </if> </where> </select>
- 因为 <where> 标签会帮去除最前面的多余 'and' 关键字 ,而不会帮去除最后面的多余 'and' 关键字
- 即 <where> 标签相当于 <trim prefix="where" prefixOverrides = "and">
set 标签
实例理解
- 此处我们想实现一个 根据用户 id 修改用户各属性 功能
准备数据库
- 在数据库中准备好一个 user 表
实现 UserMapper 接口
- 此处我们实现一个 updateById 方法
import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; //添加 @Mapper 注解 代表该接口会伴随这 项目的启动而注入到容器中 @Mapper public interface UserMapper { // 根据 id 修改用户信息 Integer updateById(User user); }
实现 UserMapper XML 文件
- 在与接口相对应的 XML 文件中
- 添加上与 updateById 方法 相对应的 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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <update id="updateById" parameterType="com.example.demo.entity.User"> update user <set> <if test="name != null"> name = #{name}, </if> <if test="age != 0"> age = #{age}, </if> <if test="password != null"> password = #{password}, </if> <if test="state != 0"> state = #{state} </if> </set> where id = #{id} </update> </mapper>
创建 updateById 的测试方法
- 此处我们修改 用户 id = 4 用户信息
- 修改其姓名、年龄、密码,不修改其状态
@Test void updateById() { User user = new User(); user.setId(4); user.setName("haoran"); user.setAge(20); user.setPassword("123123"); int result = userMapper.updateById(user); System.out.println("updateById 方法 :" + (result == 1 ? "修改成功" : "修改失败")); }
执行测试方法
- 测试方法成功执行
- 观察数据库中的 user 表
注意:
- <set> 标签相当于 <trim prefix="set" suffixOverrides = ",">
foreach 标签
属性
- collection:绑定方法参数中的集合,如 List、Set、Map 或数组对象
- item:遍历时的每一个对象
- open:语块开头的字符串
- close:语块结束的字符串
- separator:每次遍历之间间隔的字符串
实例理解
- 此处想实现一个 根据多个用户 Id 来批量封禁用户 功能
准备数据库
- 在数据库中准备好一个 user 表
实现 UserMapper 接口
- 此处我们实现一个 updateStateByIds 方法
import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; //添加 @Mapper 注解 代表该接口会伴随这 项目的启动而注入到容器中 @Mapper public interface UserMapper { // 根据多个 id 批量封禁用户 Integer updateStateByIds(@Param("user_ids") List<Integer> ids); }
实现 UserMapper XML 文件
- 在与接口相对应的 XML 文件中
- 添加上与 updateStateByIds 方法 相对应的 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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <update id="updateStateByIds"> update user set state = 0 where id in <foreach collection="user_ids" item="item" open="(" close=")" separator=","> #{item} </foreach> </update> </mapper>
创建 updateStateByIds 的测试方法
- 此处我们封禁 id = 1、4、8、9 的用户
@Test void updateStateByIds() { List<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(4); ids.add(8); ids.add(9); int result = userMapper.updateStateByIds(ids); System.out.println("updateById 方法 :" + (result > 1 ? "修改成功" : "修改失败")); }
执行测试方法
- 测试方法成功执行
- 观察数据库中的 user 表