文章目录
- 一、动态 SQL 是什么
- 二、动 态 SQL 标签
- 2.1 < if >标签
- 2.2 < trim >标签
- 2.3 < where >标签
- 2.4 < set >标签
- 2.5 < foreach >标签
一、动态 SQL 是什么
Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。动态 SQL 是 MyBatis 的强大特性之一,在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询
虽然动态 SQL 十分便利,但有时候在执行性能 (效率)上面不如静态SQL,而且使用不恰当,往往会在安全方面存在隐患 (SQL 注入式攻击)
二、动 态 SQL 标签
动 态 SQL 标签总共有上述9种,我们就最常用的举例说明 !!如下
2.1 < if >标签
在注册⽤户的时候,可能会有这样⼀个问题,如下图所示:
注册分为两种字段:必填字段和⾮必填字段,那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢?
⽐如添加头像 photo的时候为⾮必填字段,具体实现如下:
mapper 接口实现:
// 添加用户,添加用户时 photo是非必传参数
public int add2(UserInfo userInfo);
xml 文件实现:
<!-- 添加用户,添加用户时 photo是非必传参数 -->
<insert id="add2">
insert into userinfo(username,password
<if test="photo!=null">
,photo
</if>
) values(#{username},#{password}
<if test="photo!=null">
,#{photo}
</if>
)
</insert>
测试代码:
@Test
void add2() {
UserInfo userinfo = new UserInfo();
userinfo.setUsername("李四");
userinfo.setPassword("123");
int result = userInfoMapper.add2(userinfo);
log.info("添加用户的结果:" + result);
}
我们在测试代码中并没有设置 photo 参数
查看运行结果:
查看数据库结果:
由此可见,我们的 if 标签起到了作用,那它的语法到底是什么意思呢?
2.2 < trim >标签
之前的插⼊⽤户功能,只是有⼀个 photo 字段可能是选填项,如果有多个字段,⼀般考虑使⽤
< trim >标签结合< if >标签,对多个字段都采取动态⽣成的⽅式
标签中有如下属性:
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
调整上述插入语句:
<insert id="add3">
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>
在以上 sql 动态解析时,会将第⼀个 < trim > 部分做如下处理:
- 基于 prefix 配置,开始部分加上 (
- 基于 suffix 配置,结束部分加上 )
- 多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于
suffixOverrides 配置去掉最后⼀个 , - 注意 <if test=“createTime != null”> 中的 createTime 是传⼊对象的属性
2.3 < where >标签
传⼊的⽤户对象,根据属性做where条件查询,⽤户对象中属性不为 null 的,都为查询条件。如
user.username 为 “a”,则查询条件为 where username=“a”
实现 mapper 接口
// <where> 标签 ! 传⼊的⽤户对象,根据属性做where条件查询,⽤户对象中属性不为 null 的,都为查询条件
public UserInfo getUserById1(@Param("id") Integer id,@Param("username") String username);
xml 文件实现
<!-- 去除前面的 and -->
<select id="getUserById1" resultMap= "BaseMap">
select * from userinfo
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
测试代码:
@Test
void getUserById1() {
UserInfo userInfo = userInfoMapper.getUserById1(null,"admin");
log.info("chaxunxinxi:" + userInfo);
}
此时我们没有传入id,观察运行结果
由此可见,where 后面只生成了一个条件 !!当也传入 id 参数再观察
此时where 后面有两个条件 !!
注意:
- 以上< where >标签也可以使⽤ < trim prefix=“where” prefixOverrides=“and” > 替换
- where 中所有的参数都为空时,where 的 sql 语句 就不会生成了
- where 标签可以去除最前面的 and 标签
2.4 < set >标签
根据传⼊的⽤户对象属性来更新⽤户数据,可以使⽤< set >标签来指定动态内容
mapper 接口实现:
int update2(UserInfo userInfo);
xml 文件实现:
<!-- <set> 标签 -->
<update id="update2">
update userinfo
<set>
<if test="username!=null">
username=#{username},
</if>
<if test="password!=null">
password=#{password},
</if>
<if test="photo!=null">
photo=#{photo}
</if>
</set>
where id=#{id}
</update>
测试代码:
@Test
void update2() {
UserInfo userInfo = new UserInfo();
userInfo.setId(45);
userInfo.setUsername("haha");
userInfo.setPassword("123");
userInfo.setPhoto("default.png");
int result = userInfoMapper.update2(userInfo);
log.info("update 2 修改的结果为:" + result);
}
修改前的数据表
修改后的数据表
我们尝试只传入 username 参数,效果如下:
可以看到,只有 username 字段被修改 ,而没有传入参数的字段还是保持原状 !!
注意:
- < set >标签可以自动去除最后一个逗号
- 同样可以使用 < trim > 标签代替 如下
2.5 < foreach >标签
当我们需要根据多个用户 id 来删除用户数据时,平常做法如下:
delete from userinfo where id in ( ? , ? , ? )
我们会发现当删除的用户过多时, in() 里面工作量太大了,十分麻烦 !那是否有更简便的做法呢
答案是肯定的,我们可以思考将所有id 放在一个集合里面,然后直接遍历集合就行了,此时就可以考虑到 < foreach >标签了
对集合进⾏遍历时可以使⽤该标签,< foreach >标签有如下属性:
- collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每⼀个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
示例:根据多个用户 id 来删除用户数据
mapper 接口实现:
int delIds(List<Integer> ids);
xml 文件实现:
<!-- <foreach> 标签 对集合进行遍历:根据多个id 删除 -->
<delete id="delIds">
delete from userinfo where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
测试代码:
@Test
void delIds() {
List<Integer> list = new ArrayList<>();
list.add(43);
list.add(44);
list.add(45);
int result = userInfoMapper.delIds(list);
log.info("批量删除的结果:" + result);
}
删除之前的数据表
运行结果:
删除后的数据表
结果显而易见,删除成功了 !!