一、引言
在现代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 u
JOIN department d ON u.dept_id = d.id
WHERE 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动态标签 - 批量操作:注意优化批量处理的性能
通过合理使用这些特性,可以显著提高开发效率和代码质量,同时保持数据库操作的高性能。