面試突擊86:SpringBoot 事務不回滾?怎麼解決?
在 Spring Boot 中,造成事務不自動回滾的場景有很多,比如以下這些:
- 非 public 修飾的方法中的事務不自動回滾;
- 當 @Transactional 遇上 try/catch 事務不自動回滾;
- 呼叫類內部的 @Transactional 方法事務不自動回滾;
- 丟擲檢查異常時事務不自動回滾;
- 資料庫不支援事務,事務也不會自動回滾。
那麼對於上面的這些場景,我們應該如何解決呢?接下來我們一一來看。
1.非 public 方法解決方案
非 public 方法中事務不回滾的直接原因是,在非 public 方法上新增的 @Transactional 關鍵字是無效的,也就是此方法本身是以非事務的方式執行的,所以它當然不會自動回滾事務了。
因為 @Transactional 使用的是 Spring AOP 實現的,而 Spring AOP 是通過動態代理實現的,而 @Transactional 在生成代理時會判斷,如果方法為非 public 修飾的方法,則不生成代理物件,這樣也就沒辦法自動回滾事務了,它的部分實現原始碼如下:
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. // 非 public 方法,設定為 null if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // 後面程式碼省略.... }
此問題的解決方案是將方法的許可權修飾符改為 public 即可。
2.try/catch 解決方案
當程式中出現了 try/catch 程式碼時,事務不會自動回滾,這是因為 @Transactional 註解在其實現時,需要感知到異常才會自動回滾,而使用者自行在程式碼中加入了 try/catch 之後,@Transactional 就無法感知到異常了,那麼也就不能自動回滾事務了。
此問題的解決方案有兩種:一種是在 catch 中將異常重新丟擲去,另一種是使用程式碼手動將事務回滾。
解決方案1:將異常重新丟擲
解決方案2:使用程式碼手動回滾事務
除了解決方案 1 這種不是很友好的回滾事務的方式之外,我們還可以選擇更加友好的,不報錯,但可以回滾事務的方式,其核心實現程式碼如下:
3.呼叫內部 @Transactional 方法解決方案
呼叫類內部 @Transactional 的方法不自動回滾事務的原因是,@Transactional 是基於 Spring AOP 實現的,而 Spring AOP 又是基於動態代理實現的,而當呼叫類內部的方法時,不是通過代理物件完成的,而是通過 this 物件實現的,這樣就繞過了代理物件,從而事務就失效了。
此時我們的解決方案是給呼叫的方法上也加上 @Transactional,具體實現程式碼如下:
4.檢查異常的事務解決方案
所謂的檢查異常(Checked Excetion)指的是編譯器要求開發者必須處理的異常,如下圖所示:
檢查異常不回滾事務的原因是因為,@Transactional 預設只回滾執行時異常 RuntimeException 和 Error,而對於檢查異常預設是不回滾的。
此問題的解決方案是給 @Transactional 註解上,新增 rollbackFor 引數並設定 Exception.class 值即可,具體實現程式碼如下:
5.資料庫不支援事務的解決方案
當我們在程式中添加了 @Transactional,相當於給呼叫的資料庫傳送了:開始事務、提交事務、回滾事務的指令,但是如果資料庫本身不支援事務,比如 MySQL 中設定了使用 MyISAM 引擎,因為它本身是不支援事務的,這種情況下,即使在程式中添加了 @Transactional 註解,那麼依然不會有事務的行為,也就不會執行事務的自動回滾了。
在這種情況下,我們只需要設定 MySQL 的引擎為 InnoDB 就可以解決問題了,因為 InnoDB 是支援事務的,當然 MySQL 5.1 之後的預設引擎就是 InnoDB,引擎的設定分為以下兩種情況:
在新建表時設定資料庫引擎:
在修改表時設定資料庫引擎:
PS:也就是資料庫的引擎是和表直接相關的,我們只需要正確的設定引擎之後,事務就可以正常的執行了。
總結
本文我們介紹了 5 種事務不自動回滾的場景和相應的解決方案,開發者應該根據自己的實際情況,選擇合適自己解決方案進行處理。
是非審之於己,譭譽聽之於人,得失安之於數。
公眾號:Java面試真題解析
- 執行緒池底層原理詳解與原始碼分析
- 30分鐘掌握 Webpack
- 線性迴歸大結局(嶺(Ridge)、 Lasso迴歸原理、公式推導),你想要的這裡都有
- 【前端必會】webpack loader 到底是什麼
- 中心化決議管理——雲端分析
- HashMap底層原理及jdk1.8原始碼解讀
- 詳解JS中 call 方法的實現
- 列印 Logger 日誌時,需不需要再封裝一下工具類?
- 初識設計模式 - 代理模式
- 密碼學奇妙之旅、01 CFB密文反饋模式、AES標準、Golang程式碼
- Springboot之 Mybatis 多資料來源實現
- CAS核心思想、底層實現
- 面試突擊86:SpringBoot 事務不回滾?怎麼解決?
- 基於electron vue element構建專案模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和資料簡單、快速生成檔案。
- 認識執行緒,初始併發
- 1-VSCode搭建GD32開發環境
- 初識設計模式 - 原型模式
- 執行緒安全問題的產生條件、解決方式
- 2>&1到底是什麼意思?