


专栏:JavaEE 进阶跃迁营
个人主页:手握风云
目录
[一、动态 SQL](#一、动态 SQL)
[1.1. 标签](#1.1. 标签)
[1.2. 标签](#1.2. 标签)
[1.3. 标签](#1.3. 标签)
[1.4.
标签](#1.4. 标签)
[1.5. 标签](#1.5. 标签)
[1.6. 标签](#1.6. 标签)
一、动态 SQL
动态 SQL 是 MyBatis 最强大的特性之一,它的核心在于根据不同条件拼接 SQL 语句。以前使用 JDBC 时,拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。MyBatis 的动态 SQL 通过 OGNL (Object-Graph Navigation Language) 表达式来计算值,并根据结果动态组装 SQL。
1.1. <if> 标签
在注册用户时,有些字段是"必填"的(如用户名、密码),而有些字段是"非必填"的(如性别、年龄、头像等)。果用户没有填写"性别",Java 对象中的 gender 属性就是 null。如果直接拼写固定的 SQL IINSERT INTO user (..., gender) VALUES (..., #{gender}),数据库中可能会插入 null,但如果数据库该字段有默认值(如 '保密'),插入 null 会覆盖掉默认值,这不符合预期。
XML
<insert id="insertUserByCondition">
insert into user_info (username, `password`, age
<if test="gender != null">
, gender
</if>)
value (#{username}, #{password}, #{age}
<if test="gender != null">
, #{gender}
</if>)
</insert>
java
Integer insertUserByCondition(UserInfo userInfo);
java
@Test
void insertUserByCondition() {
UserInfo userInfo = new UserInfo("Jeff", "Bezos", 62);
Integer rows = userInfoMapperXML.insertUserByCondition(userInfo);
System.out.println("影响的行数" + rows);
}

1.2. <trim> 标签
之前的插入用户的功能,只有一个 gender 是选填项。如果有多个字段,一般需要多种标签结合,<trim>标签是 MyBatis 动态 SQL 的核心标签之一,主要用于结合<if>标签处理多字段动态生成 SQL 的场景,解决单<if>标签处理多个选填字段时,SQL 语句拼接出现的多余分隔符、前缀 / 后缀缺失等语法问题,适用于插入、查询等多个 SQL 操作中,是处理多动态字段的优选方案。
<trim> 标签的核心属性:
- prefix :为整个 SQL 语句块添加指定前缀;
- suffix :为整个 SQL 语句块添加指定后缀;
- prefixOverrides:剔除整个 SQL 语句块开头的指定字符 / 符号;
- suffixOverrides:剔除整个 SQL 语句块结尾的指定字符 / 符号。
XML
<insert id="insertUserByCondition">
insert into user_info (
<if test="username != null">
username,
</if>
<if test="`password` != null">
`password`,
</if>
<if test="age != null">
age
</if>
<if test="gender != null">
, gender
</if>)
value (
<if test="username != null">
#{username},
</if>
<if test="`password` != null">
#{password},
</if>
<if test="age != null">
#{age}
</if>
<if test="gender != null">
, #{gender}
</if>)
</insert>
java
@Test
void insertUserByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("Musk");
Integer rows = userInfoMapperXML.insertUserByCondition(userInfo);
System.out.println("受影响的行数:" + rows);
}

这里就会出现 SQL 语法错误,如果我们对于某些字段如果没有传值,就会造成逗号的缺失或者多余。我们可以通过<trim>包裹<if>标签实现字段和值的动态拼接,给两个<trim>均设置prefix="("、suffix=")",拼接 SQL 的括号。
XML
<insert id="insertUserByCondition">
insert into user_info
<trim suffixOverrides="," suffix=")" prefix="(">
<if test="username != null">
username,
</if>
<if test="`password` != null">
`password`,
</if>
<if test="age != null">
age
</if>
<if test="gender != null">
, gender
</if>
</trim>
value
<trim suffixOverrides="," prefix="(" suffix=")">
<if test="username != null">
#{username},
</if>
<if test="`password` != null">
#{password},
</if>
<if test="age != null">
#{age}
</if>
<if test="gender != null">
, #{gender}
</if>
</trim>
</insert>
1.3. <where> 标签

我们在 YouTube 里面搜索内容,我们可以根据视频的类型、时长进行筛选。我们这里就可以使用 <where> 标签。<where>标签是 MyBatis 动态 SQL 的核心标签之一,核心作用是根据子元素内容动态组装 SQL 的 WHERE 查询条件 ,解决手动拼接 WHERE 子句时易出现的语法错误问题。<where>标签适用于根据传入对象的属性动态生成查询条件的场景,仅将对象中不为 null 的属性作为 WHERE 查询条件,比如根据年龄、性别、删除标识等多可选条件查询用户信息。
java
<select id="selectByCondition" resultType="com.yang.test2_11_1.model.UserInfo">
select * from user_info where
<trim prefixOverrides="and">
<if test="age != null">
and age = #{age}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="deleteFlag != null">
and delete_flag = #{deleteFlag}
</if>
</trim>
</select>
java
@Test
void selectByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setAge(25);
userInfo.setGender(1);
userInfo.setDeleteFlag(0);
List<UserInfo> userInfos = userInfoMapperXML.selectByCondition(userInfo);
System.out.println(userInfos);
}
如果我们把3个参数值都设为空,那么就会报错。原因是多出一个 where,也就是没有查询条件,我们不能使用 <trim> 去掉。解决方案:在 where 后面加上 1=1;或者使用 <where> 标签,<where> 标签可以取出最前面的 and,如果有查询条件,自动添加 where 关键字。

XML
<select id="selectByCondition" resultType="com.yang.test2_11_1.model.UserInfo">
select * from user_info
<where>
<if test="age!=null">
and age = #{age}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
<if test="deleteFlag!=null">
and delete_flag = #{deleteFlag}
</if>
</where>
</select>
1.4. <set> 标签
<set>标签是 MyBatis 动态 SQL 的核心标签之一,专门用于 UPDATE 更新语句中,核心作用是动态组装 SET 子句,解决手动拼接更新语句时的语法错误问题。
核心功能:动态在 SQL 语句中插入 set 关键字,且会自动剔除由<if>标签拼接后产生的额外逗号,避免因末尾多余逗号导致的 SQL 语法错误。
在<update>标签内嵌套<set>标签,<set>标签内部通过<if>标签判断每个属性是否非空,非空则拼接对应的字段更新语句,最后通过 where 子句指定更新条件(如主键)。
XML
<update id="updateUserByCondition">
update user_info
<set>
<if test="gender != null">
gender = #{gender}
</if>
<if test="deleteFlag != null">
delete_flag = #{deleteFlag}
</if>
</set>
</update>
java
@Test
void updateUserByCondition() {
UserInfo userInfo = new UserInfo();
userInfo.setId(9);
userInfo.setGender(1);
Integer rows = userInfoMapperXML.updateUserByCondition(userInfo);
System.out.println("受影响的行数:" + rows);
}
1.5. <foreach> 标签
<foreach>是 MyBatis 中用于对 List、Set、Map、数组等集合 / 数组对象进行遍历的动态 SQL 标签,常应用于批量查询、批量删除、批量插入等批量操作场景,能动态拼接遍历后的 SQL 语句块。
| 属性名 | 作用说明 |
|---|---|
| collection | 绑定方法参数中的集合 / 数组对象,如 List、Set、Map 或数组类型的参数名 |
| item | 遍历时,代表集合 / 数组中的每一个元素 / 对象,可通过 #{item 名} 获取值 |
| open | 指定遍历生成的 SQL 语句块开头要拼接的字符串 |
| close | 指定遍历生成的 SQL 语句块结尾要拼接的字符串 |
| separator | 指定遍历过程中,每个元素之间的分隔符(如逗号、and 等) |
在<delete>标签内嵌套<foreach>,拼接 IN 条件的 SQL。
XML
<delete id="batchDelete">
delete from user_info where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
java
@Test
void batchDelete() {
List<Integer> ids = List.of(11, 12, 16, 17);
Integer integer = userInfoMapperXML.batchDelete(ids);
}
注意:collection 属性的值为方法参数的名称 (如上例的ids),需与接口方法的参数名保持一致。
1.6. <include> 标签
<include>标签是 MyBatis 用于解决 XML 映射文件中SQL 片段重复、代码冗余问题的核心标签,需与 <sql> 标签配合使用,实现 SQL 片段的抽离和重用,提升代码的可维护性。
在 XML 映射文件中,多个 SQL 语句常出现重复的片段(如查询语句中相同的字段列表),大量冗余代码会增加维护成本,<include>标签可通过抽离公共 SQL 片段解决该问题。
- <sql> :用于定义可重用的公共 SQL 片段 ,通过
id属性为片段设置唯一标识,供<include>标签引用。 - <include> :用于引用<sql>标签定义的 SQL 片段 ,通过
refid属性指定要引用的<sql>片段的id,实现片段的复用。