最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

1. 引言

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

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

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

2.1 示例代碼

@Service
public class OrderService {

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

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

在這個(gè)例子中:

placeOrder() 是一個(gè)業(yè)務(wù)方法,調(diào)用了 deductInventory()。

deductInventory() 被標(biāo)記為 @Transactional,期望在扣減庫存時(shí)開啟事務(wù)。

2.2 問題現(xiàn)象

當(dāng) placeOrder() 調(diào)用 deductInventory() 時(shí),事務(wù)并未生效。如果 deductInventory() 拋出異常,數(shù)據(jù)庫操作不會(huì)回滾。

3. 為什么事務(wù)自調(diào)用會(huì)失效

3.1 Spring事務(wù)的代理機(jī)制

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

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

3.2 自調(diào)用繞過代理

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

當(dāng) placeOrder() 調(diào)用 deductInventory() 時(shí),它使用的是 this(即當(dāng)前對象),而不是Spring生成的代理對象。

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

4. 解決方案

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

將事務(wù)方法移到另一個(gè)Service,確保調(diào)用通過代理進(jìn)行:

@Service
public class OrderService {

    @Autowired
    private InventoryService inventoryService;

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

@Service
public class InventoryService {

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

優(yōu)點(diǎn):

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

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

如果必須自調(diào)用,可以獲取當(dāng)前代理對象:

@Service
public class OrderService {

    public void placeOrder(Order order) {
        checkInventory(order);
        ((OrderService) AopContext.currentProxy()).deductInventory(order);  // 通過代理調(diào)用
        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);
    }
}

缺點(diǎn):

依賴Spring AOP機(jī)制,代碼侵入性強(qiáng)。

4.3 方法3:在調(diào)用方法上添加 @Transactional

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

@Service
public class OrderService {

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

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

適用場景:

整個(gè)方法需要事務(wù)管理,而不是單個(gè)操作。

4.4 方法4:使用編程式事務(wù)

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

@Service
public class OrderService {

    @Autowired
    private TransactionTemplate transactionTemplate;

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

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

優(yōu)點(diǎn):

更靈活,可以手動(dòng)控制事務(wù)邊界。

5. 最佳實(shí)踐

避免自調(diào)用:盡量將事務(wù)方法拆分到不同的類。

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

事務(wù)傳播機(jī)制:理解 @Transactional(propagation = Propagation.REQUIRED) 等選項(xiàng)。

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

6. 總結(jié)

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

關(guān)鍵結(jié)論:

  • Spring事務(wù)基于代理,自調(diào)用會(huì)繞過代理,導(dǎo)致事務(wù)失效。
  • 最佳方案是拆分事務(wù)方法到不同類,避免自調(diào)用問題。

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

相關(guān)文章

  • Java中的匿名內(nèi)部類小結(jié)

    Java中的匿名內(nèi)部類小結(jié)

    java內(nèi)部類分為: 成員內(nèi)部類、靜態(tài)嵌套類、方法內(nèi)部類、匿名內(nèi)部類。這篇文章主要介紹了Java中的匿名內(nèi)部類的相關(guān)資料,需要的朋友可以參考下
    2016-07-07
  • Java網(wǎng)絡(luò)編程基礎(chǔ)詳解

    Java網(wǎng)絡(luò)編程基礎(chǔ)詳解

    網(wǎng)絡(luò)編程是指編寫運(yùn)行在多個(gè)設(shè)備(計(jì)算機(jī))的程序,這些設(shè)備都通過網(wǎng)絡(luò)連接起來。本文介紹了一些網(wǎng)絡(luò)編程基礎(chǔ)的概念,并用Java來實(shí)現(xiàn)TCP和UDP的Socket的編程,來讓讀者更好的了解其原理
    2021-08-08
  • SpringBoot+mybatis+thymeleaf實(shí)現(xiàn)登錄功能示例

    SpringBoot+mybatis+thymeleaf實(shí)現(xiàn)登錄功能示例

