公司新來小伙,把 MyBatis 替換成 MyBatis-Plus,上線后被開了.....
共 4655字,需瀏覽 10分鐘
·
2024-06-21 12:20
大家好,我是胖虎,給首先大家分享兩個(gè)產(chǎn)品
背景簡(jiǎn)介
在一個(gè)老項(xiàng)目中,數(shù)據(jù)庫采用的是 MySQL 5.7.36,ORM 框架使用的是 MyBatis 3.5.0,而 mysql-connector-java 的版本是 5.1.26。
有一天,一個(gè)精力充沛、充滿折騰精神的年輕人加入了項(xiàng)目。
他覺得 MyBatis 的使用不夠簡(jiǎn)單,需要寫的代碼比較多,因此認(rèn)為有必要將其替換為 MyBatis-Plus。
MyBatis-Plus 替換 MyBatis
首先,我們準(zhǔn)備了一張名為 tbl_order 的表,并初始化了其中的兩條數(shù)據(jù)。
DROP TABLE IF EXISTS `tbl_order`;
CREATE TABLE `tbl_order` (
`id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
`order_no` varchar(50) NOT NULL COMMENT '訂單號(hào)',
`pay_time` datetime(3) DEFAULT NULL COMMENT '付款時(shí)間',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '創(chuàng)建時(shí)間',
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最終修改時(shí)間',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT = '訂單';
INSERT INTO `tbl_order` VALUES (1, '123456', '2024-02-21 18:38:32.000', '2024-02-21 18:37:34.000', '2024-02-21 18:40:01.720');
INSERT INTO `tbl_order` VALUES (2, '654321', '2024-02-21 19:33:32.000','2024-02-21 19:32:12.020', '2024-02-21 19:34:03.727');
為了簡(jiǎn)化演示,我們直接使用 MyBatis-Plus 構(gòu)建了一個(gè)示例 demo,以此來模擬這位年輕人的替換過程。
我們只是簡(jiǎn)單地將 MyBatis-Plus 替換了 MyBatis,而其他組件的版本保持不變。
我們使用的 MyBatis-Plus 版本是這位年輕人引用的版本:3.1.1,
而 mysql-connector-java 的版本保持不變,仍然是 5.1.26。
示例代碼:play_it_safe
然后,運(yùn)行 com.qsl.OrderTest#orderListAllTest,卻遇到了報(bào)錯(cuò),異常信息如下所示:
請(qǐng)注意 Caused by 部分。
不支持的轉(zhuǎn)換類型:java.time.LocalDateTime
是誰不支持呢?是 mysql-connector-java 不支持!
那么,哪個(gè)版本的 mysql-connector-java 支持呢?
?答案是:5.1.37
?
升級(jí) mysql-connector-java
我們將 mysql-connector-java 升級(jí)到 5.1.37,然后再次執(zhí)行 com.qsl.OrderTest#orderListAllTest。
這次不再報(bào)異常,并且查詢結(jié)果也是正確的。
看起來,MyBatis-Plus 替換 MyBatis 的任務(wù)完成了。
這一切都如此順利,讓人有些懷疑。
讓我們?cè)倩仡^看看之前提到的異常:
Conversion not supported for type java.time.LocalDateTime
在替換 MyBatis 之前并沒有這個(gè)異常,但在替換之后就出現(xiàn)了這個(gè)異常,這難道不是 MyBatis-Plus 的問題嗎?
那么如何找到這個(gè)異常的根本原因呢?
其實(shí)很簡(jiǎn)單,直接從異常堆棧入手。
點(diǎn)擊后,你會(huì)發(fā)現(xiàn)代碼非常簡(jiǎn)單。
這么簡(jiǎn)單的代碼怎么會(huì)有問題呢?
大家請(qǐng)注意圖中左上角的 MyBatis 版本,是 3.5.1,而不是最初的 3.5.0。
可能有人會(huì)問:「既然替換成了 MyBatis-Plus,為什么還有 Mybatis 的存在?」
這個(gè)問題問得確實(shí)好,我只想給你個(gè)大嘴巴。
現(xiàn)在讓我們看一下 MyBatis-Plus 的官方說明。
既然基于 Mybatis 3.5.0 沒有拋出異常,而基于 3.5.1 卻拋出了異常。
?“LocalDateTimeTypeHandler” 在 3.5.1 中肯定進(jìn)行了調(diào)整
?
那我們來看看調(diào)整了什么?
看出什么了嗎?
MyBatis 3.5.0 會(huì)處理 LocalDateTime 類型的轉(zhuǎn)換(將 java.sql.Timestamp 轉(zhuǎn)換成 java.time.LocalDateTime)。
然而,注意了啊,最關(guān)鍵的地方來了!
從 MyBatis 3.5.1 開始,不再處理 LocalDateTime(還包括:LocalDate、LocalTime)類型的轉(zhuǎn)換,而是交由 JDBC 組件,也就是 mysql-connector-java 來實(shí)現(xiàn)。
而巧的是:
?mysql-connector-java 5.1.26 不支持類型 LocalDateTime。
?
那么它支持哪些類型呢?
我們同樣從異常堆棧入手。
點(diǎn)擊后,可以看到下圖。
往上滑動(dòng)鼠標(biāo),就可以看到支持的類型。
確實(shí)沒有 LocalDateTime、LocalDate 和 LocalTime。
?mysql-connector-java 5.1.37 開始支持 LocalDateTime、LocalDate 和 LocalTime,前面已經(jīng)介紹過了,不再贅述。
?
總結(jié)異常根本原因:
?MyBatis 3.5.1 開始不再處理 LocalDateTime、LocalDate 和 LocalTime 的轉(zhuǎn)換,而 mysql-connector-java 5.1.37 之前都不支持這些類型。
?
搞清楚了這個(gè)異常的來龍去脈,順理成章的感覺是不是又回來了?
暴風(fēng)雨來臨
版本上線不到兩天,該來的終究還是來了。
我們往表 tbl_order 中插入了一條記錄:
INSERT INTO tbl_order
VALUES (3, 'asdfgh', NULL, '2024-02-21 20:01:31.111', '2024-02-21 20:02:56.764');
然后再次執(zhí)行 com.qsl.OrderTest#orderListAllTest。
此時(shí)我就想問這位年輕人:爽不爽?
遇到了異常,那就找出原因。
同樣從異常堆棧入手。
看出什么了嗎?
如果 getTimestamp(columnIndex) 得到的是 NULL,那不就是 NullPointerException?這也太不嚴(yán)謹(jǐn)了吧?
修復(fù)問題是當(dāng)務(wù)之急,先看哪個(gè)版本進(jìn)行了修復(fù)?
將 mysql-connector-java 升級(jí)到 5.1.42。
問題得以修復(fù)。
?經(jīng)過這一次事件,這位年輕人似乎成長(zhǎng)了許多,但眼中的光卻黯淡了不少。
?
Mybatis-Plus 的問題
無意中我看到了這個(gè) issue-1114,是不是和我們之前分析的 “Conversion not supported for type java.time.LocalDateTime” 是同一個(gè)問題?
只是我們使用的數(shù)據(jù)庫連接池是默認(rèn)的 HikariCP 而非 Druid。
結(jié)合 druid/issues/3302,如果使用 Druid 作為數(shù)據(jù)庫連接池,出現(xiàn)的異常可能與我們之前分析的確實(shí)不同。
因此,大家需要根據(jù)自己的實(shí)際情況進(jìn)行分析,但對(duì)異常的分析方法是通用的。
總結(jié)
關(guān)于組件的升級(jí)或舊代碼的調(diào)整,都可能引發(fā)連鎖反應(yīng),影響重大。
我的觀點(diǎn)是:
?能不動(dòng)就不要?jiǎng)?,改好沒功勞,改壞要背鍋,吃力不討好,又不是必須要改。
?
