MyBatis-Plus 条件构造器(Wrapper)全解析:QueryWrapper/LambdaQueryWrapper 实战
一、前言
在使用 MyBatis-Plus(MP)实现复杂查询、更新、删除操作时,单纯依赖BaseMapper的默认方法无法满足多条件筛选需求。MP 提供的条件构造器(Wrapper) 是解决复杂 SQL 条件拼接的核心工具,它支持动态组装 WHERE 条件、排序、子查询等操作,无需手写 SQL 即可实现灵活的条件查询。本文将从 Wrapper 体系结构、核心实现类(QueryWrapper、LambdaQueryWrapper 等)、实战案例等维度,全面讲解条件构造器的使用方式,帮助开发者掌握 MP 的进阶核心功能。
二、Wrapper 体系结构详解
Wrapper 是 MP 中所有条件构造器的顶层抽象类,其核心子类体系如下:
| 类名 | 核心作用 |
|---|---|
| Wrapper | 条件构造抽象父类,定义了条件构造的核心规范 |
| AbstractWrapper | 封装查询条件,生成 SQL 的 WHERE 子句,是 QueryWrapper/UpdateWrapper 的直接父类 |
| QueryWrapper | 用于 SELECT 操作的条件构造器,侧重查询条件封装 |
| UpdateWrapper | 用于 UPDATE 操作的条件构造器,支持 SET 子句和 WHERE 条件封装 |
| AbstractLambdaWrapper | 基于 Lambda 语法的条件构造抽象类,避免手写字段名 |
| LambdaQueryWrapper | Lambda 版 QueryWrapper,通过 Lambda 引用字段,编译期校验字段名 |
| LambdaUpdateWrapper | Lambda 版 UpdateWrapper,支持 Lambda 设置 SET 字段和 WHERE 条件 |
核心优势:
- 无需手写 SQL,通过面向对象的方式拼接条件;
- 支持动态条件(按需组装),适配前端多条件查询场景;
- Lambda 版构造器避免字段名拼写错误,提升代码健壮性。
三、QueryWrapper:基础条件构造器实战
QueryWrapper是最基础的条件构造器,通过字符串指定字段名,支持所有 SQL 查询条件的拼接,适用于简单场景。
3.1 案例 1:组装查询条件
需求:查询姓名包含 "张"、年龄大于 20 且邮箱不为空的用户。
@Test
public void testQueryWrapper1() {
// 1. 创建QueryWrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 2. 组装条件:like(字段名, 值)、gt(字段名, 值)、isNotNull(字段名)
queryWrapper
.like("name", "张") // 姓名 LIKE '%张%'
.gt("age", 20) // 年龄 > 20
.isNotNull("email"); // 邮箱 IS NOT NULL
// 3. 执行查询:自动拼接WHERE条件
// SQL:SELECT id,name,age,email FROM t_user WHERE (name LIKE ? AND age > ? AND email IS NOT NULL)
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
3.2 案例 2:组装排序条件
需求:查询所有用户,按年龄降序排序,若年龄相同则按 id 升序排序。
@Test
public void testQueryWrapper2() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 排序条件:orderByDesc(降序)、orderByAsc(升序)
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
// SQL:SELECT id,name,age,email FROM t_user ORDER BY age DESC, id ASC
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
3.3 案例 3:组装删除条件
需求:删除邮箱为空的用户。
@Test
public void testQueryWrapper3() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 条件:email IS NULL
queryWrapper.isNull("email");
// 执行删除:条件构造器可直接作为delete方法的参数
// SQL:DELETE FROM t_user WHERE (email IS NULL)
int result = userMapper.delete(queryWrapper);
System.out.println("受影响行数:" + result);
}
3.4 案例 4:组装 SELECT 子句(指定查询字段)
需求:仅查询用户的 id、name、age 字段,不查询 email。
@Test
public void testQueryWrapper4() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// select方法指定要查询的字段
queryWrapper
.select("id", "name", "age")
.like("name", "张");
// SQL:SELECT id,name,age FROM t_user WHERE (name LIKE ?)
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
3.5 案例 5:实现子查询
需求:查询 id 小于等于 3 的用户信息(通过子查询实现)。
@Test
public void testQueryWrapper5() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// inSql(字段名, 子查询SQL):字段值在子查询结果集中
queryWrapper.inSql("id", "select id from t_user where id <= 3");
// SQL:SELECT id,name,age,email FROM t_user WHERE (id IN (select id from t_user where id <= 3))
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
四、UpdateWrapper:更新操作的条件构造器
UpdateWrapper专门用于 UPDATE 操作,既可以封装 WHERE 条件,也可以通过set方法指定要更新的字段,比直接使用updateById更灵活。
实战案例:多条件更新
需求:将姓名包含 "a"、年龄小于 24 或邮箱为空的用户,年龄改为 18,邮箱改为user@qcby.com。
@Test
public void testUpdateWrapper() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 1. 组装SET子句
updateWrapper
.set("age", 18)
.set("email", "user@qcby.com")
// 2. 组装WHERE条件
.like("name", "a")
.and(i -> i.lt("age", 24).or().isNull("email")); // 嵌套条件:(age < 24 OR email IS NULL)
// 执行更新:第一个参数为实体对象(可传空),第二个为条件构造器
// SQL:UPDATE t_user SET age=?, email=? WHERE (name LIKE ? AND (age < ? OR email IS NULL))
int result = userMapper.update(null, updateWrapper);
System.out.println("受影响行数:" + result);
}
五、动态条件构造:condition 参数的使用
实际开发中,查询条件往往来自前端输入(如用户名、年龄区间),这些条件是可选的(用户可能输入也可能不输入)。若直接拼接条件,会导致空条件影响查询结果,此时需通过condition参数实现动态条件组装。
5.1 传统实现方式(if 判断)
@Test
public void testDynamicCondition1() {
// 模拟前端输入:username为空(未输入),ageBegin=10,ageEnd=24
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 手动判断条件是否有效,有效则拼接
if (StringUtils.isNotBlank(username)) {
queryWrapper.like("name", username);
}
if (ageBegin != null) {
queryWrapper.ge("age", ageBegin); // age >= 10
}
if (ageEnd != null) {
queryWrapper.le("age", ageEnd); // age <= 24
}
// SQL:SELECT id,name,age,email FROM t_user WHERE (age >= ? AND age <= ?)
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
5.2 简化实现(带 condition 参数的重载方法)
MP 为所有条件方法提供了带condition参数的重载版本,condition为true时才拼接该条件,大幅简化代码:
@Test
public void testDynamicCondition2() {
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 第一个参数为condition,true则拼接条件
queryWrapper
.like(StringUtils.isNotBlank(username), "name", username)
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
六、LambdaQueryWrapper:避免字段名拼写错误的最优解
QueryWrapper通过字符串指定字段名(如"name"),若字段名拼写错误(如"nme"),仅能在运行时发现异常。LambdaQueryWrapper通过Lambda 表达式引用实体类字段(如User::getName),编译期即可校验字段名正确性,是生产环境的首选。
实战案例:LambdaQueryWrapper 动态条件查询
@Test
public void testLambdaQueryWrapper() {
// 模拟前端输入:username="a",ageBegin=10,ageEnd=24
String username = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
// 创建LambdaQueryWrapper对象
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 通过Lambda引用字段,无需手写字符串
queryWrapper
.like(StringUtils.isNotBlank(username), User::getName, username)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
// SQL:SELECT id,name,age,email FROM t_user WHERE (name LIKE ? AND age >= ? AND age <= ?)
List<User> userList = userMapper.selectList(queryWrapper);
userList.forEach(System.out::println);
}
核心优势:
- 编译期校验字段名,避免运行时错误;
- 支持 IDE 自动补全字段名,提升开发效率;
- 实体类字段改名时,IDE 可自动重构 Lambda 表达式,降低维护成本。
七、LambdaUpdateWrapper:Lambda 版更新条件构造器
LambdaUpdateWrapper是UpdateWrapper的 Lambda 版本,同样通过 Lambda 引用字段,避免字符串拼写错误。
实战案例:LambdaUpdateWrapper 多条件更新
@Test
public void testLambdaUpdateWrapper() {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
// SET子句:Lambda引用字段
.set(User::getAge, 18)
.set(User::getEmail, "user@qcby.com")
// WHERE条件
.like(User::getName, "a")
// 嵌套条件:(age < 24 OR email IS NULL)
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
// 执行更新:实体对象可传空(所有更新字段通过set指定)
int result = userMapper.update(new User(), updateWrapper);
System.out.println("受影响行数:" + result);
}
八、Wrapper 核心方法速查表
| 方法名 | 作用 | 示例 |
|---|---|---|
| eq | 等于(=) | eq("age", 20) → age = 20 |
| ne | 不等于(!=) | ne("age", 20) → age != 20 |
| gt | 大于(>) | gt("age", 20) → age > 20 |
| ge | 大于等于(>=) | ge("age", 20) → age >= 20 |
| lt | 小于(<) | lt("age", 20) → age < 20 |
| le | 小于等于(<=) | le("age", 20) → age <= 20 |
| like | 模糊查询(LIKE) | like("name", "张") → name LIKE '% 张 %' |
| isNull | 为空(IS NULL) | isNull("email") → email IS NULL |
| isNotNull | 不为空(IS NOT NULL) | isNotNull("email") → email IS NOT NULL |
| in | IN 查询 | in("id", 1,2,3) → id IN (1,2,3) |
| inSql | 子查询 IN | inSql("id", "select id from t_user where age<20") |
| orderByAsc | 升序排序 | orderByAsc("age") → ORDER BY age ASC |
| orderByDesc | 降序排序 | orderByDesc("age") → ORDER BY age DESC |
| and | 并且(AND) | and(i -> i.lt("age",20).gt("id",1)) → AND (age<20 AND id>1) |
| or | 或者(OR) | or(i -> i.eq("name","张三")) → OR (name=' 张三 ') |
九、核心注意事项
- 字段名映射:使用非 Lambda 版构造器时,字段名需与数据库表字段一致(而非实体类属性名),若实体类属性是驼峰(如
userName),表字段是下划线(user_name),需写"user_name"; - Lambda 表达式注意:Lambda 构造器中引用的字段必须是实体类中存在的属性,且需有对应的 getter 方法(Lombok 的
@Data可自动生成); - 嵌套条件优先级:使用
and()/or()嵌套条件时,Lambda 表达式内的条件会优先拼接(自动加括号),避免条件优先级错误; - 空值处理:动态条件务必通过
condition参数判断,避免拼接null值导致 SQL 语法错误。
十、总结
关键点回顾
- Wrapper 是 MP 实现复杂条件查询的核心,
QueryWrapper适用于简单场景,LambdaQueryWrapper通过 Lambda 避免字段名错误,是生产环境首选; - 动态条件构造需使用带
condition参数的方法,按需拼接条件,避免空条件影响查询结果; UpdateWrapper/LambdaUpdateWrapper适用于多条件更新,支持 SET 子句和复杂 WHERE 条件封装;- 掌握 Wrapper 的核心方法(eq、like、and/or 等),可覆盖 90% 以上的复杂查询场景,无需手写 SQL。
通过本文的讲解,相信你已掌握 MP 条件构造器的核心使用方式。条件构造器是 MP 的进阶核心,灵活运用可大幅提升复杂业务的开发效率,同时保证代码的健壮性和可维护性。
更多推荐
所有评论(0)