小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

為什么 insert 配置 "SELECT LAST_INSERT_ID()" 返回個0呢?

 小傅哥 2022-06-27 發(fā)布于北京

作者:小傅哥
博客:https://

?

沉淀、分享、成長,讓自己和他人都能有所收獲!??

?
  • 一、前言:一個Bug

  • 二、分析:診斷異常

  • 三、震驚:同一個坑

  • 四、常見:事務失效

  • 五、總結:學習經驗

一、前言:一個Bug

沒想到一個Bug,竟然搞我兩次!

我大抵是卷上癮了,橫豎都睡不著,坐起來身來打開Mac和外接顯示器,這Bug沒有由來,默然看著打印異常的屏幕,一個是我的,另外一個也是我的。


最近可能是卷源碼,卷上癮了。先是《手寫Spring》,再是《手寫Mybatis》,但沒想到一個小問題竟然搞了我2次!

今天這個問題主要體現(xiàn)在大家平常用的Mybatis,在插入數(shù)據的時候,我們可以把庫表索引的返回值通過入參對象返回回來。但是通過我自己手寫的Mybatis,每次返回來的都是0,而不是最后插入庫表的索引值。因為是手寫的,不是直接使用Mybatis,所以我會從文件的解析、對象的映射、SQL的查詢、結果的封裝等一直排查下去,但竟然問題都不在這?!

  • 就是這個 selectKey 的配置,在執(zhí)行插入SQL后,開始執(zhí)行獲取最后的索引值。
  • 通常只要配置的沒問題,返回對象中也有對應的 id 字段,那么就可以正確的拿到返回值了。PS:問題就出現(xiàn)在這里,小傅哥手寫的 Mybatis 竟然只難道返回一個0!

二、分析:診斷異常

可能大部分研發(fā)伙伴沒有閱讀過 Mybatis 源碼,所以可能不太清楚這里發(fā)生了什么,小傅哥這里給大家畫張圖,告訴你發(fā)生了什么才讓返回的結果為0的。

  • Mybatis 的處理過程可以分為兩個大部分來看,一部分是解析,另外一部分是使用。解析的時候把 Mapper XML 中的 insert 標簽語句解析出來,同時解析 selectKey 標簽。最終解析完成后,把解析的語句信息使用 MappedStatement 映射語句類存放起來。便于后續(xù)在 DefaultSqlSession 執(zhí)行操作的時候,可以從 Configuration 配置項中獲取出來使用。

  • 那么這里有一個非常重要的點,就是執(zhí)行 insert 插入的時候,里面還包含了一句查詢的操作。那也就是說,我們會在一次 Insert 中,包含兩條執(zhí)行語句。重點:bug就發(fā)生在這里,為什么呢?因為最開始這兩條語句執(zhí)行的時候,在獲取鏈接的時候,每一條都是獲取一個新的鏈接,那么也就是說,insert xxx、select LAST_INSERT_ID() 在兩個 connection 連接執(zhí)行時,其實是不對的,沒法獲取到插入后的索引 ID,只有在一個鏈接或者一個事務下(一次 commit)才能有事務的特性,獲取插入數(shù)據后的自增ID。

  • 而因為這部分最開始手寫 JdbcTransaction 實現(xiàn) Transaction 接口獲取連接的時候,每一次都是新的鏈接,代碼塊如下;

    • 這里的鏈接獲取,最開始沒有 if null 的判斷,每次都是直接獲取鏈接,所以這種非一個鏈接下的兩條 SQL 操作,所以必然不會獲得到正確的結果,相當于只是單獨執(zhí)行 SELECT LAST_INSERT_ID() 所以最終的查詢結果為 0 了就!你可以測試把這條語句復制到 SQL查詢工具中執(zhí)行

三、震驚:同一個坑

?? 但其實就這么一個鏈接的問題,在小傅哥手寫Spring中也同樣遇到過。

在 Spring 中有一部分是關于事務的處理,其實這些事務的操作也是對 JDBC 的包裝操作,依賴于數(shù)據源獲得的鏈接來管理事務。而我們通常使用 Spring 也是結合著 Mybatis 配置上數(shù)據源的方式進行使用,那么在一個事務下操作多個 SQL 語句的時候,是怎么獲得同一個鏈接的呢。因為從上面????的案例中,我們得知保證事務的特性,需要在同一個鏈接下,即使是操作多條SQL

