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

Spring事務(wù)傳播機(jī)制最佳實(shí)踐

 更新時(shí)間:2025年07月01日 11:31:41   作者:平凡的夢  
Spring的事務(wù)傳播機(jī)制為我們提供了優(yōu)雅的解決方案,本文將帶您深入理解這一機(jī)制,掌握不同場景下的最佳實(shí)踐,感興趣的朋友一起看看吧

?? 導(dǎo)讀:在復(fù)雜的企業(yè)應(yīng)用中,事務(wù)管理是保證數(shù)據(jù)一致性的關(guān)鍵。當(dāng)多個(gè)事務(wù)方法相互調(diào)用時(shí),如何確保它們協(xié)同工作?Spring的事務(wù)傳播機(jī)制為我們提供了優(yōu)雅的解決方案。本文將帶您深入理解這一機(jī)制,掌握不同場景下的最佳實(shí)踐。

1. ?? 什么是事務(wù)傳播行為

?? 想象一下:你在銀行APP上進(jìn)行轉(zhuǎn)賬,這個(gè)過程涉及「扣款」和「入賬」兩個(gè)操作。如果扣款成功但入賬失敗,你的錢就會(huì)憑空消失!這就是為什么我們需要事務(wù) - 確保這兩個(gè)操作要么都成功,要么都失敗。

在Spring管理的事務(wù)中,當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播。例如,方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行。Spring定義了七種不同的傳播行為,用于控制事務(wù)的傳播方式。

事務(wù)傳播行為是Spring框架事務(wù)管理的核心概念之一,它決定了事務(wù)方法和事務(wù)方法發(fā)生嵌套調(diào)用時(shí)事務(wù)如何進(jìn)行傳播。簡單來說,它回答了這個(gè)問題:當(dāng)一個(gè)事務(wù)方法調(diào)用另一個(gè)事務(wù)方法時(shí),會(huì)發(fā)生什么?

2. ?? Spring支持的七種事務(wù)傳播行為

?? 類比理解:如果把事務(wù)比作一場戲劇表演,傳播行為就是決定演員(方法)如何上場的規(guī)則 - 是加入已有的表演,還是開啟一個(gè)全新的獨(dú)立演出,或者干脆拒絕參與?

2.1 ?? REQUIRED(默認(rèn))

?? 生活類比:就像加入一個(gè)已經(jīng)開始的家庭聚會(huì),如果聚會(huì)已經(jīng)在進(jìn)行,你就加入;如果還沒開始,你就負(fù)責(zé)組織一個(gè)新的聚會(huì)。

Propagation.REQUIRED
  • 含義:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
  • 應(yīng)用場景:適用于絕大多數(shù)情況,是Spring默認(rèn)的傳播行為。
  • 特點(diǎn)
    • 當(dāng)A方法(REQUIRED)調(diào)用B方法(REQUIRED)時(shí),B方法會(huì)加入到A方法的事務(wù)中。
    • 如果B方法發(fā)生異常,A方法和B方法都會(huì)回滾。
    • 這是最常用的傳播行為,適合大多數(shù)業(yè)務(wù)場景。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 數(shù)據(jù)庫操作
    methodB();
    // 更多數(shù)據(jù)庫操作
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 數(shù)據(jù)庫操作
}

2.2 ?? SUPPORTS

?? 生活類比:就像一個(gè)隨和的朋友,別人組織活動(dòng)時(shí)會(huì)積極參與,沒人組織時(shí)也能獨(dú)自安靜地做自己的事。

Propagation.SUPPORTS
  • 含義:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)方式執(zhí)行。
  • 應(yīng)用場景:適用于可以在事務(wù)內(nèi)也可以在事務(wù)外執(zhí)行的方法,比如一些查詢方法。
  • 特點(diǎn)
    • 當(dāng)A方法(有事務(wù))調(diào)用B方法(SUPPORTS)時(shí),B方法會(huì)加入到A方法的事務(wù)中。
    • 當(dāng)A方法(無事務(wù))調(diào)用B方法(SUPPORTS)時(shí),B方法以非事務(wù)方式執(zhí)行。
    • 非常適合只讀操作,如數(shù)據(jù)查詢,提高性能。
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作
    methodB(); // B方法會(huì)在A的事務(wù)中執(zhí)行
}
// 非事務(wù)方法調(diào)用
public void methodC() {
    methodB(); // B方法會(huì)以非事務(wù)方式執(zhí)行
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // 數(shù)據(jù)庫操作
}

2.3 ?? MANDATORY

?? 生活類比:就像一個(gè)嚴(yán)格的團(tuán)隊(duì)成員,只愿意在有組織的團(tuán)隊(duì)活動(dòng)中參與,如果沒有團(tuán)隊(duì)活動(dòng),就會(huì)直接拒絕并抗議。

