mybatis-plus-wrapper
MyBatis-Plus Wrapper 构造器
Wrapper 就是一个用于构造 SQL 语句中 WHERE 条件部分的 Java 工具类。它让你能够通过链式调用的方式,用 Java 代码拼写出各种复杂的查询、更新、删除条件,从而避免手写繁琐的 SQL 片段。Wrapper 家族主要有两个分支:QueryWrapper(用于查询)和 UpdateWrapper(用于更新),以及它们的 Lambda 版本。
1. Wrapper 继承体系(了解即可)
text
1 | Wrapper(顶级接口) |
在实际开发中,我们通常直接使用 LambdaQueryWrapper 和 LambdaUpdateWrapper,因为它们能用 Lambda 表达式引用实体类的属性(比如 User::getName),避免写错字段名(字符串形式容易写错或重构时遗漏)。
2. 为什么需要 Wrapper?
假如我们要查一张用户表 user,需要满足以下条件:
- 年龄在 18~30 之间
- 性别为男
- 姓名包含“张”
- 按年龄降序排序
不用 Wrapper 的话,你需要在 Mapper.xml 里手写 SQL:
xml
1 | <select id="selectUserList" resultType="User"> |
如果使用 Wrapper,你不用写 XML,直接在 Java 代码里构造:
java
1 | // 创建条件构造器 |
是不是简洁很多?而且完全避免了 XML 的维护工作。
3. 核心常用方法(以 LambdaQueryWrapper 为例)
🔹 比较条件
| 方法 | 说明 | 示例 |
|---|---|---|
eq | 等于 = | wrapper.eq(User::getAge, 18) → age = 18 |
ne | 不等于 <> | wrapper.ne(User::getAge, 18) → age <> 18 |
gt | 大于 > | wrapper.gt(User::getAge, 18) → age > 18 |
ge | 大于等于 >= | wrapper.ge(User::getAge, 18) |
lt | 小于 < | wrapper.lt(User::getAge, 18) |
le | 小于等于 <= | wrapper.le(User::getAge, 18) |
between | 在区间内 (包含边界) | wrapper.between(User::getAge, 18, 30) → age BETWEEN 18 AND 30 |
notBetween | 不在区间内 | … |
in | 在集合/数组中 | wrapper.in(User::getAge, Arrays.asList(18,20,22)) |
notIn | 不在集合中 | … |
isNull | 字段为 NULL | wrapper.isNull(User::getEmail) |
isNotNull | 字段不为 NULL | … |
🔹 模糊匹配
| 方法 | 说明 | 示例 |
|---|---|---|
like | 全模糊匹配 | wrapper.like(User::getName, "张") → name LIKE '%张%' |
likeLeft | 左模糊 | wrapper.likeLeft(User::getName, "张") → name LIKE '%张' |
likeRight | 右模糊 | wrapper.likeRight(User::getName, "张") → name LIKE '张%' |
🔹 排序
| 方法 | 说明 | 示例 |
|---|---|---|
orderByAsc | 升序 | wrapper.orderByAsc(User::getAge, User::getId) → ORDER BY age ASC, id ASC |
orderByDesc | 降序 | wrapper.orderByDesc(User::getAge) |
orderBy | 指定顺序 | wrapper.orderBy(true, true, User::getAge) 第一个参数是否排序,第二个是否升序 |
🔹 逻辑组合(AND / OR)
默认情况下,多个条件是 AND 连接。如果要使用 OR,可以调用 or() 方法。
AND 示例(默认) :
java
1 | wrapper.eq(User::getGender, "男") |
OR 示例:
java
1 | wrapper.eq(User::getGender, "男") |
复杂 OR 嵌套(推荐用 lambda 表达式) :
java
1 | wrapper.eq(User::getStatus, 1) |
🔹 指定查询字段
java
1 | wrapper.select(User::getId, User::getName, User::getAge); |
🔹 分组和 having
java
1 | wrapper.groupBy(User::getGender) |
🔹 其他常用方法
last:直接拼接到 SQL 末尾(注意有 SQL 注入风险,谨慎使用)wrapper.last("limit 1")exists/notExists:子查询wrapper.exists("select id from user_log where user_id = user.id")
4. QueryWrapper 与 LambdaQueryWrapper 的区别
QueryWrapper:字段名用字符串传入,容易写错,重构实体类时不会自动同步。
java1
2QueryWrapper<User> qw = new QueryWrapper<>();
qw.eq("name", "张三"); // 字符串,容易打错LambdaQueryWrapper:用Lambda 方法引用,编译期就能检查,重构友好。
java1
2LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.eq(User::getName, "张三"); // 推荐使用
强烈建议只用 Lambda 版本。
5. UpdateWrapper 的用法
UpdateWrapper 除了能构造 WHERE 条件,还能指定 SET 子句,常用于更新部分字段。
示例1:更新字段值(不通过实体对象)
java
1 | // 将所有 age > 30 的用户的状态改为 0,并且年龄增加 1 |
示例2:条件更新结合实体
java
1 | User user = new User(); |
6. 动态条件:当某个条件为空时不拼接
Wrapper 的方法都提供了带 condition 参数的重载,让你可以控制是否添加该条件。这是开发中最常用的技巧。
java
1 | // 假如前端传来参数:name(可为空),age(可为空) |
这样当前端不传某个参数时,该条件就不会出现在 SQL 中,非常安全便捷。
7. 实战例子
例1:分页 + 动态条件 + 排序
java
1 | @GetMapping("/list") |
例2:批量删除(根据多个 ID)
java
1 | List<Long> idList = Arrays.asList(1L, 2L, 3L); |
例3:复杂嵌套查询(年龄>20 并且 (性别男 或者 名字包含“张”))
java
1 | LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); |
8. 注意事项与常见坑
- 不要混用 QueryWrapper 和 LambdaQueryWrapper,选定一种风格保持统一。
- 条件构造器只适用于单表操作。如果需要多表联查,要么手写 SQL(放在 Mapper.xml 里),要么使用 MP 的
@Select注解或自定义 Mapper 方法,Wrapper 仍然可以作为参数传入(但需注意字段别名)。 or()方法会与前面的条件形成括号分组,小心逻辑错误。推荐使用and(consumer)或or(consumer)显式包裹。last("limit 1")等方法会直接拼接到 SQL 末尾,可能与分页插件冲突,且有 SQL 注入风险,尽量用wrapper.last("limit 1")仅在测试或特定场景使用。- UpdateWrapper 的 set 方法如果设置字段值为 null,数据库会更新为 null,除非你配置了字段策略(
@TableField(strategy = FieldStrategy.IGNORED))。
总结
Wrapper 是 MyBatis-Plus 的灵魂工具,它让我们用 Java 代码优雅地构造 SQL 条件,极大简化了数据访问层的开发。记住三个核心点:
- 用 LambdaQueryWrapper 和 LambdaUpdateWrapper
- 善用带
condition参数的方法实现动态条件 - 复杂条件用
.and()/.or()包裹 Lambda 表达式




