MyBatis(3) 动态语句
2023-10-30 15:00:49 # Backend # MyBatis

1. if, where

if:根据标签中test属性所对应的表达式决定标签中的内容是否需要拼接到SQL中

where:

  • 当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或or去掉
  • 当where标签中没有内容时,此时where标签没有任何效果
  • 注意:where标签不能将其中内容后面多余的and或or去掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
select emp_id, emp_name, emp_salary from t_emp
<!-- where标签会自动去掉“标签体内前面多余的and/or” -->
<where>
<!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
<!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
<if test="empName != null and empName != ''">
<!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
or emp_name = #{empName}
</if>
<if test="empSalary &gt; 2000">
or emp_salary > #{empSalary}
</if>
<!--
第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
第二种情况:部分条件满足 WHERE emp_salary>?
第三种情况:所有条件都不满足 没有where子句
-->
</where>
</select>

2. set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name = #{empName}, emp_salary = #{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name = #{empName},
</if>
<if test="empSalary &lt; 3000">
emp_salary = #{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update>

3. trim

使用trim标签控制条件部分两端是否包含某些字符

  • prefix属性:指定要动态添加的前缀
  • suffix属性:指定要动态添加的后缀
  • prefixOverrides属性:指定要动态去掉的前缀,使用”|”分隔有可能的多个值
  • suffixOverrides属性:指定要动态去掉的后缀,使用”|”分隔有可能的多个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id, emp_name, emp_age, emp_salary, emp_gender
from t_emp

<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null">
emp_name=#{empName} and
</if>
<if test="empSalary &gt; 3000">
emp_salary>#{empSalary} and
</if>
<if test="empAge &lt;= 20">
emp_age=#{empAge} or
</if>
<if test="empGender=='male'">
emp_gender=#{empGender}
</if>
</trim>
</select>

4. choose, when, otherwise

在多个分支条件中,仅执行一个。

  • 从上到下依次执行条件判断
  • 遇到的第一个满足条件的分支会被采纳
  • 被采纳分支后面的分支都将不被考虑
  • 如果所有的when分支都不满足,那么就执行otherwise分支
  • when至少要有一个,otherwise最多只能有一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id, emp_name, emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary &lt; 3000">emp_salary &lt; 3000</when>
<otherwise>1=1</otherwise>
</choose>

<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select>

5. foreach

collection属性:要遍历的集合

item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象

separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符

open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串

close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串

index属性:这里起一个名字,便于后面引用

  • 遍历List集合,这里能够得到List集合的索引值

  • 遍历Map集合,这里能够得到Map集合的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 批量插入 -->
<!--int insertMoreByList(@Param("emps") List<Emp> emps);-->
<insert id="insertMoreByList">
insert into t_emp
<foreach collection="emps" item="emp" separator="," open="values" index="myIndex">
(null, #{emp.empName}, #{emp.age}, #{emp.sex}, #{emp.email}, #{myIndex})
</foreach>
</insert>

<!-- 批量删除 -->
<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
<!--
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
-->
</delete>

关于foreach标签的collection属性

如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:

Parameter 'orderIds' not found. Available parameters are [arg0, collection, list]

数组类型可用的参数名为 [array, arg0]

批量更新时需要注意

上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:

1
dev.url=jdbc:mysql://localhost:3306/mybatis-db?allowMultiQueries=true

对应的foreach标签如下:

1
2
3
4
5
6
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
<foreach collection="empList" item="emp" separator=";">
update t_emp set emp_name = #{emp.empName} where emp_id = #{emp.empId}
</foreach>
</update>

6. sql片段

提取重复的 SQL 片段

1
2
3
4
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id, emp_name, emp_age, emp_salary, emp_gender from t_emp
</sql>

引用已提取的SQL片段

  • 直接嵌入到sql语句中即可
1
2
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>