【MyBatis-Plus 条件构造器】全面解析 Wrapper
在 MyBatis-Plus 中, 条件构造器 是一个强大的工具,能够帮助我们灵活地构建 SQL 查询条件,而无需手写繁琐的 SQL 语句。本文将从基础到高级,带你全面了解条件构造器的使用方法及其链式构造能力。
一、什么是条件构造器?
条件构造器是 MyBatis-Plus 提供的 动态 SQL 拼接工具,主要用于生成 SQL 查询中的 WHERE
条件部分,支持动态拼接、链式调用以及条件逻辑控制。
常见的条件构造器
QueryWrapper
:用于通用的条件查询。UpdateWrapper
:用于更新操作的条件构造。LambdaQueryWrapper
和LambdaUpdateWrapper
:使用 Lambda 表达式构造条件,避免直接写字段名称导致的拼写错误。
二、基础用法
常用方法一览
方法 | 描述 | 示例 | 补充说明 |
---|---|---|---|
eq | 等于 | .eq("status", 1) | 常用于精确匹配某个字段的值,例如查询某种状态的数据。 |
ne | 不等于 | .ne("status", 0) | 用于排除特定值的记录,例如排除状态为 0 的数据。 |
gt / ge | 大于 / 大于等于 | .gt("age", 18) | 可用于筛选范围,例如筛选 18 岁以上用户。 |
lt / le | 小于 / 小于等于 | .lt("age", 60) | 结合 gt ,实现区间查询,例如 18-60 岁之间的用户。 |
like / notLike | 模糊查询 / 非模糊查询 | .like("name", "Tom") | like 通常用于字符串匹配,例如姓名包含某字;notLike 用于排除模糊匹配的记录。 |
likeLeft / likeRight | 左 / 右模糊查询 | .likeLeft("name", "Tom") / .likeRight("name", "Tom") | 左模糊适用于以某个关键字结尾的情况,右模糊适用于以关键字开头的情况。 |
isNull / isNotNull | 字段是否为空 / 不为空 | .isNotNull("email") | 判断字段值是否存在,适用于有些字段可能为空值的情况,例如邮箱地址。 |
in / notIn | 在列表中 / 不在列表中 | .in("id", Arrays.asList(1, 2, 3)) / .notIn("id", Arrays.asList(4, 5)) | in 通常用于批量匹配多个条件,例如查询多个 ID 的记录。 |
between / notBetween | 在区间内 / 不在区间内 | .between("age", 18, 60) | 比 gt 和 lt 更简洁,适用于范围筛选。 |
orderBy / orderByDesc | 排序 | .orderByDesc("create_time") / .orderByAsc("age") | 支持多字段排序,例如 .orderByAsc("age").orderByDesc("create_time") 。 |
groupBy | 分组 | .groupBy("status") | 通常与聚合函数结合使用,例如统计每种状态的记录数。 |
having | 分组后的筛选条件 | .groupBy("status").having("count(*) > 5") | 适用于对分组结果的进一步筛选,例如过滤分组中记录数少于 5 的组。 |
last | 手动拼接 SQL 片段 | .last("LIMIT 1") | 手动拼接自定义的 SQL 片段,但需谨慎使用,确保不会引起 SQL 注入风险。 |
exists / notExists | 子查询条件 | .exists("SELECT 1 FROM user WHERE user.id = order.user_id") | 适用于判断某记录是否存在,结合子查询使用。 |
apply | 自定义 SQL 条件 | .apply("DATE(create_time) = {0}", "2025-01-01") |
1. QueryWrapper 的基本操作
示例:构造简单查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", 1).like("name", "Tom").orderByDesc("create_time");
List<User> users = userMapper.selectList(queryWrapper);
生成的 SQL:
SELECT * FROM user WHERE status = 1 AND name LIKE '%Tom%' ORDER BY create_time DESC;、
2. LambdaQueryWrapper 的基本操作
使用 Lambda 表达式构建查询条件,避免字段名硬编码导致的错误,可以传入一个函数式接口来动态获取查询的字段等。
示例:构造条件
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getStatus, 1).like(User::getName, "Tom");
List<User> users = userMapper.selectList(lambdaWrapper);
优点:
- 避免直接使用字符串字段名。
- 修改实体类字段名时,条件构造器能自动适配。
3. UpdateWrapper 的基本操作
UpdateWrapper
用于构造更新条件,可以同时设置更新内容和条件。
示例:构造更新条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 1).set("status", 2);
userMapper.update(null, updateWrapper);
生成的 SQL:
UPDATE user SET status = 2 WHERE id = 1;
三、高级用法
1. 链式调用的高级操作
条件构造器支持链式调用,能大幅提升代码的可读性。
示例:根据条件动态添加查询
String name = "Tom";
Integer age = null;
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq(name != null, "name", name)
.ge(age != null, "age", age);
List<User> users = userMapper.selectList(wrapper);
只会生成:
SELECT * FROM user WHERE name = 'Tom';
注意:eq
等方法的第一个参数为布尔值,控制条件是否拼接。
2. 嵌套查询条件
使用 and
或 or
结合嵌套条件,构造复杂查询。
示例:嵌套条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.and(w -> w.eq("status", 1).or().eq("status", 2))
.like("name", "Tom");
List<User> users = userMapper.selectList(wrapper);
生成的 SQL:
SELECT * FROM user WHERE (status = 1 OR status = 2) AND name LIKE '%Tom%';
3. LambdaQueryWrapper 的自定义排序
LambdaQueryWrapper
支持条件排序,可以动态调整排序字段。
示例:动态排序
boolean orderByAge = true;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.orderBy(orderByAge, true, User::getAge);
List<User> users = userMapper.selectList(wrapper);
说明:orderBy
的第一个参数控制是否应用排序(指定一个条件),第二个参数控制升序还是降序,第三个参数指定根据哪一列排序。
4. 使用条件构造器分页查询
与分页插件结合,条件构造器可以轻松实现分页查询。
示例:分页查询
IPage<User> page = new Page<>(1, 10); // 第1页,每页10条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 18).le("age", 60);
IPage<User> result = userMapper.selectPage(page, wrapper);
生成的 SQL:
SELECT * FROM user WHERE age >= 18 AND age <= 60 LIMIT 0, 10;
四、链式条件构造器 ChainWrapper
MyBatis-Plus 提供了链式条件构造器,使查询与更新操作更加简洁流畅,主要包括 QueryChainWrapper
/ LambdaQueryChainWrapper
和 UpdateChainWrapper
/ LambdaUpdateChainWrapper
。以下我们结合查询与更新操作详细讲解其使用方式。
1. 查询的链式构造器
MyBatis-Plus 提供 query()
和 lambdaQuery()
方法用于查询操作的链式调用,最终通过调用特定方法(如 .list()
或 .one()
)返回结果。
返回值方法
以下是 QueryChainWrapper
/ LambdaQueryChainWrapper
中常见的调用链终点方法及其返回值:
.list()
:返回符合条件的所有记录,类型为List<T>
。.one()
:返回符合条件的单条记录,类型为T
。如果有多条记录,则会抛出异常。.count()
:返回符合条件的记录数,类型为long
。.exists()
:判断是否有符合条件的记录,返回boolean
。.map()
:返回查询结果的Map
表示,通常适用于单表查询。
// 获取符合条件的所有记录
List<User> users = userService.lambdaQuery()
.eq(User::getStatus, 1)
.orderByDesc(User::getCreateTime)
.list();
// 获取符合条件的单条记录
User user = userService.lambdaQuery()
.eq(User::getId, 1)
.one();
// 获取符合条件的记录总数
long count = userService.lambdaQuery()
.eq(User::getStatus, 1)
.count();
// 判断是否存在符合条件的记录
boolean exists = userService.lambdaQuery()
.eq(User::getName, "Tom")
.exists();
【注意】
- 如果查询条件返回多条记录而使用
.one()
,会抛出异常,因此建议对主键或唯一字段使用该方法。.exists()
的效率较高,可用于快速判断。
基本查询
QueryChainWrapper
(基于字段名):
List<User> users = userService.query()
.eq("status", 1)
.like("name", "Tom")
.orderByDesc("create_time")
.list();
LambdaQueryChainWrapper
(基于 Lambda 表达式):
List<User> users = userService.lambdaQuery()
.eq(User::getStatus, 1)
.like(User::getName, "Tom")
.orderByDesc(User::getCreateTime)
.list();
动态条件查询
通过链式构造器可以动态拼接查询条件,避免不必要的条件参与 SQL 构造:
String name = null;
Integer age = 25;
List<User> users = userService.lambdaQuery()
.like(name != null, User::getName, name)
.eq(age != null, User::getAge, age)
.list();
当 name
为 null
时,生成的 SQL 会自动忽略对应条件:
SELECT * FROM user WHERE age = 25;
分页查询
结合分页对象可以快速实现分页功能:
IPage<User> page = userService.lambdaQuery()
.eq(User::getStatus, 1)
.orderByDesc(User::getCreateTime)
.page(new Page<>(1, 10));
2. 更新的链式构造器
对于更新操作,MyBatis-Plus 提供了 update()
和 lambdaUpdate()
方法,支持基于链式条件构造更新数据。
基本更新
- 使用
LambdaUpdateChainWrapper
更新指定条件的数据:
boolean updated = userService.lambdaUpdate()
.eq(User::getStatus, 1)
.set(User::getName, "Tom Updated")
.set(User::getAge, 30)
.update();
生成的 SQL:
UPDATE user SET name = 'Tom Updated', age = 30 WHERE status = 1;
动态字段更新
通过布尔值控制是否更新某些字段,实现动态更新逻辑:
String name = null;
Integer age = 30;
boolean updated = userService.lambdaUpdate()
.set(name != null, User::getName, name)
.set(age != null, User::getAge, age)
.eq(User::getStatus, 1)
.update();
如果 name
为 null
,生成的 SQL 将忽略对 name
的更新:
UPDATE user SET age = 30 WHERE status = 1;
批量更新
通过 in
条件轻松实现批量更新:
boolean updated = userService.lambdaUpdate()
.in(User::getId, Arrays.asList(1, 2, 3))
.set(User::getStatus, 2)
.update();
生成的 SQL:
UPDATE user SET status = 2 WHERE id IN (1, 2, 3);
五、最佳实践
-
优先使用
LambdaQueryWrapper
,能避免字段名硬编码的潜在错误,更适合复杂的查询场景。 -
善用动态拼接, 利用条件构造器的布尔控制参数,可以根据条件动态生成 SQL,减少不必要的拼接逻辑。
-
分页查询与排序结合,在大数据量场景中,结合分页插件和条件构造器能显著提升查询效率。
-
封装公共查询逻辑,对于复杂的查询条件,可以将条件构造封装为方法,方便重复使用。例如:
public QueryWrapper<User> buildActiveUserQuery() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("status", 1).isNotNull("email"); return wrapper; }
六、总结
MyBatis-Plus 的条件构造器为我们提供了构建 SQL 条件的强大工具。从基础的查询条件到复杂的嵌套逻辑,再到链式调用和动态条件,条件构造器可以极大提升开发效率。同时,灵活使用分页、排序等高级功能,可以让你的查询逻辑更清晰、更高效。在日常开发中,掌握这些技巧,将让你在 MyBatis-Plus 的使用中更加得心应手!