    這篇文章主要介紹了SpringBoot+mybatis+thymeleaf實(shí)現(xiàn)登錄功能示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java中Swagger生成后端接口測試的詳細(xì)教程

    Java中Swagger生成后端接口測試的詳細(xì)教程

    Swagger是一套圍繞OpenAPI規(guī)范構(gòu)建的開源工具集,用于設(shè)計(jì)、構(gòu)建、文檔化和消費(fèi)RESTful Web服務(wù),供了一種標(biāo)準(zhǔn)化、語言無關(guān)的接口來描述REST API,本文給大家介紹了Java中Swagger生成后端接口測試的詳細(xì)教程,需要的朋友可以參考下
    2025-06-06
  • Java字符串拼接+和StringBuilder的比較與選擇

    Java字符串拼接+和StringBuilder的比較與選擇

    Java 提供了兩種主要的方式:使用 "+" 運(yùn)算符和使用 StringBuilder 類,本文主要介紹了Java字符串拼接+和StringBuilder的比較與選擇,感興趣的可以了解一下
    2023-10-10
  • Spring Bean初始化及銷毀多種實(shí)現(xiàn)方式

    Spring Bean初始化及銷毀多種實(shí)現(xiàn)方式

    這篇文章主要介紹了Spring Bean初始化及銷毀多種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java中的Cglib動(dòng)態(tài)代理詳細(xì)解讀

    Java中的Cglib動(dòng)態(tài)代理詳細(xì)解讀

    這篇文章主要介紹了Java中的Cglib動(dòng)態(tài)代理詳細(xì)解讀,CGLib是一個(gè)強(qiáng)大的、高性能、高質(zhì)量的 Code 生成類庫,它可以在運(yùn)行期擴(kuò)展 Java 類與實(shí)現(xiàn) Java 接口,需要的朋友可以參考下
    2023-11-11
  • 一文總結(jié)使用Java創(chuàng)建對象的八種方式

    一文總結(jié)使用Java創(chuàng)建對象的八種方式

    這篇文章主要介紹了8種創(chuàng)建對象的方法,包括new關(guān)鍵字、反射機(jī)制、clone()方法、反序列化、靜態(tài)工廠方法、Builder模式、匿名內(nèi)部類和方法引用,每種方法都有其適用場景和優(yōu)缺點(diǎn),需要的朋友可以參考下
    2026-01-01
  • Lock、Condition實(shí)現(xiàn)簡單的生產(chǎn)者消費(fèi)者模式示例

    Lock、Condition實(shí)現(xiàn)簡單的生產(chǎn)者消費(fèi)者模式示例

    這篇文章主要介紹了Lock、Condition實(shí)現(xiàn)簡單的生產(chǎn)者消費(fèi)者模式示例,需要的朋友可以參考下
    2014-04-04
  • Spring中的SpringApplicationRunListener詳細(xì)解析

    Spring中的SpringApplicationRunListener詳細(xì)解析

    這篇文章主要介紹了Spring中的SpringApplicationRunListener詳細(xì)解析,SpringApplicationRunListener是一個(gè)監(jiān)聽SpringApplication中run方法的接口,在項(xiàng)目啟動(dòng)過程的各個(gè)階段進(jìn)行事件的發(fā)布,需要的朋友可以參考下
    2023-11-11

最新評論

双鸭山市| 大荔县| 星子县| 三河市| 呼和浩特市| 张家川| 安化县| 靖远县| 禄丰县| 台安县| 习水县| 康马县| 梅河口市| 灵川县| 汾阳市| 顺昌县| 浦北县| 行唐县| 乌鲁木齐市| 驻马店市| 华坪县| 宜川县| 香格里拉县| 静安区| 诸暨市| 萝北县| 汪清县| 永昌县| 长寿区| 宜昌市| 临邑县| 民县| 镇江市| 五原县| 福海县| 澎湖县| 开江县| 固镇县| 大关县| 寿阳县| 普格县|