基于mybatis-plus timestamp返回為null問(wèn)題的排除
問(wèn)題是這樣的
在開(kāi)發(fā)時(shí),為了節(jié)約時(shí)間,我選擇了mybatis框架來(lái)開(kāi)發(fā),然后又在網(wǎng)上找了一個(gè)許多人都推薦的mybatis-plus來(lái)作為持久層框架。
于是乎我按照官方的DEMO下了一個(gè)springBoot的mybatis-plus版本的DEMO
這個(gè)DEMO是基于H2數(shù)據(jù)庫(kù)的,跑了下沒(méi)有問(wèn)題。DEMO是能正常運(yùn)行的。
然后我將這個(gè)工程的代碼快速拷貝的新的一個(gè)工程里,并把數(shù)據(jù)庫(kù)由H2換為了MYSQL。但項(xiàng)目跑起來(lái)時(shí),出現(xiàn)了如下問(wèn)題:
數(shù)據(jù)庫(kù)里的數(shù)據(jù)如下圖
表結(jié)構(gòu)如下圖
查詢出來(lái)的結(jié)果中對(duì)于timestamp類型的字段為空
為了解決這個(gè)問(wèn)題,我決定通過(guò)debug斷點(diǎn)觀察下是否查詢是把數(shù)據(jù)數(shù)據(jù)查出來(lái)了
由于用的mybatis-plus框架來(lái)開(kāi)發(fā),而mybaits-plus框架是基于mybatis框架的,為了看是否把數(shù)據(jù)查詢出來(lái),直接在ResultSetHandler上把一個(gè)dubug就好。在默認(rèn)情況下,mybatis框架使用的ResultSetHandler為DefaultResultSetHandler,當(dāng)查詢mybatis查詢完畢后,會(huì)通過(guò)ResultSetHandler的handleResultSets(Statement stmt)方法對(duì)查詢的數(shù)據(jù)結(jié)果集進(jìn)行封裝
所以將斷點(diǎn)打在handlerResultSets方法上最為合適。
再通過(guò)statement -> wrapper ->results -> rowData ->rows觀察發(fā)現(xiàn)如下數(shù)據(jù):
查詢返回的結(jié)果集中rows的記錄數(shù)為1,第1個(gè)字段的ascii為49,而49是ascii中數(shù)字1的值。而第二個(gè)字段也有值,說(shuō)明所對(duì)應(yīng)的timestamp字段是有返回值的,數(shù)據(jù)庫(kù)查詢是沒(méi)有問(wèn)題的。
然而通過(guò)mybatis代碼進(jìn)一步封裝后的數(shù)據(jù)multipleResults又表示,只查詢到了類型為Int的字段的數(shù)據(jù)
這么也就是說(shuō),問(wèn)題應(yīng)該是出在了multipleResults的賦值問(wèn)題上了
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); }
說(shuō)明問(wèn)題出在 handleResultSet(rsw, resultMap, multipleResults, null);這句代碼上了
通過(guò)代碼跟蹤,發(fā)現(xiàn)如下代碼
// // 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; }
繼而發(fā)現(xiàn)如下的核心代碼
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; }
通過(guò)斷點(diǎn)發(fā)現(xiàn)以下數(shù)據(jù)
在獲取這個(gè)查詢的的返回字段時(shí),只獲取出來(lái)兩個(gè),即int類型和varchar類型的.
再通過(guò)跟蹤發(fā)現(xiàn)了如下代碼
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());
才知道問(wèn)題所在了, configuration.isMapUnderscoreToCamelCase()的值為true,即開(kāi)啟了駝峰命令。
所以查找字段時(shí)就找不到。 把之前的類似crt_time改為crtTime后,就可以了! 沒(méi)想到這么一個(gè)小錯(cuò)誤,讓我糾結(jié)了這么久!還好能跟蹤源碼!
通過(guò)這次的問(wèn)題排查,讓我明白了一個(gè)道理: 如果不知道某個(gè)框架原理的情況下,不要隨便填寫它的配置信息。在享受到框架的便捷性的同時(shí),最好也得要明白它的原理,這樣當(dāng)出現(xiàn)問(wèn)題時(shí),才好快速定位。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Mybatis resultType返回結(jié)果為null的問(wèn)題排查方式
- mybatis-plus3.0.1枚舉返回為null解決辦法
- 解決mybatis返回boolean值時(shí)數(shù)據(jù)庫(kù)返回null的問(wèn)題
- 解決mybatis使用char類型字段查詢oracle數(shù)據(jù)庫(kù)時(shí)結(jié)果返回null問(wèn)題
- 解決mybatis執(zhí)行SQL語(yǔ)句部分參數(shù)返回NULL問(wèn)題
- Mybatis查不到數(shù)據(jù)查詢返回Null問(wèn)題
- MyBatis查詢數(shù)據(jù)返回null的解決
相關(guān)文章
Java實(shí)現(xiàn)計(jì)網(wǎng)循環(huán)冗余檢驗(yàn)算法的方法示例
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)計(jì)網(wǎng)循環(huán)冗余檢驗(yàn)算法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Java使用jdbc連接實(shí)現(xiàn)對(duì)MySQL增刪改查操作的全過(guò)程
JDBC的全稱是Java?Database?Connectivity,即Java數(shù)據(jù)庫(kù)連接,它是一種可以執(zhí)行SQL語(yǔ)句的Java?API,下面這篇文章主要給大家介紹了關(guān)于Java使用jdbc連接實(shí)現(xiàn)對(duì)MySQL增刪改查操作的相關(guān)資料,需要的朋友可以參考下2023-03-03Java 實(shí)戰(zhàn)項(xiàng)目錘煉之樸素風(fēng)格個(gè)人博客系統(tǒng)的實(shí)現(xiàn)流程
讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java+vue+Springboot+ssm+mysql+maven+redis實(shí)現(xiàn)一個(gè)樸素風(fēng)格的個(gè)人博客系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11Java Testcontainers庫(kù)實(shí)現(xiàn)測(cè)試功能
這篇文章主要介紹了Java Testcontainers庫(kù)實(shí)現(xiàn)測(cè)試功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09SpringBoot Security整合JWT授權(quán)RestAPI的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot Security整合JWT授權(quán)RestAPI的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11spring的pointcut正則表達(dá)式的實(shí)現(xiàn)
本文主要介紹了spring的pointcut正則表達(dá)式的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說(shuō)明
這篇文章主要介紹了SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04