MyBatis-Plus的Wrapper核心体系
MyBatis-Plus的Wrapper核心体系
MyBatis-Plus 中的 Wrapper 体系是其条件构造器的核心组成部分,以 Wrapper 为顶层接口、AbstractWrapper 为核心抽象类,是一套基于面向对象思想设计的、用于动态构建 SQL 条件子句的类型化 API 体系。
Wrapper 体系作为 MP 的核心组件,深度集成到 BaseMapper、IService 等核心 CRUD 接口的关键方法中,是实现动态条件查询、条件更新的核心载体。
其核心能力与特性可归纳为:
- 动态条件组装:支持根据业务逻辑动态添加 SQL 条件,适配多场景的动态查询 / 更新需求,无需编写固定条件的 SQL 片段;
- 安全防护能力:内部基于 JDBC 参数占位符(?)机制自动完成参数绑定和特殊字符转义,从底层规避手动拼接 SQL 字符串导致的 SQL 注入风险;
- 语法友好性:提供链式调用语法简化条件拼接逻辑,同时支持 Lambda 表达式指定实体类字段,通过实体类方法引用代替字符串,解决字符串字段名硬编码的风险;
- 逻辑扩展性:原生支持多层 AND/OR 嵌套逻辑、自定义 SQL 片段嵌入,也可通过继承AbstractWrapper等抽象类扩展自定义条件构造逻辑,满足复杂业务场景的个性化需求;
- 场景适配性:通过 QueryWrapper/LambdaQueryWrapper(查询场景)、UpdateWrapper/LambdaUpdateWrapper(更新场景)等细分实现类精准适配不同数据操作场景 ------ 其中 Lambda 版本编译期校验字段合法性,非 Lambda 版本更灵活但存在硬编码风险,整体遵循单一职责设计原则。
Wrapper 核心体系结构如图:

图中所示并不完全详细,跳过了中间抽象层,实际上 AbstractWrapper 核心通用抽象类的直接子类包含 AbstractQueryWrapper(查询场景抽象)、AbstractUpdateWrapper(更新场景抽象)和 AbstractLambdaWrapper(Lambda 通用抽象)
- AbstractQueryWrapper 的唯一实现类是 QueryWrapper,AbstractUpdateWrapper 的唯一实现类是 UpdateWrapper;
- AbstractLambdaWrapper 进一步派生出 AbstractLambdaQueryWrapper(Lambda 查询抽象)、AbstractLambdaUpdateWrapper(Lambda 更新抽象),二者的实现类分别为 LambdaQueryWrapper、LambdaUpdateWrapper。
QueryWrapper
QueryWrapper 是 MyBatis-Plus 核心的查询条件构造器,核心用于替代手写 SQL 查询语句中的 WHERE 子句。它以面向对象的链式调用方式灵活构造各类查询条件,主要适配单表查询场景,也可支持简易多表关联查询的条件拼接,能大幅简化 SQL 条件编写流程,提升代码可读性与开发效率。
下面进行核心使用场景及示例
情况1:单表条件查询
java
@SpringBootTest
public class MybatisWrapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
}

组装的 sql 语句为:
sql
SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ? AND email IS NOT NULL)
- 比较条件:
eq:=,ne:<>,gt: >,ge:>=,lt:<,le:<= - 范围查询:
in:IN (?,?,...),notIn:NOT IN (?,?,...)
between:BETWEEN ? AND ?
notBetween:NOT BETWEEN ? AND ? - 空值判断:
isNull:IS NULL
isNotNull:IS NOT NULL
情况2:组装模糊查询
java
@Test
public void test01(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}

