日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

瀏覽:24日期:2023-10-23 13:54:38

背景

最近有一個數據統計服務需要升級 SpringBoot 的版本,由 1.5.x.RELEASE 直接升級到 2.3.0.RELEASE ,考慮到沒有用到 SpringBoot 的內建 SPI ,升級過程算是順利。但是出于代碼潔癖和版本潔癖,看到項目中依賴的 MyBatis 的版本是 3.4.5 ,相比當時的最新版本 3.5.5 大有落后,于是順便把它升級到 3.5.5 。升級完畢之后,執行所有現存的集成測試,發現有部分 OffsetDateTime 類型入參的查詢方法出現異常,于是進行源碼層面的 DEBUG 找到最終的問題并且解決。

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

問題復現

項目中有一個查詢方法類似下面的演示例子:

public interface OrderMapper { List<Order> selectByCreateTime(@Param('startCreateTime') OffsetDateTime startCreateTime, @Param('endCreateTime') OffsetDateTime endCreateTime);}

對應的 XML 文件中的 SQL 代碼段如下:

<select resultMap='BaseResultMap'> SELECT * FROM t_order WHERE deleted = 0 AND create_time <![CDATA[>=]]> #{startCreateTime} AND create_time <![CDATA[<=]]> #{e ndCreateTime}</select>

上面的 OrderMapper#selectByCreateTime() 方法在 MyBatis 版本為 3.4.5 的前提下執行沒有任何異常,當 MyBatis 版本升級為 3.5.5 后再次執行,在 SQL 執行日志輸出正確的前提下返回了一個空集合,具體的內容如下:

查詢訂單列表:[]

雖然上帝視角是確認了入參解析有問題,但是基于第一次發生異常的日志,其實定位不到具體發生問題的位置,當時條件反射認為有幾處地方會出現這類異常( SQL 比較簡單,可以排除人為寫錯 SQL 占位符的情況):

MyBatis 解析 OffsetDateTime 類型方法參數的方法有版本兼容問題。 MySQL 驅動包解析 OffsetDateTime 類型的參數有版本兼容問題。 前面兩種情況混合相互影響導致的,其實這里也可以理解為同一種情況,因為 MyBatis 歸根到底是對 MySQL 驅動包進行了封裝。

當時項目中使用的 mysql-connector-java 版本為 8.0.18 ,并未升級為當前的最新版本 8.0.21 ,所以當時也有懷疑是低版本 MySQL 驅動包沒有兼容解析 OffsetDateTime 類型的參數。

簡析MyBatis的執行流程

MyBatis 的源碼并不復雜,如果省去分析它的配置和映射文件解析模塊,一個查詢 SQL ( SelectList )的執行流程大致如下:

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

當然,因為問題出現在參數解析部分,只需要關注 StatementHandler 的處理邏輯即可。 StatementHandler 的父類 BaseStatementHandler 構造函數中,初始化了 ParameterHandler 和 ResultSetHandler 實例,提交到 SimpleExecutor 中的 doQuery() 方法中執行,使用了占位符參數的查詢會經由 doQuery() 方法中的 prepareStatement() 方法然后調用 PreparedStatementHandler#parameterize() ,最終委托到 DefaultParameterHandler#setParameters() 方法進行參數設置,這個 setParameters() 方法會用到 ParameterMapping 和 TypeHandler 。

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

如果用到了內建的 TypeHandler 或者自定義的 TypeHandler 實現,同時出現了參數解析異常,那么很大幾率異常就是從 DefaultParameterHandler#setParameters() 方法中出現,這樣就能順藤摸瓜找到出現異常的 TypeHandler 。

參數解析異常的根本原因

本文前面提到的解析 OffsetDateTime 類型異常,實際上執行查詢的時候代碼會步入 OffsetDateTimeTypeHandler ,這里對比一下 3.4.5 和 3.5.5 版本中 MyBatis 對應的 OffsetDateTimeTypeHandler 實現:

發現了主要區別如下:

3.4.5 版本中,會把 OffsetDateTime 參數類型轉換為 Timestamp 類型,再委托到 PreparedStatement#setTimestamp() 進行參數設置。

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