Propagation.MANDATORY
  • 含義:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
  • 應(yīng)用場景:適用于必須在事務(wù)中執(zhí)行的方法,確保方法在事務(wù)環(huán)境中被調(diào)用。
  • 特點(diǎn)
    • 強(qiáng)制要求外部調(diào)用者提供事務(wù)環(huán)境。
    • 如果沒有事務(wù)環(huán)境,則拋出IllegalTransactionStateException異常。
    • 用于確保關(guān)鍵業(yè)務(wù)操作必須在事務(wù)控制下執(zhí)行,提高安全性。
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作
    methodB(); // 正常執(zhí)行,B方法加入A的事務(wù)
}
// 非事務(wù)方法調(diào)用
public void methodC() {
    methodB(); // 拋出異常,因?yàn)闆]有事務(wù)環(huán)境
}
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // 數(shù)據(jù)庫操作
}

2.4 ?? REQUIRES_NEW

??? 生活類比:就像一個(gè)獨(dú)立的人,即使已經(jīng)在參加一個(gè)聚會(huì),也會(huì)暫時(shí)離開去做自己的事情,完成后再回到原來的聚會(huì)中。

Propagation.REQUIRES_NEW
  • 含義:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)。
  • 應(yīng)用場景:適用于需要獨(dú)立事務(wù)的方法,不受外部事務(wù)影響。
  • 特點(diǎn)
    • 總是啟動(dòng)一個(gè)新的事務(wù)。
    • 如果當(dāng)前存在事務(wù),則將當(dāng)前事務(wù)掛起。
    • 內(nèi)部事務(wù)與外部事務(wù)相互獨(dú)立,互不影響。
    • 適合記錄日志、發(fā)送通知等不應(yīng)受主事務(wù)影響的操作。
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作 - 事務(wù)A
    try {
        methodB(); // 執(zhí)行新事務(wù)B,事務(wù)A被掛起
    } catch (Exception e) {
        // 即使B事務(wù)回滾,A事務(wù)不受影響
    }
    // 繼續(xù)事務(wù)A的操作
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 數(shù)據(jù)庫操作 - 在新的事務(wù)B中
    // 如果這里拋出異常,只有事務(wù)B回滾,事務(wù)A不受影響
}

2.5 ?? NOT_SUPPORTED

?? 生活類比:就像一個(gè)需要安靜環(huán)境的讀書人,即使周圍有熱鬧的聚會(huì),也會(huì)找一個(gè)安靜的角落獨(dú)自閱讀,不受外界干擾。

Propagation.NOT_SUPPORTED
  • 含義:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)。
  • 應(yīng)用場景:適用于不需要事務(wù)的操作,特別是一些耗時(shí)的只讀操作。
  • 特點(diǎn)
    • 總是以非事務(wù)方式執(zhí)行。
    • 如果當(dāng)前存在事務(wù),則將當(dāng)前事務(wù)掛起。
    • 適合執(zhí)行耗時(shí)的查詢操作,避免長時(shí)間占用數(shù)據(jù)庫連接。
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作 - 事務(wù)A
    methodB(); // 調(diào)用B方法,事務(wù)A被掛起
    // 繼續(xù)事務(wù)A的操作
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
    // 數(shù)據(jù)庫操作 - 非事務(wù)執(zhí)行
    // 這里的操作不會(huì)影響事務(wù)A
}

2.6 ?? NEVER

??? 生活類比:就像一個(gè)堅(jiān)持獨(dú)處的隱士,不僅自己不參加任何社交活動(dòng),而且如果有人試圖把他拉進(jìn)社交圈,他會(huì)立刻表示強(qiáng)烈抗議。

Propagation.NEVER
  • 含義:以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • 應(yīng)用場景:適用于必須在非事務(wù)環(huán)境下執(zhí)行的操作。
  • 特點(diǎn)
    • 強(qiáng)制要求非事務(wù)環(huán)境。
    • 如果當(dāng)前存在事務(wù),則拋出IllegalTransactionStateException異常。
    • 用于確保某些操作絕對不在事務(wù)中執(zhí)行,如某些特殊的查詢或統(tǒng)計(jì)操作。
// 非事務(wù)方法調(diào)用
public void methodC() {
    methodB(); // 正常執(zhí)行,因?yàn)闆]有事務(wù)環(huán)境
}
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作
    methodB(); // 拋出異常,因?yàn)榇嬖谑聞?wù)環(huán)境
}
@Transactional(propagation = Propagation.NEVER)
public void methodB() {
    // 數(shù)據(jù)庫操作 - 要求非事務(wù)環(huán)境
}

2.7 ?? NESTED

?? 生活類比:就像俄羅斯套娃,在大娃娃中還有一個(gè)小娃娃。小娃娃可以獨(dú)立被取出或放回,但如果大娃娃被丟棄,小娃娃也會(huì)跟著一起消失。

Propagation.NESTED
  • 含義:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則等效于REQUIRED。
  • 應(yīng)用場景:適用于需要嵌套事務(wù)的場景,內(nèi)部事務(wù)的回滾不影響外部事務(wù)。
  • 特點(diǎn)
    • 使用保存點(diǎn)機(jī)制,可以回滾到保存點(diǎn)。
    • 內(nèi)部事務(wù)回滾不影響外部事務(wù)。
    • 外部事務(wù)回滾會(huì)導(dǎo)致內(nèi)部事務(wù)也回滾。
    • 依賴于特定的事務(wù)管理器實(shí)現(xiàn)(如JDBC DataSourceTransactionManager)。
    • 適合處理可以部分回滾的業(yè)務(wù)邏輯,如批量操作中允許部分失敗。