由于多個SQL的操作,已經是相當于每次都獲取一個新的 Session 有一個新的鏈接從連接池中獲得,但為了能達到事務的特性,所以在需要有事務操作下的多個 SQL 前需要開啟事務操作,無論是手動還是注解。

而這個事務的開啟動作處理做一些事務傳播行為和隔離級別的限制,其實更重要的是讓多個 SQL 的執(zhí)行獲取的鏈接,需要是同一個。所以這里就引入了 ThreadLocal 基于它在同一個線程操作下保存信息的同步特性,其實這里的從事務下獲取的鏈接,其實就是保存到 TransactionSynchronizationManager#resources 屬性中的。

雖然就這么一小塊內容,但在小傅哥最開始手寫Spring的時候,也是給漏下了。直到到測試的時候,才發(fā)現(xiàn)鏈接發(fā)現(xiàn)事務總是不成功,最初還以為是整個切面邏輯沒有切進去或者是我的操作方式有誤。直到逐步排查調試代碼,發(fā)現(xiàn)原來多個SQL的執(zhí)行竟然不是獲得的同一個鏈接,所以也就沒法讓事務生效。

四、常見:事務失效

可能就是這么一個小小的鏈接問題,有時候就會引起一堆的異常,如果說我們沒有學習過源碼,那么可能也不知道這樣的問題到底是如何發(fā)生的。所以往往深入的研究和探索,才能讓你解釋一個問題的時候,更加簡單直接。

那么你說,事務失效的原因還有哪些?- 分享一些常見,如果你還有遇到其他的,可以發(fā)到評論區(qū)一起看看。

  1. 數(shù)據庫引擎不支持事務:這里以 MySQL 為例,其 MyISAM 引擎是不支持事務操作的,InnoDB 才是支持事務的引擎,一般要支持事務都會使用 InnoDB。https://dev./doc/refman/8.0/en/storage-en... 從 MySQL 5.5.5 開始的默認存儲引擎是:InnoDB,之前默認的都是:MyISAM,所以這點要值得注意,底層引擎不支持事務再怎么搞都是白搭。
  2. 方法不是 public 的:來自 Spring 官方文檔【When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.】@Transactional 只能用于 public 的方法上,否則事務不會失效,如果要用在非 public 方法上,可以開啟 AspectJ 代理模式。
  3. 沒有被 Spring 管理:// @Service - 這里被注釋掉了 public class OrderServiceImpl implements OrderService { @Transactional public void placeOrder(Order order) { // ... } }
  4. 數(shù)據源沒有配置事務管理器:一般來自于自研的數(shù)據庫路由組件 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
  5. 異常被吞了。catch 后直接吃了,事務異常無法回滾。同時要配置上對應的異常 @Transactional(rollbackFor = Exception.class)

五、總結:學習經驗

很多類似這樣的技術問題,都是來自于小傅哥對源碼的學習,最開始是遇到問題的時候去翻看源碼,雖然很多時候也很難把整個邏輯捋順,但一點點的積累確實會讓研發(fā)人員對技術有更加夯實的認知。

那么在現(xiàn)在我之所以去手寫Spring、手寫Mybatis,也是希望通過把這樣的知識全部整理處理,從中學習復雜邏輯的設計方案、設計原則和如何運用設計模式解決復雜場景的問題。PS:通常我們的業(yè)務代碼復雜度很難到這個程度,所以在見過”天“后,以后所承接的業(yè)務就很容易做設計了。

另外就是對各類技術細節(jié)的把控,以及積累于這樣的經驗把相關技術設計運用到一些類似 SpringBoot Starter 等的開發(fā),只有類似這樣的廣度、高度、深度,才能真的把個人的研發(fā)能力提升起來。PS:也是為了在技術的路上走的更遠,無論是高級開發(fā)、架構師、CTO!

歡迎一起學習手寫源碼和實戰(zhàn)項目! 適合:有需要校招、面試、晉升,想提高自己的技術深度,為自己的職業(yè)生涯續(xù)期,可以長穩(wěn)發(fā)展,完善自己的技術體系,奔著高級開發(fā)和架構師路線的研發(fā)伙伴。

校招、面試、晉升,加入小傅哥的私有技術朋友圈!

    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多