MyBatis动态SQL

article/2025/8/27 12:26:31

还不了解MySQL的可以看我这篇文章:

MyBatis入门:快速搭建数据库操作框架 + 两种增删改查的方法(CRUD)-CSDN博客

动态 SQL 是Mybatis的强⼤特性之⼀,能够完成不同条件下不同的 sql 拼接

官方文档:动态 SQL_MyBatis中文网

创建相关数据库

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;-- 使用数据数据
USE mybatis_test;-- 创建表[用户表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; -- 添加用户信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

一、<if>标签

在注册⽤⼾的时候,可能会有这样⼀个问题,如下图所⽰:

注册分为两种字段:必填字段和⾮必填字段,那如果在添加⽤⼾的时候有不确定的字段传⼊,程序应

该如何实现呢?

这个时候就需要使⽤动态标签来判断了,⽐如添加的时候性别 gender 为⾮必填字段,具体实现如

下:

@Mapper
public interface UserInfoXMLMapper {Integer insertUserByCondition(UserInfo userInfo);}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><insert id="insertUserByCondition">insert into user_info (username,password,age,<if test="gender != null">gender</if>)values (#{username},#{password},#{age},<if test="gender != null">#{gender}</if>)</insert></mapper>

单元测试:

测试纯在gender的情况:

@SpringBootTest
class UserInfoXMLMapperTest {@AutowiredUserInfoXMLMapper userInfoXMLMapper;@Testvoid insertUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("王麻子");userInfo.setPassword("123");userInfo.setAge(22);userInfo.setGender(0);userInfoXMLMapper.insertUserByCondition(userInfo);}
}

结果:

测试不存在gender的情况:

@SpringBootTest
class UserInfoXMLMapperTest {@AutowiredUserInfoXMLMapper userInfoXMLMapper;@Testvoid insertUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("王麻子");userInfo.setPassword("123");userInfo.setAge(22);//userInfo.setGender(0);userInfoXMLMapper.insertUserByCondition(userInfo);}
}

需要处理一下sql中的“,”

再次执行:

这样就可以了

那如果所有的都是非必传字段呢?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><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>)values (<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>
</mapper>

测试所有字段都传的情况下:

这个时候我又不穿gender:

那我们将sql中的“,”全部放在前面:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><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>)values (<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>
</mapper>

那我再不传username呢?

测试

这个时候就需要用另一个注解了:

二、<trim>标签

之前的插⼊⽤⼾功能,只是有⼀个 gender 字段可能是选填项,如果有多个字段,⼀般考虑使⽤标签结

合标签,对多个字段都采取动态⽣成的⽅式。

标签中有如下属性:

  • prefix:表⽰整个语句块,以prefix的值作为前缀
  • suffix:表⽰整个语句块,以suffix的值作为后缀
  • prefixOverrides:表⽰整个语句块要去除掉的前缀
  • suffixOverrides:表⽰整个语句块要去除掉的后缀

那么我将“,”移到字段末尾:

<mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><insert id="insertUserByCondition">insert into user_info<trim prefix="(" suffix=")" suffixOverrides=","><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>values<trim prefix="(" suffix=")" suffixOverrides=","><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>
</mapper>

测试不加gender的情况:

@SpringBootTest
class UserInfoXMLMapperTest {@AutowiredUserInfoXMLMapper userInfoXMLMapper;@Testvoid insertUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("王麻子");userInfo.setPassword("123");userInfo.setAge(22);
//        userInfo.setGender(0);userInfoXMLMapper.insertUserByCondition(userInfo);}
}

结果:

trim就帮我们处理掉了末尾的","

在以上 sql 动态解析时,会将第⼀个部分做如下处理:

  • 基于 prefix 配置,开始部分加上 (
  • 基于 suffix 配置,结束部分加上 )
  • 多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后⼀个 ,
  • 注意 <if test="username !=null"> 中的 username 是传⼊对象的属性

三、<where>标签

看下⾯这个场景, 系统会根据我们的筛选条件, 动态组装where 条件

这种如何实现呢?

接下来我们看代码实现:

需求: 传⼊的⽤⼾对象,根据属性做where条件查询,⽤⼾对象中属性不为 null 的,都为查询条件. 如username 为 "a",则查询条件为 where username="a"

原有sql:

接口定义:

@Mapper
public interface UserInfoXMLMapper {List<UserInfo> queryByCondition(UserInfo userInfo);
}

XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><select id="queryByCondition" resultType="com.Li.mybatis.demo.model.UserInfo">SELECT * FROM user_info<where><if test="age != null">age = #{age} and</if><if test="deleteFlag != null">delete_flag = #{deleteFlag} and</if></where></select>
</mapper>

测试用例:

@SpringBootTest
class UserInfoXMLMapperTest {@AutowiredUserInfoXMLMapper userInfoXMLMapper;@Testvoid queryByCondition() {UserInfo userInfo = new UserInfo();userInfo.setAge(18);userInfo.setDeleteFlag(0);userInfoXMLMapper.queryByCondition(userInfo);}
}

当age和deleteFlag都有值:

如果我们不写deleteFlag:

where不会帮我们取出末尾的and 或者 or , 但是最 前面 的or 和 and 可以,测试:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><select id="queryByCondition" resultType="com.Li.mybatis.demo.model.UserInfo">SELECT * FROM user_info<where><if test="age != null">and age = #{age}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where></select>
</mapper>

结果:

所以where 是可以帮我们取出前面的 and 或者 or的

那么如果我一个都不写:

💡

<where> 只会在⼦元素有内容的情况下才插⼊where⼦句,⽽且会⾃动去除⼦句的开头的AND或OR

以上标签也可以使⽤ <trim prefix="where" prefixOverrides="and"> 替换, 但是此种

情况下, 当⼦元素都没有内容时, where关键字也会保留

四、<set>标签

需求: 根据传⼊的⽤⼾对象属性来更新⽤⼾数据,可以使⽤标签来指定动态内容.

接⼝定义: 根据传⼊的⽤⼾ id 属性,修改其他不为 null 的属性

源SQL:

UPDATE user_info set username = 'zzz', age = 20, delete_flag = 1 WHERE id = 13

定义接口:

@Mapper
public interface UserInfoXMLMapper {Integer updateUserByCondition(UserInfo userInfo);
}

xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><update id="updateUserByCondition">UPDATE user_info<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="deleteFlag != null">delete_flag = #{deleteFlag}</if></set><where>id = 13</where></update>
</mapper>

单元测试:

如果不写deleteFlag(末尾选项):

结果:

注意age后面是有“,”的

如果一个元素都没有:

set就会不存在,但是sql肯定就错误了,所以set也做不了什么,过多的东西

💡

<set> :动态的在SQL语句中插⼊set关键字,并会删掉额外的逗号. (⽤于update语句中)

五、<foreach>标签

对集合进⾏遍历时可以使⽤该标签。标签有如下属性:

  • collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
  • item:遍历时的每⼀个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separator:每次遍历之间间隔的字符串

需求: 根据多个userid, 删除⽤⼾数据

DELETE FROM user_info WHERE id IN (21,22,23)

写接口:

@Mapper
public interface UserInfoXMLMapper {Integer batchDelete(List<Integer> ids);
}

xml:

<?xml version="1.0" encoding="UTF-8"?><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper"><delete id="batchDelete">DELETE FROM user_info WHERE id IN <foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete></mapper>

测试用例:

@SpringBootTest
class UserInfoXMLMapperTest {@AutowiredUserInfoXMLMapper userInfoXMLMapper;@Testvoid batchDelete() {List<Integer> ids = new ArrayList<>();ids.add(19);ids.add(24);ids.add(25);userInfoXMLMapper.batchDelete(ids);}
}

结果:

六、<include>标签

在xml映射⽂件中配置的SQL,有时可能会存在很多重复的⽚段,此时就会存在很多冗余的代码

我们可以对重复的代码⽚段进⾏抽取,将其通过 <sql> 标签封装到⼀个SQL⽚段,然后再通过<include> 标签进⾏引⽤。

  • <sql> :定义可重⽤的SQL⽚段
  • <include> :通过属性refid,指定包含的SQL⽚段
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.Li.mybatis.demo.mapper.UserInfoXMLMapper">
<!--    假设这是一串冗余的代码--><sql id="deleteUser">DELETE FROM user_info</sql><delete id="batchDelete"><include refid="deleteUser"></include>WHERE idIN <foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete></mapper>

相当于通过sql定义字符串,可以通过include标签通过id指定对应的字符串

上面的内容也标签也可以用,通过<script></script> 标签括起来就可以,但是不推荐,因为看着非常乱,就容易出错


http://www.hkcw.cn/article/KBaTuVyRQZ.shtml

相关文章

分享智能技能提升6月份排考计划

中级&#xff1a; 6月13号考试 AIGC应用工程师&#xff08;初级&#xff09; 高级&#xff1a; 6月15号考试 人工智能应用工程师&#xff08;高级&#xff09; 大数据分析师&#xff08;高级&#xff09; AIGC应用工程师&#xff08;高级&#xff09; 大数据/数字技术应用工程师…

防火墙ASPF(针对应用层包过滤技术) FTP(主动模式)

1.实验拓扑 2.基础配置 IP地址配置省略 [FW1-policy-security]di th 2025-05-29 12:20:13.740 # security-policy rule name trust->dmz source-zone trust destination-zone dmz source-address 10.1.11.0 mask 255.255.255.0 destination-address 10.1.21.0 ma…

RDS PostgreSQL手动删除副本集群副本的步骤

由于PostgreSQL不支持直接删除副本集群&#xff0c;而是需要先将副本集群升级到主实例(区域集群)&#xff0c;然后在逐一将写入器实例删除&#xff0c;然后才可以删除副本集群 查看现有的主从实例集群 将副本集群提升到区域集群 选择副本集群–>操作–>提升 提升只读副本…

矿用电控系统专用配件铜头铠装4C型护套连接器

矿用电控系统专用配件铜头铠装4C型护套连接器是矿山电气设备中不可或缺的关键组件&#xff0c;其设计、性能与可靠性直接关系到井下作业的安全性和生产效率。随着智能化矿山建设的推进&#xff0c;对连接器的技术要求日益提高&#xff0c;铜头铠装4C型护套连接器凭借其独特的结…

【二】9.关于pinctrl和gpio子系统

前言&#xff1a; 为什么要有pinctrl和gpio子系统呢&#xff1f;--->>>因为LZ不在想推着凯迪拉克其上班了。 1.pinctrl子系统&#xff1a; 因为 ST 针对 STM32MP1 提供的 Linux 系统中&#xff0c;其 pinctrl 配置的电气属性只能在platform 平台下被引用&#xff0c;…

能源领域新兴技术论坛:EMQ 实时数据引擎构建工业智能中枢

5 月 26 日&#xff0c;由沙特阿美亚洲公司主办的能源领域新兴技术论坛在上海顺利举行。本次论坛聚焦智能工厂、无人机与机器人、可靠性与完整性、先进材料四大技术赛道&#xff0c;吸引了来自全球的能源企业、技术供应商及行业专家。 作为业内知名的 MQ AI 实时数据与智能产…

NC52 有效括号序列【牛客网】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 NC52 有效括号序列 一、题目描述 二、测试用例 三、解题思路 基本思路&#xff1a;   使用栈进行括号匹配具体思路&#xff1a; 构建括号和数字映射&#xff0c;这样后面就不用写一堆…

Sqlalchemy 连mssql坑

连接失败: (pyodbc.OperationalError) (08001, [08001] [Microsoft][ODBC Driver 17 for SQL Server]SSL Provider: [error:0A00014D:SSL routines::legacy sigalg disallowed or unsupported] (-1) (SQLDriverConnect)) (Background on this error at: https://sqlalche.me/e/…

乾元通渠道商中标青海省自然灾害应急能力提升工程基层防灾项目

近日&#xff0c;乾元通渠道商中标青海省自然灾害应急能力提升工程基层防灾项目&#xff0c;乾元通作为设备厂家&#xff0c;为项目提供通信指挥类装备&#xff08;多链路聚合设备&#xff09;QYT-X1。 青岛乾元通数码科技有限公司作为国家应急产业企业&#xff0c;深耕于数据调…

记一次edu未授权访问漏洞

首先进入该网址是一个登录界面&#xff0c;查看源代码&#xff0c;找到js文件&#xff0c;发现存在js.map前端信息泄露&#xff0c;于是我们进行js还原。 得到前端的一些源代码&#xff0c;以及路由API等&#xff0c;我们就可以通过这个源代码&#xff0c;进行目录遍历&#xf…

Python----目标检测(《用于精确目标检测和语义分割的丰富特征层次结构》和R-CNN)

一、《用于精确目标检测和语义分割的丰富特征层次结构》 1.1、基本信息 原文标题&#xff1a;Rich feature hierarchies for accurate object detection and semantic segmentation 中文译名&#xff1a;用于精确目标检测与语义分割的丰富特征层次结构 版本&#xff1a;第5版技…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Split Landing Page(拆分展示页)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— SplitLandingPage 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 在这篇文章中&#xff0c;我们将实现一个交互式的左右面板…

Dif-Fusion:第一个基于扩散模型实现的红外光与可见光图像融合的论文

1. 论文介绍 论文主要创新点&#xff1a;提出了第一个基于扩散模型 (Diffusion) 实现的红外光与可见光图像融合模型&#xff0c;但模型不止简单的依赖于 Diffusion&#xff0c;而是一个新颖的 two-stage 的图像融合模型。 Dif-Fusion 利用扩散模型的生成能力&#xff0c;直接在…

Java开发经验——阿里巴巴编码规范实践解析5

摘要 这篇文章主要介绍了阿里巴巴Java开发规范中关于安全和性能优化的实践解析。内容涵盖了配置文件密码加密、用户输入内容风控、SQL注入防护、参数有效性验证、XSS攻击防护、CSRF安全验证、文件上传安全检查、防重放机制等多个方面&#xff0c;通过正反示例和推荐做法&#…

如何在python3.8环境中安装pytorch

我的conda配置了两个独立环境&#xff1a; base环境 - 安装有Python 3.12及各类依赖包&#xff1b;pytorch环境 - 基于Python 3.8创建&#xff0c;包含特定功能包。 在Anaconda Prompt中激活您的PyTorch环境&#xff1a; 访问PyTorch官网历史版本页面&#xff08;https://pyt…

源的企业级网络安全检测工具Prism X(棱镜X)

Prism X&#xff08;棱镜X&#xff09;是由yqcs团队自主研发的开源网络安全检测解决方案&#xff0c;专注于企业级风险自动化识别与漏洞智能探测。该工具采用轻量化架构与跨平台设计&#xff0c;全面兼容Windows、Linux及macOS操作系统&#xff0c;集成资产发现、指纹鉴别、弱口…

ADB识别手机系统弹授权框包含某段文字-并自动点击确定按钮

ADB识别手机系统弹授权框包含某段文字-并自动点击确定按钮 --蓝牙电话App自动部署 上一篇&#xff1a;手机打电话时将对方DTMF数字转为RFC2833发给局域网SIP坐席 下一篇&#xff1a;编写中。 一、前言 蓝牙电话方案中&#xff0c;我们提供了将手机通话的语音拦截后转发到局域…

kafka 常用知识点

文章目录 前言kafka 常用知识点1. kafka 概念2. 消息共享和广播3. 分区和副本数量奇偶数 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0…

什么是接口测试,我们如何实现接口测试?

1. 什么是接口测试 顾名思义&#xff0c;接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。其中接口协议分为HTTP,WebService,Dubbo,Thrift,Socket等类型&#xff0c;测试类型又主要…

我这三年……测试开发工作的一点感悟

从职场小白到测试开发&#xff0c;已经三年有余。回首这段职业旅程&#xff0c;踩过坑&#xff0c;吃过饼&#xff0c;背过锅&#xff0c;也拿过奖。和产品互掐&#xff0c;和开发干仗……也许这就是成长的代价和难忘的经历吧。今天忍不住跟宝子们分享分享我的这段心路历程 初…