@Transactional
public void methodA() {
    // 數(shù)據(jù)庫操作 - 事務(wù)A
    try {
        methodB(); // 執(zhí)行嵌套事務(wù)B
    } catch (Exception e) {
        // 捕獲異常,事務(wù)B回滾,事務(wù)A可以繼續(xù)
    }
    // 繼續(xù)事務(wù)A的操作
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 數(shù)據(jù)庫操作 - 在嵌套事務(wù)B中
    // 如果這里拋出異常,只有事務(wù)B回滾到保存點(diǎn),事務(wù)A可以繼續(xù)
}

3. ?? 事務(wù)傳播行為對比表

?? 導(dǎo)航指南:面對這么多傳播行為,如何選擇最合適的一個(gè)?下面的對比表將幫助你快速了解各種傳播行為的特點(diǎn)和適用場景,就像一張地圖指引你在Spring事務(wù)的世界中找到正確的方向。

傳播行為當(dāng)前有事務(wù)當(dāng)前無事務(wù)是否創(chuàng)建新事務(wù)異常回滾影響適用場景
?? REQUIRED加入當(dāng)前事務(wù)創(chuàng)建新事務(wù)可能全部回滾默認(rèn)選擇,大多數(shù)業(yè)務(wù)場景
?? SUPPORTS加入當(dāng)前事務(wù)非事務(wù)執(zhí)行跟隨外部事務(wù)查詢操作,可選事務(wù)
?? MANDATORY加入當(dāng)前事務(wù)拋出異常全部回滾強(qiáng)制要求事務(wù)環(huán)境
?? REQUIRES_NEW掛起當(dāng)前事務(wù),創(chuàng)建新事務(wù)創(chuàng)建新事務(wù)獨(dú)立回滾獨(dú)立操作,如日志記錄
?? NOT_SUPPORTED掛起當(dāng)前事務(wù)非事務(wù)執(zhí)行不影響外部事務(wù)耗時(shí)的只讀操作
?? NEVER拋出異常非事務(wù)執(zhí)行不涉及事務(wù)必須非事務(wù)環(huán)境
?? NESTED創(chuàng)建嵌套事務(wù)等同REQUIRED嵌套可部分回滾批量處理,部分失敗場景

3.1 ?? 傳播行為決策流程圖

?? 思維導(dǎo)圖:下面的流程圖展示了Spring如何根據(jù)當(dāng)前事務(wù)環(huán)境和傳播行為做出決策。這就像一個(gè)交通指揮員,根據(jù)道路情況為每個(gè)車輛指定最合適的行駛路線。

?? 開始調(diào)用方法
    ↓
?? 檢查當(dāng)前是否存在事務(wù)?
    ↓               ↓
   是 ?            否 ?
    ↓               ↓
?? 根據(jù)傳播行為決定:    ?? 根據(jù)傳播行為決定:
- ?? REQUIRED: 加入     - ?? REQUIRED: 創(chuàng)建新事務(wù)
- ?? SUPPORTS: 加入     - ?? SUPPORTS: 非事務(wù)執(zhí)行
- ?? MANDATORY: 加入    - ?? MANDATORY: 拋異常
- ?? REQUIRES_NEW: 新建 - ?? REQUIRES_NEW: 創(chuàng)建新事務(wù)
- ?? NOT_SUPPORTED: 掛起- ?? NOT_SUPPORTED: 非事務(wù)執(zhí)行
- ?? NEVER: 拋異常      - ?? NEVER: 非事務(wù)執(zhí)行
- ?? NESTED: 嵌套事務(wù)   - ?? NESTED: 創(chuàng)建新事務(wù)

4. ?? 事務(wù)傳播行為的實(shí)際應(yīng)用場景

?? 實(shí)戰(zhàn)指南:理論知識已經(jīng)掌握,但如何在實(shí)際項(xiàng)目中應(yīng)用這些傳播行為?下面我們通過真實(shí)業(yè)務(wù)場景來展示各種傳播行為的最佳應(yīng)用方式,幫助你在實(shí)踐中做出正確的選擇。

4.1 ?? REQUIRED的應(yīng)用場景

?? 場景示例:想象一個(gè)電商平臺的訂單支付流程,需要同時(shí)更新訂單狀態(tài)、扣減庫存和生成支付記錄,這三個(gè)操作要么全部成功,要么全部失敗。

適用于大多數(shù)業(yè)務(wù)場景,特別是那些需要保證數(shù)據(jù)一致性的操作。例如:

  • 訂單創(chuàng)建和庫存更新必須在同一個(gè)事務(wù)中。
  • 用戶注冊時(shí),創(chuàng)建用戶信息和初始化用戶配置必須同時(shí)成功或失敗。

4.2 ?? REQUIRES_NEW的應(yīng)用場景

