MyBatisPlus--条件构造器及自定义SQL详解

news2025/6/4 21:43:34
条件构造器

在前面学习快速入门的时候,练习的增删改查都是基于id去执行的,但是在实际开发业务中,增删改查的条件往往是比较复杂的,因此MyBatisPlus就提供了一个条件构造器来帮助构造复杂的条件。

MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。

我们在快速入门中学习的使用MyBatisPlus的步骤中就需要继承BaseMapper接口,而在BaseMapper接口中有些方法的参数有些与众不同。

image-20250602171749729

这些与众不同的参数是Wrapper类型的参数,那么Wrapper类型是什么呢?

Wrapper就是条件构造器,用来构造复杂SQL语句的,而Wrapper也不只是一个类,而是一个体系,Wrapper是最顶级的父类。

而下面的子类是AbstractWrapper,里面定义了大量方法,就如下图所示:

image-20250602172735468

eq()就是等于,相当于SQL语句中where条件中哪个字段等于什么值。ne():not equal,不等于。gt():great than,大于。ge():大于等于。lt():小于。le():小于等于,...等等,只要是以前在where语句中写过的条件,在AbstractWrapper中都有对应的方法,也就是说原来的复杂条件都可以帮忙构造。

而在AbstractWrapper下面还有不少子类,而子类的作用就是继承兼拓展。

image-20250602175037174

比较重要的子类分别是UpdateWrapper以及QueryWrapper

在父类AbstractWrapper中提供的方法已经将where语句后面的复杂条件构造完成了,而QueryWrapper就是在父类基础上拓展了select功能,允许在构造SQL语句时,还可以指定要查询哪些字段,因为在默认情况下是全部查询的,在这里是可以指定对应的字段的

image-20250602175125148

与此类似的是,UpdateWrapper就是拓展类更新相关的功能,UpdateWrapper就是拓展了set语句后的复杂条件

而setsql()的参数是String类型,这里是让以字符串的形式将set的部分写出来,将来拼接到SQL语句中,在一些特殊的场景下会用到。

image-20250602175155682

而结合这三个不同类型的wrapper,就可以去构造出各种各样复杂的增删改查的语句了。

而其他的三个子类和这三个类型的Wrapper名字几乎一样,都是多了一个Lambda。

AbstractLambdaWrapper,LambdaQueryWrapper以及LambdaUpdateWrapper。

image-20250602175500328

他们的功能与前面的功能相差无几,无非是在构建条件时,基于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);
  }

测试结果:

image-20250602202127731

  • 更新用户名为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);
 }

测试结果:修改成功

image-20250602202643380

案例展示:基于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);
 }

测试结果:

image-20250602203510050

image-20250602203516947

以上就是查询以及更新时利用条件构造器来完成,事实上不仅这两个,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条件的便捷特性。

进行测试:

image-20250602220637521

检查数据库:

image-20250602220744067

测试成功。

在哪些场景下可以用到自定义SQL?

  • SQL语句where条件之外的那些部分没有利用MyBatisPlus更方便的实现,而只能选择拼接,这时就可以使用自定义SQL。

希望对大家有所帮助,让我们一起努力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2396585.html

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

相关文章

OVD开放词汇检测 Detic 训练COCO数据集实践

0、引言 纯视觉检测当前研究基本比较饱和&#xff0c;继续创新提升空间很小&#xff0c;除非在CNN和transformer上提出更强基础建模方式。和文本结合是当前的一大趋势&#xff0c;也是计算机视觉和自然语言处理结合的未来趋势&#xff0c;目前和文本结合的目标检测工作还是有很…

docker、ctr、crictl命令简介与使用

概述 在使用k3s过程中&#xff0c;经常需要使用ctr和crictl两个命令&#xff0c;本文记录一下。 ctr 类似docker命令是docker-shim容器运行时的客户端工具&#xff0c;ctr是Containerd的客户端工具。一个简单的CLI接口&#xff0c;用作Containerd本身的一些调试用途&#xf…

WEB3——什么是ABI

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

嵌入式软件--stm32 DAY 8.5 基础复习总结

1.时钟树 在数据手册里面&#xff0c;有一张密密麻麻的图&#xff0c;正是时钟系统里的时钟树。 对于时钟&#xff0c;我们注意有两点。一个是系统时钟SYSCLK,一个是依赖外部晶振生成的RTC. RTC以外部低速晶振作为时钟源或者外部高速晶振128分频后作为时钟源&#xff0c;又或者…

MMRL: Multi-Modal Representation Learning for Vision-Language Models(多模态表示学习)

摘要 预训练的VLMs,对于跨任务的迁移学习至关重要&#xff0c;然而&#xff0c;在few-shot数据集上微调会导致过拟合&#xff0c;降低在新任务上的性能。为解决这个问题&#xff0c;提出一种新的多模态表征学习框架&#xff08;MMRL&#xff09;,该框架引入了一个共享、可学习…

rsync服务的搭建

目录 一、rsync介绍 rsync的安装 二、rsync的语法 三、rsync命令使用 1. 本机同步 2. 远程同步 四、rsync作为服务使用 1、尝试启动rsync程序 2、rsync的配置文件介绍 注意事项&#xff1a; 3. rsyncinotify实时同步 3.依赖服务托管xinetd&#xff08;CentOS 6中rs…

vscode 配置 QtCreat Cmake项目

