前言
作为一名后端开发者,日常开发中经常使用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,还要理解其底层实现机制,并结合实际测试进行验证。
1091

被折叠的 条评论
为什么被折叠?