?? 場景示例:想象一個(gè)銀行轉(zhuǎn)賬系統(tǒng),無論轉(zhuǎn)賬是否成功,都需要記錄操作日志用于審計(jì)和監(jiān)管。即使轉(zhuǎn)賬失敗并回滾,審計(jì)日志也必須保留。

適用于需要獨(dú)立事務(wù)的場景,例如:

  • 記錄操作日志:即使主業(yè)務(wù)失敗,也希望保留操作日志。
  • 發(fā)送通知消息:即使主業(yè)務(wù)回滾,通知消息也應(yīng)該發(fā)送出去。
  • 異步任務(wù)觸發(fā):主流程完成某步驟后,需要觸發(fā)一個(gè)獨(dú)立的異步任務(wù)。
@Transactional
public void createOrder(Order order) {
    // 創(chuàng)建訂單
    orderRepository.save(order);
    // 記錄操作日志(使用獨(dú)立事務(wù))
    logService.recordLog("創(chuàng)建訂單: " + order.getId());
    // 如果這里發(fā)生異常,訂單創(chuàng)建會(huì)回滾,但日志記錄不會(huì)回滾
    updateInventory(order.getItems());
}
@Service
public class LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordLog(String message) {
        logRepository.save(new Log(message));
    }
}

4.3 ?? NESTED的應(yīng)用場景

?? 場景示例:想象一個(gè)批量導(dǎo)入系統(tǒng),需要處理成千上萬條記錄。如果要求全部成功或全部失敗,那么一條記錄的錯(cuò)誤就會(huì)導(dǎo)致整個(gè)批處理失敗,效率極低。使用嵌套事務(wù),可以讓每條記錄單獨(dú)處理,出錯(cuò)的記錄回滾不影響其他記錄。

適用于可以部分回滾的場景,例如:

  • 批量操作中,允許部分成功部分失敗。
  • 多步驟操作,后續(xù)步驟失敗不影響前面步驟的結(jié)果。
  • 復(fù)雜業(yè)務(wù)流程中的可選子流程,允許子流程失敗而不影響主流程。
@Transactional
public void processBatch(List<Item> items) {
    for (Item item : items) {
        try {
            processItem(item);
        } catch (Exception e) {
            // 記錄錯(cuò)誤,繼續(xù)處理下一個(gè)
            logError(item, e);
        }
    }
}
@Transactional(propagation = Propagation.NESTED)
private void processItem(Item item) {
    // 處理單個(gè)項(xiàng)目
    // 如果這里拋出異常,只會(huì)回滾這個(gè)項(xiàng)目的處理
}

5. ?? 事務(wù)傳播行為與隔離級別的關(guān)系

?? 雙重保障:如果說事務(wù)傳播行為決定了"事務(wù)的邊界",那么隔離級別則決定了"事務(wù)的質(zhì)量"。就像一個(gè)安全系統(tǒng),傳播行為決定誰能進(jìn)入安全區(qū)域,而隔離級別決定進(jìn)入后的安全等級。

事務(wù)傳播行為定義了事務(wù)的邊界和傳播方式,而隔離級別定義了事務(wù)之間的隔離程度。兩者結(jié)合使用,可以更好地控制事務(wù)的行為。

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED
)
public void someMethod() {
    // 方法體
}

6. ?? 事務(wù)傳播行為的注意事項(xiàng)

?? 避坑指南:即使你已經(jīng)掌握了Spring事務(wù)的基本概念,在實(shí)際應(yīng)用中仍然可能遇到一些意想不到的問題。以下是一些常見的陷阱和注意事項(xiàng),幫助你在開發(fā)中避免這些潛在的問題。

6.1 ?? 自調(diào)用問題

?? 問題類比:就像你對著鏡子喊話,聲音不會(huì)傳得更遠(yuǎn)。同樣,在同一個(gè)類中直接調(diào)用自己的方法,Spring的事務(wù)魔法也無法生效。

