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对象(也就是接受对象)没有无参构造,而写了个全参构造在里面,错误代码:
只需要把@AllArgsConstructor
去掉即可,因为写了@AllArgsConstructor
当前对象就不会有无参构造
1、索引越界异常就很奇怪,一开始下意识以为是one的问题:
但当我改成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
类
debug到这一步一步执行,我们可以发现是在createResultObject()方法发生异常的,进到此方法内部:
2、DefaultResultSetHandler.createResultObject.createResultObject
进入这里,当有无参构造时会进入上一个if语句
metaType.hasDefaultConstructor() //判断是否有无参构造
3、进入createByConstructorSignature —>applyConstructorAutomapping方法
configuration.isArgNameBasedConstructorAutoMapping()//判断是否基于Arg名称的构造函数自动映射,都没有无参构造当然不是
所以进入else里面的
applyColumnOrderBasedConstructorAutomapping
方法
4、进入applyColumnOrderBasedConstructorAutomapping方法(最终报错点)
最终是在第二次允许这个for循环时
String columnName = rsw.getColumnNames().get(i);
报索引超出异常我们可以运行
constructor.getParameterTypes()
方法,得到的是你赋值对象每个属性的属性类型
但我们运行
rsw.getColumnNames()
时,结果是只有你查询的字段:
还记得我们最开始的语句吗:
RegionPO one = new LambdaQueryChainWrapper<>(regionDAO)
.select(RegionPO::getRegionId)
.eq(RegionPO::getName, "广东省")
.one();
从这里我们可以看出
rsw.getColumnNames()
是拿到你需要查询的字段,所以按照这个逻辑,当我们查询所有字段的时候就不会报错!!事实也跟我想的一样,确实查所有就不会报错!!
4、当我们对象有无参构造时他的赋值则是:
由createResultObject创建了一个所有属性都是null的对象,也就是new了一个无参构造一样
然后来到createResultObject
的调用方getRowValue
方法
applyAutomaticMappings
我相信很多人看到这个代码都有种很熟悉的感觉,很像原生的JDBC
createAutomaticMappings()
构建映射条件,也就是数据库表字段和对象属性对应,会根据你所查询的字段构建
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
这行代码是根据字段名找到对应结果集中的value
metaObject.setValue(mapping.property, value)
; 赋值
当对象没有无参构造,而有全参构造时,mybatis是根据对象的所有属性来遍历赋值的
而当对象有无参构造时:则是根据查询字段来赋值