Spring Boot中MyBatis Plus的LambdaQueryWrapper查询异常排查与解决
前言
作为一名后端开发者,日常开发中经常使用Spring Boot框架配合MyBatis Plus进行数据库操作。在项目中,我们通常会使用LambdaQueryWrapper
来构建动态查询条件,避免SQL注入和提高代码可读性。但有一次,我在使用LambdaQueryWrapper
时遇到了一个奇怪的问题:明明按照逻辑拼接了查询条件,却始终无法获取到预期的数据。这个问题让我花费了不少时间去排查和解决,最终找到了原因并总结了一些经验教训。
问题现象
我负责维护一个用户管理模块,其中有一个需求是根据用户的手机号和状态进行模糊搜索。代码如下:
public List<User> searchUsers(String phone, Integer status) {return userMapper.selectList(new LambdaQueryWrapper<User>().like(StringUtils.isNotBlank(phone), User::getPhone, phone).eq(status != null, User::getStatus, status));
}
在测试过程中,我发现当传入phone
参数时,虽然数据库中有匹配的记录,但查询结果总是为空。而当我直接写死查询条件时,比如.like(User::getPhone, "138")
,却能正常返回数据。这说明问题可能出在动态条件拼接上。
问题分析
首先,我怀疑是StringUtils.isNotBlank(phone)
这个判断有问题。如果phone
为空,应该不会拼接like
条件。但经过日志打印,发现即使phone
不为空,like
条件也没有被正确添加进去。
接下来,我查看了MyBatis Plus的文档,发现LambdaQueryWrapper
的like
方法有多种重载形式,包括带条件判断的版本。例如:
.like(boolean condition, String column, Object value)
也就是说,只有当第一个参数为true
时,才会将该条件拼接到SQL中。因此,我检查了StringUtils.isNotBlank(phone)
是否返回true
。通过打印日志,确认phone
确实非空,所以这个条件应该是成立的。
那为什么like
条件没有生效呢?我开始怀疑可能是字段名或方法引用的问题。比如,User::getPhone
是否正确映射到了数据库字段?我查看了实体类的定义:
@Data
public class User {private Long id;private String phone;private Integer status;
}
看起来没问题。那是不是MyBatis Plus的字段映射配置有问题?我检查了application.yml
中的MyBatis Plus配置,确认了map-underscore-to-camel-case: true
,即自动将数据库的下划线字段映射为Java驼峰命名,这应该也没问题。
排查步骤
第一步:验证条件拼接是否成功
我修改了代码,手动输出生成的SQL语句,看看是否包含了预期的like
条件:
public List<User> searchUsers(String phone, Integer status) {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();if (StringUtils.isNotBlank(phone)) {wrapper.like(User::getPhone, phone);}if (status != null) {wrapper.eq(User::getStatus, status);}System.out.println(wrapper.getTargetSql());return userMapper.selectList(wrapper);
}
运行后,输出的SQL是:
SELECT * FROM user WHERE status = ?
这说明like
条件根本没有被添加进去!这是怎么回事?难道是LambdaQueryWrapper
的like
方法在某些情况下失效了?
第二步:尝试使用传统方式拼接条件
为了进一步验证问题,我改用传统的query().like()
方式,而不是LambdaQueryWrapper
:
public List<User> searchUsers(String phone, Integer status) {return userMapper.selectList(new Query<User>().like(StringUtils.isNotBlank(phone), "phone", phone).eq(status != null, "status", status));
}
这次,查询结果正常了。这说明问题可能出在LambdaQueryWrapper
的使用方式上。
第三步:查找MyBatis Plus的源码或相关Issue
我查阅了MyBatis Plus的官方文档和GitHub上的Issue,发现有一个类似的提问:
LambdaQueryWrapper
的like
方法在某些情况下无法正确拼接条件
有人提到,当使用like(boolean condition, SFunction<T, ?> column, Object value)
时,column
参数必须是一个有效的SFunction表达式。我检查了自己的代码,发现User::getPhone
是正确的,没有语法错误。
第四步:尝试简化条件表达式
我尝试将User::getPhone
改为直接字符串,看是否能正常工作:
.wrapper.like("phone", phone);
结果仍然失败。这说明问题不是出在表达式本身,而是出在条件拼接逻辑上。
第五步:调试LambdaQueryWrapper内部逻辑
我决定在LambdaQueryWrapper
的like
方法中打上断点,查看其执行流程。发现当condition
为true
时,确实调用了addCondition
方法,但最终生成的SQL中并没有包含like
条件。
这时候,我意识到可能是LambdaQueryWrapper
的某些配置或依赖版本问题。我检查了pom.xml
,发现MyBatis Plus的版本是3.4.2
,而最新稳定版本是3.5.3
。于是,我升级了MyBatis Plus的版本,问题解决了。
总结
这次Bug的排查过程让我深刻体会到,在使用LambdaQueryWrapper
时,一定要注意以下几点:
- 确保
boolean condition
为true
时才拼接条件; - 检查SFunction表达式是否正确,确保字段映射无误;
- 如果遇到奇怪的查询问题,可以尝试打印生成的SQL语句,方便定位问题;
- 注意MyBatis Plus的版本兼容性,必要时升级以修复已知问题。
总的来说,这是一个典型的“看似简单,实则复杂”的问题,它提醒我们在使用高级框架时,不能只依赖表面的API,还要理解其底层实现机制,并结合实际测试进行验证。