Spring事務(wù)是通過AOP實(shí)現(xiàn)的,當(dāng)一個(gè)事務(wù)方法在同一個(gè)類中調(diào)用另一個(gè)事務(wù)方法時(shí),事務(wù)傳播行為可能不會(huì)生效。這是因?yàn)榉椒ㄕ{(diào)用沒有經(jīng)過代理對象。

@Service
public class UserService {
    @Transactional
    public void createUser(User user) {
        // 保存用戶
        saveUser(user);
        // 問題:這里直接調(diào)用同類中的方法,updateUserStats的事務(wù)設(shè)置不會(huì)生效
        updateUserStats();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserStats() {
        // 更新統(tǒng)計(jì)信息
    }
}

解決方案

  1. 使用自注入:
@Service
public class UserService {
    @Autowired
    private UserService self;
    @Transactional
    public void createUser(User user) {
        // 保存用戶
        saveUser(user);
        // 通過自注入的代理對象調(diào)用,事務(wù)設(shè)置會(huì)生效
        self.updateUserStats();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserStats() {
        // 更新統(tǒng)計(jì)信息
    }
}
  1. 將方法移到另一個(gè)服務(wù)類中:
@Service
public class UserService {
    @Autowired
    private StatsService statsService;
    @Transactional
    public void createUser(User user) {
        // 保存用戶
        saveUser(user);
        // 通過另一個(gè)服務(wù)調(diào)用,事務(wù)設(shè)置會(huì)生效
        statsService.updateUserStats();
    }
}
@Service
public class StatsService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserStats() {
        // 更新統(tǒng)計(jì)信息
    }
}

6.2 ?? 事務(wù)超時(shí)設(shè)置

? 時(shí)間管理:就像給會(huì)議設(shè)置時(shí)間限制一樣,事務(wù)也需要有超時(shí)機(jī)制,避免長時(shí)間運(yùn)行的事務(wù)占用過多資源,影響系統(tǒng)整體性能。

在使用REQUIRES_NEW或NESTED等傳播行為時(shí),可以為不同的事務(wù)設(shè)置不同的超時(shí)時(shí)間。

@Transactional(timeout = 30) // 30秒超時(shí)
public void methodA() {
    // 長時(shí)間操作
    methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 5) // 5秒超時(shí)
public void methodB() {
    // 短時(shí)間操作
}

6.3 ?? 只讀事務(wù)

?? 性能提升:就像圖書館的閱覽室只允許閱讀不允許修改書籍,只讀事務(wù)告訴數(shù)據(jù)庫"我只是來看看,不會(huì)做任何修改",從而讓數(shù)據(jù)庫可以優(yōu)化處理方式。

對于只讀操作,可以將事務(wù)設(shè)置為只讀,這樣可以優(yōu)化性能。

@Transactional(readOnly = true)
public List<User> getAllUsers() {
    return userRepository.findAll();
}

6.4 ?? 事務(wù)管理器的選擇

??? 工具選擇:就像不同的工作需要不同的工具,不同的數(shù)據(jù)訪問技術(shù)也需要匹配相應(yīng)的事務(wù)管理器。選擇合適的事務(wù)管理器就像選擇合適的工具,能讓你的工作事半功倍。

不同的事務(wù)傳播行為可能需要不同的事務(wù)管理器支持。例如,NESTED傳播行為需要使用支持保存點(diǎn)的事務(wù)管理器,如JDBC的DataSourceTransactionManager。

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

7. ? 性能優(yōu)化建議

?? 性能加速:事務(wù)雖然保證了數(shù)據(jù)的一致性,但不恰當(dāng)?shù)氖褂每赡軐?dǎo)致性能問題。就像賽車手需要在彎道減速直道加速一樣,我們也需要在不同場景下調(diào)整事務(wù)策略,以獲得最佳性能。

7.1 ?? 合理選擇傳播行為

// 優(yōu)化前:所有方法都使用REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public List<User> queryUsers() {
    return userRepository.findAll(); // 只讀操作不需要事務(wù)
}
// 優(yōu)化后:只讀操作使用SUPPORTS或設(shè)置readOnly
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<User> queryUsers() {
    return userRepository.findAll();
}

7.2 ?? 避免長事務(wù)

? 性能瓶頸:長事務(wù)就像占用跑道太久的飛機(jī),會(huì)阻塞其他飛機(jī)的起降。在高并發(fā)系統(tǒng)中,長事務(wù)會(huì)導(dǎo)致數(shù)據(jù)庫連接被長時(shí)間占用,鎖定資源,降低系統(tǒng)吞吐量。