3.5.5 版本中,直接調用 PreparedStatement#setObject() 進行參數設置。

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

PreparedStatement#setTimestamp() 是很早期的產物,這個方法是沒有任何問題的, 3.4.5 版本 MyBatis 把 OffsetDateTime 類型兼容為 Timestamp 類型處理 。那么基本可以確定問題出現在 PreparedStatement#setObject() 方法上,對于 MySQL8.x 的驅動, PreparedStatement 選用的實現類是 com.mysql.cj.jdbc.ClientPreparedStatement ,通過層層 DEBUG 最終到達 AbstractQueryBindings#setObject() 方法:

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

由于驅動中沒有任何解析 OffsetDateTime 類型的片段,所以最終會使用 AbstractQueryBindings#setSerializableObject() 方法(也就是 else 分支的代碼)兜底,直接轉化為一個 byte[] 傳輸到 MySQL 服務端, 問題就出在這里,直接把 OffsetDateTime 類型序列化疑似在 MySQL 服務端拿到的不是預期的參數,導致查詢條件出現失效(這里筆者沒有花時間去閱讀 MySQL 的協議,也沒有花大量時間去抓包,所以這里還只是猜測) 。然而, 這個問題在 2020-7-12 最新發布的 mysql:mysql-connector-java:8.0.21 依然沒有解決 。但是看到這里又出現一個疑惑, MyBatis 的開發者應該不可能在這種關鍵而不復雜的問題上出現紕漏,于是花時間去看看這里的代碼提交記錄:

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

這是 Raupach 在 2017-08-22 的一個提交,提交的 message 是:測試 OffsetDateTimeHandler 保留了 UTC 的偏移量。單元測試類 OffsetDateTimeTypeHandlerTest 也只是驗證了 TypeHandler#setParameter() 和 PreparedStatement#setObject() 參數傳遞的正確性, 并沒有做集成測試去跟蹤所有類型數據庫的傳參問題,估計就是這一步疏忽了,但是這個應該不屬于MyBatis的問題,畢竟它只是對數據庫驅動包的封裝 。其中集成測試 TimestampWithTimezoneTypeHandlerTest 使用了內存數據庫,這里可以猜測是 HSQLDB 驅動完善了日期時間的參數解析。

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

同樣的問題在 h2 數據庫中不會出現,于是稍微 DEBUG 了一下 h2 數據庫驅動進行參數設置的源碼,最終定位到 org.h2.value.DataType (驅動包的版本為 com.h2database:h2:1.4.200 )的第 1333 行有對應 JSR310.OFFSET_DATE_TIME 的解析邏輯,所以 h2 數據庫驅動可以支持所有 JSR310 引入的參數類型的參數值設置。下面的截圖是 h2 數據庫驅動中 PreparedStatement#setObject() 的解析實現(見 org.h2.jdbc.JdbcPreparedStatement 和 DataType#convertToValue() 的源碼):

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

這里可見, h2 的驅動真的對 JDK8+ 新增的所有日期時間類型都做了解析:

MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤

針對問題的解決方案

如果選用了 MySQL ,這個參數解析異常的問題截至 mysql:mysql-connector-java:8.0.21 只有一種解決方案:要把 OffsetDateTime 類型兼容為 Timestamp 類型進行參數設置。其實對于所有非 LocalXX 的日期時間類型都需要進行兼容,兼容表格如下:

序號 類型 兼容類型 調用方法 1 OffsetDateTime Timestamp PreparedStatement#setTimestamp() 2 ZonedDateTime Timestamp PreparedStatement#setTimestamp() 3 OffsetDate java.sql.Date PreparedStatement#setDate() 4 OffsetTime java.sql.Time PreparedStatement#setTime()

以 OffsetDateTime 為例,只需要參考或者直接使用 3.4.5 版本中的 MyBatis 的 OffsetDateTimeTypeHandler ,然后通過配置直接覆蓋內置實現即可。

