1. 沒有使用代理
場景: 如果你在一個類內部呼叫同一個類中的另一個方法,Spring 事務管理無法生效。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 當在 controller 呼叫 executeTask 時事務會失效
* 1,因為標註了 @Transaction 註解,spring 會為 MyService 建立一個代理物件
* 2,controller 呼叫 executeTask 方法時,呼叫的是真實物件的方法,因為這個方法沒有標註事務註解
* 3,executeTask 再呼叫 performTask 時,其實是 this.performTask,這裡的 this 是普通物件,而不是代理物件
*/
@Service
public class MyService {
@Transactional
public void performTask() {
// 模擬資料庫操作
System.out.println("Performing task...");
// 這裡可以新增更多資料庫操作程式碼
// 例如:userRepository.save(new User());
}
public void executeTask() {
performTask(); // 直接呼叫同一類中的方法
}
}
原因: 這是因為 Spring 事務管理是基於代理的。當你在一個類中呼叫另一個方法時,實際上是在同一個物件的上下文中執行的,這樣事務註解不會被代理攔截,導致事務失效。
解決方法: 1,將方法提取到不同的類中,2,在同一個類中使用 self
注入。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyService {
@Autowired
private MyService self; // 自我注入
@Transactional
public void performTask() {
System.out.println("Performing task...");
// 資料庫操作
}
public void executeTask() {
self.performTask(); // 使用自我注入呼叫,因為此時 self 已經是代理物件了,所以會走 AOP 的通知
}
}
2. 非執行時異常
場景: Spring 預設只對執行時異常(RuntimeException
)進行回滾,如果丟擲的是檢查性異常(如 IOException
),則不會回滾事務。
@Transactional
public void test(){
userDao.save(user);
new File("D:\\不存在的檔案.jpg")
}
原因: 這是 Spring 事務管理的預設行為,非執行時異常不會觸發事務回滾。
解決方法: 可以在 @Transactional
註解中指定回滾的異常型別,例如:
@Transactional(rollbackFor = Exception.class)
public void test(){
userDao.save(user);
new File("D:\\不存在的檔案.jpg")
}
3. 事務傳播行為設定不當
場景: 在多個事務之間的互動時,傳播行為設定不當可能導致事務失效。
// 訂單 service
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
@Transactional
public void createOrder() {
// 訂單建立邏輯
System.out.println("Creating order...");
// 呼叫 PaymentService 處理支付
paymentService.processPayment();
// 其他訂單處理邏輯
}
}
// 支付 service
@Service
public class PaymentService {
// 這個方法使用的是 REQUIRES_NEW,會新開一個事務,出現異常只是當前事務會回滾,不會影響上層的事務
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment() {
// 支付處理邏輯
System.out.println("Processing payment...");
// 模擬異常
if (true) {
throw new RuntimeException("Payment failed!");
}
}
}
4. 隔離級別不合適
場景: 在高併發情況下,使用不合適的隔離級別可能導致事務表現不符合預期。
# 1,假設隔離級別是讀未提交
# 2,A 事務先新增一條資料
# 3,此時 B 事務讀到這條資料了,做業務的途中發生異常,要回滾資料(將要回滾還沒有回滾的時候,A RollBack 了)
# 4,B 事務咋回滾,要回滾的資料都沒了
5. Spring Boot 和 Spring 版本不相容
場景: 在使用 Spring Boot 的時候,某些 Spring 版本之間可能存在相容性問題。
原因: 在不同版本的 Spring 或 Spring Boot 中,事務管理的實現可能會有所不同。
解決方法: 確保使用相容的 Spring 和 Spring Boot 版本,並查閱相應的文件。