// 避免:長時(shí)間的事務(wù)
@Transactional
public void processLargeDataSet() {
    List<Data> dataList = dataRepository.findAll(); // 可能很大的數(shù)據(jù)集
    for (Data data : dataList) {
        // 復(fù)雜的業(yè)務(wù)邏輯處理
        complexBusinessLogic(data);
        dataRepository.save(data);
    }
}
// 推薦:分批處理
@Transactional
public void processLargeDataSetInBatches() {
    int pageSize = 100;
    int pageNumber = 0;
    Page<Data> dataPage;
    do {
        dataPage = dataRepository.findAll(PageRequest.of(pageNumber, pageSize));
        processBatch(dataPage.getContent());
        pageNumber++;
    } while (dataPage.hasNext());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void processBatch(List<Data> batch) {
    for (Data data : batch) {
        complexBusinessLogic(data);
        dataRepository.save(data);
    }
}

7.3 ?? 事務(wù)邊界優(yōu)化

?? 精準(zhǔn)控制:事務(wù)邊界就像圍欄,應(yīng)該只圍住真正需要事務(wù)保護(hù)的核心業(yè)務(wù)邏輯。將非核心操作(如發(fā)送郵件、記錄日志)移出事務(wù)邊界,可以顯著提高系統(tǒng)性能。

// 優(yōu)化前:事務(wù)邊界過大
@Transactional
public void createUserWithNotification(User user) {
    userRepository.save(user);
    // 發(fā)送郵件通知(耗時(shí)操作)
    emailService.sendWelcomeEmail(user.getEmail());
    // 記錄日志
    logService.recordUserCreation(user.getId());
}
// 優(yōu)化后:縮小事務(wù)邊界
@Transactional
public void createUser(User user) {
    userRepository.save(user);
}
public void createUserWithNotification(User user) {
    createUser(user); // 核心業(yè)務(wù)在事務(wù)中
    // 異步發(fā)送郵件
    asyncEmailService.sendWelcomeEmail(user.getEmail());
    // 獨(dú)立事務(wù)記錄日志
    logService.recordUserCreation(user.getId());
}

8. ?? 常見問題與故障排查

?? 排障指南:即使你按照最佳實(shí)踐編寫代碼,有時(shí)事務(wù)仍然可能不按預(yù)期工作。以下是一些常見問題及其解決方案,幫助你快速定位和修復(fù)事務(wù)相關(guān)的問題。

8.1 ?? 事務(wù)不生效的常見原因

?? 問題診斷:當(dāng)你發(fā)現(xiàn)事務(wù)沒有按預(yù)期回滾或提交時(shí),可以從以下幾個(gè)常見原因入手排查。

問題1:方法不是public
// 錯(cuò)誤:private方法上的@Transactional不會(huì)生效
@Transactional
private void saveUser(User user) {
    userRepository.save(user);
}
// 正確:使用public方法
@Transactional
public void saveUser(User user) {
    userRepository.save(user);
}
問題2:自調(diào)用問題
// 問題代碼
@Service
public class UserService {
    @Transactional
    public void createUser(User user) {
        saveUser(user);
        updateUserStats(); // 這里的事務(wù)設(shè)置不會(huì)生效
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserStats() {
        // 統(tǒng)計(jì)更新
    }
}
問題3:異常被捕獲
// 錯(cuò)誤:捕獲了異常,事務(wù)不會(huì)回滾
@Transactional
public void riskyOperation() {
    try {
        // 可能拋出異常的操作
        dangerousMethod();
    } catch (Exception e) {
        // 異常被捕獲,事務(wù)不會(huì)回滾
        log.error("操作失敗", e);
    }
}
// 正確:重新拋出異常或手動(dòng)回滾
@Transactional
public void riskyOperation() {
    try {
        dangerousMethod();
    } catch (Exception e) {
        log.error("操作失敗", e);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        throw e; // 重新拋出異常
    }
}

8.2 ? 性能問題排查

?? 性能診斷:事務(wù)執(zhí)行緩慢可能導(dǎo)致系統(tǒng)響應(yīng)遲鈍,用戶體驗(yàn)下降。就像醫(yī)生需要監(jiān)測病人的體溫和心率一樣,我們也需要監(jiān)控事務(wù)的執(zhí)行時(shí)間,及時(shí)發(fā)現(xiàn)性能瓶頸。

監(jiān)控事務(wù)執(zhí)行時(shí)間
@Component
@Aspect
public class TransactionMonitorAspect {
    private static final Logger logger = LoggerFactory.getLogger(TransactionMonitorAspect.class);
    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object monitorTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        try {
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - startTime;
            if (executionTime > 1000) { // 超過1秒的事務(wù)記錄警告
                logger.warn("長事務(wù)檢測: 方法 {} 執(zhí)行時(shí)間: {}ms", methodName, executionTime);
            }
            return result;
        } catch (Exception e) {
            long executionTime = System.currentTimeMillis() - startTime;
            logger.error("事務(wù)執(zhí)行失敗: 方法 {} 執(zhí)行時(shí)間: {}ms", methodName, executionTime, e);
            throw e;
        }
    }
}

8.3 ?? 死鎖問題排查

?? 交通堵塞:死鎖就像兩輛車在狹窄的道路上相向而行,誰都不愿意讓步,最終導(dǎo)致雙方都無法前進(jìn)。在數(shù)據(jù)庫事務(wù)中,當(dāng)多個(gè)事務(wù)互相等待對方釋放鎖時(shí),就會(huì)發(fā)生死鎖。

// 容易產(chǎn)生死鎖的代碼
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    Account fromAccount = accountRepository.findById(fromAccountId);
    Account toAccount = accountRepository.findById(toAccountId);
    fromAccount.withdraw(amount);
    toAccount.deposit(amount);
    accountRepository.save(fromAccount);
    accountRepository.save(toAccount);
}
// 避免死鎖的改進(jìn)版本
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    // 按ID排序獲取鎖,避免死鎖
    Long firstId = Math.min(fromAccountId, toAccountId);
    Long secondId = Math.max(fromAccountId, toAccountId);
    Account firstAccount = accountRepository.findByIdForUpdate(firstId);
    Account secondAccount = accountRepository.findByIdForUpdate(secondId);
    Account fromAccount = fromAccountId.equals(firstId) ? firstAccount : secondAccount;
    Account toAccount = toAccountId.equals(firstId) ? firstAccount : secondAccount;
    fromAccount.withdraw(amount);
    toAccount.deposit(amount);
    accountRepository.save(fromAccount);
    accountRepository.save(toAccount);
}

9. ?? 最佳實(shí)踐

