解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep
作者:mmseoamin日期:2023-12-25

解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:

文章目录

  • 解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    • 场景:
    • 异常明细:
    • 解决办法
    • 分析
      • 一、查看日志
      • 二、查看源码
      • 总结

        使用 mybatisplus的 LambdaQueryChainWrapper报错

        场景:

        RegionPO one = new LambdaQueryChainWrapper<>(regionDAO)
                        .select(RegionPO::getRegionId)
                        .eq(RegionPO::getName, "广东省")
                        .one();
        

        异常明细:

        org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
        ### Error querying database.  Cause: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
        ### The error may exist in com/yhd/open/content/management/dao/RegionDAO.java (best guess)
        ### The error may involve com.yhd.open.content.management.dao.RegionDAO.selectList
        ### The error occurred while handling results
        ### SQL: SELECT   region_id   FROM region     WHERE (`name` = ?)
        ### Cause: java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
        	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96)
        	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
        	at com.sun.proxy.$Proxy210.selectList(Unknown Source)
        	at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForMany(MybatisMapperMethod.java:166)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:77)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
        	at com.sun.proxy.$Proxy211.selectList(Unknown Source)
        	at com.baomidou.mybatisplus.core.mapper.BaseMapper.selectOne(BaseMapper.java:173)
        	at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:627)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$DefaultMethodInvoker.invoke(MybatisMapperProxy.java:162)
        	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
        	at com.sun.proxy.$Proxy211.selectOne(Unknown Source)
        	at com.baomidou.mybatisplus.extension.conditions.query.ChainQuery.one(ChainQuery.java:48)
            ........................
        

        解决办法

        是因为RegionPO对象(也就是接受对象)没有无参构造,而写了个全参构造在里面,错误代码:

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026120949316,第1张

        只需要把@AllArgsConstructor去掉即可,因为写了@AllArgsConstructor当前对象就不会有无参构造

        分析

        1、索引越界异常就很奇怪,一开始下意识以为是one的问题:

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026120534808,第2张

        但当我改成list时也还是一样报错

        一、查看日志

        mybatis的sql日志

        JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6329e680] will not be managed by Spring
        ==>  Preparing: SELECT region_id FROM region WHERE (`name` = ?)
        ==> Parameters: 广东省(String)
        <==    Columns: region_id
        <==        Row: 44
        Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17826d60]
        

        从上面日志得知结果已经查询出来,证明sql是没有问题的,那就一定是给对象赋值的时候有问题了

        二、查看源码

        在 MyBatis-Plus 源码中, LambdaQueryChainWrapper 类是 MyBatis-Plus 提供的链式查询的入口类,它并不直接负责将查询结果设值到实体对象中。实际上,查询结果的设值是由 MyBatis 的 ResultSetHandler 完成的。

        具体来说,当执行 LambdaQueryChainWrapper 的查询方法(如 list() 、 getOne() 等)时,它会将查询条件封装成一个 MappedStatement 对象,并通过 MyBatis 的 SqlSession 进行查询操作。查询结果会由 MyBatis 的 ResultSetHandler 进行处理,将查询结果映射到实体对象中。

        在 MyBatis 的 ResultSetHandler 实现类中,常用的实现是 DefaultResultSetHandler 。它会调用 ObjectFactory 创建实体对象,并通过 MetaObject 对象将查询结果设值到实体对象的属性中。

        总结起来,在 MyBatis-Plus 中, LambdaQueryChainWrapper 类负责构建查询条件链式调用,而查询结果的设值是由 MyBatis 的 ResultSetHandler 实现类完成的。具体的设值过程涉及到 MyBatis 的对象工厂、元对象和反射等机制。

        1、由上面可知赋值在DefaultResultSetHandler类

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026154328372,第3张

        debug到这一步一步执行,我们可以发现是在createResultObject()方法发生异常的,进到此方法内部:

        2、DefaultResultSetHandler.createResultObject.createResultObject

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026155147505,第4张

        进入这里,当有无参构造时会进入上一个if语句

        metaType.hasDefaultConstructor() //判断是否有无参构造
        

        3、进入createByConstructorSignature —>applyConstructorAutomapping方法

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026155545465,第5张

        configuration.isArgNameBasedConstructorAutoMapping()//判断是否基于Arg名称的构造函数自动映射,都没有无参构造当然不是
        

        所以进入else里面的applyColumnOrderBasedConstructorAutomapping方法

        4、进入applyColumnOrderBasedConstructorAutomapping方法(最终报错点)

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026160045016,第6张

        最终是在第二次允许这个for循环时String columnName = rsw.getColumnNames().get(i);报索引超出异常

        我们可以运行constructor.getParameterTypes()方法,得到的是你赋值对象每个属性的属性类型

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026160204145,第7张

        但我们运行rsw.getColumnNames()时,结果是只有你查询的字段:

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026160259982,第8张

        还记得我们最开始的语句吗:

        RegionPO one = new LambdaQueryChainWrapper<>(regionDAO)
                        .select(RegionPO::getRegionId)
                        .eq(RegionPO::getName, "广东省")
                        .one();
        

        从这里我们可以看出rsw.getColumnNames()是拿到你需要查询的字段,所以按照这个逻辑,当我们查询所有字段的时候就不会报错!!

        事实也跟我想的一样,确实查所有就不会报错!!

        4、当我们对象有无参构造时他的赋值则是:

        由createResultObject创建了一个所有属性都是null的对象,也就是new了一个无参构造一样

        然后来到createResultObject的调用方getRowValue方法

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,image-20231026161442754,第9张

        applyAutomaticMappings
        

        解决mybatisplus报org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.excep,在这里插入图片描述,第10张

        我相信很多人看到这个代码都有种很熟悉的感觉,很像原生的JDBC

        createAutomaticMappings() 构建映射条件,也就是数据库表字段和对象属性对应,会根据你所查询的字段构建

        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); 这行代码是根据字段名找到对应结果集中的value

        metaObject.setValue(mapping.property, value); 赋值

        总结

        当对象没有无参构造,而有全参构造时,mybatis是根据对象的所有属性来遍历赋值的

        而当对象有无参构造时:则是根据查询字段来赋值

上一篇:Nginx超时配置

下一篇:Node.js安装教程