
点击上方蓝字关注我们
点击下方在在看在走
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常
一、前言
今天调试分页查询代码的时候遇到一个奇葩的问题,该问题后来排查下来跟
lombok
的使用有关。我们在使用
mybatis
或者
mybatis-plus
的时候一般会定义一个类对应表的每个字段,一个成熟的java程序员喜欢使用
lombok
把代码简洁点。这是大前提,我直接说结论吧:
实体类最好都加上
@Data
,
@AllArgsConstructor
,
@NoArgsConstructor
才能避免我现在遇到的问题
二、我的问题
我的表:
我的实体类:
然后一个普通的
select
查询的时候报了下面这个错误:
可以看到异常很奇怪,我的
total_increase_percent
明明是
BigDecimal
,为何要被反序列化为
java.util.Date
呢?\
后面修改为如下代码就正常了:
二、问题排查
-
首先定位到问题出现在哪里
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
由于
是columnName
,该列不是total_increase_percent
,类型不一致,所以报异常。所以要看卡timestamp
是如何获取到的以及为何使用了rs
转换该字段。org.apache.ibatis.type.DateTypeHandler
-
org.apache.ibatis.type.DateTypeHandler的获取
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
点击调用栈的红框出,跳转到
和rs
的获取处。org.apache.ibatis.type.DateTypeHandler
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
在
方法这里既取到了createUsingConstructor
,又获取到了typeHandler
rs
。
我们可以推断出,应该是通过
和columnName
获取parameterType
typeHandler
出错了。
我们看看这里的代码:
看看
,parameterType
,columnName
三个值的类型:typeHandler
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
ok,这几个值都吻合了,根据代码
我们看出1-3行
和parameterType
没对上导致的。这里可以推断出columnName
出现了问题,为啥不怀疑是constructor
出现了问题了呢?因为columnName
是获取到sql返回结果构造的,是rsw
的代码,大概率不会出现问题。mybatis
-
constructor分析
这是
的constructor
:parameterTypes
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
这里出现了4个字段,但是没有
,id
,gmtUpdate
这几个字段,而看看gmtCreate
的rsw
的值:columnNames
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
这里却多了
,id
,gmtUpdate
这几列,问题有进一步定位到了,原来是构造器的字段和gmtCreate
的rsw
不一一对应导致的。可以从上面代码columnNames
for
循环得知,都是根据索引一一获取,这里个数都对不上,肯定有问题了。
在这里基本上已经定位到问题了,就是构造器只接受了3个参数导致的。
-
如何获取到的constructor
把方法栈在往上移一个,就能找到
的具体获取处:constructor
在这里插入图片描述java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常
就是我的实体类,resultType
通过defaultConstructor
获取到,这个方法就不细看了,里面逻辑就是:如果只有一个构造器,那就使用该构造器,获取寻找被标记了findDefaultConstructor
注解的构造器。由于AutomapConstructor
注解只能生成一个构造器@Data
,没有OutputValueDO(java.math.BigDecimal,java.math.BigDecimal,java.math.BigDecimal,java.util.Date)
,id
,gmtUpdate
gmtCreate
。
此时就有一个解决方案了,去掉
,自己写一个完整的构造器,包括继承的所有字段,但是这样是不太好的,从上面@Data
循环代码可知,要数据库的表的列的顺序要和实体类的构造器的参数的顺序一致,不然还是出现问题。那就在往上个方法栈看看为什么使用了该处理逻辑,印象中的for
没这么坑。mybatis
-
更优雅的解决方案
再往上到上一个方法栈,
方法就找到了正主。createResultObject
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 在这里插入图片描述
由于
判断为真就走到了通过构造器反射得到结果的逻辑。接下来分别分析这个!resultType.isInterface() && !metaType.hasDefaultConstructor()
if...else...
-
明显我们没有定义自己的hasTypeHandlerForResultObject
,故忽略typeHandler
-
表示在constructorMappings
中定义了实体类的字段和表字段的映射关系,但是我们没有定义,忽略mapper.xml
- 我的实体类
不是接口,并且没有无参构造器,我的代码正好适合这个判断,所以我要想办法是这个判断为假。OutputValueDO
-
最后一个就是默认的,我要改成走到这个方法来。
解决方案方向:加个默认构造器。加
即可解决,该注解就能生成无参构造器,由于我加了@NoArgsConstructor
,所以我还必须加@Builder
,稳了,解决了。@AllArgsConstructor
三、总结
我一般使用
lombok
最好加上这几个注解:
但是这个代码一开始不是我写的,而且该实体类没有继承的话也不会出问题,巧了。以前也看过
mybatis
的代码,但是没有翻过映射这块。今天再一次体味到了
mybatis
的代码:真正牛的代码不需要注释。很容易就找到了问题。
- 我的微信: 在这里插入图片描述
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常 - 我的微信公众号: 云原生玩码部落
java mybatis sql 查询耗时_Mybatis反序列化sql查询结果异常