?? 實(shí)踐精華:經(jīng)過多年的項(xiàng)目實(shí)踐和經(jīng)驗(yàn)總結(jié),以下是使用Spring事務(wù)的最佳實(shí)踐,幫助你避開常見陷阱,寫出高質(zhì)量、可維護(hù)的事務(wù)代碼。

9.1 ?? 事務(wù)注解使用規(guī)范

?? 規(guī)范指引:就像良好的代碼風(fēng)格可以提高可讀性一樣,統(tǒng)一的事務(wù)注解使用規(guī)范可以讓團(tuán)隊(duì)成員更容易理解和維護(hù)事務(wù)代碼。

// 推薦:在Service層使用事務(wù)
@Service
@Transactional(readOnly = true) // 類級別設(shè)置默認(rèn)只讀
public class UserService {
    @Transactional // 寫操作覆蓋類級別設(shè)置
    public void createUser(User user) {
        userRepository.save(user);
    }
    // 繼承類級別的只讀事務(wù)
    public List<User> findAllUsers() {
        return userRepository.findAll();
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void auditLog(String operation, String details) {
        auditRepository.save(new AuditLog(operation, details));
    }
}

9.2 ??? 異常處理最佳實(shí)踐

?? 安全防護(hù):良好的異常處理就像消防系統(tǒng),可以在問題發(fā)生時(shí)將損失降到最低。在事務(wù)中,正確處理異常不僅關(guān)系到數(shù)據(jù)一致性,還影響著系統(tǒng)的健壯性和用戶體驗(yàn)。

@Service
public class OrderService {
    @Transactional(rollbackFor = Exception.class) // 所有異常都回滾
    public void createOrder(Order order) throws OrderException {
        try {
            validateOrder(order);
            orderRepository.save(order);
            updateInventory(order.getItems());
        } catch (ValidationException e) {
            // 業(yè)務(wù)異常,記錄日志但不回滾
            logService.recordValidationError(order.getId(), e.getMessage());
            throw new OrderException("訂單驗(yàn)證失敗", e);
        } catch (InventoryException e) {
            // 庫存異常,需要回滾
            throw new OrderException("庫存不足", e);
        }
    }
    @Transactional(noRollbackFor = ValidationException.class)
    public void processOrderWithPartialFailure(Order order) {
        // 某些業(yè)務(wù)異常不需要回滾
    }
}

9.3 ?? 配置最佳實(shí)踐

?? 精細(xì)調(diào)優(yōu):就像調(diào)整汽車引擎以獲得最佳性能一樣,合理配置事務(wù)管理器可以讓你的應(yīng)用在不同場景下都能高效運(yùn)行。以下是一些常用的配置技巧。

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
        // 設(shè)置默認(rèn)超時(shí)時(shí)間
        manager.setDefaultTimeout(30);
        // 設(shè)置事務(wù)同步
        manager.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);
        return manager;
    }
    // 自定義事務(wù)屬性
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        TransactionTemplate template = new TransactionTemplate(transactionManager);
        template.setTimeout(30);
        template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        return template;
    }
}

9.4 ?? 編程式事務(wù)使用

?? 手動(dòng)控制:有時(shí)聲明式事務(wù)就像自動(dòng)駕駛,雖然方便但不夠靈活。編程式事務(wù)則像手動(dòng)擋汽車,讓你能夠在特定場景下精確控制事務(wù)的每個(gè)環(huán)節(jié),實(shí)現(xiàn)更復(fù)雜的業(yè)務(wù)邏輯。

@Service
public class ComplexBusinessService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    public void complexBusinessLogic() {
        // 需要精確控制事務(wù)邊界的場景
        String result = transactionTemplate.execute(status -> {
            try {
                // 事務(wù)內(nèi)的操作
                performDatabaseOperations();
                return "success";
            } catch (Exception e) {
                // 手動(dòng)回滾
                status.setRollbackOnly();
                return "failed";
            }
        });
        // 事務(wù)外的操作
        sendNotification(result);
    }
}

10. ?? 總結(jié)

?? 知識拼圖:恭喜你完成了Spring事務(wù)傳播機(jī)制的學(xué)習(xí)之旅!就像拼圖一樣,我們已經(jīng)將各個(gè)知識點(diǎn)組合成一幅完整的圖景。讓我們回顧一下這個(gè)旅程中的重要收獲。

Spring事務(wù)傳播機(jī)制是Spring事務(wù)管理的核心概念,通過合理使用不同的傳播行為,可以靈活控制事務(wù)的邊界和行為,滿足不同的業(yè)務(wù)需求。

?? 關(guān)鍵要點(diǎn)回顧:

