MyBatis -- 动态 SQL
- 动态 SQL 使用
- 1. `<if>` 标签
- 2. `<trim>` 标签
- 3. `<where>` 标签
- 4. `<set>` 标签
- 5. `<foreach>` 标签
动态 SQL 使用
动态 sql 是 Mybatis 的强大特性之⼀,能够完成不同条件下不同的 sql 拼接。
可以参考官方文档:https://mybatis.org/mybatis-3/zh/dynamic-sql.html
可以通过这个配置在运行后看到对应还原出的 sql 语句:
1. <if>
标签
在注册用户的时候,可能会有这样⼀个问题,如下图所示:
注册分为两种字段:必填字段和非必填字段,那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢?
这个时候就需要使用动态标签 <if>
来判断了,比如添加的时候性别 sex 为非必填字段,具体实现如下:
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user(
username,
password,
nickname,
<if test="sex != null">
sex,
</if>
birthday,
head
) values (
#{username},
#{password},
#{nickname},
<if test="sex != null">
#{sex},
</if>
#{birthday},
#{head}
)
</insert>
(注意 test 中的 sex,是传入对象中的属性,不是数据库字段)
此时我们在接口方法中参数传 null,sql 语句就会忽略这个参数,最终传入数据库表后,这个字段为''
!(注意区分MySQL的 NULL 和 ''
)
如果不使用 动态 SQL,则需要构造很多的方法和实现,非常麻烦。
考虑一个极端情况:所有字段都是非必填项,这时我们没有办法控制最后一个属性后没有
,
(必须符合语法规范),怎么办呢?
2. <trim>
标签
之前的插入用户功能,只是有⼀个 sex 字段可能是选填项,如果所有字段都是⾮必填项,就考虑使用<trim>
标签结合<if>
标签,对多个字段都采取动态⽣成的方式。
<trim>
标签中有如下属性:
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
调整 UserMapper.xml 的插入语句为:
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="sex != null">
sex,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="head != null">
head,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="nickname != null">
#{nickname},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="birthday != null">
#{birthday},
</if>
<if test="head != null">
#{head},
</if>
<if test="createTime != null">
#{createTime},
</if>
</trim>
</insert>
在以上 sql 动态解析时,会将第⼀个
<trim>
部分做如下处理:
- 基于
prefix
配置,开始部分加上(
- 基于
suffix
配置,结束部分加上)
- 多个
<if>
组织的语句都以,
结尾,在最后拼接好的字符串还会以,
结尾,会基于suffixOverrides
配置去掉最后⼀个,
- 注意
<if test=“createTime != null”>
中的 createTime 是传入对象的属性;而 create_time 为数据库表的字段
3. <where>
标签
传入的用户对象,根据属性做 where 条件查询,用户对象中属性不为 null 的,都为查询条件。如 user.username 为 “a”,则查询条件为 where username=“a”:
UserMapper 接口中新增条件查询方法:
List<User> selectByCondition(User user);
UserMapper.xml 中新增条件查询 sql:
<select id="selectByCondition" parameterType="org.example.model.User" resultMap="BaseResultMap">
select id, username, password, nickname, sex, birthday, head, create_t
ime
from user
<where>
<if test="username != null">
and username=#{username}
</if>
<if test="password != null">
and password=#{password}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
<if test="birthday != null">
and birthday=#{birthday}
</if>
<if test="head != null">
and head=#{head}
</if>
<if test="createTime != null">
and create_time=#{createTime}
</if>
</where>
</select>
如果参数都传 null,那就是查询全部喽。
以上 <where>
标签也可以使⽤ <trim prefix="where" prefixOverrides="and">
替换。
(也就是开始部分判断是否加 where;去掉第一个 and)
4. <set>
标签
根据传入的用户对象属性来更新用户数据,可以使用 <set>
标签来指定动态内容。
UserMapper 接口中修改用户方法:根据传入的用户 id 属性,修改其他不为 null 的属性:
int updateById(User user);
UserMapper.xml 中添加更新用户 sql:
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
<if test="nickname != null">
nickname=#{nickname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="head != null">
head=#{head},
</if>
<if test="createTime != null">
create_time=#{createTime},
</if>
</set>
where id=#{id}
</update>
以上 <set>
标签也可以使用 <trim prefix="set" suffixOverrides=",">
替换。
5. <foreach>
标签
对集合进行遍历时可以使用该标签。<foreach>
标签有如下属性:
- collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每⼀个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
示例:根据多个用户 id 来删除用户数据 (对应 sql 中的 in)。
UserMapper 中新增接口方法:
// 多条用户的删除
public int delByIds(List<Integer> ids);
UserMapper.xml 中新增删除 sql:
<delete id="delByIds">
delete from userinfo where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
单元测试:
@SpringBootTest // 当前测试的上下文环境为 springboot
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void delByIds() {
List<Integer> list = new ArrayList<>();
list.add(8);
list.add(9);
list.add(10);
int result = userMapper.delByIds(list);
System.out.println("删除了:" + result);
}
}