MyBatis的动态SQL(下)

346 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

set

学会了如何动态查询之后,我们来尝试一下动态更新,即:根据传递过来的参数进行员工信息更新,携带了什么参数就更新什么参数,实现如下:

void updateEmp(Employee employee);
<update id="updateEmp">
  update tbl_employee
  set
  <if test="lastName != null">
    last_name = #{lastName},
  </if>
  <if test="email != null">
    email = #{email},
  </if>
  <if test="gender != null">
    gender = #{gender}
  </if>
  <where>
    id = #{id}
  </where>
</update>

这样实现的话,当参数均携带的时候不会出现问题,然而这种情况:

Employee employee = new Employee(1, "tom", null, null);
mapper.updateEmp(employee);

因为lastName后面的参数都为null,导致  被直接拼在了id的前面,造成语法错误,来看看生成的sql: 

==>  Preparing: update tbl_employee set last_name = ?, WHERE id = ? 
==> Parameters: tom(String), 1(Integer)

为此,我们可以使用set标签,将这些动态更新的内容放在set标签内就能够解决这一问题:

<update id="updateEmp">
  update tbl_employee
  <set>
    <if test="lastName != null">
      last_name = #{lastName},
    </if>
    <if test="email != null">
      email = #{email},
    </if>
    <if test="gender != null">
      gender = #{gender}
    </if>
  </set>
  <where>
    id = #{id}
  </where>
</update>

这里也可以使用trim标签解决逗号拼接的问题,方式如下:

<update id="updateEmp">
  update tbl_employee
  <trim prefix="set" suffixOverrides=",">
    <if test="lastName != null">
      last_name = #{lastName},
    </if>
    <if test="email != null">
      email = #{email},
    </if>
    <if test="gender != null">
      gender = #{gender}
    </if>
  </trim>
  <where>
    id = #{id}
  </where>
</update>

foreach

有时候我们需要同时查询多个值的数据,为此,可以使用foreach实现,比如 查询id为1、2、3的员工信息,实现如下:

List<Employee> getEmpsByConditionForeach(@Param("ids") List<Integer> ids);
<select id="getEmpsByConditionForeach" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id in(
  <foreach collection="ids" item="id" separator=",">
    #{id}
  </foreach>
  )
</select>

首先collection属性指定需要遍历的集合,item属性指定遍历出的每个元素的名字,separator属性指定的是每个遍历出的元素的分隔符,一定不要忘记in后面的小括号,foreach也支持将小括号写在标签内:

<select id="getEmpsByConditionForeach" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id in
  <foreach collection="ids" item="id" separator="," open="(" close=")">
    #{id}
  </foreach>
</select>

编写业务代码:

List<Employee> emps = mapper.getEmpsByConditionForeach(Arrays.asList(1, 2, 3));
for (Employee emp : emps) {
    System.out.println(emp);
}

执行结果:

==>  Preparing: select * from tbl_employee where id in ( ? , ? , ? ) 
==> Parameters: 1(Integer), 2(Integer), 3(Integer)
<==    Columns: id, last_name, gender, email, d_id
<==        Row: 1, tom, 1, jack@qq.com, 1
<==        Row: 3, tom, 1, tom@qq.com, 2
<==      Total: 2
Employee(id=1, lastName=null, gender=1, email=jack@qq.com)
Employee(id=3, lastName=null, gender=1, email=tom@qq.com)

借助foreach标签我们还能够实现批量插入,实现如下:

void addEmpBatch(@Param("emps") List<Employee> emps);
<insert id="addEmpBatch">
  insert into tbl_employee(last_name,gender,email)
  values
  <foreach collection="emps" item="emp" separator=",">
    (#{emp.lastName},#{emp.gender},#{emp.email})
  </foreach>
</insert>

编写业务代码:

List<Employee> emps = new ArrayList<>();
emps.add(new Employee(null, "jack", '1', "jack@qq.com"));
emps.add(new Employee(null, "jerry", '1', "jerry@qq.com"));
mapper.addEmpBatch(emps);

执行结果:

==>  Preparing: insert into tbl_employee(last_name,gender,email) values (?,?,?) , (?,?,?) 
==> Parameters: jack(String), 1(String), jack@qq.com(String), jerry(String), 1(String), jerry@qq.com(String)
<==    Updates: 2

也可以通过执行多条sql来实现批量插入:

<insert id="addEmpBatch">
  <foreach collection="emps" item="emp" separator=";">
    insert into tbl_employee(last_name, gender, email)
    values (#{emp.lastName}, #{emp.gender}, #{emp.email})
  </foreach>
</insert>

sql语句之间用 ; 分隔,但是MySQL默认是不支持这种语法的,为此,需要设置一个参数使其支持该语法:

jdbc.url=jdbc:mysql:///mybatis?allowMultiQueries=true

MyBatis提供了两个内置参数用于辅助操作,也就是说,即使方法没有传递任何参数,我们也能够在操作标签中取出这两个参数进行处理:

  • _parameter:所有参数,单个参数情况下,_parameter就是该参数;多个参数情况下,_parameter就是封装所有参数的Map集合
  • _databaseId:若配置了databaseIdProvider 标签,则该参数就是当前数据库的别名

bind

MyBatis提供了bind标签用于将表达式的值绑定到一个变量中,用法如下:

<select id="getEmpsByConditionChoose" resultType="com.wwj.mybatis.bean.Employee">
  <bind name="_lastName" value="'%'+ lastName + '%'"/>
  select *
  from tbl_employee
  <where>
    <choose>
      <when test="id != null">
        id = ${id}
      </when>
      <when test="lastName != null">
        last_name like #{_lastName}
      </when>
      <otherwise>
      </otherwise>
    </choose>
  </where>
</select>

这里使用bind标签定义了一个变量_lastName,其值为lastName值前后拼接 % ,接下来就可以引用该变量,此时调用方法进行模糊查询就不需要自己拼接 %c% 了,而是直接传入字符串 c 即可。

sql

该标签能够抽取可重用的sql片段,使Mapper文件更加简洁:

<sql id="insertColumn">
  last_name,gender,email
</sql>

接下来若是想使用这一字符串,直接用include标签包含进来即可:

<insert id="addEmpBatch">
  insert into tbl_employee(
  <include refid="insertColumn"></include>
  )
  values
  <foreach collection="emps" item="emp" separator=",">
    (#{emp.lastName},#{emp.gender},#{emp.email})
  </foreach>
</insert>