- 直接在同一個類內(nèi)部調(diào)用 @Transactional 方法
失效示例
@Service
public class SomeService {
@Transactional
public void doSomethingTransactional() {
// 進行一些數(shù)據(jù)庫操作
System.out.println("Doing transactional work...");
}
public void callNonTransactional() {
doSomethingTransactional(); // 事務(wù)失效,因為是內(nèi)部方法調(diào)用
}
}
問題:callNonTransactional() 方法直接調(diào)用了 doSomethingTransactional(),而沒有通過 Spring 容器管理的代理調(diào)用,因此事務(wù)不會生效。
成功示例
@Service
public class SomeService {
@Autowired
private SomeService someService; // 依賴注入
@Transactional
public void doSomethingTransactional() {
// 進行一些數(shù)據(jù)庫操作
System.out.println("Doing transactional work...");
}
public void callNonTransactional() {
someService.doSomethingTransactional(); // 通過 Spring 管理的實例調(diào)用,事務(wù)生效
}
}
解決方法:
將 SomeService 注入到自己類中,并通過依賴注入調(diào)用 doSomethingTransactional() 方法,這樣 Spring 會通過代理進行方法調(diào)用,事務(wù)管理就能生效。
- 在非 Spring 管理的類中使用 @Transactional
失效示例:
public class NonSpringManagedClass {
@Transactional
public void doSomething() {
// 進行一些數(shù)據(jù)庫操作
System.out.println("Transactional work in non-Spring managed class...");
}
}
問題:NonSpringManagedClass 不是一個 Spring 管理的 Bean,因此事務(wù)注解不會生效。
成功示例
@Component // 讓該類成為 Spring 管理的 Bean
public class NonSpringManagedClass {
@Transactional
public void doSomething() {
// 進行一些數(shù)據(jù)庫操作
System.out.println("Transactional work in Spring managed class...");
}
}
解決方法:使用 @Component、@Service 或 @Repository 等注解將類標(biāo)記為 Spring 管理的 Bean,這樣 Spring 會為它創(chuàng)建代理,事務(wù)注解才會生效。
- 構(gòu)造函數(shù)上使用 @Transactional
失效示例:
@Service
public class SomeService {
@Transactional
public SomeService() { // 事務(wù)失效:構(gòu)造函數(shù)不能使用 @Transactional
System.out.println("Initializing with transactional work...");
}
}
問題:構(gòu)造函數(shù)不能使用 @Transactional 注解,因為事務(wù)只能應(yīng)用于實例方法。
解決示例
@Service
public class SomeService {
@Transactional
public void doTransactionalWork() {
System.out.println("Doing transactional work...");
}
}
- 在事務(wù)方法內(nèi)部捕獲異常
失效示例:
@Service
public class SomeService {
@Transactional
public void doSomething() {
try {
// 可能拋出 RuntimeException
throw new RuntimeException("Something went wrong");
} catch (RuntimeException e) {
// 異常被捕獲,事務(wù)不會回滾
System.out.println("Exception caught, transaction will not rollback.");
}
}
}
問題:捕獲并處理了 RuntimeException 異常,導(dǎo)致 Spring 無法觸發(fā)事務(wù)回滾。
@Service
public class SomeService {
@Transactional
public void doSomething() {
// 不捕獲異常,直接拋出
throw new RuntimeException("Something went wrong");
}
}
解決方法:
讓異常 不被捕獲,或者明確讓 Spring 了解哪些異常需要回滾(通過 rollbackFor 屬性)。
例如,使用 @Transactional(rollbackFor = RuntimeException.class) 來指定哪些異常需要觸發(fā)事務(wù)回滾。
備注:
當(dāng)方法一parentMethod 調(diào)用方法二childMethod時不同情況說明:
eg: @Transactional(rollbackFor = {Exception.class}, timeout = 2, propagation = Propagation.REQUIRED),@Transactional 注解傳播行為 propagation 屬性默認(rèn)值是 Propagation.REQUIRED,即當(dāng)前存在就加入,當(dāng)前不存在則創(chuàng)建
| 情況 |
parentMethod 是否加 @Transactional
|
childMethod 是否加 @Transactional
|
事務(wù)行為 | 異常處理 |
|---|---|---|---|---|
| 情況 1 | 否 | 否 | 兩個方法都不在事務(wù)管理范圍內(nèi),每個數(shù)據(jù)庫操作獨立執(zhí)行 | 出現(xiàn)異常時,已執(zhí)行的數(shù)據(jù)庫操作不會回滾 |
| 情況 2 | 是 | 否 |
parentMethod 處于事務(wù)管理范圍,childMethod 不在事務(wù)管理范圍,childMethod 操作不參與 parentMethod 的事務(wù) |
- childMethod 異常,parentMethod 事務(wù)不回滾- parentMethod 異常,parentMethod 事務(wù)回滾,childMethod 已執(zhí)行操作不受影響 |
| 情況 3 | 否 | 是 |
parentMethod 不在事務(wù)管理范圍,childMethod 處于事務(wù)管理范圍,childMethod 創(chuàng)建新事務(wù) |
- childMethod 異常,其事務(wù)回滾- parentMethod 操作不受影響,因本身不在事務(wù)中 |
| 情況 4 | 是(不指定傳播行為) | 是(不指定傳播行為) |
parentMethod 開啟事務(wù),childMethod 加入 parentMethod 的事務(wù),二者共享事務(wù)上下文 |
任何一個方法異常,整個事務(wù)回滾 |