欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring事務中@Transactional注解不生效的原因分析與解決

 更新時間:2025年03月31日 10:12:53   作者:碼農(nóng)阿豪@新空間  
在Spring框架中,@Transactional注解是管理數(shù)據(jù)庫事務的核心方式,本文將深入分析事務自調用的底層原理,解釋為什么事務不生效,并提供多種解決方案,希望對大家有所幫助

1. 引言

在Spring框架中,@Transactional注解是管理數(shù)據(jù)庫事務的核心方式。然而,許多開發(fā)者在使用時會遇到一個常見問題:在同一個類中,一個方法調用另一個帶有@Transactional注解的方法時,事務并未生效。這種現(xiàn)象被稱為事務自調用失效。

本文將深入分析事務自調用的底層原理,解釋為什么事務不生效,并提供多種解決方案,幫助開發(fā)者正確使用Spring事務管理。

2. 事務自調用問題重現(xiàn)

2.1 示例代碼

@Service
public class OrderService {

    public void placeOrder(Order order) {
        checkInventory(order);       // 檢查庫存(非事務)
        deductInventory(order);      // 扣減庫存(事務方法)
        createOrder(order);          // 創(chuàng)建訂單(非事務)
    }

    @Transactional
    public void deductInventory(Order order) {
        inventoryRepository.reduceStock(order.getProductId(), order.getQuantity());
    }
}

在這個例子中:

placeOrder() 是一個業(yè)務方法,調用了 deductInventory()。

deductInventory() 被標記為 @Transactional,期望在扣減庫存時開啟事務。

2.2 問題現(xiàn)象

當 placeOrder() 調用 deductInventory() 時,事務并未生效。如果 deductInventory() 拋出異常,數(shù)據(jù)庫操作不會回滾。

3. 為什么事務自調用會失效

3.1 Spring事務的代理機制

Spring的事務管理是基于AOP(面向切面編程)實現(xiàn)的,具體來說:

  • 代理模式:Spring會為帶有@Transactional的類生成一個代理對象(JDK動態(tài)代理或CGLIB代理)。
  • 攔截器:代理對象會在目標方法執(zhí)行前后添加事務管理邏輯(如開啟事務、提交或回滾)。

3.2 自調用繞過代理

public void placeOrder(Order order) {
    this.deductInventory(order);  // 直接調用,不走代理
}

當 placeOrder() 調用 deductInventory() 時,它使用的是 this(即當前對象),而不是Spring生成的代理對象。

因此,事務攔截器沒有被觸發(fā),事務自然也不會生效。

4. 解決方案

4.1 方法1:拆分到不同類(推薦)

將事務方法移到另一個Service,確保調用通過代理進行:

@Service
public class OrderService {

    @Autowired
    private InventoryService inventoryService;

    public void placeOrder(Order order) {
        checkInventory(order);
        inventoryService.deductInventory(order);  // 通過代理調用
        createOrder(order);
    }
}

@Service
public class InventoryService {

    @Transactional
    public void deductInventory(Order order) {
        inventoryRepository.reduceStock(order.getProductId(), order.getQuantity());
    }
}

優(yōu)點:

  • 符合單一職責原則(SRP)。
  • 事務管理清晰,不會出現(xiàn)自調用問題。

4.2 方法2:使用 AopContext.currentProxy()

如果必須自調用,可以獲取當前代理對象:

@Service
public class OrderService {

    public void placeOrder(Order order) {
        checkInventory(order);
        ((OrderService) AopContext.currentProxy()).deductInventory(order);  // 通過代理調用
        createOrder(order);
    }

    @Transactional
    public void deductInventory(Order order) {
        inventoryRepository.reduceStock(order.getProductId(), order.getQuantity());
    }
}

注意:

需要開啟 @EnableAspectJAutoProxy(exposeProxy = true):

@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

缺點:

依賴Spring AOP機制,代碼侵入性強。

4.3 方法3:在調用方法上添加 @Transactional

如果整個流程需要事務管理,可以直接在 placeOrder() 上添加 @Transactional:

@Service
public class OrderService {

    @Transactional
    public void placeOrder(Order order) {
        checkInventory(order);
        deductInventory(order);  // 即使自調用,外層事務仍生效
        createOrder(order);
    }

    public void deductInventory(Order order) {
        inventoryRepository.reduceStock(order.getProductId(), order.getQuantity());
    }
}

適用場景:

整個方法需要事務管理,而不是單個操作。

4.4 方法4:使用編程式事務

如果無法拆分類或修改代理設置,可以使用 TransactionTemplate:

@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void placeOrder(Order order) {
        checkInventory(order);
        transactionTemplate.execute(status -> {
            deductInventory(order);  // 在事務內(nèi)執(zhí)行
            return null;
        });
        createOrder(order);
    }

    public void deductInventory(Order order) {
        inventoryRepository.reduceStock(order.getProductId(), order.getQuantity());
    }
}

