Spring Transactional

Posted by Adam on August 24, 2022
### [Transactions with Spring and JPA](https://www.baeldung.com/transaction-configuration-with-jpa-and-spring) @Transactional 是 Spring 框架中的一個註解,用於聲明當前方法需要在事務管理下執行。它可以應用在方法層面,也可以應用在類層面。 使用 @Transactional 註解時,Spring 框架會自動為這個方法或類建立一個事務。如果方法執行成功,事務就會被提交,如果方法執行失敗,事務就會被回滾。通常在使用 @Transactional 註解時,還需要在 Spring 的配置文件中進行相關的配置,比如配置事務管理器。 以下是一個使用 @Transactional 註解的範例: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @PostConstruct public void init() { // 在建構子完成後執行的初始化操作 // 可以使用 this.dependency 來存取依賴物件 // ... } @Override @Transactional public void createUser(User user) { userDao.createUser(user); } } ``` 在上述範例中,createUser 方法使用了 @Transactional 註解,這表示當前方法需要在事務管理下執行。當方法被調用時,Spring 框架會自動為這個方法建立一個事務,並在方法執行完成後提交或回滾這個事務。 ```java @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) ``` 在設定事務的時候,可以使用 propagation 參數設定事務的傳播行為,以及 readOnly 參數設定事務的唯讀屬性。 * propagation:設定事務的傳播行為,表示當前方法被另一個方法呼叫時,事務的行為。常用的取值有: * Propagation.REQUIRED:預設值,當前方法必須在一個事務中運行。如果當前存在一個事務,則使用該事務;否則創建一個新的事務。 * Propagation.SUPPORTS:當前方法沒有事務時,不創建新的事務。如果當前存在一個事務,則使用該事務。 * Propagation.MANDATORY:當前方法必須在一個事務中運行。如果當前不存在一個事務,則拋出異常。 * Propagation.REQUIRES_NEW:代表著當一個方法被調用時,它會創建一個新的事務,並在該方法執行期間暫停當前事務。新的事務會獨立於當前事務,執行自己的一系列操作,當該方法執行完畢時,新的事務被提交,而當前事務被恢復並繼續執行。 * Propagation.NOT_SUPPORTED:當前方法不能在一個事務中運行。如果當前存在一個事務,則將該事務挂起。 * Propagation.NEVER:當前方法不能在一個事務中運行。如果當前存在一個事務,則拋出異常。 * Propagation.NESTED:當前方法必須在一個嵌套的事務中運行。如果當前不存在一個事務,則創建一個新的嵌套事務。如果當前存在一個事務,則在該事務內創建一個嵌套事務。 * readOnly:設定事務的唯讀屬性,表示當前方法是否可以修改數據庫中的數據。當 readOnly 設定為 true 時,表示當前方法只能讀取數據,不能修改數據。當 readOnly 設定為 false 時,表示當前方法既可以讀取數據,也可以修改數據。 因此,@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) 表示當前方法可以在一個事務中運行,但如果當前不存在一個事務,則不創建新的事務 ### 確保資料庫(DB)資料的原子性 要確保資料庫(DB)資料的原子性,可以使用交易(Transaction)來進行操作。以下是一個使用 Spring 框架的範例: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Autowired private UserRepository userRepository; @Transactional public void updateUserAndCreateLog(User user, Log log) { // 更新使用者資料 userRepository.save(user); // 建立日誌紀錄 createLog(log); } private void createLog(Log log) { // 寫入日誌資料 } } ``` 在這個範例中,`updateUserAndCreateLog` 方法將更新使用者資料並創建日誌紀錄。這個方法使用 `@Transactional` 標記,表示所有操作都應該在一個交易中進行,要不全部成功,要不全部失敗,確保資料的原子性。 當更新使用者資料和創建日誌紀錄時,如果任一操作失敗將自動回滾交易,使得資料庫資料保持原子性。