?? 備忘錄:以下是你需要牢記的核心知識點(diǎn),它們將幫助你在實(shí)際項(xiàng)目中正確應(yīng)用事務(wù)傳播機(jī)制。

  1. 傳播行為選擇

    • REQUIRED:適用于大多數(shù)需要事務(wù)的場景
    • REQUIRES_NEW:適用于需要獨(dú)立事務(wù)的場景
    • NESTED:適用于需要嵌套事務(wù)的場景
    • SUPPORTS:適用于可選事務(wù)的查詢操作
    • MANDATORY:適用于必須在事務(wù)中執(zhí)行的方法
    • NOT_SUPPORTED:適用于不需要事務(wù)的操作
    • NEVER:適用于必須在非事務(wù)環(huán)境下執(zhí)行的操作
  2. 性能優(yōu)化

    • 合理設(shè)置事務(wù)邊界,避免長事務(wù)
    • 只讀操作使用readOnly=true
    • 分批處理大數(shù)據(jù)集
    • 異步處理非核心業(yè)務(wù)
  3. 常見陷阱

    • 避免自調(diào)用問題
    • 正確處理異常和回滾
    • 注意方法訪問修飾符
    • 防止死鎖問題
  4. 最佳實(shí)踐

    • 在Service層使用事務(wù)注解
    • 合理配置事務(wù)管理器
    • 監(jiān)控事務(wù)性能
    • 編程式事務(wù)用于復(fù)雜場景

理解和正確使用事務(wù)傳播機(jī)制,對于保證應(yīng)用程序的數(shù)據(jù)一致性、可靠性和性能至關(guān)重要。在實(shí)際開發(fā)中,應(yīng)該根據(jù)具體的業(yè)務(wù)場景選擇合適的傳播行為,并結(jié)合性能監(jiān)控和故障排查,確保事務(wù)管理的有效性。

到此這篇關(guān)于Spring事務(wù)傳播機(jī)制詳解的文章就介紹到這了,更多相關(guān)Spring事務(wù)傳播機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis?Mapper?接口是如何找到并執(zhí)行SQL的

    MyBatis?Mapper?接口是如何找到并執(zhí)行SQL的

    本文將為你揭開MyBatis這層神秘的面紗,深入剖析其接口與SQL的映射原理,并總結(jié)在Spring?Boot環(huán)境下的最佳實(shí)踐和常見陷阱,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • jvm中指定時(shí)區(qū)信息user.timezone問題及解決方式

    jvm中指定時(shí)區(qū)信息user.timezone問題及解決方式

    同一份程序使用時(shí)間LocalDateTime類型,在國內(nèi)和國外部署后,返回的時(shí)間信息前端使用出問題,這篇文章主要介紹了jvm中指定時(shí)區(qū)信息user.timezone問題及解決方法,需要的朋友可以參考下
    2023-02-02
  • 舉例講解Java的JSON類庫GSON的基本用法

    舉例講解Java的JSON類庫GSON的基本用法

    GSON是谷歌在GitHub上開源的Java的JSON格式轉(zhuǎn)換類庫,能夠?qū)崿F(xiàn)Java對象向JSON的序列和反序列,這里我們就來舉例講解Java的JSON類庫GSON的基本用法:
    2016-06-06
  • Druid基本配置及內(nèi)置監(jiān)控使用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Druid基本配置及內(nèi)置監(jiān)控使用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Druid基本配置及內(nèi)置監(jiān)控使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • SpringBoot雪花算法主鍵ID傳到前端后精度丟失問題的解決

    SpringBoot雪花算法主鍵ID傳到前端后精度丟失問題的解決

    本文主要介紹了SpringBoot雪花算法主鍵ID傳到前端后精度丟失問題的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • MyBatis中SqlSession生命周期的使用

    MyBatis中SqlSession生命周期的使用

    SqlSession是MyBatis的核心接口之一,本文主要介紹了MyBatis中SqlSession生命周期的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-09-09
  • Spring注解@Value在controller無法獲取到值的解決

    Spring注解@Value在controller無法獲取到值的解決

    這篇文章主要介紹了Spring注解@Value在controller無法獲取到值的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 常用java正則表達(dá)式的工具類

    常用java正則表達(dá)式的工具類

    這篇文章主要為大家詳細(xì)介紹了常用java正則表達(dá)式的工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • SpringBoot與knife4j的整合使用過程

    SpringBoot與knife4j的整合使用過程

    Knife4j?是一個(gè)基于Swagger構(gòu)建的開源?JavaAPI文檔工具,主要包括兩大核心功能:文檔說明和在線調(diào)試,這篇文章主要介紹了SpringBoot與knife4j的整合使用,需要的朋友可以參考下
    2024-08-08
  • Spring JPA聯(lián)表查詢之OneToOne源碼詳解

    Spring JPA聯(lián)表查詢之OneToOne源碼詳解

    這篇文章主要為大家介紹了Spring JPA聯(lián)表查詢之OneToOne源碼詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評論

阳江市| 谢通门县| 信丰县| 玉树县| 敦化市| 渝中区| 海安县| 介休市| 大余县| 曲周县| 石柱| 怀远县| 江源县| 桂阳县| 永春县| 云龙县| 西丰县| 松溪县| 禄劝| 漠河县| 潢川县| 饶平县| 天气| 桦甸市| 贵州省| 剑河县| 宜宾市| 商丘市| 都匀市| 揭阳市| 渑池县| 海丰县| 郴州市| 宁明县| 朝阳区| 京山县| 五原县| 当阳市| 南皮县| 宁波市| 西昌市|