组装的 sql 语句为:
sql
SELECT id,name,age,email FROM user WHERE (name LIKE ?)
模糊查询:
like:LIKE '% 值 %'
likeLeft:LIKE '% 值'
likeRight:LIKE ' 值 %'
notLike:NOT LIKE '% 值 %'
情况3:组装排序条件
java
@Test
public void test02(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

组装的 sql 语句为:
sql
SELECT id,name,age,email FROM user ORDER BY age DESC,id ASC
- 排序:
orderByAsc:ORDER BY 字段 ASC,可按照多字段进行排序
orderByDesc:ORDER BY 字段 DESC,可按照多字段进行排序 - 分组:
groupBy:GROUP BY 字段
having:HAVING sql,用于分组后筛选
情况4:删除条件
条件构造器也可以构建删除语句的条件
java
@Test
public void test03(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}

组装的 sql 语句为:
sql
DELETE FROM user WHERE (email IS NULL)
情况5:条件优先级
使用逻辑运算
java
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("name", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@123.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}

组装的 sql 语句为:
sql
UPDATE user SET age=?, email=? WHERE (name LIKE ? AND age > ? OR email IS NULL)
该方法意为:更新表中(姓名包含 "a" 且年龄 > 20)或者邮箱为 NULL 的所有用户,将其年龄改为 18邮箱改为 user@123.com
- and:拼接 AND 条件(默认)
- or:拼接 OR 条件
- nested:嵌套条件(优先执行)
若想提升 OR 优先级可以使用 and() 或 nested() 方法嵌套条件
使用 and() 方法嵌套条件
java
@Test
public void test05() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@123.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}

使用 nested() 嵌套条件
java
@Test
public void test05_1() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("name", "a")
.nested(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@123.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}

组装的 sql 语句均为:
sql
UPDATE user SET age=?, email=? WHERE (name LIKE ? AND (age > ? OR email IS NULL))
情况6:字段筛选
只查指定字段
java
@Test
public void test06() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}

组装的 sql 为:
sql
SELECT name,age FROM user
若我们选择返回值类型为 List<User>,那么实体类中引用类型字段对应的列未被 select() 指定查询时,该字段的值为 null,基本数据类型字段对应的列未被 select() 指定查询时,该字段会取基本类型的默认值,如图:

字段筛选实现聚合查询
java
@Test
public void test06_1(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("email", "test");
queryWrapper.select("COUNT(*) as total_count");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}

组装的 sql 为:
sql
SELECT COUNT(*) as total_count FROM user WHERE (email LIKE ?)
总结:在 MyBatis-Plus 中,selectMaps () 与 select () 是高频且实用的搭配
select () 用于精准指定需要查询的字段,而 selectMaps () 则适配两类核心场景 ------ 一是仅需查询部分字段,无需封装为实体类以简化开发,二是查询结果包含聚合函数、自定义字段别名等无法直接映射到实体类的情况,能以 Map<String, Object> 形式灵活接收结果。
情况7:实现子查询
java
@Test
public void test07() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
inSql() 是为查询条件拼接 IN 关键字,且 IN 后的括号内直接填入自定义的子查询 SQL 片段,最终生成 字段 IN (子查询语句) 的 SQL 条件

组装的 sql 语句为:
sql
SELECT id,name,age,email FROM user WHERE (id IN (select id from user where id <= 3))
情况8:分页查询
结合 MP 分页插件
java
@Test
public void test08(){
Page<User> page = new Page<>(1, 5);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("email", "test");
userMapper.selectPage(page, queryWrapper);
List<User> list = page.getRecords();
list.forEach(System.out::println);
}

组装的 sql 为:
sql
SELECT id,name,age,email FROM user WHERE (email LIKE ?) LIMIT ?
UpdateWrapper
UpdateWrapper 是 MyBatis-Plus 专为带条件的更新操作设计的核心工具,用于拼接更新字段、动态 / 复杂筛选条件,替代原生 SQL 拼接的繁琐与易出错问题。
简化更新操作的 WHERE 条件构建 + SET 字段赋值,无需手动拼接 SQL,同时通过参数化查询防止 SQL 注入;
核心特性:链式编程、动态条件、支持字段算术运算、自定义 SQL 片段、自动映射实体 / 数据库字段。
情况1: T entity 为 null
java
@Test
public void test09() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper
.set("age", 18)
.set("email", "user@123.com")
.like("name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
int result = userMapper.update(null, updateWrapper);
System.out.println(result);
}
BaseMapper 提供的 update 方法中,第一个参数 T entity:
非 null 时,用于提供更新字段(后面有例子进行演示);
null 值意为放弃通过实体设置更新字段,此时更新字段完全依赖第二个参数 UpdateWrapper 的 set/setSql 方法指定,而 UpdateWrapper 中的 like/and/gt 等方法仅用于构建 WHERE 筛选条件。

