Spring事務失效、事務不回滾問題記錄

在開發過程中,Spring的聲明式事務可以通過一個簡單的@Transactional註解,就讓我們輕鬆進行事務處理。我們知道Spring事務基於AOP,採用動態代理實現,雖然使用簡單,但是在實際場景中,我們也會遇到一些坑。這簡單記錄了一些常見的情況。

 

一.常見事務失效問題
 

1.@Transactional屬性設置問題

@Transactional的rollbackFor用於指定能夠觸發事務回滾的異常類型,可以指定多個,用逗號分隔。
rollbackFor默認值爲UncheckedException,包括了RuntimeException和Error.
當我們直接使用@Transactional不指定rollbackFor時,Exception及其子類都不會觸發回滾。所以需要根據情況而定,一般我們會所使用:@Transactional(rollbackFor=Exception.class),這樣,Exception及其子類都會觸發回滾。

Exception結構圖:

2.一個沒有事務的方法調用本類的另一個有事務的方法導致事務失效

如下這種情況:

public class TranTestService {
	@Autowired
	private CuponConfirmDao confirmDao;
	@Autowired
	private TranTest2Service tranTest2Service;
	
	// 方法  A
	public int updateStatusT(CuponModel cuponModel) {
		System.out.println("更新方法1執行開始");
		// 調用本類的方法  B
		int count =  this.updateOrderStatusT(cuponModel);
		System.out.println("更新方法1執行結束");
		return count;
	}
	
	// 方法  B
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor=Exception.class)
	public int updateOrderStatusT(CuponModel cuponModel) {
		int count = confirmDao.updateOrderStatusT(cuponModel); // 更新表操作
		int sum = 6/0;// 模擬異常
		int count1 = confirmDao.updateCupoNoStateT(cuponModel); // 更新表操作
		return count + count1;
	}
}

以上是部分實驗代碼,小結一下:

a. 一個沒有事務的方法,調用同一個類下面的另一個有事務的方法——事務失效

b. 一個沒有事務的方法,調用另一個類下面一個有事務的方法——事務有效。

c. 一個有事務的方法,調用同一個類下面的另一個沒有事務的方法——事務有效。

d. 一個有事務的方法,調用另一個類下面一個沒有事務的方法——事務有效。

e. 兩個方法都有事務—— 兩種調用方式事務都有效。

3.try catch異常處理導致事務失效

示例代碼如下:

@Service
public class TranTest2Service {
	@Autowired
	private CuponConfirmDao confirmDao;
	
	// 方法 B  
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor=Exception.class)
	public int updateOrderStatusT(CuponModel cuponModel) {

		try {
			 confirmDao.updateOrderStatusT(cuponModel); // 更新表操作
			int sum = 1 / 0;// 模擬異常
			 confirmDao.updateCupoNoStateT(cuponModel); // 更新表操作
		} catch (Exception e) {
			e.printStackTrace(); 
		}
		return 0;
	}

}

由此可知,在spring中如果某個業務方法被一個

     try{  

            //bisiness logic code   

          } catch(Exception e) {   

             //handle the exception   

}

整個包裹起來,則這個業務方法也就等於脫離了spring事務的管理,因爲沒有任何異常會從業務方法中拋出!全被捕獲併吞掉,導致spring異常拋出觸發事務回滾策略失效。

解決方案:

方案一 、外拋

把異常往外拋(不加cry catch),然後在外層 try catch(比如:service層拋出,controller層try catch處理),但是問題來了,出現異常之後,service層代碼就停止運行了,如果我們希望流程能繼續走下去,那就使用方案二(最佳)。

方案二、手動回滾

示例代碼:

public class TranTest2Service {
	@Autowired
	private CuponConfirmDao confirmDao;
	
	// 方法 B  
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor=Exception.class)
	public int updateOrderStatusT(CuponModel cuponModel) {

		try {
			 confirmDao.updateOrderStatusT(cuponModel); // 更新表操作
			int sum = 1 / 0;// 模擬異常
			 confirmDao.updateCupoNoStateT(cuponModel); // 更新表操作
		} catch (Exception e) {
			e.printStackTrace(); 
			//手動回滾事務
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		}
		return 0;
	}

}

在catch代碼塊里加上回滾語句:

 try {

    // 操作代碼塊

    } catch (Exception e) {
    //手動回滾事務
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }

加上該語句即可:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章