当前位置: 首页 > backend >正文

Mybatis05-动态sql

一、应用场景

MyBatis 的 动态 SQL 是指根据不同的条件动态拼接生成 SQL 语句的能力。它的最大优势是:避免写多个 XML 映射语句、避免 SQL 冗余、提升代码复用性和可维护性

示例1:用户可以通过勾选框,勾选不同的数据进行批量删除,此时:

delete from xxx_table where id in (a, b, c, d, ......)

in后面括号中的数据就是动态的。

示例2:用户搜索功能,查询条件不确定(常用于搜索/筛选)

二、使用方式(XML + 标签)

MyBatis 动态 SQL 主要通过 XML 配置文件中的标签 来实现,核心标签如下:

标签作用
<if>条件判断
<choose> / <when> / <otherwise>类似 Java 中的 if-else if-else
<where>自动加上 WHERE 并避免多余的 AND/OR
<set>用于 UPDATE,自动去除最后的逗号
<foreach>用于遍历集合(常见于 IN、批量插入)
<trim>自定义前缀、后缀、去掉前后符号等

2-1、<if>标签

示例:

    List<Car> selectByMutiConditions(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);

 

    <select id="selectByMutiConditions" resultType="car">select * from t_car where 1=1/*1. if标签中,test属性是必须的,值:表达式(false/true)2. test属性可以使用的是:使用了@Param标签,就用@Param标签指定的参数名;没有使用@Param标签,就用:param1, param2, param3, arg0, arg1, arg2...当使用了pojo,就用pojo中的属性名3. 在mybatis的动态sql中,不能使用&&,只能使用and*/<if test="brand != null and brand != ''">and brand like "%"#{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guidePrice > #{guidePrice}</if><if test="carType != null and carType != ''">and carType = #{carType}</if></select>

【注意点】:

1. if标签中,test属性是必须的,值:表达式(false/true)

2. test属性可以使用的是:

  •     使用了@Param标签,就用@Param标签指定的参数名;
  •     没有使用@Param标签,就用:param1, param2, param3, arg0, arg1, arg2...
  •     当使用了pojo,就用pojo中的属性名

3. 在mybatis的动态sql中,不能使用&&,只能使用and

4、为了防止有部分条件不成立的时候,where后面直接拼接了and,需要加上1=1

2-2、<where>标签

它的作用是在生成 SQL 语句时:

  1. 自动去掉首个条件多余的 ANDOR

  2. 当有条件时自动加上 WHERE 关键字

  3. 当没有任何条件时不会生成 WHERE 子句

【注意】:

        <where> 标签只对内容中出现的 第一个 AND/OR 进行清理,建议把每个 <if> 条件前都加上 AND,它会自动清理第一个多余的。

示例:

 

2-3、<trim>标签

在 MyBatis 中,<trim> 是一个功能非常强大的 动态 SQL 标签,它可以灵活地控制:

  • 前缀(prefix):开头加什么

  • 后缀(suffix):结尾加什么

  • 前缀去除(prefixOverrides):去掉开头多余的关键字,比如 AND / OR / ,

  • 后缀去除(suffixOverrides):去掉结尾多余的内容,比如 ,


你可以把 <trim> 看成 <where><set> 的“底层基础版本”,功能更灵活。


1、常见属性说明

