### [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` 標記,表示所有操作都應該在一個交易中進行,要不全部成功,要不全部失敗,確保資料的原子性。
當更新使用者資料和創建日誌紀錄時,如果任一操作失敗將自動回滾交易,使得資料庫資料保持原子性。