基于mybatis-plus timestamp返回为null问题的排除
作者:水中加点糖 时间:2023-08-07 00:43:50
问题是这样的
在开发时,为了节约时间,我选择了mybatis框架来开发,然后又在网上找了一个许多人都推荐的mybatis-plus来作为持久层框架。
于是乎我按照官方的DEMO下了一个springBoot的mybatis-plus版本的DEMO
这个DEMO是基于H2数据库的,跑了下没有问题。DEMO是能正常运行的。
然后我将这个工程的代码快速拷贝的新的一个工程里,并把数据库由H2换为了MYSQL。但项目跑起来时,出现了如下问题:
数据库里的数据如下图
表结构如下图
查询出来的结果中对于timestamp类型的字段为空
为了解决这个问题,我决定通过debug断点观察下是否查询是把数据数据查出来了
由于用的mybatis-plus框架来开发,而mybaits-plus框架是基于mybatis框架的,为了看是否把数据查询出来,直接在ResultSetHandler上把一个dubug就好。在默认情况下,mybatis框架使用的ResultSetHandler为DefaultResultSetHandler,当查询mybatis查询完毕后,会通过ResultSetHandler的handleResultSets(Statement stmt)方法对查询的数据结果集进行封装
所以将断点打在handlerResultSets方法上最为合适。
再通过statement -> wrapper ->results -> rowData ->rows观察发现如下数据:
查询返回的结果集中rows的记录数为1,第1个字段的ascii为49,而49是ascii中数字1的值。而第二个字段也有值,说明所对应的timestamp字段是有返回值的,数据库查询是没有问题的。
然而通过mybatis代码进一步封装后的数据multipleResults又表示,只查询到了类型为Int的字段的数据
这么也就是说,问题应该是出在了multipleResults的赋值问题上了
handleResultSets的完整代码为
//
// HANDLE RESULT SETS
//
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
说明问题出在 handleResultSet(rsw, resultMap, multipleResults, null);这句代码上了
通过代码跟踪,发现如下代码
//
// GET VALUE FROM ROW FOR SIMPLE RESULT MAP
//
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
}
return rowValue;
}
继而发现如下的核心代码
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
通过断点发现以下数据
在获取这个查询的的返回字段时,只获取出来两个,即int类型和varchar类型的.
再通过跟踪发现了如下代码
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
直到看到这里
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
才知道问题所在了, configuration.isMapUnderscoreToCamelCase()的值为true,即开启了驼峰命令。
所以查找字段时就找不到。 把之前的类似crt_time改为crtTime后,就可以了! 没想到这么一个小错误,让我纠结了这么久!还好能跟踪源码!
通过这次的问题排查,让我明白了一个道理: 如果不知道某个框架原理的情况下,不要随便填写它的配置信息。在享受到框架的便捷性的同时,最好也得要明白它的原理,这样当出现问题时,才好快速定位。
来源:https://puhaiyang.blog.csdn.net/article/details/79183638
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
详解IntelliJ IDEA 自带的 HTTP Client 接口调用插件吊打 Postman
![](https://img.aspxhome.com/file/2023/7/84327_0s.jpg)
springboot-jta-atomikos多数据源事务管理实现
![](https://img.aspxhome.com/file/2023/8/125558_0s.png)
Java String 字符串常量池解析
![](https://img.aspxhome.com/file/2023/6/66936_0s.png)
详解C#对XML、JSON等格式的解析
![](https://img.aspxhome.com/file/2023/5/68615_0s.jpg)
C#实现网页画图功能
![](https://img.aspxhome.com/file/2023/4/98204_0s.jpg)
Java多线程-线程的同步与锁的问题
C# Redis学习系列(二)Redis基本设置
![](https://img.aspxhome.com/file/2023/3/106403_0s.png)
Android TextView显示Html类解析的网页和图片及自定义标签用法示例
Java String类的常用方法汇总
SQL语句删除和添加外键、主键的方法
C#中[]的几种用法示例代码
![](https://img.aspxhome.com/file/2023/4/94554_0s.png)
Java Filter 过滤器详细介绍及实例代码
Springboot与Maven多环境配置的解决方案
![](https://img.aspxhome.com/file/2023/9/60679_0s.png)
Java 切割字符串的几种方式集合
![](https://img.aspxhome.com/file/2023/9/84329_0s.jpg)
Android仿微信单击拍照长按录像功能实例代码
Android Scroller大揭秘
![](https://img.aspxhome.com/file/2023/7/137377_0s.png)
基于Java检查IPv6地址的合法性
C#利用Task实现任务超时多任务一起执行的方法
SpringBoot @Validated注解实现参数分组校验的方法实例
![](https://img.aspxhome.com/file/2023/3/63633_0s.png)
.NET实现父窗体关闭而不影响子窗体的方法
![](https://img.aspxhome.com/file/2023/4/104264_0s.jpg)