Spring 自調用事務失效,你是怎么解決的?


前言

相信大家都遇到一種事務失效場景,那就是 Spring 自調用,就是在 Service 方法內,調用另一個加 @Transactional 注解的方法,發現事務失效,這時候你是怎么解決的呢?

事情回顧

那是一個我忘了天氣咋樣的下午,突然蹦出一個小紅點,嗯~ 挺着急的小紅點。

3E8q0W-3Cg65u

原來是事務失效了!

莫慌!莫慌!

kRoqUW-MKbcBj

KVdtlZ-F3jW3p

T4poZK-qyiLEV

最后小伙伴選擇了抽走,是我的工具類不香了么?

zVf6LC-tLzseS

當然故事的結果是完美的,問題解決了。

v3W8tg-VJEgZS

事務

在開發中涉及到同時操作多個表的時候,要保證兩個操作要么一起成功,要么一起失敗,這時候就需要用到事務。

現在一般使用的都是基於 @Transactional 注解的聲明式事務

而事務使用過程中有以下幾個注意事項:

  1. 事務只能應用到 public 方法上才會有效;
  2. 事務需要從外部調用,Spring 自調用會失效;
  3. 建議事務注解 @Transactional 一般添加在實現類上。

當然這幾句話不是說我的,人家官方文檔可是明確說明的!

IlNXVn-uemNYF

這里可是說明了應僅將 @Transactional 注解應用於具有公開可見性的方法。如果對受 protected, private o或 package-visible 修飾的方法使用,則不會引發任何錯誤,但是被注解的方法不會顯示已配置的事務設置。

說白了,就是你用了,不會報錯,但是不生效!

M5XTck-YnrPiu

至於建議加在實現類上,這個只是建議,不過如果加在接口類或接口方法上時,只有配置基於接口的代理才會生效。所以這塊還是老老實實的加在實現類或實現類方法上吧。

P28Ciu-4YvetS

因為代理模式只攔截通過代理傳入的外部方法調用,所以自調用事務是不生效的。

官方的解釋還是比較簡單明了的,雖然我看不懂,但是不影響我截圖。

那我還是再截一個吧……

sBkeIz-80K1UE

實際使用

但是在開發中,小伙伴們往往會遇到這種情況!

g1BG6s-BgQfOw

本來自己寫的代碼就一坨坨的又臭又長,里面有各種驗簽、驗參、查詢、驗證等等,就想着來個事務,讓事務包裹的范圍最小,僅僅在同時更新的時候加上事務吧!

38SYyX-gEXV58

這么寫,咦~ IDEA 報錯了,好像不能 private 修飾,那我改成 public

很顯然事務是不生效的。

把更新的代碼放到又臭又長的代碼里面,讓它變得更臭更長,然后用 @Transactional 注解一加。完美解決!

9r8ioC-W6kSbv

請放過那坨代碼吧!來看看下面的辦法。

解決方案 1

08fPAy-ee1X6A

那我改成外部調用不就行了么?

再聲明一個 Service,把更新表的邏輯放過去。

我一般就喜歡使用這個辦法。

解決方案 2

使用編程式事務,前面說了,使用聲明式事務時,又這又那,我換一種總可以吧!

yuJKad-LGJuVt

你看,我還把方法改成 private 修飾了,事務也生效。完美解決!

8orQs4-tgtQts

其實這個方法也很不錯哦!

解決方案 3

又想用注解,又想自調用怎么辦?

MBSeHo-E5RDuC

不過... 麻煩一點還是可以的。

咱們可以參考編程式事務的方式,不就是不讓自調用么,我調外部方法,然后外部方法再給我調回來不就可以了。

@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

當然基於這個版本也可以做一個迭代,就是使用靜態方法調用,不用每次都用 @Autowired 注入一次。

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

這樣通過工具類 TransactionalUtils 便可以直接調用靜態方法的方式執行事務操作。

總結

結束語

本文主要介紹為什么會遇到事務失效,以及事務失效的避免方式,同時提供了三種方式來解決自調用事務失效的問題。不足之處,歡迎指正。

相關資料

  1. Spring 文檔:https://docs.spring.io/spring-framework/docs/5.3.0/reference/html/data-access.html#transaction-declarative-annotations


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM