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.pswp.cn/diannao/91309.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VSCODE 选中多行 需要同时按住alt键才可以

在 VS Code 中&#xff0c;如果你发现 必须按住 Alt 键才能选中多行&#xff08;即“列选择”或“块选择”模式&#xff09;&#xff0c;而直接拖动鼠标无法多选&#xff0c;可能是由于以下原因导致的&#xff1a;1. 检查是否启用了“列选择模式”VS Code 默认情况下&#xff1…

2025前端面试真题以及答案-不断整理中,问题来源于牛客真题

一、 项目内存泄露react与vue的渲染机制有哪些不同react fiber架构vue2与3&#xff0c;为什么用proxy代替defineproperty性能优化有哪些三栏布局实现方式重排与重绘一个对话聊天框如何减少重排&#xff08;我回答的是绝对定位&#xff0c;将聊天框定位在下面&#xff0c;类似于…

雷军的 IP 革命:人格化力量如何重塑商业规则|创客匠人

小米 YU7 发布会 3 分钟售罄 20 万台的奇迹&#xff0c;撕开了一个时代真相&#xff1a;当商业竞争进入深水区&#xff0c;决定胜负的不再是产品参数&#xff0c;而是创始人 IP 的人格穿透力。雷军仅凭个人影响力撬动数十亿级交易&#xff0c;这绝非偶然&#xff0c;而是人格化…

SpringBoot3:应对C10K并发挑战的优化指南

嘿&#xff0c;哥们&#xff01;还在为服务的并发量上不去而头疼吗&#xff1f;用户量一上来&#xff0c;CPU、内存就告急&#xff0c;接口响应慢得像蜗牛&#xff1f;别慌&#xff0c;今天我们就来盘一盘&#xff0c;怎么用最新的Spring Boot 3&#xff0c;把服务性能调教到极…

响应式编程入门教程第三节:ReactiveCommand 与 UI 交互

响应式编程入门教程第一节&#xff1a;揭秘 UniRx 核心 - ReactiveProperty - 让你的数据动起来&#xff01; 响应式编程入门教程第二节&#xff1a;构建 ObservableProperty&#xff1c;T&#xff1e; — 封装 ReactiveProperty 的高级用法 响应式编程入门教程第三节&#x…

500+技术栈覆盖:Web测试平台TestComplete的对象识别技术解析

在用户界面&#xff08;UI&#xff09;测试领域&#xff0c;传统的测试工具往往依赖于XPath或CSS选择器来定位页面元素。然而&#xff0c;在面对动态变化的界面、多语言支持或是跨越多种技术框架的应用时&#xff0c;这些传统方法常导致脚本失效&#xff0c;增加了维护成本。 …

研究人员利用提示注入漏洞绕过Meta的Llama防火墙防护

Trendyol应用安全团队发现了一系列绕过技术&#xff0c;使得Meta的Llama防火墙在面对复杂的提示注入攻击时防护失效。这一发现引发了人们对现有大语言模型&#xff08;LLM&#xff09;安全措施准备情况的担忧&#xff0c;并凸显出在企业日益将大语言模型嵌入工作流程时&#xf…

Shell 脚本系统学习 · 第5篇:多命令顺序执行的三种方式详解(`;`、``、`||`)

在日常的 Linux 运维与脚本编写中&#xff0c;我们经常需要依次执行多条命令。本篇将带你彻底搞懂三种命令顺序执行方式&#xff1a;;、&& 和 ||&#xff0c;并通过实用示例掌握它们的区别与应用场景。一、为什么要了解多命令执行方式&#xff1f; 在实际运维或脚本编写…

K8s存储系统(通俗易懂版)

Kubernetes中存储中有四个重要的概念&#xff1a;Volume、PersistentVolume PV、PersistentVolumeClaim PVC、StorageClass一、存储系统核心概念Volume&#xff08;卷&#xff09;定义&#xff1a;Kubernetes 中最基础的存储单元&#xff0c;用于将外部存储挂载到 Pod 中的容器…

小白学Python,标准库篇——随机库、正则表达式库

