news 2026/4/30 22:23:33

【MybatisPlus-核心功能】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 22:22:44

智慧校园软件选型,如何避开低价的陷阱?

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

作者头像 李华
网站建设 2026/4/30 22:19:43

暗黑破坏神2存档解析与编辑:基于Vue.js的现代化解决方案

暗黑破坏神2存档解析与编辑&#xff1a;基于Vue.js的现代化解决方案 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 你是否曾因暗黑破坏神2存档损坏而失去数小时的游戏进度&#xff1f;是否想要快速测试不同职业Build却苦于漫长…

作者头像 李华
网站建设 2026/4/30 22:13:22

题解:AcWing 6027 后缀表达式的值

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来&#xff0c;并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构&#xff0c;旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大…

作者头像 李华
网站建设 2026/4/30 22:09:07

昆明靠谱装修设计工作室大盘点,这些宝藏之选你知道吗?

在昆明&#xff0c;装修设计工作室众多&#xff0c;如何从中挑选出靠谱的工作室成为了众多业主的难题。今天&#xff0c;就为大家详细盘点一下昆明靠谱的装修设计工作室&#xff0c;其中&#xff0c;云南胡桃善锦装饰工程有限公司&#xff08;胡桃善锦原创设计&#xff09;更是…

作者头像 李华