1.vscode安装CmakeTool插件并配置QT中cmake的路径&#xff0c;不止这一处 2.cmake生成器使用Ninja&#xff08;Ninja在安装QT时需要勾选&#xff09;&#xff0c;可以解决[build] cc1plus.exe: error: too many filenames given; type ‘cc1plus.exe --help’ for usage 编译时…

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。

名人说&#xff1a;龙舟争渡&#xff0c;助威呐喊&#xff0c;凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、项目概览&#xff1a;传统与现代的技术碰撞1. 核心特…

uniapp uni-id 如果是正式项目,需自行实现发送邮件的相关功能

(3) 使用云对象sendEmailCode 发送邮箱验证码&#xff0c;报错送邮箱验证码失败 Error: 已启动测试模式&#xff0c;直接使用&#xff1a;123456作为邮箱验证码即可。 如果是正式项目&#xff0c;需自行实现发送邮件的相关功能 - DCloud问答 uni-id 没有实现邮箱验证码逻辑&am…

C++学习-入门到精通【12】文件处理

C学习-入门到精通【12】文件处理 目录 C学习-入门到精通【12】文件处理一、文件和流二、创建顺序文件三、从顺序文件读取数据文件定位指针对之前的程序进行修改&#xff1a;贷款查询程序 四、更新顺序文件五、随机存取文件1.创建随机存取文件2.修改程序&#xff1a;贷款处理程序…

记一次 Starrocks be 内存异常宕机

突发性 be 内存飙高&#xff0c;直至被系统 kill 掉&#xff0c;be 内存如下&#xff1a;其中 starrocks_be_update_mem_bytes 指标打满&#xff0c;重启也是如此 [rootlocalhost bin]# curl -XGET -s http://192.168.1.49:8040/metrics | grep "^starrocks_be_.*_mem_b…

LangChain-结合GLM+SQL+函数调用实现数据库查询(一)

业务流程 实现步骤 1. 加载数据库配置 在项目的根目录下创建.env 文件&#xff0c;设置文件内容&#xff1a; DB_HOSTxxx DB_PORT3306 DB_USERxxx DB_PASSWORDxxx DB_NAMExxx DB_CHARSETutf8mb4 加载环境变量&#xff0c;从 .env 文件中读取数据库配置信息 使用 os.getenv…

2025年渗透测试面试题总结-匿名[校招]安全工程师(甲方)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]安全工程师(甲方) 1. 介绍自己熟悉的渗透领域 2. 编程语言与开发能力 3. 实习工作内容与流程 …

PySide6 GUI 学习笔记——常用类及控件使用方法(地址类QUrl)

文章目录 地址类QUrl主要功能URL 格式介绍常见 scheme&#xff08;协议&#xff09;类型QUrl 类常用方法常用方法示例典型应用场景 地址类QUrl QUrl 是 PySide6.QtCore 模块中的一个类&#xff0c;用于处理和操作 URL&#xff08;统一资源定位符&#xff09;。它可以解析、构建…

任务23:创建天气信息大屏Django项目

任务描述 知识点&#xff1a; Django 重 点&#xff1a; Django创建项目Django视图函数Django路由Django静态文件Django渲染模板 内 容&#xff1a; 使用PyCharm创建大屏项目渲染大屏主页 任务指导 1. 使用PyCharm创建大屏项目。 创建weather项目配置虚拟环境创建ch…

数学分析——一致性(均匀性)和收敛

目录 1. 连续函数 1.1 连续函数的定义 1.2 连续函数的性质 1.2.1 性质一 1.2.2 性质二 1.2.3 性质三 1.2.4 性质四 2. 一致连续函数 2.1 一致连续函数的定义 2.2 一致连续性定理(小间距定理)(一致连续函数的另一种定义) 2.3 一致连续性判定法 2.4 连…

Flutter GridView网格组件

目录 常用属性 GridView使用配置 GridView.count使用 GridView.extent使用 GridView.count Container 实现列表 GridView.extent Container 实现列表 GridView.builder使用 GridView网格布局在实际项目中用的也是非常多的&#xff0c;当我们想让可以滚动的元素使用矩阵…

【深度学习】18. 生成模型:Variational Auto-Encoder(VAE)详解

Variational Auto-Encoder&#xff08;VAE&#xff09;详解 本节内容完整介绍 VAE 的模型结构、优化目标、重参数化技巧及其生成机制。 回顾&#xff1a;Autoencoder&#xff08;自编码器&#xff09; Autoencoder 是一种无监督学习模型&#xff0c;旨在从未标注的数据中学习压…

解决Window10上IP映射重启失效的问题

问题 在实际网络搭建过程中&#xff0c;大家有可能会遇到在局域网范围内&#xff0c;在自己本机上搭建一个网站或者应用时&#xff0c;其他设备通过本机的IP地址无法访问的问题,这个问题可以通过设置IP映射来解决&#xff0c;但是通过netsh interface命令设置的IP映射&#xf…

python h5py 读取mat文件的<HDF5 object reference> 问题

我用python加载matlab的mat文件 mat文件&#xff1a; 加载方式&#xff1a; mat_file h5py.File(base_dir str(N) _nodes_dataset_snr- str(snr) _M_ str(M) .mat, r) Signals mat_file["Signals"][()] Tp mat_file["Tp"][()] Tp_list mat_fil…