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

【MybatisPlus-核心功能】

MybatisPlus核心功能

  • 条件构造器
    • QueryWrapper
    • UpdateWrapper
    • LambdaQueryWrapper
  • 自定义SQL
    • 基本用法
    • 多表关联
  • Service接口
    • CRUD
    • 基本用法
    • Lambda
    • 批量新增

条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件


参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

QueryWrapper

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。接下来看一些例子:

查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:

@TestvoidtestQueryWrapper(){// 1.构建查询条件 where name like "%o%" AND balance >= 1000QueryWrapper<User>wrapper=newQueryWrapper<User>().select("id","username","info","balance").like("username","o").ge("balance",1000);// 2.查询数据List<User>users=userMapper.selectList(wrapper);users.forEach(System.out::println);}

上诉代码使用泛型可以提供编译时类型检查,确保你在构建查询时使用的字段名和条件与 User 实体的属性匹配。这可以避免在运行时出现类型不匹配的错误。

更新:更新用户名为jack的用户的余额为2000,代码如下:

@TestvoidtestUpdateByQueryWrapper(){// 1.构建查询条件 where name = "Jack"QueryWrapper<User>wrapper=newQueryWrapper<User>().eq("username","Jack");// 2.更新数据,user中非null字段都会作为set语句Useruser=newUser();user.setBalance(2000);userMapper.update(user,wrapper);}

UpdateWrapper

基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。

例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:

UPDATEuserSETbalance=balance-200WHEREidin(1,2,4)

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:

@TestvoidtestUpdateWrapper(){List<Long>ids=List.of(1L,2L,4L);// 1.生成SQLUpdateWrapper<User>wrapper=newUpdateWrapper<User>().setSql("balance = balance - 200")// SET balance = balance - 200.in("id",ids);// WHERE id in (1, 2, 4)// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,// 而是基于UpdateWrapper中的setSQL来更新userMapper.update(null,wrapper);}

LambdaQueryWrapper

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。
那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的getter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。

因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

分别对应QueryWrapper和UpdateWrapper

其使用方式如下:

@TestvoidtestLambdaQueryWrapper(){// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000QueryWrapper<User>wrapper=newQueryWrapper<>();wrapper.lambda().select(User::getId,User::getUsername,User::getInfo,User::getBalance).like(User::getUsername,"o").ge(User::getBalance,1000);// 2.查询List<User>users=userMapper.selectList(wrapper);users.forEach(System.out::println);}

自定义SQL

自定义SQL使用
1、先用Wrapper构建where条件
2、再调用Mapper层自定义的方法,将构建好的where条件和sql需要的参数传进去。
3、再进行自定义SQL与Wrapper构建的where条件进行拼接

基本用法

以当前案例来说,我们可以这样写:

@TestvoidtestCustomWrapper(){// 1.准备自定义查询条件List<Long>ids=List.of(1L,2L,4L);QueryWrapper<User>wrapper=newQueryWrapper<User>().in("id",ids);// 2.调用mapper的自定义方法,直接传递WrapperuserMapper.deductBalanceByIds(200,wrapper);}
publicinterfaceUserMapperextendsBaseMapper<User>{@Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")voiddeductBalanceByIds(@Param("money")intmoney,@Param("ew")QueryWrapper<User>wrapper);}

多表关联

理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。

例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户
要是自己基于mybatis实现SQL,大概是这样的:

<selectid="queryUserByIdAndAddr"resultType="com.itheima.mp.domain.po.User">SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id WHERE u.id<foreachcollection="ids"separator=","item="id"open="IN ("close=")">#{id}</foreach>AND a.city = #{city}</select>

但是基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECT及FROM部分,实现多表查询。
查询条件这样来构建:

@TestvoidtestCustomJoinWrapper(){// 1.准备自定义查询条件QueryWrapper<User>wrapper=newQueryWrapper<User>().in("u.id",List.of(1L,2L,4L)).eq("a.city","北京");// 2.调用mapper的自定义方法List<User>users=userMapper.queryUserByWrapper(wrapper);users.forEach(System.out::println);}

然后在UserMapper中自定义方法:

@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")List<User>queryUserByWrapper(@Param("ew")QueryWrapper<User>wrapper);

当然,也可以在UserMapper.xml中写SQL:

<selectid="queryUserByIdAndAddr"resultType="com.itheima.mp.domain.po.User">SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}</select>

Service接口

MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询

CRUD

新增:

  • save是新增单个元素
  • saveBatch是批量新增
  • saveOrUpdate是根据id判断,如果存在就更新,不存在则新增
  • saveOrUpdateBatch是批量的新增或修改

删除:

  • removeById:根据id删除
  • removeByIds:根据id批量删除
  • removeByMap:根据Map中的键值对为条件删除
  • remove(Wrapper<T>):根据Wrapper条件删除
  • removeBatchByIds:暂不支持

修改:

  • updateById:根据id修改
  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含set和where部分
  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
  • updateBatchById:根据id批量修改

Get:

  • getById:根据id查询1条数据
  • getOne(Wrapper<T>):根据Wrapper查询1条数据
  • getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper

List:

  • listByIds:根据id批量查询
  • list(Wrapper<T>):根据Wrapper条件查询多条数据
  • list():查询所有

Count:

  • count():统计所有数量
  • count(Wrapper<T>):统计符合Wrapper条件的数据数量

基本用法


为什么我们需要指定UserMapper这个泛型类型,当我们需要直接调用Mapper内的自定义SQL时可以用getBaseMapper方法获取到UserMapper

Lambda

IService中还提供了Lambda功能来简化我们的复杂查询及更新功能

@GetMapping("/list")@ApiOperation("根据id集合查询用户")publicList<UserVO>queryUsers(UserQueryquery){// 1.组织条件Stringusername=query.getName();Integerstatus=query.getStatus();IntegerminBalance=query.getMinBalance();IntegermaxBalance=query.getMaxBalance();LambdaQueryWrapper<User>wrapper=newQueryWrapper<User>().lambda().like(username!=null,User::getUsername,username).eq(status!=null,User::getStatus,status).ge(minBalance!=null,User::getBalance,minBalance).le(maxBalance!=null,User::getBalance,maxBalance);// 2.查询用户List<User>users=userService.list(wrapper);// 3.处理voreturnBeanUtil.copyToList(users,UserVO.class);}

在组织查询条件的时候,我们加入了username != null这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的<if>标签。这样就实现了动态查询条件效果了。

不过,上述条件构建的代码太麻烦了。
因此Service中对LambdaQueryWrapperLambdaUpdateWrapper的用法进一步做了简化。我们无需自己通过new的方式来创建Wrapper,而是直接调用lambdaQuerylambdaUpdate方法:

@GetMapping("/list")@ApiOperation("根据id集合查询用户")publicList<UserVO>queryUsers(UserQueryquery){// 1.组织条件Stringusername=query.getName();Integerstatus=query.getStatus();IntegerminBalance=query.getMinBalance();IntegermaxBalance=query.getMaxBalance();// 2.查询用户List<User>users=userService.lambdaQuery().like(username!=null,User::getUsername,username).eq(status!=null,User::getStatus,status).ge(minBalance!=null,User::getBalance,minBalance).le(maxBalance!=null,User::getBalance,maxBalance).list();// 3.处理voreturnBeanUtil.copyToList(users,UserVO.class);}

可以发现lambdaQuery方法中除了可以构建条件,还需要在链式编程的最后添加一个list(),这是在告诉MP我们的调用结果需要是一个list集合。这里不仅可以用list(),可选的方法有:

  • .one():最多1个结果
  • .list():返回集合结果
  • .count():返回计数结果

MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果。

批量新增

批处理方案:

  • 普通for循环逐条插入速度极差,不推荐
  • MP的批量新增,基于预编译的批处理,性能不错
  • 配置jdbc参数,开rewriteBatchedStatements,性能最好

MybatisPlus的批处理是基于PrepareStatement的预编译模式,然后批量提交,最终在数据库执行时还是会有多条insert语句,逐条插入数据。SQL类似这样:

Preparing:INSERTINTOuser(username,password,phone,info,balance,create_time,update_time)VALUES(?,?,?,?,?,?,?)Parameters: user_1,123,18688190001,"",2000,2023-07-01,2023-07-01Parameters: user_2,123,18688190002,"",2000,2023-07-01,2023-07-01Parameters: user_3,123,18688190003,"",2000,2023-07-01,2023-07-01

而如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样:

INSERTINTOuser(username,password,phone,info,balance,create_time,update_time)VALUES(user_1,123,18688190001,"",2000,2023-07-01,2023-07-01),(user_2,123,18688190002,"",2000,2023-07-01,2023-07-01),(user_3,123,18688190003,"",2000,2023-07-01,2023-07-01),(user_4,123,18688190004,"",2000,2023-07-01,2023-07-01);

该怎么做呢?

MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句,这个参数的默认值是false

修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true:

spring:datasource:url:jdbc:mysql://127.0.0.1:3306/XX?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=truedriver-class-name:com.mysql.cj.jdbc.Driverusername:root password:MySQL123
http://www.jsqmd.com/news/727868/

相关文章:

  • 告别懵圈!手把手教你用UDS 0x31服务搞定车载雷达标定(附完整请求响应示例)
  • 现在外卖哪个平台最划算?美团五折外卖解锁省钱新姿势 - 资讯焦点
  • 视觉分词技术:多语言混合与噪声鲁棒性的突破
  • 用CANoe/CANalyzer抓包分析UDS否定响应:从0x11到0x7F的实战案例解析
  • Taotoken的按Token计费模式如何让开发预算更可控
  • 为内部知识库构建一个基于多模型聚合的智能问答模块
  • 阿里云服务器部署Cloudreve教程
  • AI越贴心,陷阱越隐蔽:星盾验真教你如何避坑
  • 别再死记硬背了!用一张图+实战配置,彻底搞懂华为VXLAN里的NVE、VTEP和VNI
  • Linux RT 调度器的 rt_queued:RT 任务入队标记
  • 在濮阳选GEO公司,亲测避开哪些坑? - 速递信息
  • 吊顶式空调机组怎么选?
  • Linux RT 调度器的 rt_time:RT 任务运行时间统计
  • Hermes Agent 技术选型专题报告
  • 「盛世钢联日报」2026年4月30日成都市场主要品种钢材价格行情汇总 - 四川盛世钢联营销中心
  • 濮阳GEO服务商选哪家才不踩坑? - 速递信息
  • 生活有品质,安全须随行:Ledger大陆官方授权购买指引
  • 国内主流锌钢护栏厂家实测排行:品质与服务对标 - 奔跑123
  • PHP-FPM子进程被AI推理请求拖垮?内存泄漏定位、Swoole协程适配、OpenTelemetry追踪三重加固方案
  • 在濮阳找GEO服务,居然踩了这么多坑? - 速递信息
  • 【小白易懂版】OpenClaw 飞书机器人绑定配置详细教程(含安装包)
  • 测试文章 #8211; WordPress API 连接验证
  • 虫草贵族变平价?深圳福田这家店做到了
  • Linux RT 调度器的 rt_runtime:RT 任务配额管理
  • 别再花钱买商用Portal系统了!用OpenWRT和Wifidog自己动手搭建一个(附完整配置与认证服务器PHP代码)
  • 全国瓷砖空鼓修复服务品牌排行:专业度实测盘点 - 奔跑123
  • 国内铁艺护栏主流生产厂家实测排行一览 - 奔跑123
  • 2026年济南婚纱摄影全流程攻略:从选型到交付一站式指南 - 速递信息
  • C盘空间不足?C盘爆满这样操作才干净 一招教你安全清理C盘
  • 亲测濮阳GEO公司服务真的靠谱吗? - 速递信息