探索Mybatis框架返回值为NULL的原因

1,354 阅读2分钟

背景

使用Mybatis-plus框架查询数据库,sql语句能查到数据,但是框架返回结果为null。

actHiActinstMapper
.selectOne(Wrappers<ActHiActinst>lambdaQuery()
.eq(ActHiActinst::getProcInstId,processInstanceId)
.isNull(ActHiActinst::getEndTime)
.orderByDesc(ActHiActinst::getstartTime)
.last("limit 1"));

上面这条sql语句在数据库中是能够查到返回结果,但mybatis-plus返回结果为null。

实体类如下:

左侧为修改前有问题的实体类,右侧为解决问题后的实体类。左(错误)右(正确)

实体类

排查思路

  1. mapper文件所在包下路径启动debug级别日志输出,以便看到执行的sql语句与参数。
com.xxx.xxx.mapper=debug

2. 使用utools中的SQL效率工具进行日志解析,转为可执行的sql语句 3. 将sql语句到数据库中执行,如果能查询到结果则证明查询语句逻辑没有问题 4. 查询逻辑没有问题,那么返回结果为null就很有可能是版本冲突或者没有用好框架导致的 5. 但是在项目中其它sql语句都没问题,只有我这条sql语句有问题因此觉得冲突可能性不大,优先考虑框架在处理返回值的时候有哪些是我没有注意到的事情。 6. 于是我想到了debug 到源码中看到底因为什么导致返回了个null。 7. 先说结论:mybatis-plus框架默认开启了驼峰转换配置,以及默认将isReturnInstanceForEmptyRow配置置为false【用于控制当查询结果为空白行时是否返回实体类的空实例】。从而导致返回为null。

源码debug

要走源码得先了解调用流程,下面是我从mybatis-plus官网找到的调用链路

image-20240916220845799

mybatis-plus在整个执行链路上处理数据库的返回结果集是在ResultSetHandler中进行的。因此全局搜索了mybatis框架中的ResultSetHandler类,并在默认实现中打上断点进行调试。

最后发现:

  • mybatis-plus框架默认开启了驼峰转换配置,该配置会导致查找字段映射表时,会先删除掉字段中所有下划线再去查询,因此返回的属性字段名称propertyName为null,从而导致后续反射赋值代码未被执行,进而导致对象属性字段为null

下图为使用驼峰映射时替换下划线源码:

自动驼峰配置

下图为mybatis从内部映射表中获取属性名的方法,可以看到去掉下划线后原本对应的key不匹配了,因此找不到目标属性名称了。

内部映射

  • mybatis-plus框架默认将isReturnInstanceForEmptyRow配置设置为false,从而导致返回的结果实际为null

配置

解决方案

最终的解决方案就是去掉实体类属性中的所有下划线