【程式設計開發】之開發解決的“坑“

王廷雲的部落格發表於2020-10-25

本地事務失效問題


總結:同一個物件內事務方法之間互相呼叫時,事務預設會失效,原因是繞過了代理物件。

在 SpringBoot 裡面我們使用 @Transactional(propagation=Propagation.REQUIRED) 註解來開啟事務,如果是不同服務之間的呼叫,比如:A 服務的 a() 方法呼叫了 B 服務的 b() 方法,a、b 方法都開啟了事務,那麼它們各自的事務是有效的。

但如果在同一個 Service 中有 a()、b()、c() 三個方法,每個方法都開啟了事務,比如,我 a 方法的事務傳播行為是 REQUIRED,而 b 的事務傳播行為是 REQUIRED_NEW,當 a 方法呼叫了 b 方法時,b 方法的事務是不生效的。

後面我去查詢了一下關於 SpringBoot 事務的相關原理,發現,原來我們的事務是使用代理物件來控制的,如果在同一個物件內進行事務方法互相呼叫的話預設會失效,因為它繞過了代理物件,相當於把我們 b 方法直接複製到了 a 方法中。

然後我使用的解決辦法就是:使用代理物件來呼叫事務方法。

1、引入 aop - starter 依賴

<!-- 引入aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

它裡面包含了 AspectJ 動態代理

2、然後使用 @EnableAspectJAutoProxy(exposeProxy = true) 註解開啟 AspectJ 動態代理功能。

開啟以後,所有的動態代理都是使用 AspectJ 建立而不是使用 JDK 的預設動態代理(即使沒有藉口也可以建立動態代理)。

3、本地事務之間的互相呼叫使用動態代理來呼叫即可:

public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
	@Transactional(timeout = 30)
	public void a() {
        // 使用動態代理呼叫本地其他事務
        OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
        orderService.b();
        orderService.c();
    }
    
    @Transactional(propagation=Propagation.REQUIRED_NEW)
    public void b() {
        // ....
    }
    
    @Transactional(propagation=Propagation.REQUIRED)
    public void c() {
        // ....
    }
}

相關文章