引言
动态 SQL 主要是用来完成不同条件下的 SQL 拼接的,它可以通过一些例如 if 标签、where 标签等,来直接限制 SQL 语句的条件。
如果说 SQL 是 HTML 代码,那么动态 SQL 的标签就相当于 JS,它可以对 SQL 语句进行变换、更改、限制。
传统 SQL 与 动态 SQL 之间的区别
我们观察下面的 userinfo 表,发现 username 和 password 是必传参数,而 photo 是有默认值的。
所以,我们预期实现下面的两个测试。
第一个测试:插入 username、password、photo
带一个测试:插入 username 、password
那么,我们预期的第二个测试的 photo 的字段值为默认的 " 123.png ",然而,第一个测试就是我们自己插入而设置的 photo 值。
很显然,如果我们利用 MyBatis 写 SQL 语句,应该如下写:
-- 第一个测试 insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo}) -- 第二个测试 insert into userinfo (username, password) values (#{username}, #{password})
基于上述的过程,我们发现,在传统的 SQL 语句写法上,针对于两次测试,我们必须写两个 SQL 语句。这就带来一个问题,如果客户端需要改变需求了,那么后端程序员就要重写写 SQL,也就要重新改变 xml 文件中的一些配置,这会很麻烦…
但实际上,如果我们利用 if 标签,就能够直接对 photo 这个字段进行限制,从而只需要写一个 SQL 语句即可。根据一个 SQL 语句,再加上一些限制标签,就能够很好地解决相似的问题。
综上所述,我们利用 MyBatis 为我们提供的标签来实现动态 SQL,会带来一个灵活的功能:后端在传 SQL 参数时,能够方便地控制增删改查的条件了。
1. if 标签
主要作用
if 标签常用来判断一个参数是否被需要,如果参数不需要,就会隐藏 SQL.
代码实现
UserInfo 类:
@Data public class UserInfo { private int id; private String username; private String password; private String photo; private String createtime; private String updatetime; private int state; }
" UserMapper " 接口:
@Mapper public interface UserMapper { // 添加新用户,使用动态 SQL 中的 if 标签 public int addUser2(UserInfo userInfo); }
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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <!-- 添加新用户,使用动态 SQL 中的 if 标签 --> <!-- insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo}) --> <insert id="addUser2"> insert into userinfo (username, password <if test="photo != null"> ,photo </if> )values (#{username}, #{password} <if test="photo != null"> ,#{photo} </if> ) </insert> </mapper>
测试类1:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 添加新用户,使用动态 SQL 中的 if 标签 @Test void addUser2() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("JJ"); userInfo.setPassword("246"); userInfo.setPhoto("JJ.png"); int result = userMapper.addUser2(userInfo); System.out.println("测试结果: " + result); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
测试类2:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 添加新用户,使用动态 SQL 中的 if 标签 @Test void addUser2() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("云云"); userInfo.setPassword("137"); int result = userMapper.addUser2(userInfo); System.out.println("测试结果: " + result); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
总结 if 标签语法
if 标签中的 test 是必传参数,传的是对象的属性。
<if test=""> </if>
如下图所示,if 标签中的值既可以写表的字段,也可以写对象的属性,通常情况下,这两者就是用来相互配合的,我们应该弄清楚两者的区别。
此外,如果真的分不清楚两者的关系,我们就可以将实体类的成员变量和数据表的字段写成一样的名字,这样就可以无差别对待了。
2. trim 标签
主要作用
trim 标签一般和 if 标签搭配使用,它能够为 SQL 语句添加或去除某个字符(串)。
代码实现
UserInfo 类:
@Data public class UserInfo { private int id; private String username; private String password; private String photo; private String createtime; private String updatetime; private int state; }
" UserMapper " 接口:
@Mapper public interface UserMapper { // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签 public int addUser3(UserInfo userInfo); }
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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <!-- 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签 --> <!-- insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo}) --> <insert id="addUser3"> insert into userinfo <trim prefix="(" suffix=")" suffixOverrides=","> <if test="username != null"> username, </if> <if test="password != null"> password, </if> <if test="photo != null"> photo </if> </trim> values <trim prefix="(" suffix=")" suffixOverrides=","> <if test="username != null"> #{username}, </if> <if test="password != null"> #{password}, </if> <if test="photo != null"> #{photo} </if> </trim> </insert> </mapper>
测试类1:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签 @Test void addUser3() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("莉莉"); userInfo.setPassword("632"); int result = userMapper.addUser3(userInfo); System.out.println("测试结果: " + result); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
测试类2:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签 @Test void addUser3() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("杰杰"); userInfo.setPassword("168"); userInfo.setPhoto("杰杰.png"); int result = userMapper.addUser3(userInfo); System.out.println("测试结果: " + result); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
代码分析
测试类1 的 SQL 转换,即去除 " 逗号 " 的过程,如下图所示:
此外,trim 标签非常智能,它能够自动检测 SQL 语句块的末尾是否真的有 " 逗号 ",例如我们上面的测试类2 中,我们实现了 【 suffixOverrides = “,” 】,但 photo 的末尾是没有 " 逗号 "的,也不会报错。如下所示:
insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo})
总结 trim 标签语法
trim 标签有四个属性,它们可以达到往 SQL 语句块中拼接或删除字符(串)的作用,从而控制 SQL 的写法。四个属性不是必须的,可以根据语法自行设置。
① prefix:需要拼接的前缀字符串
② suffix:需要拼接的后缀字符串
③ prefixOverrides:需要删除的前缀字符串
④ suffixOverrides:需要删除的后缀字符串
3. where 标签
主要作用
where 标签一般与 if 标签搭配使用,它可以直接代替 SQL 语句中的 where 关键字,在 where 标签中,我们可以控制条件的数量,从而达到不同的 select 查询要求。
代码实现
UserInfo 类:
@Data public class UserInfo { private int id; private String username; private String password; private String photo; private String createtime; private String updatetime; private int state; }
" UserMapper " 接口:
@Mapper public interface UserMapper { // 查询用户使用 where 标签 public List<UserInfo> getUserById2(@Param("id") Integer id); }
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"> <mapper namespace="com.example.demo.mapper.UserMapper"> <!-- 根据用户 id 来查询某个用户的所有信息 --> <!-- select * from userinfo where id = #{id} --> <select id="getUserById2" resultType="com.example.demo.model.UserInfo"> select * from userinfo <where> <if test="id != null"> id = #{id} </if> </where> </select> </mapper>
测试类1:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 查询用户使用 where 标签 @Test void getUserById2() { List<UserInfo> list = userMapper.getUserById2(2); System.out.println("测试结果:" + list); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
测试类2:
@SpringBootTest class UserMapperTest { @Resource private UserMapper userMapper; // 查询用户使用 where 标签 @Test void getUserById2() { List<UserInfo> list = userMapper.getUserById2(null); System.out.println("测试结果:" + list); } }
启动测试方法,查看 MyBatis 日志打印:
观察数据库:
总结 where 标签语法
如果 where 标签中的有值,那么就会正常执行 where 后面的 SQL 语句,如果 where 标签中没有值,那么整个 where 语句也会被省略。所以一般情况下,where 被省略了,就会变成一个全列查询。
此外,where 标签也等价于下面的写法,实际上它能省略语句中的前缀 and,但是它省略不了语句中的后缀 and.
<trim prefix="where" prefixOverrides="and"> </trim>
基于上述的 where 语法,形如下面的代码,一个 SQL 语句中,我们就可以变幻出四种语法。
select * from userinfo <where> <if test="username!= null"> username= #{usernname} </if> <if test="password!= null"> and password= #{password} </if> </where>
① 只查询 username
② 只查询 password
③ 既查询 username,也查询 password
④ 全列查询