• Mybatis-Plus报错:can not use this method for “getSqlSet“ | 带你从源码层面解析异常


    Mybatis-Plus报错:can not use this method for “getSqlSet” | 带你从源码层面解析异常

    背景

    我的某个项目中有这样一段代码, 使用了 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);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    逻辑很简答,就是根据用户传入的更新数据,然后拿到数据库去把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 ,那也是不行的,但可以使用 UpdateWrapperLambdaUpdateWrapper

    比如把上面的代码改成这样:

    /**
     * @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);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这段代码就可以正常运行了,这是为什么呢,感觉好像没什么区别啊?

    这个报错的解决方案网上一搜也是一大堆的,但是都讲的很浅显,只说了解决方案,而没有说为什么。

    那想要探索原因,我们就一起去看看源码吧!


    导致报错的原因

    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");
        }
    
    • 1
    • 2
    • 3
    • 4

    噢呦,这直接给我看蒙了哦,直接就抛个异常?难怪报错。

    查看源码LambdaUpdateWrapper

    那我们再看看 LambdaUpdateWrappergetSqlSet(),这次得写两行代码了:

    LambdaUpdateWrapper<IndicatorClassifyPO> wrapper = new LambdaUpdateWrapper<>();
    wrapper.getSqlSet();
    
    • 1
    • 2

    跟进去可以看到:

        @Override
        public String getSqlSet() {
            if (CollectionUtils.isEmpty(sqlSet)) {
                return null;
            }
            return String.join(StringPool.COMMA, sqlSet);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个就很正常了,就是把参数拼接一下。

    现在我们再去看看 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”),好像并不是太能理解的样子🤧,就不能给个看着明白点的吗。

  • 相关阅读:
    《计算机操作系统-第三章》之中断与系统调用
    后端技术栈
    【AI学习笔记】jupyter notebook 默认路径修改(超简介,超详细)
    AntPathMatcher【Spring中提供的路径匹配器】
    老司机带你用python从另外一个角度看市场需求~
    Eclipse配置python环境全步骤
    四、hdfs文件系统基础操作-保姆级教程
    编写X86环境下的ShellCode
    什么是负载均衡集群?
    EL表达式的内置对象及其作用域
  • 原文地址:https://blog.csdn.net/little_stick_i/article/details/126776112