目录
一、什么是MyBatis-Plus的条件构造器?
在传统MyBatis中,我们经常需要在XML映射文件中编写大量的<if>标签来构建动态SQL,这不仅繁琐,而且容易出错。MyBatis-Plus的条件构造器正是为了解决这一问题而生。
核心思想: 通过链式调用的API,以Java对象的方式来表示SQL的查询条件(如WHERE、SELECT、ORDER BY等),然后由MyBatis-Plus自动将其转换为真正的SQL语句。
主要优势:
- 类型安全: 使用Lambda表达式时,可以避免字段名的硬编码,编译期就能发现错误。
- 代码简洁: 链式调用,逻辑清晰,代码简洁。
- 功能强大: 支持几乎所有的SQL语法,包括嵌套查询、排序、分组等。
二、核心类介绍
1. 核心类类图:

条件构造器的核心抽象类是Wrapper,我们最常用的是它的两个子类:
QueryWrapper(查询条件构造器): 主要用于构建SELECT语句的WHERE条件。 例如:where name = ‘John’ and age > 25。UpdateWrapper(更新条件构造器): 主要用于构建UPDATE语句的SET部分和WHERE条件。 例如:update user set name = ‘Tom’ where status = 0。LambdaQueryWrapper和LambdaUpdateWrapper(Lambda表达式版本): 功能分别与QueryWrapper和UpdateWrapper对应。 最大优点: 通过Entity::getXxx方法引用来获取字段名,避免了字符串硬编码,是官方推荐的用法。
2. 各实现类适用场景
| 实现类 | 适用场景 | 优势 |
|---|---|---|
QueryWrapper | 实体查询操作 | 简单直接 |
UpdateWrapper | 实体更新操作 | 可设置更新字段 |
LambdaQueryWrapper | 实体查询操作 | 类型安全,推荐使用 |
LambdaUpdateWrapper | 实体更新操作 | 类型安全,推荐使用 |
三、 数据准备
1. 创建表t_user
CREATE TABLE t_user (
id bigint NOT NULL AUTO_INCREMENT,
username varchar(32) NOT NULL,
age int DEFAULT NULL,
tel varchar(11) NOT NULL,
email varchar(32) DEFAULT NULL,
create_time datetime NOT NULL,
status tinyint DEFAULT '1',
PRIMARY KEY (id),
UNIQUE KEY tel (tel)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2. 插入数据
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (1, '张三', 18, '15100000000', 'zhangsan@163.com', '2025-08-05 17:44:56', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (2, '李四', 22, '17100000000', 'lisi@163.com', '2025-08-05 17:45:38', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (3, '王五', 35, '15800000000', 'wangwu@163.com', '2025-08-06 10:36:19', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (4, '赵六', 29, '15800000001', 'zhaoliu@163.com', '2025-08-06 10:36:19', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (5, '陈七', 30, '15800000002', 'chenqi@163.com', '2025-08-06 10:36:19', 1);
3. User实体类
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Long id;
private String username;
private int age;
private String tel;
private String email;
private int status;
private LocalDateTime createTime;
}
四、QueryWrapper详解与实战
1. 基础条件方法
这些方法对应SQL中的基本操作符。
| 方法名 | 说明 | SQL等价形式 |
|---|---|---|
eq | 等于 = | where name = ‘张三’ |
ne | 不等于 <> | where age <> 18 |
gt | 大于 > | where age > 18 |
ge | 大于等于 >= | where age >= 18 |
lt | 小于 < | where age < 30 |
le | 小于等于 <= | where age <= 30 |
between | BETWEEN 值1 AND 值2 | where age BETWEEN 20 AND 30 |
notBetween | NOT BETWEEN 值1 AND 值2 | where age NOT BETWEEN 20 AND 30 |
like | LIKE ‘%值%’ | where name like ‘%张%’ |
likeLeft | LIKE ‘%值’ | where name like ‘%三’ |
likeRight | LIKE ‘值%’ | where name like ‘张%’ |
isNull | 字段 IS NULL | where email IS NULL |
isNotNull | 字段 IS NOT NULL | where email IS NOT NULL |
in | 字段 IN (v0, v1, …) | where id in (1, 2, 3) |
notIn | 字段 NOT IN (v0, v1, …) | where id not in (1, 2, 3) |
1.1. 等值、范围查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// SQL: username = '张三'
queryWrapper.eq("username", "张三");
// 不等查询
// SQL: status <> 0
queryWrapper.ne("status", 0);
// 范围查询
// SQL: age BETWEEN 18 AND 25
queryWrapper.between("age", 18, 25);
// 模糊查询
// SQL: name LIKE '%张%'
queryWrapper.like("username", "张");
List<User> users = userMapper.selectList(queryWrapper);
1.2. 空值检查、IN查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// SQL: email IS NULL
queryWrapper.isNull("email");
// SQL: create_time IS NOT NULL
queryWrapper.isNotNull("create_time");
// SQL: id IN (1,2,3)
queryWrapper.in("id", Arrays.asList(1, 2, 3));
// SQL: status NOT IN (0,2)
queryWrapper.notIn("status", Arrays.asList(0, 2));
2. 复杂查询
| 方法名 | 说明 | SQL等价形式 |
|---|---|---|
and | 默认是AND,可省略。用于连接条件 | where (age > 25 AND name = ‘张’) |
or | 使用OR连接条件 | where (age > 25 OR status = 1) |
nested | 嵌套条件,用于复杂的括号组合 | where (age > 25 AND (name = ‘张’ OR name = ‘王’)) |
orderByAsc | 按字段升序排序 | ORDER BY age ASC |
orderByDesc | 按字段降序排序 | ORDER BY age DESC |
groupBy | 按字段分组 | GROUP BY department |
having | 分组后过滤 | GROUP BY ... HAVING count(*) > 1 |
select | 指定查询的列 | SELECT id, name |
2.1 逻辑操作与嵌套查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// AND 连接(默认,可省略)
// SQL: age > 18 AND age <= 65 AND status = 1
queryWrapper.gt("age", 18)
.le("age", 30)
.eq("status", 1);
// OR 连接
// SQL: username = '张三' OR username = '李四'
queryWrapper.eq("username", "张三").or().eq("username", "李四");
// 复杂嵌套条件
// SQL: ( age > 20 OR email IS NULL ) OR ( age < 10 AND status = 1 )
queryWrapper.and(wp -> wp.gt("age", 20).or().isNull("email"))
.or(wp -> wp.lt("age", 10).and(wp2 -> wp2.eq("status", 1)));
2.2 排序、分组、分页
// 选择特定字段(避免SELECT *)
// SELECT id, username, age
queryWrapper.select("id", "username", "age");
// 排序
// ORDER BY age ASC, id ASC
queryWrapper.orderByAsc("age", "id");
// ORDER BY create_time DESC
queryWrapper.orderByDesc("create_time");
// 分组
// GROUP BY status
queryWrapper.groupBy("status");
// HAVING count(*) > 1
queryWrapper.having("count(*) > 1");
五、LambdaQueryWrapper:类型安全的写法
1. 基础用法对比
// 传统QueryWrapper - 字段名硬编码(不推荐)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "张三")
.lt("age", 25)
.select("id", "username", "age");
// LambdaQueryWrapper - 类型安全(推荐)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getUsername, "张三")
.lt(User::getAge, 25)
.select(User::getId, User::getUsername, User::getAge);
优势:
- 编译期检查,避免字段名拼写错误
- 支持IDE智能提示和重构
- 代码可读性更强
2. 复杂Lambda查询示例
@Test
public void testLambdaWrapper() {
// 复杂条件查询
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.gt(User::getAge, 18)
.and(wp -> wp.eq(User::getStatus, 1).or().eq(User::getStatus, 2))
.likeRight(User::getUsername, "王")
.orderByDesc(User::getCreateTime)
.last("LIMIT 2"); // 自定义SQL拼接(谨慎使用)
List<User> users = userMapper.selectList(lambdaWrapper);
}
运行单元测试结果如下:

六、UpdateWrapper更新操作详解
UpdateWrapper除了能设置WHERE条件,还能设置要更新的字段。
1. 基本更新操作
// 1. 使用UpdateWrapper设置更新字段和条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("age", 30) // SET age = 30
.eq("id", 1); // WHERE id = 1
userMapper.update(updateWrapper);
// 2. 使用LambdaUpdateWrapper(推荐)
LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.set(User::getAge, 30)
.eq(User::getId, 1);
userMapper.update(lambdaUpdateWrapper);
// 3. 实体+Wrapper组合更新
User updateUser = new User();
updateUser.setAge(30);
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, 1);
// 将id=1的用户年龄更新为30
userMapper.update(updateUser, wrapper);
2. 特殊更新操作
// 数学运算更新
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.setSql("age = age + 1") // 年龄+1
.eq(User::getStatus, 1);
// 批量更新符合条件的数据
userMapper.update(wrapper);
七、最佳实践与避坑指南
-
首选 Lambda 表达式:
- 硬编码(不推荐):
queryWrapper.eq("username", "张三”)。如果数据库字段名改了,这里容易出错。 - Lambda(强烈推荐):
lambdaQueryWrapper.eq(User::getUsername, "张三”)。类型安全,编译期检查,支持IDE的代码重构。
- 硬编码(不推荐):
-
避免在
Wrapper中写死数据库表字段名:始终通过实体类的属性映射,这样即使数据库字段名发生变化(如从
user_name改为username),也只需修改实体类的注解即可。 -
注意
null值的处理:- 条件方法(如
eq)传入的值为null时,默认生成的SQL是字段 = null,这通常不是我们想要的(应该用isNull)。 - 可以使用
condition参数进行判断。
String name = null; // 可能为null的查询参数 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); // 传统if判断 // if (name != null) { // wrapper.eq(User::getName, name); // } // 更优雅的方式:使用condition参数 // 只有当name != null时,该条件才会被拼接 wrapper.eq(name != null, User::getUsername, name); - 条件方法(如
-
复杂 SQL 的取舍: 对于极其复杂的多表关联查询或动态SQL,条件构造器可能会变得难以维护。此时,退回到使用MyBatis原生的XML映射文件可能是更明智的选择。
八、总结
MyBatis-Plus的条件构造器是一个极其强大且灵活的工具,它极大地简化了动态SQL的编写,提升了开发效率和代码的可读性、可维护性。掌握QueryWrapper、UpdateWrapper以及它们的Lambda版本,你就能在项目中游刃有余地处理各种数据查询和更新场景。虽然MyBatis-Plus的条件构造器的功能强大,好用,但我们也需要记住一大原则:简单查询用Wrapper,复杂查询用XML,只有合理选择才能真正解决我们的问题!
;
4. **复杂 SQL 的取舍:** 对于极其复杂的多表关联查询或动态SQL,条件构造器可能会变得难以维护。此时,退回到使用MyBatis原生的XML映射文件可能是更明智的选择。
### 八、总结
MyBatis-Plus的条件构造器是一个极其强大且灵活的工具,它极大地简化了动态SQL的编写,提升了开发效率和代码的可读性、可维护性。掌握`QueryWrapper`、`UpdateWrapper`以及它们的Lambda版本,你就能在项目中游刃有余地处理各种数据查询和更新场景。虽然MyBatis-Plus的条件构造器的功能强大,好用,但我们也需要记住一大原则:简单查询用Wrapper,复杂查询用XML,只有合理选择才能真正解决我们的问题!
1605

被折叠的 条评论
为什么被折叠?



