MyBatisPlus--条件构造器及自定义SQL详解
条件构造器
在前面学习快速入门的时候,练习的增删改查都是基于id去执行的,但是在实际开发业务中,增删改查的条件往往是比较复杂的,因此MyBatisPlus就提供了一个条件构造器来帮助构造复杂的条件。
MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。
我们在快速入门中学习的使用MyBatisPlus的步骤中就需要继承BaseMapper接口,而在BaseMapper接口中有些方法的参数有些与众不同。
这些与众不同的参数是Wrapper类型的参数,那么Wrapper类型是什么呢?
Wrapper就是条件构造器,用来构造复杂SQL语句的,而Wrapper也不只是一个类,而是一个体系,Wrapper是最顶级的父类。
而下面的子类是AbstractWrapper
,里面定义了大量方法,就如下图所示:
eq()就是等于,相当于SQL语句中where条件中哪个字段等于什么值。ne():not equal,不等于。gt():great than,大于。ge():大于等于。lt():小于。le():小于等于,...等等,只要是以前在where语句中写过的条件,在AbstractWrapper中都有对应的方法,也就是说原来的复杂条件都可以帮忙构造。
而在AbstractWrapper下面还有不少子类,而子类的作用就是继承兼拓展。
比较重要的子类分别是UpdateWrapper以及QueryWrapper
在父类AbstractWrapper中提供的方法已经将where语句后面的复杂条件构造完成了,而QueryWrapper就是在父类基础上拓展了select功能,允许在构造SQL语句时,还可以指定要查询哪些字段,因为在默认情况下是全部查询的,在这里是可以指定对应的字段的
与此类似的是,UpdateWrapper就是拓展类更新相关的功能,UpdateWrapper就是拓展了set语句后的复杂条件
而setsql()的参数是String类型,这里是让以字符串的形式将set的部分写出来,将来拼接到SQL语句中,在一些特殊的场景下会用到。
而结合这三个不同类型的wrapper,就可以去构造出各种各样复杂的增删改查的语句了。
而其他的三个子类和这三个类型的Wrapper名字几乎一样,都是多了一个Lambda。
AbstractLambdaWrapper,LambdaQueryWrapper以及LambdaUpdateWrapper。
他们的功能与前面的功能相差无几,无非是在构建条件时,基于Lambda特殊语法。
案例展示:基于QueryWrapper的查询
需求:
-
查询出名字中带O的,存款大于等于1000元的人的ID,username,info,balance字段。
SQL语句:
select id,username,info,balance from user where username like ? and balance >= ?
利用MyBatisPlus中的条件构造器来实现:
void testQueryWrapper(){//1.构建查询条件 同时支持连式编程QueryWrapper<User> wrapper = new QueryWrapper<User>()//1.1.查询条件.select( "id","username","info","balance")// 1.2.范围条件.like( "username","o")// 1.3.排序条件 .ge( "balance",1000);//2.查询List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}
测试结果:
-
更新用户名为Jack的用户的余额为2000
SQL语句
update user set balance = 2000 where username = "jack"
利用MyBatisPlus中的条件构造器来实现:
void testQueryWrapper2(){//1.要更新的数据User user = new User();user.setBalance(2000);//2.更新的条件QueryWrapper<User> wrapper = new QueryWrapper<User>().eq( "username","Jack");//3.执行更新userMapper.update(user,wrapper);}
测试结果:修改成功
案例展示:基于UpdateWrapper的更新
需求:更新ID为1,2,4,的用户的余额,扣200
SQL语句:
update user set balance = balance - 200 where id in (1,2,4)
利用MyBatisPlus中的条件构造器来实现:
void testUpdateWrapper(){UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql( "balance=balance-200").in("id",List.of(1L,2L,4L));userMapper.update(wrapper);}
测试结果:
以上就是查询以及更新时利用条件构造器来完成,事实上不仅这两个,userMapper中除了新增以外,删除方法也可以使用条件构造器来构造复杂条件。
然后就是Lambda的Wrapper,实际上与普通的Wrapper功能基本一致,区别就在于:LambdaXXWrapper构建条件是是基于Lambda语法。为什要使用Lambda语法,因为我们在使用queryWrapper中字段名是写死的,这种写法属于硬编码的,耦合度较高,不太建议适用。
LambdaXXWrapper就是来解决这样的问题的。那么如何使用?
就以前面的两个案例来演示为例:
//1.构建查询条件 同时支持连式编程LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()//1.1.查询条件.select( User::getId,User::getUsername,User::getInfo,User::getBalance)// 1.2.范围条件.like( User::getUsername,"o")// 1.3.排序条件.ge( User::getBalance,1000);//2.查询List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);
而LambdaXXXWrapper实质就是通过将写死的字段名转换成该字段对应的get函数即可,底层是通过反射机制得到该函数操作的字段名拿到,这样就不存在字符串硬编码了。只要是传字段名的时候都传递函数即可。
而在实际开发中也是推荐去使用这种Lambda开发模式的。
小结:
条件构造器的用法:
-
QueryWrapper和LambdaQueryWrapper通常是用来构建select、delete、update的where条件部分
-
UpdateWrapper和LambdaUpdateWrapper通常只有在set语句较为特殊时才使用
-
尽量使用LambdaQueryWrapper与LambdaUpdateWrapper,避免硬编码。
自定义SQL
自定义SQL并不是亲自去写SQL语句,而是利用MyBatisPlus的Wrapper来构建复杂的where条件,然后自己定义SQL语句剩下的部分。
即在SQL语句中,用Wrapper来做SQL语句的where部分,剩下的自己来写,也就是说,以前使用MyBatisPlus的时候,所有的SQL语句都是由MyBatisPlus来生成,等于是全自动,现在是只有Where部分由MyBatisPlus生成,相当于半自动。
那为什么要这么去做?
根据案例分析:
案例:自定义SQL
需求:将id在指定范围内的用户的余额扣减指定值。
完全手写SQL语句代码如下:
<update id = "updateBalanceByIds">update user set balance - #{amount} where id in <foreach collection="ids" separator =",",item ="id" open = "("close=")">#{id}</foreach></update>
全部基于MyBatisPlus来实现的代码展示:
void testUpdateWrapper(){ UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql( "balance=balance-200").in("id",List.of(1L,2L,4L));userMapper.update(wrapper);}
这样看来全自动的非常方便,但是这里有一个问题:现在写的逻辑都是业务逻辑,将来时在service中去定义,这就相当于我们将SQL语句中的一部分写进了业务代码之中,这在很多企业的开发规范中是不允许的,他要求只能在Mapper层中xml文件中去定义SQL语句,因此全自动的写法也不能采用。
但是手写SQL语句的where条件非常复杂,代码量也可能非常巨大,但是如果我们用MyBatisPlus去编写where条件就会简单许多。因此更推荐使用MyBatisPlus来写SQL语句。
但是MyBatisPlus更擅长编写where条件,但是对于SQL语句的前半部分,在大部分情况下,MyBatisPlus也可以帮助我们书写,但是在一些特定情况下就较难实现。
比如前面的案例中的set语句并不是给字段赋一个固定的值,而是在原有值的基础上作数值运算,这属于动态变化的,只能在业务层中拼这条SQL语句,这就违背了企业的开发规范了,这就形成了矛盾。
而且这种矛盾不只是在更新语句中能够看到,在查询语句中也会存在。就比如在查询时调用聚合函数并且起别名,这样的MyBatisPlus就不能生成,只能在业务层中拼出来。这样又违背了企业规范。
为此自定义SQL应运而生:
既然MyBatisPlus擅长的是where条件的构建,一旦出现这种复杂的业务场景,就将where条件的构建交给mp去做,剩下的前半部分就去手动书写SQL语句即可。
但是这个手动书写不能再到业务层去拼接,需要将MyBatisPlus构建好的where条件往下传递,传递到Mapper层,在Mapper层中去拼接。
那具体如何实现这个传递与组装呢?
实现步骤:
-
基于Wrapper构建where条件
List<Long> ids = List.of(1L,2L,4L);int amount = 200;//1.构建条件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId,ids);//2.自定义SQL方法调用userMapper.updateBalanceByIds(wrapper,amount);
-
在mapper方法参数中用@Param注解声明wrapper变量名称,必须是ew(这是一个固定的写法)
void updateBalanceByIds(@Param( "ew") LambdaQueryWrapper<User> wrapper, @Param( "amount") int amount);
-
自定义SQL,并使用Wrapper条件
<update id="updateBalanceByIds">update tb_user set balance = balance -#{amount} ${ew.customSqlSegment}</update>
这样就解决了之前说的问题,既保证了不在业务层编写SQL,遵循了企业规范,同时又享受到了MybatisPlus生成SQL条件的便捷特性。
进行测试:
检查数据库:
测试成功。
在哪些场景下可以用到自定义SQL?
-
SQL语句where条件之外的那些部分没有利用MyBatisPlus更方便的实现,而只能选择拼接,这时就可以使用自定义SQL。
希望对大家有所帮助,让我们一起努力。
相关文章:

MyBatisPlus--条件构造器及自定义SQL详解
条件构造器 在前面学习快速入门的时候,练习的增删改查都是基于id去执行的,但是在实际开发业务中,增删改查的条件往往是比较复杂的,因此MyBatisPlus就提供了一个条件构造器来帮助构造复杂的条件。 MyBatisPlus支持各种复杂的wher…...

OVD开放词汇检测 Detic 训练COCO数据集实践
0、引言 纯视觉检测当前研究基本比较饱和,继续创新提升空间很小,除非在CNN和transformer上提出更强基础建模方式。和文本结合是当前的一大趋势,也是计算机视觉和自然语言处理结合的未来趋势,目前和文本结合的目标检测工作还是有很…...

docker、ctr、crictl命令简介与使用
概述 在使用k3s过程中,经常需要使用ctr和crictl两个命令,本文记录一下。 ctr 类似docker命令是docker-shim容器运行时的客户端工具,ctr是Containerd的客户端工具。一个简单的CLI接口,用作Containerd本身的一些调试用途…...
WEB安全--SQL注入--bypass技巧2
继之前文章的补充: WEB安全--SQL注入--bypass技巧_sql注入过滤空格-CSDN博客 Q1:发现sql注入的时间盲注时,如果时间盲注的函数都被过滤了,怎么办? 除了找其他函数替换、编码等方式,还有以下方式绕过&…...
【强化学习哲学 Day 1】Q-Learning - 在不确定中寻找确定
🎭 故事:那些选择的时刻 你还记得那些站在十字路口的时刻吗? 也许是刚进实验室,面对满墙的研究方向海报,不知道哪条路通向你想要的未来;也许是第一份工作的选择,大厂的螺丝钉还是小公司的多面…...

WEB3——什么是ABI
怎么获得ABI? 在编译完合约后,可以在左边下面点击复制ABI ABI(Application Binary Interface,应用二进制接口)是用来让前端或服务端 JavaScript 代码与智能合约进行交互的桥梁,它描述了合约的函数、事件和…...

嵌入式软件--stm32 DAY 8.5 基础复习总结
1.时钟树 在数据手册里面,有一张密密麻麻的图,正是时钟系统里的时钟树。 对于时钟,我们注意有两点。一个是系统时钟SYSCLK,一个是依赖外部晶振生成的RTC. RTC以外部低速晶振作为时钟源或者外部高速晶振128分频后作为时钟源,又或者…...

MMRL: Multi-Modal Representation Learning for Vision-Language Models(多模态表示学习)
摘要 预训练的VLMs,对于跨任务的迁移学习至关重要,然而,在few-shot数据集上微调会导致过拟合,降低在新任务上的性能。为解决这个问题,提出一种新的多模态表征学习框架(MMRL),该框架引入了一个共享、可学习…...
贪心算法求解汽车加油问题
一、问题描述 一辆汽车加满油后可以行驶 n km。在前往目的地的途中,有多个加油站。我们的目标是设计一个有效的算法,确定汽车应该在哪些加油站停靠加油,以使得沿途的加油次数最少。 二、输入输出形式 算法的输入包括两部分:第一…...
JVM Full GC 频繁问题排查、优化及解决方案
引言 在Java应用程序中,JVM(Java虚拟机)通过垃圾回收机制自动管理内存,确保不再使用的对象能够被及时清理和释放。虽然垃圾回收在大多数情况下运行顺利,但当Full GC频繁发生时,它会严重影响应用性能&#x…...

rsync服务的搭建
目录 一、rsync介绍 rsync的安装 二、rsync的语法 三、rsync命令使用 1. 本机同步 2. 远程同步 四、rsync作为服务使用 1、尝试启动rsync程序 2、rsync的配置文件介绍 注意事项: 3. rsyncinotify实时同步 3.依赖服务托管xinetd(CentOS 6中rs…...
JDK21深度解密 Day 8:Spring Boot 3与虚拟线程整合
【JDK21深度解密 Day 8】Spring Boot 3与虚拟线程整合 引言:Spring Boot 3遇上JDK21虚拟线程 在本系列的第8天,我们将聚焦于Spring Boot 3与JDK21虚拟线程的整合实践。作为全网首套完整的JDK21特性解析,我们不仅会探讨虚拟线程如何颠覆传统Java并发模型,还会通过完整的Sp…...

vscode 配置 QtCreat Cmake项目
1.vscode安装CmakeTool插件并配置QT中cmake的路径,不止这一处 2.cmake生成器使用Ninja(Ninja在安装QT时需要勾选),可以解决[build] cc1plus.exe: error: too many filenames given; type ‘cc1plus.exe --help’ for usage 编译时…...
排序算法-归并排序与快速排序
归并排序与快速排序 快速排序是利用的递归思想:选取一个基准数,把小于基准数的放左边 大于的放右边直到整个序列有序 。快排分割函数 O(lognn), 空间 :没有额外开辟新的数组但是递归树调用函数会占用栈内存 O(logn) 。 归并排序:在递归返回的…...

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。
名人说:龙舟争渡,助威呐喊,凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、项目概览:传统与现代的技术碰撞1. 核心特…...

uniapp uni-id 如果是正式项目,需自行实现发送邮件的相关功能
(3) 使用云对象sendEmailCode 发送邮箱验证码,报错送邮箱验证码失败 Error: 已启动测试模式,直接使用:123456作为邮箱验证码即可。 如果是正式项目,需自行实现发送邮件的相关功能 - DCloud问答 uni-id 没有实现邮箱验证码逻辑&am…...
Spring boot 策略模式
public abstract class Node {/*** 执行** param a* param b* return*/public abstract Integer execute(int a, int b); }package my.node;import org.springframework.stereotype.Component;Component("exec") public class ExecNode extends Node {Overridepublic…...
websocket在vue中的使用步骤,以及实现聊天
一、WebSocket集成步骤 连接初始化 在Vue组件中创建WebSocket实例,建议在mounted生命周期中执行: data() {return {socket: null,messages: []} }, mounted() {this.socket new WebSocket(wss://your-server-endpoint); }事件监听配置 连接成…...

C++学习-入门到精通【12】文件处理
C学习-入门到精通【12】文件处理 目录 C学习-入门到精通【12】文件处理一、文件和流二、创建顺序文件三、从顺序文件读取数据文件定位指针对之前的程序进行修改:贷款查询程序 四、更新顺序文件五、随机存取文件1.创建随机存取文件2.修改程序:贷款处理程序…...
第十一篇:MySQL 在分布式系统中的一致性保障与中间件实践
随着微服务和分布式架构的发展,单点数据库早已无法满足系统的横向扩展需求。本篇聚焦 MySQL 在分布式系统中的一致性保障机制,以及相关中间件的使用策略与实战经验。 一、一致性问题的由来 在 单机 MySQL 环境 中,事务具有原子性、隔离性&am…...
Java中如何枚举正则表达式捕获组的名字
在使用正则表达式在匹配文本时,除了可以通过表达式捕获命中的文本串外,还可以对捕获的文本串进行命名。尤其是在解析日志的场景中,经常会被用到。表达式如下: \<(?<pri>\d)\>(?<time>.*) (?<host>\S)…...
matlab实现图像压缩编码
一、基于DCT的JPEG压缩(有损) 1. 核心步骤 图像分块:将图像划分为88的小块。离散余弦变换(DCT):对每个块进行DCT变换。量化:对DCT系数进行量化以减少高频信息。熵编码:使用哈夫曼或…...
如何排查Redis单个Key命中率骤降?
问题现象 Redis整体命中率98%,但监控发现特定Key(如user:1000:profile)的命中率从99%骤降至40%,引发服务延迟上升。 排查步骤 1. 确认现象与定位Key // 通过Redis监控工具获取Key指标 public void monitorKey(String key) {Je…...

记一次 Starrocks be 内存异常宕机
突发性 be 内存飙高,直至被系统 kill 掉,be 内存如下:其中 starrocks_be_update_mem_bytes 指标打满,重启也是如此 [rootlocalhost bin]# curl -XGET -s http://192.168.1.49:8040/metrics | grep "^starrocks_be_.*_mem_b…...
Spring Boot 读取.env文件获取配置
Spring Boot 读取.env文件获取配置 在Resouce 目录下创建.env文件 # DEEP SEEK TOKEN DEEP_SEEK_TOKENyour_deep_seek_key # 阿里云百炼 TOKEN ALI_BAILIAN_TOKENyour_ali_bailian_keyyml引入.env文件 spring:config:import: optional:classpath:.env[.properties]使用.env文…...

LangChain-结合GLM+SQL+函数调用实现数据库查询(一)
业务流程 实现步骤 1. 加载数据库配置 在项目的根目录下创建.env 文件,设置文件内容: DB_HOSTxxx DB_PORT3306 DB_USERxxx DB_PASSWORDxxx DB_NAMExxx DB_CHARSETutf8mb4 加载环境变量,从 .env 文件中读取数据库配置信息 使用 os.getenv…...
python训练营打卡第41天
简单CNN 知识回顾 数据增强卷积神经网络定义的写法batch归一化:调整一个批次的分布,常用与图像数据特征图:只有卷积操作输出的才叫特征图调度器:直接修改基础学习率 卷积操作常见流程如下: 1. 输入 → 卷积层 → Batch…...
1.3HarmonyOS NEXT统一开发范式与跨端适配:开启高效跨设备应用开发新时代
HarmonyOS NEXT统一开发范式与跨端适配:开启高效跨设备应用开发新时代 在HarmonyOS NEXT的技术体系中,统一开发范式与跨端适配是两大关键特性,它们为开发者打破了设备边界,极大地提升了开发效率与应用体验。本章节将深入探讨方舟…...
麒麟v10,arm64架构,编译安装Qt5.12.8
Window和麒麟x86_64架构,官网提供安装包,麒麟arm64架构的,只能自己用编码编译安装。 注意,“桌面”路径是中文,所以不要把源码放在桌面上编译。 1. 下载源码 从官网下载源码:https://download.qt.io/arc…...
ArcGIS Pro 3.4 二次开发 - 布局
环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 布局1 布局工程项1.1 引用布局工程项及其关联的布局1.2 在新视图中打开布局工程项1.3 激活已打开的布局视图1.4 引用活动布局视图1.5 将 pagx 导入工程1.6 移除布局工程项1.7 创建并打开一个新的基本布局1.8 使用修改后的CIM创建新…...