// 假設全類名為club.throwable.OffsetDateTimeTypeHandlerpublic class OffsetDateTimeTypeHandler extends BaseTypeHandler<OffsetDateTime> { @Override public void setNonNullParameter(PreparedStatement ps, int i, OffsetDateTime parameter, JdbcType jdbcType) throws SQLException { ps.setTimestamp(i, Timestamp.from(parameter.toInstant())); } @Override public OffsetDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { Timestamp timestamp = rs.getTimestamp(columnName); return getOffsetDateTime(timestamp); } @Override public OffsetDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Timestamp timestamp = rs.getTimestamp(columnIndex); return getOffsetDateTime(timestamp); } @Override public OffsetDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Timestamp timestamp = cs.getTimestamp(columnIndex); return getOffsetDateTime(timestamp); } private static OffsetDateTime getOffsetDateTime(Timestamp timestamp) { if (timestamp != null) { // 這里可以考慮自定義系統的時區,例如ZoneId.of('Asia/Shanghai') return OffsetDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault()); } return null; }}

配置文件中進行 TypeHandler 配置覆蓋,下面是類路徑下配置文件 mybatis-config.xml 的示例:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE configuration PUBLIC '-//mybatis.org//DTD Config 3.0//EN' 'http://mybatis.org/dtd/mybatis-3-config.dtd'><configuration> <settings> <!--下劃線轉駝峰--> <setting name='mapUnderscoreToCamelCase' value='true'/> <!--未知列映射忽略--> <setting name='autoMappingUnknownColumnBehavior' value='NONE'/> </settings> <typeHandlers> <!--覆蓋內置OffsetDateTimeTypeHandler--> <typeHandler handler='throwable.club.OffsetDateTimeTypeHandler'/> </typeHandlers></configuration>

其他類型解析異常都可以參照此思路進行兼容。

小結

升級基礎框架版本需要謹慎。另外,文中提到的解決方案只是筆者目前通過問題分析和定位得到的一種相對合理的解決方案,也可能有更優解。

本文的 demo 項目倉庫:

Github : https://github.com/zjcscut/spring-boot-guide/tree/master/ch9-mybatis-mysql

