MyBatis动态SQL(1)

75 阅读4分钟

概述

随着用户的输入或外部条件变化而变化的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中,假如说我们在传递的参数中只传递姓名(模糊匹配)和性别,那么就会根据姓名和性别进行查找。

image.png

image.png 只查找到了名字中有“张”的和性别为男的员工,说明<if>标签成功完成了条件判断。

但是我们再看一个情况:

@Test
public void selectEmpTest() {
    List<Emp> empList = empMapper.selectEmp(null, 1, null, null);
    for (Emp emp : empList) {
        System.out.println(emp);
    }
}

假如说这么进行测试,按理来说会查询所有性别为男的员工(1代表男),但是事实并非如此:

image.png 运行测试方法发现报错了,这其实很好理解:

image.png 回顾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>

image.png

image.png 发现使用了<where>标签之后,就可以正确的拼接SQL了。

更新字段<set>

在更新数据时我们也需要使用<if>进行条件判断然后拼接SQL,传递了的参数就进行更新,若没有参数传递,那么就不会更新,保持原样。如果不使用<if>进行条件判断,那么只要没有传递参数的字段就会被设置为null,即使我们不传递参数只是不想让它更新;但是在使用<if>条件判断的时候也会出现和使用<where>标签一样的问题:update语句中的set,设置多项值的时候需要使用“,”进行分隔,那么假如有这样一种情况:

image.png 假如此时只有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>

image.png 这是原表: image.png 测试之后:

image.png

image.png 发现id为17的员工成功按照我们封装的Emp对象中的属性被修改了,Emp中没有的属性也成功的保留了原来的数据,说明动态修改信息成功,多亏了<if>标签和<set>标签。