背景
我的某个项目中有这样一段代码, 使用了 LambdaUpdateChainWrapper
和 继承ServiceImpl而来的update
,并且把 LambdaUpdateChainWrapper
作为参数传给了 update
,代码如下(业务部分已删除):
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, StudentPO> implements StudentService {
public void updateStudent(StudentUpdateRequest request) {
StudentPO student = new StudentPO();
BeanUtils.copyProperties(request, student); // 这个是复制属性的
LambdaUpdateChainWrapper<StudentPO> wrapper = lambdaUpdate().eq(StudentPO::getId, request.getId())
.eq(StudentPO::getEnabledFlag, 1);
this.update(student, wrapper);
}
}
逻辑很简单,就是根据用户传入的更新数据,然后拿到数据库去把id相同并且flag为1的数据给更新了。
语法上是没有任何问题的,update确实有这两个参数的重载方法,项目也能正常跑起来😁。
但是啊,如果调用这个方法,程序就会报错:MybatisPlusException:can not use this method for “getSqlSet”
为什么这样写?
可能就有聪明的同学要问了
Q:为什么我不直接用updateById,硬要搞这么麻烦?
A:因为updateById只能判断id是否一样,我还有另一个条件要判断呢。
Q:为什么我不直接使用 lambdaUpdate().set().set().set().eq().eq;
?
A:这倒也能行,但是这未免太麻烦了一点,如果我的类里面属性很多的话,那我一个一个set就显得太难看了,所以我才希望用可以直接传实体类去更新的。
先讲解决方案
解决方案很简单,不使用 LambdaUpdateChainWrapper
,而是用 LambdaUpdateWrapper
,就是说,不能使用中间带个 Chain 的。
如果你使用 UpdateChainWrapper
,那也是不行的,但可以使用 UpdateWrapper
和LambdaUpdateWrapper
。
比如把上面的代码改成这样:
/**
* @author 阿杆
* @version 1.0
* @date 2022/9/8 21:50
*/
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, StudentPO> implements StudentService {
public void updateStudent(StudentUpdateRequest request) {
StudentPO student = new StudentPO();
BeanUtils.copyProperties(request, student); // 这个是复制属性的
// 仅修改了下面这一小段
LambdaUpdateWrapper<StudentPO> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(StudentPO::getId, request.getId())
.eq(StudentPO::getEnabledFlag, 1);
this.update(student, wrapper);
}
}
这段代码就可以正常运行了,这是为什么呢,感觉好像没什么区别啊?
这个报错的解决方案网上一搜也是一大堆的,但是都讲的很浅显,只说了解决方案,而没有说为什么。
那想要探索原因,我们就一起去看看源码吧!
导致报错的原因
MybatisPlusException:can not use this method for "getSqlSet"
翻译一下这个报错,就是说无法调用 getSqlSet 这个方法,那我们去看看这个方法到底写了些什么吧!
查看源码LambdaUpdateChainWrapper
先直接写一个 lambdaUpdate().getSqlSet();
,然后Ctrl + 左键 点进去,我们可以看到:
@Override
public String getSqlSet() {
throw ExceptionUtils.mpe("can not use this method for "%s"", "getSqlSet");
}
噢呦,这直接给我看蒙了哦,直接就抛个异常?难怪报错。
查看源码LambdaUpdateWrapper
那我们再看看 LambdaUpdateWrapper
的 getSqlSet()
,这次得写两行代码了:
LambdaUpdateWrapper<IndicatorClassifyPO> wrapper = new LambdaUpdateWrapper<>();
wrapper.getSqlSet();
跟进去可以看到:
@Override
public String getSqlSet() {
if (CollectionUtils.isEmpty(sqlSet)) {
return null;
}
return String.join(StringPool.COMMA, sqlSet);
}
这个就很正常了,就是把参数拼接一下。
现在我们再去看看 update()
的代码。
查看源码 update()
这里的话不太好找,我们通过打断点来找。
直接给这个类的getSqlSet打个断点,反正我们知道这里一定是会运行并且报错的(注意是LambdaUpdateChainWrapper):
然后调用随便写点啥,去调用update方法:
然后就可以开启调试了,我们可以直接让程序运行到报错的地方,然后再去看栈里的调用方法顺序:
现在已经运行到这句了,我们直接点击左下角栈列表里的方法,然后去看代码逻辑。
这里主要是通过代理来操作的数据,含有大量的反射代理代码,比较复杂,而且看起来也不是很能懂逻辑,我也看不懂 所以就不带大家看这些代码了。
但是我们可以看到的是,确实是由update方法一步一步调用过来的(看不清可以点开放大看):
结论
那根据上面的这些信息,我们可以知道,Mybatis-Plus的开发者不希望用户使用LambdaUpdateChainWrapper作为update的更新条件参数。
这是为何呢?下面讲一下我的理解,不一定对,仅供参考。
在功能上,LambdaUpdateChainWrapper就是一个可以独立执行SQL的类,它内置了执行SQL的方法:
而 LambdaUpdateWrapper,是不具备这个能力的,输入update会报红,它只能作为参数传递到更新的方法当中:
这么看的话,LambdaUpdateChainWrapper 其实并不应该被用做一个传递给update方法的wrapper参数,因为它本身就有update的功能。所以我们使用它来作为描述参数的时候就报错咯🤣!
但是不得不吐槽的是,官方给的报错信息(can not use this method for “getSqlSet”),好像并不是太能理解的样子🤧,就不能给个看着明白点的吗。
原文始发于微信公众号(程序员阿杆):MybatisPlus报错Can not use this method for getSqlSet | 带你从源码分析异常
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207007.html