深入解析MyBatis-Plus中的lambdaUpdate与lambdaQuery
一、引言
在现代Java持久层开发中,MyBatis-Plus作为MyBatis的增强工具,提供了许多便捷的功能,其中lambdaUpdate
和lambdaQuery
是基于Lambda表达式的两种强大操作方式。它们不仅提高了代码的可读性,还减少了SQL编写错误,是MyBatis-Plus的核心特性之一。
二、lambdaQuery详解
2.1 基本概念
lambdaQuery
是MyBatis-Plus提供的基于Lambda表达式的查询构造器,它允许开发者以类型安全的方式构建查询条件。
2.2 基本使用
// 查询name为"张三"的用户列表
List<User> users = userService.lambdaQuery().eq(User::getName, "张三").list();// 查询age大于18且name包含"张"的用户
List<User> userList = userService.lambdaQuery().gt(User::getAge, 18).like(User::getName, "张").list();
2.3 链式操作
lambdaQuery
支持丰富的链式操作方法:
方法名 | 说明 | 示例 |
---|---|---|
eq | 等于 (=) | .eq(User::getName, “张三”) |
ne | 不等于 (<>) | .ne(User::getAge, 18) |
gt | 大于 (>) | .gt(User::getAge, 18) |
ge | 大于等于 (>=) | .ge(User::getScore, 60) |
lt | 小于 (<) | .lt(User::getAge, 30) |
le | 小于等于 (<=) | .le(User::getScore, 100) |
between | BETWEEN 值1 AND 值2 | .between(User::getAge, 18, 30) |
notBetween | NOT BETWEEN 值1 AND 值2 | .notBetween(User::getAge, 18, 30) |
like | LIKE ‘%值%’ | .like(User::getName, “张”) |
notLike | NOT LIKE ‘%值%’ | .notLike(User::getName, “张”) |
likeLeft | LIKE ‘%值’ | .likeLeft(User::getName, “三”) |
likeRight | LIKE ‘值%’ | .likeRight(User::getName, “张”) |
2.4 复杂查询示例
// 多条件组合查询
List<User> complexQuery = userService.lambdaQuery().eq(User::getStatus, 1).and(q -> q.gt(User::getAge, 18).or().lt(User::getScore, 60)).orderByDesc(User::getCreateTime).last("LIMIT 10").list();
三、lambdaUpdate详解
3.1 基本概念
lambdaUpdate
是MyBatis-Plus提供的基于Lambda表达式的更新构造器,它允许开发者以类型安全的方式构建更新操作。
3.2 基本使用
// 将name为"张三"的用户的age更新为25
boolean update = userService.lambdaUpdate().eq(User::getName, "张三").set(User::getAge, 25).update();// 将status为0且age大于30的用户status更新为1
boolean batchUpdate = userService.lambdaUpdate().eq(User::getStatus, 0).gt(User::getAge, 30).set(User::getStatus, 1).update();
3.3 链式操作
lambdaUpdate
除了支持lambdaQuery
的条件方法外,还支持以下更新特定方法:
方法名 | 说明 | 示例 |
---|---|---|
set | 设置更新字段 | .set(User::getAge, 25) |
setSql | 设置更新SQL片段 | .setSql(“age = age + 1”) |
remove | 删除记录(需谨慎使用) | .eq(User::getId, 1).remove() |
3.4 复杂更新示例
// 条件更新多个字段
boolean complexUpdate = userService.lambdaUpdate().eq(User::getDepartment, "技术部").set(User::getLevel, "高级").set(User::getSalary, salaryService.calculateNewSalary()).update();// 使用setSql进行复杂更新
boolean sqlUpdate = userService.lambdaUpdate().eq(User::getStatus, 0).setSql("balance = balance + bonus").update();
四、lambdaQuery与lambdaUpdate的高级特性
4.1 实体条件与Lambda条件的混合使用
User queryEntity = new User();
queryEntity.setStatus(1);List<User> users = userService.lambdaQuery().eq(User::getAge, 25).and(q -> q.applyEntity(queryEntity)) // 混合实体条件.list();
4.2 嵌套条件查询
// 复杂嵌套条件
List<User> nestedQuery = userService.lambdaQuery().eq(User::getType, 1).and(q -> q.gt(User::getAge, 18).or().lt(User::getScore, 60)).or(q -> q.eq(User::getStatus, 2).and(q2 -> q2.like(User::getName, "admin"))).list();
4.3 联表查询模拟
虽然MyBatis-Plus本身不直接支持联表查询,但可以通过以下方式模拟:
// 先查询主表ID,再查询关联表
List<Long> userIds = userService.lambdaQuery().eq(User::getDepartment, "销售部").select(User::getId).list().stream().map(User::getId).collect(Collectors.toList());List<Order> orders = orderService.lambdaQuery().in(Order::getUserId, userIds).list();
五、性能优化与最佳实践
5.1 索引友好性
确保Lambda表达式中的字段对应数据库索引:
// 好的实践 - 使用索引字段
userService.lambdaQuery().eq(User::getId, 123) // 通常id是主键索引.eq(User::getEmail, "test@example.com") // 通常email有唯一索引.list();// 不好的实践 - 使用非索引字段作为主要条件
userService.lambdaQuery().like(User::getDescription, "重要客户") // 全文搜索字段.list();
5.2 批量操作优化
// 批量更新优化
boolean batchUpdate = userService.lambdaUpdate().in(User::getId, idList) // 使用IN而不是多次单条更新.set(User::getStatus, 1).update();// 分批次处理大数据量
List<Long> allIds = getAllUserIds();
int batchSize = 1000;
for (int i = 0; i < allIds.size(); i += batchSize) {List<Long> batchIds = allIds.subList(i, Math.min(i + batchSize, allIds.size()));userService.lambdaUpdate().in(User::getId, batchIds).set(User::getFlag, true).update();
}
5.3 避免N+1查询问题
// 不好的实践 - N+1查询
List<User> users = userService.list();
users.forEach(user -> {List<Order> orders = orderService.lambdaQuery().eq(Order::getUserId, user.getId()).list();// 处理订单
});// 好的实践 - 批量查询
List<User> users = userService.list();
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
Map<Long, List<Order>> ordersMap = orderService.lambdaQuery().in(Order::getUserId, userIds).list().stream().collect(Collectors.groupingBy(Order::getUserId));
六、常见问题与解决方案
6.1 动态条件构建
public List<User> searchUsers(String name, Integer minAge, Integer maxAge, Integer status) {return userService.lambdaQuery().eq(StringUtils.isNotBlank(name), User::getName, name).gt(minAge != null, User::getAge, minAge).lt(maxAge != null, User::getAge, maxAge).eq(status != null, User::getStatus, status).list();
}
6.2 字段类型不匹配问题
// 枚举类型处理
userService.lambdaQuery().eq(User::getUserType, UserType.ADMIN.getCode()) // 明确使用code值.list();// 日期类型处理
LocalDate today = LocalDate.now();
userService.lambdaQuery().ge(User::getCreateTime, today.atStartOfDay()) // 转换为正确的日期时间类型.list();
6.3 与MyBatis XML映射的协作
// 在Service中
public List<User> complexQueryWithXml(String name, Integer status) {return userService.lambdaQuery().eq(StringUtils.isNotBlank(name), User::getName, name).eq(status != null, User::getStatus, status).apply("id IN (SELECT user_id FROM user_relation WHERE relation_type = {0})", 1).list();
}// 在XML中可以有更复杂的SQL
<select id="selectWithComplexJoin" resultType="com.example.User">SELECT u.* FROM user uJOIN department d ON u.dept_id = d.idWHERE d.name = #{deptName}<if test="status != null">AND u.status = #{status}</if>
</select>
七、总结
lambdaQuery
和lambdaUpdate
是MyBatis-Plus提供的强大工具,它们通过Lambda表达式实现了类型安全的SQL构建,具有以下优势:
- 类型安全:编译时检查字段名,避免拼写错误
- 代码可读性:链式调用使代码更加清晰
- 维护方便:实体类字段变更时,相关查询会立即在编译时报错
- 灵活的条件组合:支持复杂的条件组合和嵌套
在实际开发中,应根据具体场景选择合适的查询方式:
- 简单查询:使用
lambdaQuery
/lambdaUpdate
- 复杂查询:结合XML映射文件
- 动态SQL:使用
apply
方法或XML动态标签 - 批量操作:注意优化批量处理的性能
通过合理使用这些特性,可以显著提高开发效率和代码质量,同时保持数据库操作的高性能。