Transactional 在 Spring Boot 中的優(yōu)秀實(shí)踐
在開(kāi)發(fā)應(yīng)用程序時(shí),保證數(shù)據(jù)的完整性和一致性是非常重要的。而對(duì)于復(fù)雜的業(yè)務(wù)邏輯來(lái)說(shuō),事務(wù)管理成為了一個(gè)必不可少的組件。在 Spring Boot 中,我們有強(qiáng)大的事務(wù)管理機(jī)制,可以幫助我們簡(jiǎn)化事務(wù)的處理并確保數(shù)據(jù)的正確性。本文將介紹在 Spring Boot 中使用事務(wù)的最佳實(shí)踐。
1.了解 Spring Boot 中的事務(wù)管理
Spring Boot 提供了方便的注解驅(qū)動(dòng)的事務(wù)管理功能。通過(guò)使用 `@Transactional` 注解,我們可以將方法或類(lèi)標(biāo)記為事務(wù)性的,并由 Spring Boot 自動(dòng)管理這些事務(wù)的生命周期。
2. TransactionManager 的作用
TransactionManager 在事務(wù)管理中扮演著關(guān)鍵角色。當(dāng)調(diào)用使用 `@Transactional` 注解的方法時(shí),Spring Boot 利用 TransactionManager 來(lái)創(chuàng)建或加入事務(wù),并根據(jù)操作結(jié)果提交或回滾事務(wù)。
3. 事務(wù)隔離級(jí)別
Spring Boot 支持多種事務(wù)隔離級(jí)別,如 READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE。選擇適當(dāng)?shù)氖聞?wù)隔離級(jí)別非常重要,它決定了事務(wù)之間以及底層數(shù)據(jù)之間的交互方式。
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someTransactionalMethod() {
// ...
}
4. 了解事務(wù)傳播機(jī)制
事務(wù)傳播定義了當(dāng)一個(gè)事務(wù)方法調(diào)用另一個(gè)事務(wù)方法時(shí),事務(wù)是如何傳播的。Spring Boot 支持多種傳播行為,如 REQUIRED、REQUIRES_NEW、SUPPORTS、NOT_SUPPORTED 等。根據(jù)業(yè)務(wù)需求選擇合適的傳播行為非常重要。
以下是幾個(gè)常見(jiàn)的事務(wù)傳播機(jī)制示例:
- REQUIRED:如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù);如果已經(jīng)存在事務(wù),則加入到當(dāng)前事務(wù)中。這是默認(rèn)的事務(wù)傳播機(jī)制。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// ... some code here
methodB();
// ... some code here
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// ... some code here
}
在上述示例中,當(dāng) methodA() 調(diào)用 methodB() 時(shí),methodB() 將加入到 methodA() 的事務(wù)中。
- REQUIRES_NEW:無(wú)論當(dāng)前是否存在事務(wù),都創(chuàng)建一個(gè)新的事務(wù),并掛起當(dāng)前事務(wù)。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// ... some code here
methodB();
// ... some code here
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// ... some code here
}
在上述示例中,當(dāng) methodA() 調(diào)用 methodB() 時(shí),methodB() 將啟動(dòng)一個(gè)新的事務(wù),并暫停 methodA() 的事務(wù)。
- SUPPORTS:如果當(dāng)前存在事務(wù),則加入到當(dāng)前事務(wù)中;如果沒(méi)有事務(wù),則以非事務(wù)方式執(zhí)行。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// ... some code here
methodB();
// ... some code here
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// ... some code here
}
在上述示例中,當(dāng) methodA() 調(diào)用 methodB() 時(shí),methodB() 將以與 methodA() 相同的事務(wù)狀態(tài)執(zhí)行。
- NOT_SUPPORTED:表示當(dāng)前方法在非事務(wù)環(huán)境下執(zhí)行,即使存在一個(gè)活動(dòng)的事務(wù)也會(huì)被掛起。
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// ... some code here
methodB();
// ... some code here
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
// ... some code here
}
在上述示例中,當(dāng) methodA() 調(diào)用 methodB() 時(shí),methodB() 將以非事務(wù)方式執(zhí)行,即使 methodA() 執(zhí)行在一個(gè)事務(wù)中。
5. 處理同一類(lèi)內(nèi)的事務(wù)
當(dāng)在同一類(lèi)中的 `@Transactional` 方法調(diào)用另一個(gè) `@Transactional` 方法時(shí),需要注意 Spring 的默認(rèn)行為。默認(rèn)情況下,如果一個(gè) `@Transactional` 方法在同一類(lèi)中調(diào)用另一個(gè) `@Transactional` 方法,則不會(huì)應(yīng)用事務(wù)行為。為了解決這個(gè)問(wèn)題,可以考慮使用基于 AspectJ 的編織或?qū)?`@Transactional` 方法移動(dòng)到單獨(dú)的類(lèi)中。
6. 默認(rèn)事務(wù)行為
Spring Boot 中的 `@Transactional` 方法在任何未檢查異常發(fā)生時(shí)都會(huì)回滾事務(wù)。這樣可以確保在發(fā)生錯(cuò)誤時(shí),事務(wù)中的數(shù)據(jù)更改不會(huì)被持久化。
7. 管理不同 Bean 之間的事務(wù)
當(dāng)調(diào)用另一個(gè) Bean 上的方法時(shí),Spring 會(huì)在目標(biāo) Bean 周?chē)鷦?chuàng)建一個(gè)新代理,從而使其能夠管理事務(wù)行為。這樣可以確???Bean 的方法調(diào)用也能參與到事務(wù)管理中。
8. 處理未檢查的異常
當(dāng) `@Transactional` 方法拋出未檢查異常時(shí),默認(rèn)情況下 Spring 會(huì)自動(dòng)回滾事務(wù)。這樣可以確保在發(fā)生錯(cuò)誤時(shí),事務(wù)中的數(shù)據(jù)更改不會(huì)被持久化。
9. 自定義回滾行為
通過(guò)使用 `@Transactional` 注解的 `rollbackFor` 或 `noRollbackFor` 屬性,我們可以自定義回滾行為。這在需要在一些情況下保留事務(wù)內(nèi)的更改時(shí)非常有用。
@Transactional(rollbackFor = CustomException.class)
public void processWithCustomRollback() throws CustomException {
try {
// 執(zhí)行一些數(shù)據(jù)庫(kù)操作或其他邏輯
// 如果發(fā)生了某種業(yè)務(wù)異常,需要回滾事務(wù)
if (someCondition) {
throw new CustomException("發(fā)生了業(yè)務(wù)異常");
}
// 執(zhí)行其他操作
} catch (CustomException ex) {
// 捕獲到自定義異常后,可以根據(jù)業(yè)務(wù)需求進(jìn)行相應(yīng)處理
// 可以選擇手動(dòng)回滾事務(wù)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// 或者拋出其他異常,由全局異常處理器進(jìn)行處理
throw new AnotherCustomException("發(fā)生了另一個(gè)自定義異常", ex);
}
}
10. 默認(rèn)回滾行為
默認(rèn)情況下,`@Transactional` 方法在任何未檢查異常發(fā)生時(shí)都會(huì)回滾事務(wù)。如果需要自定義此行為,可以使用 `rollbackFor` 或 `noRollbackFor` 屬性來(lái)指定具體的異常類(lèi)型。
11. 私有方法和 @Transactional
`@Transactional` 注解僅適用于公共方法。Spring 會(huì)在公共方法周?chē)鷦?chuàng)建代理來(lái)管理事務(wù)行為。私有方法對(duì)代理不可見(jiàn),因此 `@Transactional` 注解不會(huì)生效。如果需要在私有方法中使用事務(wù)管理,可以考慮將私有方法移動(dòng)到公共方法中,并在該公共方法上應(yīng)用 `@Transactional` 注解。
12. 處理并發(fā)問(wèn)題
Spring Boot的@Transactional注解提供了一種通過(guò)序列化事務(wù)來(lái)處理并發(fā)問(wèn)題的機(jī)制。默認(rèn)隔離級(jí)別通過(guò)確保事務(wù)不會(huì)相互干擾來(lái)防止大多數(shù)并發(fā)問(wèn)題。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void updateUser(String username, String email) {
User user = userRepository.findByUsername(username);
user.setEmail(email);
// ...
}
}
在此示例中,updateUser()標(biāo)記為@Transactional,并且當(dāng)多個(gè)線(xiàn)程嘗試同時(shí)修改同一用戶(hù)的電子郵件地址時(shí),Spring 能確保事務(wù)被序列化。這可以防止數(shù)據(jù)不一致和競(jìng)爭(zhēng)條件。
請(qǐng)記住使用 @Transactional時(shí), Spring使用的默認(rèn)隔離級(jí)別是Isolation.DEFAULT,它與底層數(shù)據(jù)源的默認(rèn)值一致。
總結(jié)
以上是在 Spring Boot 中使用事務(wù)的一些最佳實(shí)踐。了解這些實(shí)踐對(duì)于構(gòu)建可靠和一致的應(yīng)用程序至關(guān)重要。通過(guò)正確地配置事務(wù)管理,我們可以確保數(shù)據(jù)的完整性,并避免出現(xiàn)潛在的并發(fā)問(wèn)題。