在使用Spring事務時,我們只需在某方法上使用@Transactional註解簡單的標註一下,便可以實現很強大的事務控制功能,這其中的緣由,看小編娓娓道來;
首先需要明確:在spring事務的切面中,實際上是一個Around切面,在註解的業務方法前後都可以被調用,而實際上實現切面操作的是TransactionInterceptor類;
[本篇我們先從實例代碼看該註解]
1)在代碼中使用@Transactional 註解,不設置事務的其他屬性,默認只對RuntimeException和Error進行回滾:
@Transactional
public void rollback() throws SQLException {
// update db
throw new SQLException("exception");
}
事實證明:上述的代碼並不會回滾;
2)對於需要回滾的異常,需要在註解中設置rollbackFor屬性,但這時,對於RuntimeException和Error的異常依舊會回滾:
@Transactional(rollbackFor = {SQLException.class})
public void rollback() throws SQLException {
// update db
throw new RuntimeException("exception");
}
在上述代碼片段中,雖然指定了SqlException,但方法體中的RuntimeException依舊會造成事務回滾;
3)嵌套事務
@Transactional
public void rollback() {
// updateA
try{
selfProxy.innelTransaction()
}catch(RuntimeException e){
//do nothing
}
//updateC
}
@Transactional
public void innelTransaction() throws SQLException {
// updateB
throw new RuntimeException("exception");
}
在上述代碼中,innerTransaction方法中的異常並未攔截,在被rollback方法攔截之前,AOP已經獲取了異常,此時,當前事務的readOnly會被設置爲true,所以即使在rollback方法中進行了攔截和處理,代碼依舊會造成事務回滾;
產生以上現象的原因:
內部方法(innelTransaction)拋出異常,觸發了回滾,這時當前事務被設置爲 rollback-only,當外部方法所在的事務提交時,Spring拋出以下異常,同時會將當前事務回滾;
org.springframework.transaction.UnexpectedRollbackException:
Transaction rolled back because it has been marked as rollback-only
在上述代碼中:
即使innelTransaction方法不加事務處理,依舊會有這個異常;
原因:在外部方法中,使用了一個事務,當內部方法拋出異常時,事務會被標記爲需要回滾,但是最終要在外部方法的事務提交時,校驗,spring的回滾標誌,檢測到回滾標記就會在回滾事務,並拋出UnexceptedRollbackException;
若要控制上述代碼不做回滾:
1)我們需要在內部方法體中,將異常捕獲,通過Boolean返回值的方式來完成;
2)使用ServiceB的代理類來完成對functionB 的調用