组装的 sql 语句为:
sql
UPDATE user SET age=?,email=? WHERE (name LIKE ? AND (age > ? OR email IS NULL))
需要注意的是,若不使用 lambda 嵌套,直接写 and().gt().or().isNull(),会导致OR会和外层条件同级,条件的优先级产生错误。
情况2: T entity 不为 null
java
@Test
public void test10(){
User user = new User();
user.setAge(18);
user.setEmail(null);
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper
.like("name", "a")
.set("email", "user@123.com");
userMapper.update(user, updateWrapper);
}
BaseMapper 提供的 update 方法中第一个参数 T entity 若传入非 null 实体对象,MP 会基于实体的非 null 字段生成 SET 语句,实体中为 null 的字段不会参与更新,避免覆盖原有值。

组装的 sql 语句为:
sql
UPDATE user SET age=?, email=? WHERE (name LIKE ?)
若实体(entity)和 UpdateWrapper 同时设置了同一个字段,那么 UpdateWrapper 的 set / setSql 会覆盖实体的字段值 ------ 原因是 MP 拼接 SET 子句时,Wrapper 的 set 逻辑执行在后,最终生成的 SET 语句中,Wrapper 设定的字段值会覆盖实体的同名字段值。
LambdaQueryWrapper
LambdaQueryWrapper 是 MyBatis-Plus 提供的 Lambda 风格类型的查询条件构造器,继承自 AbstractLambdaWrapper,核心用于构建 SQL 的 SELECT 语句的查询条件。
该组件通过 Lambda 方法引用(实体类::字段)替代字符串硬编码指定字段名,将字段合法性校验前置至编译阶段,解决了传统 QueryWrapper 字段名硬编码导致的拼写错误、重构成本高、运行时风险等问题;
同时支持链式调用、动态条件构造、复杂嵌套条件拼接,可高效、规范地构建各类查询条件,保障查询逻辑的类型安全与可维护性。
java
@Test
public void test14() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
.like(User::getName, "a") .ge(User::getAge, 10)
.le(User::getAge, 24);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

组装的 sql 语句为:
sql
SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age >= ? AND age <= ?)
LambdaUpdateWrapper
LambdaUpdateWrapper 是 MyBatis-Plus 提供的 Lambda 风格类型的更新条件构造器,继承自 AbstractLambdaWrapper,核心用于构建 SQL 的 UPDATE 语句的更新字段与更新过滤条件。
该组件通过 Lambda 方法引用(实体类::字段)替代字符串硬编码指定更新字段和条件字段,将字段合法性校验前置至编译阶段,解决了传统 UpdateWrapper 字段名硬编码导致的拼写错误、重构成本高、运行时风险等问题;
同时支持链式调用、动态条件构造、复杂嵌套条件拼接,除继承 AbstractLambdaWrapper 体系下所有 WHERE 条件构造能力外,还扩展了 set/setSql 等专属方法用于精准配置更新字段,可高效、规范地构建各类更新逻辑,保障更新操作的类型安全与可维护性。
java
@Test
public void test15() {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@123.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
User user = new User();
int result = userMapper.update(user, updateWrapper);
System.out.println("受影响的行数:" + result);
}

组装的 sql 语句为:
sql
UPDATE user SET age=?,email=? WHERE (name LIKE ? AND (age < ? OR email IS NULL))