亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring事務傳播機制最佳實踐

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

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

1. ?? 什么是事務傳播行為

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

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

事務傳播行為是Spring框架事務管理的核心概念之一,它決定了事務方法和事務方法發(fā)生嵌套調用時事務如何進行傳播。簡單來說,它回答了這個問題:當一個事務方法調用另一個事務方法時,會發(fā)生什么?

2. ?? Spring支持的七種事務傳播行為

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

2.1 ?? REQUIRED(默認)

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

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

2.2 ?? SUPPORTS

?? 生活類比:就像一個隨和的朋友,別人組織活動時會積極參與,沒人組織時也能獨自安靜地做自己的事。

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

2.3 ?? MANDATORY

?? 生活類比:就像一個嚴格的團隊成員,只愿意在有組織的團隊活動中參與,如果沒有團隊活動,就會直接拒絕并抗議。

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

2.4 ?? REQUIRES_NEW

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

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

2.5 ?? NOT_SUPPORTED

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

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

2.6 ?? NEVER

??? 生活類比:就像一個堅持獨處的隱士,不僅自己不參加任何社交活動,而且如果有人試圖把他拉進社交圈,他會立刻表示強烈抗議。

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

2.7 ?? NESTED

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

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

3. ?? 事務傳播行為對比表

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

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

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

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

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

4. ?? 事務傳播行為的實際應用場景

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

4.1 ?? REQUIRED的應用場景

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

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

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

4.2 ?? REQUIRES_NEW的應用場景

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

適用于需要獨立事務的場景,例如:

  • 記錄操作日志:即使主業(yè)務失敗,也希望保留操作日志。
  • 發(fā)送通知消息:即使主業(yè)務回滾,通知消息也應該發(fā)送出去。
  • 異步任務觸發(fā):主流程完成某步驟后,需要觸發(fā)一個獨立的異步任務。
@Transactional
public void createOrder(Order order) {
    // 創(chuàng)建訂單
    orderRepository.save(order);
    // 記錄操作日志(使用獨立事務)
    logService.recordLog("創(chuàng)建訂單: " + order.getId());
    // 如果這里發(fā)生異常,訂單創(chuàng)建會回滾,但日志記錄不會回滾
    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的應用場景

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

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

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

5. ?? 事務傳播行為與隔離級別的關系

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

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

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

6. ?? 事務傳播行為的注意事項

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

6.1 ?? 自調用問題

?? 問題類比:就像你對著鏡子喊話,聲音不會傳得更遠。同樣,在同一個類中直接調用自己的方法,Spring的事務魔法也無法生效。

Spring事務是通過AOP實現(xiàn)的,當一個事務方法在同一個類中調用另一個事務方法時,事務傳播行為可能不會生效。這是因為方法調用沒有經(jīng)過代理對象。

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

解決方案

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

6.2 ?? 事務超時設置

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

在使用REQUIRES_NEW或NESTED等傳播行為時,可以為不同的事務設置不同的超時時間。

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

6.3 ?? 只讀事務

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

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

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

6.4 ?? 事務管理器的選擇

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

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

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

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

?? 性能加速:事務雖然保證了數(shù)據(jù)的一致性,但不恰當?shù)氖褂每赡軐е滦阅軉栴}。就像賽車手需要在彎道減速直道加速一樣,我們也需要在不同場景下調整事務策略,以獲得最佳性能。

7.1 ?? 合理選擇傳播行為

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

7.2 ?? 避免長事務

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

// 避免:長時間的事務
@Transactional
public void processLargeDataSet() {
    List<Data> dataList = dataRepository.findAll(); // 可能很大的數(shù)據(jù)集
    for (Data data : dataList) {
        // 復雜的業(yè)務邏輯處理
        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 ?? 事務邊界優(yōu)化

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

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

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

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

8.1 ?? 事務不生效的常見原因

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

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

8.2 ? 性能問題排查

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

監(jiān)控事務執(zhí)行時間
@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秒的事務記錄警告
                logger.warn("長事務檢測: 方法 {} 執(zhí)行時間: {}ms", methodName, executionTime);
            }
            return result;
        } catch (Exception e) {
            long executionTime = System.currentTimeMillis() - startTime;
            logger.error("事務執(zhí)行失敗: 方法 {} 執(zhí)行時間: {}ms", methodName, executionTime, e);
            throw e;
        }
    }
}

