1. 程式人生 > >Spring 自呼叫事務失效,你是怎麼解決的?

Spring 自呼叫事務失效,你是怎麼解決的?

> **前言** > > > 相信大家都遇到一種事務失效場景,那就是 Spring 自呼叫,就是在 Service 方法內,呼叫另一個加 `@Transactional` 註解的方法,發現事務失效,這時候你是怎麼解決的呢? > > > 公眾號:『 劉志航 』,記錄工作學習中的技術、開發及原始碼筆記;時不時分享一些生活中的見聞感悟。歡迎大佬來指導! ### 事情回顧 那是一個我忘了天氣咋樣的下午,突然蹦出一個小紅點,嗯~ 挺著急的小紅點。 ![3E8q0W-3Cg65u](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/3E8q0W-3Cg65u.png) 原來是事務失效了! 莫慌!莫慌! ![kRoqUW-MKbcBj](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/kRoqUW-MKbcBj.png) ![KVdtlZ-F3jW3p](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/KVdtlZ-F3jW3p.png) ![T4poZK-qyiLEV](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/T4poZK-qyiLEV.png) 最後小夥伴選擇了抽走,是我的工具類不香了麼? ![zVf6LC-tLzseS](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/zVf6LC-tLzseS.png) 當然故事的結果是完美的,問題解決了。 ![v3W8tg-VJEgZS](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/v3W8tg-VJEgZS.jpg) ### 事務 在開發中涉及到同時操作多個表的時候,要保證兩個操作要麼一起成功,要麼一起失敗,這時候就需要用到事務。 現在一般使用的都是基於 `@Transactional` 註解的**宣告式事務**。 而事務使用過程中有以下幾個注意事項: 1. 事務只能應用到 public 方法上才會有效; 2. 事務需要從外部呼叫,Spring 自呼叫會失效; 3. 建議事務註解 @Transactional 一般新增在實現類上。 當然這幾句話不是說我的,人家官方文件可是明確說明的! ![IlNXVn-uemNYF](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/IlNXVn-uemNYF.png) 這裡可是說明了`應僅將 @Transactional 註解應用於具有公開可見性的方法。如果對受 protected, private o或 package-visible 修飾的方法使用,則不會引發任何錯誤,但是被註解的方法不會顯示已配置的事務設定。` 說白了,就是你用了,不會報錯,但是不生效! ![M5XTck-YnrPiu](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/M5XTck-YnrPiu.png) 至於建議加在實現類上,這個只是建議,不過如果加在介面類或介面方法上時,只有配置基於介面的代理才會生效。所以這塊還是老老實實的`加在實現類或實現類方法上`吧。 ![P28Ciu-4YvetS](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/P28Ciu-4YvetS.png) 因為代理模式只攔截通過代理傳入的外部方法呼叫,所以自呼叫事務是不生效的。 官方的解釋還是比較簡單明瞭的,雖然我看不懂,但是不影響我截圖。 那我還是再截一個吧…… ![sBkeIz-80K1UE](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/sBkeIz-80K1UE.png) ### 實際使用 但是在開發中,小夥伴們往往會遇到這種情況! ![g1BG6s-BgQfOw](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/g1BG6s-BgQfOw.png) 本來**自己寫的**程式碼就一坨坨的又臭又長,裡面有各種驗籤、驗參、查詢、驗證等等,就想著來個事務,讓事務包裹的範圍最小,僅僅在同時更新的時候加上事務吧! ![38SYyX-gEXV58](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/38SYyX-gEXV58.png) 這麼寫,咦~ IDEA 報錯了,好像不能 `private` 修飾,那我改成 `public`。 很顯然事務是不生效的。 把更新的程式碼放到`又臭又長`的程式碼裡面,讓它變得更臭更長,然後用 `@Transactional` 註解一加。完美解決! ![9r8ioC-W6kSbv](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/9r8ioC-W6kSbv.jpg) 請放過那坨程式碼吧!來看看下面的辦法。 #### 解決方案 1 ![08fPAy-ee1X6A](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/08fPAy-ee1X6A.png) 那我改成外部呼叫不就行了麼? 再宣告一個 Service,把更新表的邏輯放過去。 我一般就喜歡使用這個辦法。 #### 解決方案 2 使用`程式設計式事務`,前面說了,使用`宣告式事務`時,又這又那,我換一種總可以吧! ![yuJKad-LGJuVt](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/yuJKad-LGJuVt.png) 你看,我還把方法改成 `private` 修飾了,事務也生效。完美解決! ![8orQs4-tgtQts](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/8orQs4-tgtQts.jpg) 其實這個方法也很不錯哦! #### 解決方案 3 又想用註解,又想自呼叫怎麼辦? ![MBSeHo-E5RDuC](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/MBSeHo-E5RDuC.jpg) 不過... 麻煩一點還是可以的。 咱們可以參考`程式設計式事務`的方式,不就是不讓自呼叫麼,我調外部方法,然後外部方法再給我調回來不就可以了。 ```java @Component public class TransactionalComponent { public interface Cell { void run() throws Exception; } @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void required(Cell cell) throws Exception { cell.run(); } } ``` 這樣的話不就可以通過 `TransactionalComponent` 呼叫了麼,並且還可以使用 `lambda` 表示式。 ![y5bnw5-TMtjKN](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/y5bnw5-TMtjKN.png) 當然基於這個版本也可以做一個迭代,就是使用靜態方法呼叫,不用每次都用 `@Autowired` 注入一次。 ```java public class TransactionalUtils { private static volatile TransactionalComponent transactionalComponent; private static synchronized TransactionalComponent getTransactionalComponent() { if (transactionalComponent == null) { // 從容器中獲取 transactionalComponent transactionalComponent = ApplicationContextUtils.getBean(TransactionalComponent.class); } return transactionalComponent; } public static void required(TransactionalComponent.Cell cell) throws Exception { getTransactionalComponent().required(cell); } } ``` ![awzH4i-JjJNp4](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/awzH4i-JjJNp4.png) 這樣通過工具類 `TransactionalUtils` 便可以直接呼叫靜態方法的方式執行事務操作。 ### 總結 #### 結束語 本文主要介紹為什麼會遇到事務失效,以及事務失效的避免方式,同時提供了三種方式來解決自呼叫事務失效的問題。不足之處,歡迎指正。 #### 相關資料 1. Spring 文件:[https://docs.spring.io/spring-framework/docs/5.3.0/reference/html/data-access.html#transaction-declarative-annotations](https://docs.spring.io/spring-framework/docs/5.3.0/reference/html/data-access.html#transaction-declarative-anno