理解 Spring Boot 中的 @Transactional 讓你的應用更高效

Spring Boot @Transactional

1. 什麼是 @Transactional?

定義與目的

@Transactional 是 Spring 框架中用來管理事務的註解。它提供了一種簡單的方式來定義事務邊界,並自動處理事務的提交與回滾。當一個方法被標註為 @Transactional 時,Spring 將會在方法執行之前開始一個新的事務,並在方法執行結束後根據執行結果決定是提交還是回滾該事務。

事務的特性

事務具有四個基本特性,通常被稱為 ACID 原則:

  • 原子性 (Atomicity): 事務中的所有操作要麼全部成功,要麼全部失敗。這確保了數據的一致性。

  • 一致性 (Consistency): 事務的執行必須使數據從一個一致的狀態轉換到另一個一致的狀態。任何事務的執行都不應違反數據庫的完整性約束。

  • 隔離性 (Isolation): 各個事務之間的執行不應互相干擾。隔離的強度取決於所選擇的隔離級別。

  • 持久性 (Durability): 一旦事務被提交,其結果應該是永久性的,即使系統故障也不會丟失。

2. 使用 @Transactional 的基本語法

註解的使用

在 Spring 中,@Transactional 通常用於服務層或 DAO 層。可以將此註解應用於類或方法,具體範例如下:

import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        // 其他可能的操作
    }
}

註解的基本屬性

  • propagation: 定義事務的傳播行為,默認為 Propagation.REQUIRED
  • rollbackFor: 指定哪些異常會導致事務回滾,默認為所有運行時異常。
  • timeout: 定義事務的超時時間,超過這個時間會自動回滾。

範例代碼

以下是一個使用 @Transactional 的範例,展示如何在資料庫中進行操作:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

@Service
public class AccountService {

    @Autowired
    private AccountRepository accountRepository;

    @Transactional(rollbackFor = Exception.class)
    public void transferMoney(Long fromAccountId, Long toAccountId, Double amount) {
        Account fromAccount = accountRepository.findById(fromAccountId);
        Account toAccount = accountRepository.findById(toAccountId);

        fromAccount.setBalance(fromAccount.getBalance() - amount);
        toAccount.setBalance(toAccount.getBalance() + amount);

        accountRepository.save(fromAccount);
        accountRepository.save(toAccount);
    }
}

3. 事務的傳播行為 (Propagation)

不同的傳播行為

在 Spring 中,事務的傳播行為決定了當一個事務方法被調用時,該方法的事務行為如何與當前的事務進行交互。以下是常見的傳播行為:

傳播行為 描述
REQUIRED 如果存在事務,則加入該事務;如果不存在,則創建新的事務。
REQUIRES_NEW 創建一個新的事務,並且暫停當前的事務。
NESTED 如果存在事務,則在該事務中嵌套一個事務;否則,與 REQUIRED 行為相同。
SUPPORTS 如果存在事務,則加入該事務;如果不存在,則以非事務方式執行。
NOT_SUPPORTED 暫停當前事務,以非事務方式執行。
NEVER 以非事務方式執行,如果存在事務則拋出異常。
MANDATORY 如果存在事務,則加入該事務;如果不存在,則拋出異常。

選擇合適的傳播行為

在選擇傳播行為時,開發者需要考慮業務需求。例如,如果一個方法需要在已存在的事務中執行,則可以使用 REQUIRED,而如果需要單獨處理,則可以選擇 REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void anotherMethod() {
    // 這個方法將在新的事務中執行
}

4. 事務的回滾機制 (Rollback)

自動回滾

Spring 提供自動回滾機制,當方法拋出未捕獲的 RuntimeExceptionError 時,事務將自動回滾。這確保了在出現問題時不會有部分操作被提交,保持數據的一致性。

自定義回滾

使用 rollbackFor 屬性可以自定義回滾行為。例如,您可以指定在特定的檢查異常發生時進行回滾:

@Transactional(rollbackFor = CustomException.class)
public void methodThatMayThrow() throws CustomException {
    // 這裡可能會拋出 CustomException
}

5. @Transactional 的注意事項

使用範圍

@Transactional 應該用於需要原子性操作的業務邏輯中。在某些情況下,例如在相同類中調用 @Transactional 方法,事務可能無法正常工作。

AOP 與代理

@Transactional 是基於 AOP(面向切面編程)實現的,這意味著只有在 Spring 管理的 Bean 中使用 @Transactional 才能生效。這會影響方法調用的行為,尤其是在同一類內部調用時。

測試中的配置

在進行單元測試時,可以使用 @Transactional 確保每個測試方法的執行不會影響其他測試。這通常與 @Rollback 一起使用,以便在測試結束後自動回滾事務。

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;

public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    @Transactional
    @Rollback
    public void testCreateUser() {
        // 測試用戶創建邏輯
    }
}

6. 常見問題與最佳實踐

常見錯誤

  • 方法在同一類中調用不適用 @Transactional: Spring 的 AOP 機制無法攔截同一個類內部的方法調用,導致事務無法正常管理。

  • 未捕獲的異常: 確保在必要時捕獲異常,否則事務可能不會如預期回滾。

最佳實踐

  • 使用接口: 將 @Transactional 應用於接口而不是具體類,以便讓 Spring AOP 正確工作。

  • 縮小事務範圍: 盡量將事務範圍控制在最小的邊界內,以提高性能並減少鎖競爭。

  • 避免長時間持有事務: 不要在事務內部執行長時間運行的操作(如慢查詢),以避免占用資料庫資源。

@Transactional
public void performTransaction() {
    // 最小化事務範圍
    repository.save(entity);
    // 執行非事務性操作
    otherService.doSomething();
}

透過以上內容,讀者應該能夠對 Spring Boot 中的 @Transactional 有一個全面的了解,從基本概念到使用方式,以及注意事項和最佳實踐。這些知識能幫助開發者在實際應用中更有效地管理事務,確保數據的一致性和完整性。

關於作者

Carger
Carger
我是Oscar (卡哥),前Yahoo Lead Engineer、高智商同好組織Mensa會員,超過十年的工作經驗,服務過Yahoo關鍵字廣告業務部門、電子商務及搜尋部門,喜歡彈吉他玩音樂,也喜歡投資美股、虛擬貨幣,樂於與人分享交流!