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

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


使用 mybatisplusLambdaQueryChainWrapper报错

场景:

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对象(也就是接受对象)没有无参构造,而写了个全参构造在里面,错误代码:

image-20231026120949316

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

分析

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

image-20231026120534808

但当我改成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

image-20231026154328372

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

2、DefaultResultSetHandler.createResultObject.createResultObject

image-20231026155147505

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

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

3、进入createByConstructorSignature —>applyConstructorAutomapping方法

image-20231026155545465

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

所以进入else里面的applyColumnOrderBasedConstructorAutomapping方法

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

image-20231026160045016

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

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

image-20231026160204145

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

image-20231026160259982

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

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

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

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

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

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

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

image-20231026161442754

applyAutomaticMappings

在这里插入图片描述

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

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

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

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

总结

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

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

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值