到此這篇關于MyBatis版本升級導致OffsetDateTime入參解析異常問題復盤的文章就介紹到這了,更多相關MyBatis OffsetDateTime入參異常內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Mybatis 數據庫
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲精品成人一区| 国产精品久久久久久久久久妞妞| 精品中文字幕一区二区三区四区| 日韩av一区二| 石原莉奈在线亚洲三区| 日韩精品中文字幕吗一区二区| 免费观看在线色综合| 日韩有吗在线观看| 国产亚洲欧美日韩精品一区二区三区 | 精品一区毛片| 国产麻豆一区二区三区精品视频| 亚洲天堂黄色| 成人国产精品| 欧美精品97| 婷婷亚洲五月色综合| 香蕉视频成人在线观看| 福利一区二区三区视频在线观看| 日韩成人亚洲| 99热精品久久| 亚洲精品视频一二三区| 国产日本精品| 精品精品99| 日韩精品视频在线看| 久久激情婷婷| 国产精品66| 日韩在线播放一区二区| 国产免费播放一区二区| 欧美美女一区| 国产情侣久久| 国产亚洲高清视频| 亚洲精品一区三区三区在线观看| 亚洲视频二区| 一区二区三区四区日本视频| 亚洲欧美日韩一区在线观看| 日本不卡视频一二三区| 精品一区在线| 久久男人av资源站| 国产精品一线| 日本免费一区二区视频| 亚洲免费一区三区| 丝袜美腿亚洲一区| 伊人久久亚洲热| 97精品国产福利一区二区三区| 日韩av中文字幕一区二区| 成人在线网站| 精品欠久久久中文字幕加勒比| 日本精品在线播放| 91精品美女| 亚洲精品字幕| 丝袜av一区| 在线看片福利| 日韩国产专区| 日韩欧美精品一区| 久久精品播放| 米奇777超碰欧美日韩亚洲| 日韩中文在线电影| 亚洲精品一级二级| 岛国精品一区| 麻豆一区二区99久久久久| 国产精品对白| 国产精品毛片久久久| 欧美精品导航| 久久婷婷一区| 水蜜桃久久夜色精品一区的特点 | 国产中文欧美日韩在线| 亚洲+小说+欧美+激情+另类| 亚洲久久视频| 欧美日韩一区自拍| 国产精品高潮呻吟久久久久| www.九色在线| 激情久久久久久| 欧美亚洲在线日韩| 亚洲精选av| 97成人在线| 欧美aa在线视频| 老司机精品视频网| 吉吉日韩欧美| 激情视频一区二区三区| 国产理论在线| 国产日韩专区| 国产精品欧美日韩一区| 国产不卡人人| 免费精品视频最新在线| 少妇高潮一区二区三区99| 久久久久伊人| 青青青免费在线视频| 黄色成人91| 国产欧美另类| 97视频热人人精品免费| 亚洲天堂成人| 在线日韩欧美| 久久电影一区| 国产精品视频一区二区三区综合 | 免费在线播放第一区高清av| 国产精品黄色| 西西人体一区二区| 国产日产一区| 久久精品九色| 久久国产成人午夜av影院宅| 喷白浆一区二区| 国产精品一区二区中文字幕| 美女视频网站久久| 婷婷精品视频| 免费久久99精品国产| 久久精品福利| 亚洲一区二区av| 免费精品国产的网站免费观看| 国产美女亚洲精品7777| 欧美gv在线| 日韩一区二区三区在线看| 精品午夜视频| 日本v片在线高清不卡在线观看| 日韩在线欧美| 国产精品欧美三级在线观看| 国产亚洲精品v| 国产一二在线播放| 日韩专区视频网站| 久久久久中文| 亚洲www啪成人一区二区| 青青国产精品| 亚洲特色特黄| 91日韩免费| 国产视频网站一区二区三区| 欧美91精品| 免费av一区二区三区四区| 开心激情综合| 麻豆一区二区在线| 欧美黄色一区| 亚洲精品乱码日韩| 日本一区二区三区视频在线看| 美女精品在线| 久久精品国产精品亚洲毛片| 国产精品三p一区二区| 中文字幕一区二区三区四区久久| 免费黄色成人| 久久久777| 国产日韩电影| 精品中文在线| 日韩专区精品| 亚洲成人va| 亚洲激情久久| 免费久久精品| 久久成人亚洲| 国产精品中文字幕亚洲欧美 | 久色成人在线| 久久国产成人午夜av影院宅| 黄色亚洲大片免费在线观看| 国产亚洲亚洲| 一级欧美视频| 国产精品中文字幕亚洲欧美| 91欧美在线| av中文资源在线资源免费观看| 97视频热人人精品免费| 日韩在线一二三区| 视频一区在线播放| 日韩中文字幕1| 国产亚洲电影| 成人亚洲精品| 一本一本久久| 蜜臀av在线播放一区二区三区| 亚洲在线观看| 鲁大师精品99久久久| 精品久久亚洲| 国产伊人精品| 国产麻豆一区| 激情六月综合| 日韩欧美在线精品| 久久毛片亚洲| 国产亚洲一区二区三区啪| 欧美sm一区| 亚洲一级淫片| 亚州av乱码久久精品蜜桃| 国产视频亚洲| 国产一区 二区| 国精品产品一区| 国产精品日本| 高清精品久久| 中文字幕免费一区二区| 国产一区二区三区不卡视频网站| 久久久久免费av| 国产精品啊啊啊| 国产婷婷精品| 日韩欧美一区二区三区在线观看| 国产精品自拍区| 日韩免费精品| 热久久国产精品| 日韩中文视频| 1000部精品久久久久久久久| 国产午夜一区| 999国产精品| 欧美久久天堂| 国产欧美日韩一级| 亚洲乱码久久| 国产精品美女久久久| 四虎国产精品免费观看| 亚洲精品一区二区在线播放∴| 爽爽淫人综合网网站| 成人羞羞视频在线看网址| 国产精品久久亚洲不卡| 亚洲字幕久久|