概述
随着用户的输入或外部条件变化而变化的SQL语句就叫做动态SQL,动态SQL在数据库的CRUD中使用得十分广泛,非常重要,想要使用动态SQL就必须通过XML配置MyBatis完成,MyBatis提供了很多进行动态SQL的标签。
条件判断标签<if>
<if>标签用于判断条件是否成立,通过test属性进行条件判断,如果条件为true,那么才拼接SQL;否则就不会拼接SQL。
先看一个没有使用动态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.wzb.mapper.UserMapper">
<select id="selectEmp" resultType="com.wzb.pojo.Emp">
select * from emp where name like concat('%', #{name}, '%') and gender = #{gender}
and entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
这个语句将查询的条件写死了,必须要传递姓名、性别、开始时间和结束时间才可以成功查询到员工,但是在很多时候,查询时都只需要一个或几个条件就可以进行查询,而并非是必须输入全部条件,所以说这个时候就需要使用<if>标签来解决条件查询的问题:
<?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.wzb.mapper.EmpMapper">
<select id="selectEmp" resultType="com.wzb.pojo.Emp">
select * from emp
where
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
order by update_time desc
</select>
</mapper>
通过<if>标签,可以进行条件判断,只有当方法传递的参数不为null时,才会将该条件拼接到SQL中,假如说我们在传递的参数中只传递姓名(模糊匹配)和性别,那么就会根据姓名和性别进行查找。
只查找到了名字中有“张”的和性别为男的员工,说明<if>标签成功完成了条件判断。
但是我们再看一个情况:
@Test
public void selectEmpTest() {
List<Emp> empList = empMapper.selectEmp(null, 1, null, null);
for (Emp emp : empList) {
System.out.println(emp);
}
}
假如说这么进行测试,按理来说会查询所有性别为男的员工(1代表男),但是事实并非如此:
运行测试方法发现报错了,这其实很好理解:
回顾xml中的SQL,发现除了第一个条件name之前没有and,后面的条件都是使用and进行连接的,这符合SQL的语法规范,但是当name为null时,后面的条件不为null,运行起来就会拼出这样的SQL:“where and gender = ?”这显然是不符合SQL语法规范的,为了解决类似的问题,MyBatis提供了<where>标签,<where>有两个作用:1.判断是否要进行条件拼接,若where中的条件全部不满足的时候,SQL中根本不会生成where关键字,相当于没有进行条件查询。2.where会自动去除掉条件前面多余的and或者是or来保证SQL的正确性。
修改代码后再次进行测试:
<select id="selectEmp" resultType="com.wzb.pojo.Emp">
select * from emp
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
order by update_time desc
</where>
</select>
发现使用了<where>标签之后,就可以正确的拼接SQL了。
更新字段<set>
在更新数据时我们也需要使用<if>进行条件判断然后拼接SQL,传递了的参数就进行更新,若没有参数传递,那么就不会更新,保持原样。如果不使用<if>进行条件判断,那么只要没有传递参数的字段就会被设置为null,即使我们不传递参数只是不想让它更新;但是在使用<if>条件判断的时候也会出现和使用<where>标签一样的问题:update语句中的set,设置多项值的时候需要使用“,”进行分隔,那么假如有这样一种情况:
假如此时只有username这个条件不为空,为true,其余条件全部为false,那么此时拼接出来的SQL就会是这样:update emp set username = ?, where id = ?,这很明显是一条错误的SQL,那么为了避免这样的问题,就可以使用<set>,<set>的作用就是在SQL拼接时提供set关键字,并且更重要的一点是可以消除多余的“,”。那么修改之后的代码进行测试:
<update id="updateEmp">
update emp
<set>
<if test="username != null">username = #{username},</if>
<if test="name != null">name = #{name},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
where id = #{id}
</update>
这是原表: 测试之后:
发现id为17的员工成功按照我们封装的Emp对象中的属性被修改了,Emp中没有的属性也成功的保留了原来的数据,说明动态修改信息成功,多亏了<if>标签和<set>标签。