属性名说明
prefix给内容加上前缀,比如 "WHERE""SET"
suffix给内容加上后缀
prefixOverrides去除内容前面多余的前缀(如多余的 "AND""OR"
suffixOverrides去除内容最后多余的后缀(如多余的 ","

【注意】:

它们 都是在整体 trim 内容拼接完毕后一次性处理整个 SQL 片段的前缀和后缀! 


示例 1:动态 WHERE 条件,代替 <where>

<select id="selectUser" parameterType="User" resultType="User">SELECT * FROM user<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="username != null"> AND username = #{username} </if><if test="gender != null"> AND gender = #{gender} </if></trim>
</select>

效果(如果 username != null 且 gender == null):

SELECT * FROM user WHERE username = 'Tom'

自动去掉多余的 AND,和 <where> 效果一样,但更灵活(你也可以换成 OR)。


示例 2:动态更新字段,代替 <set>

<update id="updateUser" parameterType="User">UPDATE user<trim prefix="SET" suffixOverrides=","><if test="username != null"> username = #{username}, </if><if test="gender != null"> gender = #{gender}, </if></trim>WHERE id = #{id}
</update>

效果:

  • 如果 username = 'Tom', gender = null:

    UPDATE user SET username = 'Tom' WHERE id = 1
    

    自动去掉最后多余的逗号 ,


示例 3:完全自定义的 SQL 拼接

<trim prefix="(" suffix=")" suffixOverrides=","><foreach collection="ids" item="id">#{id},</foreach>
</trim>

输出示例:

(1, 2, 3)

2-4、<set>标签

<set> 标签的作用是:

  • 自动添加 SET 关键字

  • 自动去除多余的结尾逗号 ,

  • <if> 标签组合使用,实现根据条件动态更新字段

目的:希望更新的时候,只更新不为null的字段,其余字段不动!

 

2、基本语法结构

<update id="updateUser" parameterType="User">UPDATE user<set><if test="username != null"> username = #{username}, </if><if test="email != null"> email = #{email}, </if><if test="age != null"> age = #{age}, </if></set>WHERE id = #{id}
</update>

3、执行逻辑过程

假设只传了 usernameageemail 是 null。

MyBatis 会生成这样的 SQL:

UPDATE user
SET username = ?, age = ?
WHERE id = ?

注意:

  • ✅ 自动添加了 SET

  • ✅ 自动移除了末尾多余的 ,


4、和 <trim> 的关系

<set> 本质上就是 <trim> 的封装版本

等价于:

<trim prefix="SET" suffixOverrides=",">...
</trim>

也就是说,如果你希望自定义更多行为(如不使用 SET、用别的关键字),可以改用 <trim> 标签

2-5、<choose>、<when>、<otherwise>

MyBatis 中的 <choose> 标签是用来实现 类似 Java 中 if-else if-else 结构的动态 SQL 分支选择语句,非常适合你有多个条件分支,但只想执行其中一个的情况


1、 作用:

<choose> 是 MyBatis 提供的一个逻辑分支标签,用来表示:

如果满足第一个条件就执行它,否则判断第二个条件……都不满足则执行默认分支。

它的结构和 Java 中的 if ... else if ... else 是一致的:

if (a != null) {// ...
} else if (b != null) {// ...
} else {// default
}

在 MyBatis 中写法如下:

<choose><when test="a != null">...</when><when test="b != null">...</when><otherwise>...</otherwise>
</choose>

2、使用示例

例子:根据传入的条件查询用户

<select id="findUser" parameterType="map" resultType="User">SELECT * FROM user<where><choose><when test="id != null">id = #{id}</when><when test="username != null">username = #{username}</when><otherwise>gender = 'male'</otherwise></choose></where>
</select>

3、执行逻辑:

假设传入参数是:

Map<String, Object> param = new HashMap<>();
param.put("username", "Tom");

生成的 SQL 就是:

SELECT * FROM user WHERE username = 'Tom'

如果传入参数是:

param.put("id", 123);
param.put("username", "Tom");

优先满足第一个条件:

SELECT * FROM user WHERE id = 123

如果两个都没传,则使用 otherwise

SELECT * FROM user WHERE gender = 'male'

4、几点注意事项

特性说明
<choose> 内只能选中 一个 <when> 被执行只执行第一个匹配成功的 <when>
<otherwise> 是可选的不写也可以
多个 <when> 的顺序很重要从上往下匹配,匹配到就停

2-6、<foreach>标签

MyBatis 中的 <foreach> 标签是动态 SQL 中非常重要的一个标签,主要用于 遍历集合(如 List、数组、Map)生成一段重复的 SQL 语句,特别适合用于:

批量插入、批量删除、IN (...) 条件语句


1、 的核心作用

用来遍历集合(如 List数组Map),在 SQL 中动态生成重复片段,比如:

  • id IN (1, 2, 3)

  • 多行 VALUES 插入

  • 多个字段拼接


2、常见属性说明

属性名说明
collection要遍历的集合名,如 listarraymap
item遍历过程中每一项的变量名
index当前项的下标可选
open开始拼接的前缀(如 (
close拼接结束的后缀(如 )
separator每项之间的分隔符(如 ,

3、经典使用场景

(1). IN (...) 查询
<select id="selectByIds" parameterType="list" resultType="User">SELECT * FROM userWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

传入:

List<Integer> ids = Arrays.asList(1, 2, 3);

生成 SQL:

SELECT * FROM user WHERE id IN (1, 2, 3)

(2). 批量插入
<insert id="insertUsers" parameterType="list">INSERT INTO user (username, age)VALUES<foreach collection="list" item="u" separator=",">(#{u.username}, #{u.age})</foreach>
</insert>

【注意】:

此时,属性:open,close都没有写! 

传入:

List<User> users = List.of(new User("Tom", 20),new User("Jerry", 22)
);

生成 SQL:

INSERT INTO user (username, age)
VALUES ('Tom', 20), ('Jerry', 22)

(3). 遍历 Map,实现动态 UPDATE 字段
<foreach collection="map" item="val" index="key">${key} = #{val}
</foreach>

示例:

    int updateForEach(@Param("updateFields") Map<String, Object> updateFields,@Param("id") Long id);

【注意】:

        此时的key用的是${key},因为是数据库字段,不能用#{key},否则拼接的数据库字段会加上字符串!


4、常见坑点提示

问题说明
collection="list"如果参数是 List,要写 "list"(不是写变量名)
<foreach> 必须配合 open/close/separator否则 SQL 拼接可能出错
item=#{} 中的 item 要和声明保持一致否则无法解析参数
避免用 ${} 插值容易引发 SQL 注入问题

2-7、<include>标签

MyBatis 中的 <include> 标签用于 在 XML 映射文件中复用 SQL 片段,类似 Java 中的方法提取,可以提高 SQL 语句的复用性和可维护性。


1、使用场景

  • 多个 SQL 中有相同字段、相同条件、重复的列名等

  • 希望统一维护公共 SQL 内容,避免拷贝粘贴


2、基本语法

1. 定义可复用的 SQL 片段(使用 <sql> 标签)

<sql id="baseColumnList">id, name, age, gender, email
</sql>

2. 引用 SQL 片段(使用 <include> 标签)

<select id="selectAll" resultType="User">SELECT<include refid="baseColumnList" />FROM users
</select>

3、完整示例

<!-- 定义通用字段列表 -->
<sql id="baseColumnList">id, name, age, gender, email
</sql><!-- 使用 include 引用 -->
<select id="selectById" resultType="User">SELECT <include refid="baseColumnList" />FROM usersWHERE id = #{id}
</select>

4、refid 范围说明

  • <sql id="...">id 必须在当前命名空间中唯一

  • 如果跨命名空间使用,需要使用 namespace.id 引用,例如:

<include refid="com.example.mapper.UserMapper.baseColumnList" />

⚠️ 注意事项

  • 不支持 <sql> 中再嵌套 <sql><include>!!!

http://www.xdnf.cn/news/15469.html

相关文章:

  • Java实现word、pdf转html保留格式
  • HTTP性能优化实战技术
  • 【电脑】显卡(GPU)的基础知识
  • 暑期算法训练.1
  • 【解决】联想电脑亮度调节
  • 行为模式-状态模式
  • 前端打包自动压缩为zip--archiver
  • MongoDB数据问题说明
  • 大模型在1型糖尿病肾病V期预测及治疗方案制定中的应用研究
  • 《大数据技术原理与应用》实验报告五 熟悉 Hive 的基本操作
  • 用uniapp开发鸿蒙应用(暂停更新-根据项目更新,现在项目未开始)
  • LangChain智能体开发实战:从零构建企业级AI助手
  • 17、鸿蒙Harmony Next开发:状态管理(组件拥有的状态和应用拥有的状态)
  • 3种添加视频水印的加密方式,守护视频安全!
  • OpenCV 对比度拉伸图像增强函数contrastStretching()
  • 基于UDP/IP网络游戏加速高级拥塞控制算法(示意:一)
  • 21-C#的委托简单使用-1
  • 【zynq7020】PS的“Hello World”
  • Android弹窗
  • 【C++】初识C++(1)
  • 映美打印机-URL页面打印
  • AI产品经理面试宝典第20天:AI+金融场景相关面试题及回答指导
  • 缓存穿透的“黑暗森林”假说——当攻击者学会隐藏恶意流量
  • 02 51单片机之LED闪烁
  • Knife4j快速入门
  • Java-IO流
  • Redis单线程详解
  • 多线程--单例模式and工厂模式
  • 2025-7-14-C++ 学习 排序(2)
  • C#——数据与变量