在日常使用 Mybatis Plus 进行数据访问层开发时,条件构造器 (QueryWrapper
和 LambdaQueryWrapper
) 是我们构建动态 SQL 的得力助手。而链式调用(Chain Invocation)则是 Mybatis Plus 为这些构造器提供的一种极其优雅、可读性极高的编码风格,它能显著提升代码的简洁度和开发体验。
一、什么是链式调用
链式调用的核心思想在于:一个对象的方法在调用后,返回对象本身(通常是 this
),使得后续的方法可以紧接着在前一个方法调用的结果上继续调用,形成一条连续的“链条”。
在 Mybatis Plus 的 QueryWrapper
和 LambdaQueryWrapper
中,绝大多数用于添加条件(如 eq
, like
, gt
, orderBy
等)的方法都采用了这种设计。
二、传统方式 vs 链式调用
假设我们需要查询:状态为启用 (status=1
)、姓名包含"张" (name like '%张%'
)、年龄大于 18 岁 (age > 18
),并按照创建时间倒序排列 (create_time DESC
) 的用户。
1. 传统方式 (非链式)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", 1);
queryWrapper.like("name", "张");
queryWrapper.gt("age", 18);
queryWrapper.orderByDesc("create_time");
List<User> userList = userMapper.selectList(queryWrapper);
这种方式清晰但略显冗余,每次调用都需要重复 queryWrapper
对象名。
2. 链式调用方式
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.eq("status", 1)
.like("name", "张")
.gt("age", 18)
.orderByDesc("create_time");
List<User> userList = userMapper.selectList(queryWrapper);
代码行数减少 所有条件按照逻辑顺序依次排列,避免了重复书写对象变量名
三、LambdaQueryWrapper
LambdaQueryWrapper
在 QueryWrapper
的基础上,利用 Java 的 Lambda 表达式和方法引用,彻底消除了条件字段名硬编码字符串,带来了编译期的类型安全检查
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>()
.eq(User::getStatus, 1) // 使用 User::getStatus 方法引用
.like(User::getName, "张") // 编译时检查字段名正确性
.gt(User::getAge, 18) // 避免字段名拼写错误
.orderByDesc(User::getCreateTime); // 重构友好
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
编译器会在你修改实体类属性名称时立即提示错误,避免运行时因字段名拼写错误导致的 SQL 异常, 也不会出现数据库表没有该字段的问题了
四、链式调用的常见方法
方法名 | 作用 | 示例 | 注意事项 |
---|---|---|---|
比较操作 | |||
eq | 等于 (=) | .eq(User::getName, "Alice") | 最常用条件,字段值精确匹配 |
ne | 不等于 (<>) | .ne(User::getStatus, 0) | 排除特定值 |
gt | 大于 (>) | .gt(User::getAge, 18) | 常用于数值/日期比较 |
ge | 大于等于 (>=) | .ge(User::getScore, 60) | 包含边界值 |
lt | 小于 (<) | .lt(User::getCreateTime, LocalDate.now()) | 常用于范围查询 |
le | 小于等于 (<=) | .le(User::getPrice, 100.0) | 包含边界值 |
between | 在范围内 (BETWEEN) | .between(User::getAge, 20, 30) | 包含两端值(闭区间) |
notBetween | 不在范围内 (NOT BETWEEN) | .notBetween(User::getId, 100, 200) | 排除指定区间 |
模糊查询 | |||
like | 模糊匹配 (%value%) | .like(User::getName, "张") → name LIKE '%张%' | 全模糊搜索,性能敏感时慎用 |
notLike | 模糊不匹配 | .notLike(User::getTitle, "test") | 排除包含特定字符串的记录 |
likeLeft | 左模糊 (%value) | .likeLeft(User::getCode, "001") → code LIKE '%001' | 常用于后缀匹配 |
likeRight | 右模糊 (value%) | .likeRight(User::getPhone, "138") → phone LIKE '138%' | 常用于前缀匹配 |
空值判断 | |||
isNull | 为 NULL | .isNull(User::getDescription) | 等价 SQL: column IS NULL |
isNotNull | 不为 NULL | .isNotNull(User::getAvatar) | 等价 SQL: column IS NOT NULL |
包含/不包含 | |||
in | 包含在集合中 | .in(User::getRoleId, Arrays.asList(1, 2, 3)) | 集合参数避免空集合(会报错) |
notIn | 不包含在集合中 | .notIn(User::getDeptId, deptIdList) | 常用于排除特定分类 |
分组 | |||
groupBy | 分组聚合 | .groupBy(User::getDeptId) .groupBy(User::getGender, User::getAge) | 可多字段分组,需配合select() 使用聚合函数 |
排序 | |||
orderByAsc | 升序排序 | .orderByAsc(User::getCreateTime) .orderByAsc(User::getAge, User::getId) | 多字段排序时按参数顺序应用 |
orderByDesc | 降序排序 | .orderByDesc(User::getSalary) | 常用在时间戳、数值字段 |
orderBy | 自定义排序(可混用升降序) | .orderBy(true, true, User::getScore) → ORDER BY score ASC | 参数:(boolean isAsc, boolean isAsc2, SFunction<?,?>... columns) |
逻辑操作 | |||
and | AND 连接后续条件 | .eq(...).and(w -> w.gt(...).or().lt(...)) | 默认所有条件都是AND关系,显式and() 用于嵌套复杂逻辑 |
or | OR 连接后续条件 | .eq(...).or().like(...) → (a = b) OR (c LIKE d) | 会改变整个条件链关系,慎用 |
选择字段 | |||
select | 指定返回字段 | .select(User::getId, User::getName) | 大幅提升性能,避免返回SELECT * |
嵌套条件 | |||
nested | 嵌套条件组 | .nested(w -> w.eq(...).or(...)) | 解决复杂逻辑:(A AND B) OR (C AND D) |