一、随机库1.随机生成数值在random库中可以随机生成数值的方法有uniform()、random()、randint()、randrange()等。&#xff08;1&#xff09;uniform()方法uniform(参数1, 参数2)方法用于生成参数1到参数2之间的随机小数&#xff0c;其中参数的类型都为数值类型。示例代码&…

Qt窗口:菜单栏

目录 一、窗口预览 二、菜单栏 快捷键 子菜单 分割线 图标 内存泄露 一、窗口预览 在前面几篇文章中&#xff0c;或者说&#xff0c;Qt初学阶段&#xff0c;接触到的都是QWidget&#xff0c;QWidget指控件&#xff0c;往往作为一个窗口的一部分出现。所谓的窗口&#x…

STM32裸机开发(中断,轮询,状态机)与freeRTOS

裸机&#xff1a;没有操作系统&#xff0c;程序是单流程的&#xff08;比如一个大循环里依次执行各个功能&#xff0c;或者用中断嵌套处理事件&#xff09;。优点是资源占用极少&#xff08;几乎不占 RAM/Flash&#xff09;、执行流程直观&#xff1b;但复杂项目里&#xff0c;…

电脑上如何查看WiFi密码

打开控制面板>点击网络和Internet在查看网络和共享中心找到网络状态和任务点击进去点击连接的WLAN在WLAN状态中点击无线属性在无线网络属性中点击安全&#xff0c;点击显示字符&#xff08;H&#xff09;就可以显示密码了

文心一言4.5企业级部署实战:多模态能力与Docker容器化测评

随着大语言模型在企业服务中的应用日益广泛&#xff0c;如何选择一款既能满足多模态创作需求&#xff0c;又具备良好企业级适配性的AI模型成为了关键问题。文心一言4.5作为百度最新开源的大模型&#xff0c;不仅在传统的文本处理上表现出色&#xff0c;更是在多模态理解和企业级…

VUE Promise基础语法

目录 异步和同步 异步的问题 new Promise语法 promise的状态 promise.then() Promise.resolve() Promise.reject() Promise.all() Promise.race() Promise.catch() Promise.finally() 异步和同步 同步模式下&#xff0c;代码按顺序执行&#xff0c;前一条执行完毕后…

用TensorFlow进行逻辑回归(六)

import tensorflow as tfimport numpy as npfrom tensorflow.keras.datasets import mnistimport time# MNIST数据集参数num_classes 10 # 数字0到9, 10类num_features 784 # 28*28# 训练参数learning_rate 0.01training_steps 1000batch_size 256display_step 50# 预处…

【HTTP版本演变】

在浏览器中输入URL并按回车之后会发生什么1. 输入URL并解析输入URL后&#xff0c;浏览器会解析出协议、主机、端口、路径等信息&#xff0c;并构造一个HTTP请求&#xff08;浏览器会根据请求头判断是否又HTTP缓存&#xff0c;并根据是否有缓存决定从服务器获取资源还是使用缓存…

Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析

一 窗口过渡动画 1.1 案例效果图1.2 案例源码 1.2.1 添加权限 (AndroidManifest.xml) <!-- 系统悬浮窗权限&#xff08;Android 6.0需动态请求&#xff09; --> <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW" />1.2.2 窗口显示…

腾讯云WAF域名分级防护实战笔记

基于业务风险等级、合规要求及腾讯云最佳实践&#xff0c;提供可直接落地的配置方案&#xff0c;供学习借鉴&#xff1a;一、域名分级与防护原则1. ​域名分级清单&#xff08;核心资产&#xff09;​​​主域名​​业务类型​​风险等级​​合规要求​​防护等级​example.com…

1. 请说出你知道的水平垂直居中的方法

总结 容器 flex 布局&#xff0c;jsutify-content: center; align-items: center;容器 flex 布局&#xff0c;子项 margin: auto;容器 relative 布局&#xff0c;子项 absolute 布局&#xff0c;left: 50%; top: 50%; transform: translate(-50%, -50%);子项 absolute 布局&…