優(yōu)點:

更靈活,可以手動控制事務邊界。

5. 最佳實踐

避免自調用:盡量將事務方法拆分到不同的類。

合理使用事務:不要在事務方法中執(zhí)行耗時操作(如HTTP請求、IO操作)。

事務傳播機制:理解 @Transactional(propagation = Propagation.REQUIRED) 等選項。

異常處理:確保異常能觸發(fā)回滾(默認僅回滾 RuntimeException)。

6. 總結

方案適用場景優(yōu)點缺點
拆分到不同類推薦方案符合SRP,事務清晰需要額外類
AopContext.currentProxy()必須自調用時可解決自調用問題侵入性強
外層方法加 @Transactional整個流程需要事務簡單直接事務范圍擴大
編程式事務需要精細控制靈活代碼冗余

關鍵結論:

  • Spring事務基于代理,自調用會繞過代理,導致事務失效。
  • 最佳方案是拆分事務方法到不同類,避免自調用問題。

到此這篇關于Spring事務中@Transactional注解不生效的原因分析與解決的文章就介紹到這了,更多相關Spring @Transactional注解不生效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • springcloud連接遠程nacos失敗顯示localhost服務連接失敗的問題解決

    springcloud連接遠程nacos失敗顯示localhost服務連接失敗的問題解決

    這篇文章主要介紹了springcloud連接遠程nacos失敗顯示localhost服務連接失敗的問題解決,文中有詳細的代碼示例供大家參考,對大家解決問題有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • Spring Security登錄接口兼容JSON格式登錄實現(xiàn)示例

    Spring Security登錄接口兼容JSON格式登錄實現(xiàn)示例

    前后端分離中,前端和后端的數(shù)據(jù)交互通常是JSON格式,本文主要介紹了Spring Security登錄接口兼容JSON格式登錄實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • SpringBoot實現(xiàn)連接nacos并支持多環(huán)境部署

    SpringBoot實現(xiàn)連接nacos并支持多環(huán)境部署

    這篇文章主要介紹了SpringBoot實現(xiàn)連接nacos并支持多環(huán)境部署方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Spring Security 使用 OncePerRequestFilter 過濾器校驗登錄過期、請求日志等操作

    Spring Security 使用 OncePerRequestFilter 

    OncePerRequestFilter是一個過濾器,每個請求都會執(zhí)行一次;一般開發(fā)中主要是做檢查是否已登錄、Token是否過期和授權等操作,而每個操作都是一個過濾器,下面介紹Spring Security 使用 OncePerRequestFilter 過濾器校驗登錄過期、請求日志等操作方法,感興趣的朋友一起看看吧
    2024-06-06
  • SpringBoot接收前端參數(shù)的幾種常用方式

    SpringBoot接收前端參數(shù)的幾種常用方式

    在Spring Boot開發(fā)中接收參數(shù)是非常常見且重要的一部分,依賴于請求的不同場景,Spring Boot提供了多種方式來處理和接收參數(shù),這篇文章主要給大家介紹了關于SpringBoot接收前端參數(shù)的幾種常用方式,需要的朋友可以參考下
    2024-07-07
  • java編程兩種樹形菜單結構的轉換代碼

    java編程兩種樹形菜單結構的轉換代碼

    這篇文章主要介紹了java編程兩種樹形菜單結構的轉換代碼,首先介紹了兩種樹形菜單結構的代碼,然后展示了轉換器實例代碼,最后分享了相關實例及結果演示,具有一定借鑒價值,需要的朋友可以了解下。
    2017-12-12
  • springboot項目啟動類錯誤(找不到或無法加載主類 com.**Application)

    springboot項目啟動類錯誤(找不到或無法加載主類 com.**Application)

    本文主要介紹了spring-boot項目啟動類錯誤(找不到或無法加載主類 com.**Application),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-05-05
  • SpringBoot?MCP?入門使用步驟詳解

    SpringBoot?MCP?入門使用步驟詳解

    這篇文章主要介紹了SpringBoot?MCP?入門使用,本文分步驟給大家介紹的非常詳細,需要的朋友可以參考下
    2015-09-09
  • Eclipse下使用ANT編譯提示OutOfMemory的解決方法

    Eclipse下使用ANT編譯提示OutOfMemory的解決方法

    由于需要使用ANT編譯的代碼比較多,特別是在第一次變異的時候,會出現(xiàn)OutOfMemory錯誤。并提示更改ANT_OPTS設定。
    2009-04-04
  • 一文詳解Java中的反射與new創(chuàng)建對象

    一文詳解Java中的反射與new創(chuàng)建對象

    Java中的反射(Reflection)和使用new關鍵字創(chuàng)建對象是兩種不同的對象創(chuàng)建方式,各有優(yōu)缺點和適用場景,本文小編給大家詳細介紹了Java中的反射與new創(chuàng)建對象,感興趣的小伙伴跟著小編一起來看看吧
    2024-07-07

最新評論