MyBatis-Plus 核心功能详解:条件构造器、Service 封装与批量优化实践
在日常开发中,简单的基于
id的 CRUD 并不能满足复杂业务需求。MyBatis-Plus 在BaseMapper的基础上,提供了条件构造器(Wrapper) 、通用 Service 层封装 以及高效批量处理能力,可以极大提升开发效率与代码可维护性。本文将系统总结这些核心能力,并结合实际示例进行说明。
上一节课的内容:MyBatis-Plus 快速入门与常用注解、配置总结
📚 目录(点击跳转对应章节)
一、条件构造器(Wrapper)
[二、QueryWrapper 与 UpdateWrapper 的使用](#二、QueryWrapper 与 UpdateWrapper 的使用)
[三、LambdaQueryWrapper 与 LambdaUpdateWrapper](#三、LambdaQueryWrapper 与 LambdaUpdateWrapper)
[四、自定义 SQL 与 Wrapper 结合使用](#四、自定义 SQL 与 Wrapper 结合使用)
[五、Service 层封装(IService & ServiceImpl)](#五、Service 层封装(IService & ServiceImpl))
[六、基于 Lambda 的动态条件查询](#六、基于 Lambda 的动态条件查询)
[七、LambdaUpdate 实现复杂业务更新](#七、LambdaUpdate 实现复杂业务更新)
八、批量新增性能优化
九、总结
一、条件构造器(Wrapper)
在 MyBatis-Plus 中,除了新增操作外,修改、删除、查询 都需要 WHERE 条件。BaseMapper 不仅支持基于 id 的操作,还支持通过 Wrapper 条件构造器实现复杂 SQL。
1. Wrapper 体系结构
Wrapper:条件构造的顶层抽象类AbstractWrapper:提供所有WHERE条件的通用构造方法QueryWrapper:在AbstractWrapper基础上扩展了select,用于指定查询字段UpdateWrapper:在AbstractWrapper基础上扩展了set,用于构建SET语句
二、QueryWrapper 与 UpdateWrapper 的使用
1. QueryWrapper 查询示例
需求 :查询用户名中包含 o,且余额大于等于 1000 的用户。
java
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper);
2. QueryWrapper 更新示例
需求 :将用户名为 Jack 的用户余额修改为 2000。
java
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username", "Jack");
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
3. UpdateWrapper:基于原值更新字段
需求:对 id 为 1、2、4 的用户,余额统一扣减 200。
对应 SQL:
sql
UPDATE user SET balance = balance - 200 WHERE id IN (1,2,4);
使用 UpdateWrapper#setSql:
java
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", List.of(1L, 2L, 4L));
userMapper.update(null, wrapper);
三、LambdaQueryWrapper 与 LambdaUpdateWrapper
1. 解决字符串魔法值问题
QueryWrapper 和 UpdateWrapper 需要手写字段名字符串,不利于维护。MyBatis-Plus 提供了 Lambda Wrapper,通过方法引用自动解析字段名。
2. LambdaQueryWrapper 示例
java
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
List<User> users = userMapper.selectList(wrapper);
四、自定义 SQL 与 Wrapper 结合使用
1. Wrapper + 注解 SQL
当业务不允许在 Service 层直接拼 SQL,可将 SQL 定义在 Mapper 中,并使用 Wrapper 生成条件。
java
@Update("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money,
@Param("ew") QueryWrapper<User> wrapper);
调用方式:
java
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("id", List.of(1L, 2L, 4L));
userMapper.deductBalanceByIds(200, wrapper);
2. 多表关联查询
MyBatis-Plus 不直接支持多表查询,但可通过 自定义 SQL + Wrapper 条件实现。
java
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("u.id", List.of(1L, 2L, 4L))
.eq("a.city", "北京");
Mapper:
java
@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);
五、Service 层封装(IService & ServiceImpl)
MyBatis-Plus 提供了通用的 Service 接口 IService,以及默认实现 ServiceImpl,封装了常见业务方法:
- save / saveBatch / saveOrUpdate
- remove / update
- get / list / count / page
- lambdaQuery / lambdaUpdate
1. 自定义 Service
java
public interface IUserService extends IService<User> {
void deductBalance(Long id, Integer money);
}
实现类:
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements IUserService {
}
六、基于 Lambda 的动态条件查询
1. 动态查询条件实体
java
public class UserQuery {
private String name;
private Integer status;
private Integer minBalance;
private Integer maxBalance;
}
2. lambdaQuery 链式查询
java
List<User> users = userService.lambdaQuery()
.like(query.getName() != null, User::getUsername, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.ge(query.getMinBalance() != null, User::getBalance, query.getMinBalance())
.le(query.getMaxBalance() != null, User::getBalance, query.getMaxBalance())
.list();
这种写法等价于 MyBatis 的 <if> 动态 SQL,代码更简洁、可读性更强。
七、LambdaUpdate 实现复杂业务更新
需求:扣减余额后,如果余额为 0,则将用户状态修改为冻结。
java
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0, User::getStatus, 2)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance())
.update();
这种方式可以实现 SET 部分的动态拼接,非常适合复杂更新场景。
八、批量新增性能优化
1. saveBatch 原理分析
MyBatis-Plus 的 saveBatch 本质是 PreparedStatement 批量执行 ,但默认情况下,数据库仍会执行多条 INSERT 语句。
2. MySQL 批处理 SQL 合并优化
通过开启 MySQL 驱动参数:
yaml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
即可将多条 INSERT 合并为一条多值 SQL:
sql
INSERT INTO user (...) VALUES (...), (...), (...);
开启后,批量插入性能可获得数量级提升。
九、总结
MyBatis-Plus 在传统 MyBatis 的基础上,提供了:
- 强大的条件构造器(Wrapper / LambdaWrapper)
- 通用 Service 层封装,减少模板代码
- Wrapper + 自定义 SQL 的灵活扩展能力
- lambdaQuery / lambdaUpdate 简化复杂业务逻辑
- 批量操作的性能优化手段
在实际项目中,合理使用 Wrapper + Service 封装 + 批量优化参数,可以显著提升开发效率与系统性能,是企业级项目中非常值得掌握的一套技术方案。