8.3 ?? 死鎖問題排查

?? 交通堵塞:死鎖就像兩輛車在狹窄的道路上相向而行,誰都不愿意讓步,最終導致雙方都無法前進。在數(shù)據(jù)庫事務中,當多個事務互相等待對方釋放鎖時,就會發(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);
}
// 避免死鎖的改進版本
@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. ?? 最佳實踐

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

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

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

// 推薦:在Service層使用事務
@Service
@Transactional(readOnly = true) // 類級別設置默認只讀
public class UserService {
    @Transactional // 寫操作覆蓋類級別設置
    public void createUser(User user) {
        userRepository.save(user);
    }
    // 繼承類級別的只讀事務
    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 ??? 異常處理最佳實踐

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

@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è)務異常,記錄日志但不回滾
            logService.recordValidationError(order.getId(), e.getMessage());
            throw new OrderException("訂單驗證失敗", e);
        } catch (InventoryException e) {
            // 庫存異常,需要回滾
            throw new OrderException("庫存不足", e);
        }
    }
    @Transactional(noRollbackFor = ValidationException.class)
    public void processOrderWithPartialFailure(Order order) {
        // 某些業(yè)務異常不需要回滾
    }
}

9.3 ?? 配置最佳實踐

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

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

9.4 ?? 編程式事務使用

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

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

10. ?? 總結

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

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

?? 關鍵要點回顧:

?? 備忘錄:以下是你需要牢記的核心知識點,它們將幫助你在實際項目中正確應用事務傳播機制。

  1. 傳播行為選擇

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

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

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

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

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

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

相關文章

  • Spring依賴注入的兩種方式(根據(jù)實例詳解)

    Spring依賴注入的兩種方式(根據(jù)實例詳解)

    這篇文章主要介紹了Spring依賴注入的兩種方式(根據(jù)實例詳解),非常具有實用價值,需要的朋友可以參考下
    2017-05-05
  • Mybatis一對多查詢的兩種姿勢(值得收藏)

    Mybatis一對多查詢的兩種姿勢(值得收藏)

    這篇文章主要給大家介紹了關于Mybatis一對多查詢的兩種姿勢,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-05-05
  • Maven插件詳細步驟

    Maven插件詳細步驟

    在學習和了解 Nexus-public 過程中,發(fā)現(xiàn) Java 項目中有一大部分是以 Maven 插件的形式進行深度融合不同功能的,下面給大家介紹Maven插件詳細步驟,感興趣的朋友一起看看吧
    2025-05-05
  • Java圖形界面GUI布局方式(小結)

    Java圖形界面GUI布局方式(小結)

    這篇文章主要介紹了Java圖形界面GUI布局方式(小結),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • Java中List列表去重有序和無序的6種方法

    Java中List列表去重有序和無序的6種方法

    在日常的業(yè)務開發(fā)中,會遇到List中的重復數(shù)據(jù)去除掉的場景,本文就來介紹一下Java中List列表去重有序和無序的6種方法,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • Java?SE封裝、包、static關鍵字和代碼塊示例詳解

    Java?SE封裝、包、static關鍵字和代碼塊示例詳解

    這篇文章主要給大家介紹了關于Java?SE封裝、包、static關鍵字和代碼塊的相關資料,需要的朋友可以參考下
    2023-11-11
  • 如何自定義Jackson序列化?@JsonSerialize

    如何自定義Jackson序列化?@JsonSerialize

    這篇文章主要介紹了如何自定義Jackson序列化?@JsonSerialize,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Spring AspectJ AOP框架注解原理解析

    Spring AspectJ AOP框架注解原理解析

    這篇文章主要介紹了Spring AspectJ AOP框架注解原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • 學java得這樣學,學習確實也得這樣

    學java得這樣學,學習確實也得這樣

    學java得這樣學,學習東西確實也得這樣
    2008-02-02
  • 解決使用httpclient傳遞json數(shù)據(jù)亂碼的問題

    解決使用httpclient傳遞json數(shù)據(jù)亂碼的問題

    這篇文章主要介紹了解決使用httpclient傳遞json數(shù)據(jù)亂碼的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01

最新評論