在使用Jpa的@Version的時候偶然發(fā)現(xiàn)了一個Spring事務(wù)的坑,花了一個下午的時間才成功解決,記錄一下。
@Version當(dāng)修改或刪除操作的數(shù)據(jù)版本不一致的時候會拋出一個異常:
ObjectOptimisticLockingFailureException
Service用以下代碼捕獲了這個異常,消化并拋出了一個新的自定義異常:
try {
//調(diào)用持久化方法拋出ObjectOptimisticLockingFailureException異常
} catch (ObjectOptimisticLockingFailureException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage());
}
//捕獲處理后拋出一個新的自定義異常
throw new SkeletonBaseException("數(shù)據(jù)版本錯誤");
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage());
}
throw SkeletonBaseException.getException(e, e.getMessage());
}
但是在Controller的最終異常處理中
try {
//調(diào)用上面的方法
} catch (Exception e) {
e.printStackTrace();
if (logger.isErrorEnabled()) {
logger.error(e.getMessage());
}
responseRange.setException(e);
}
return responseRange;
卻得到了一個完全不一樣的異常信息:
Transaction rolled back because it has been marked as rollback-only
這個異常信息是說事務(wù)已經(jīng)回滾,因為它被標(biāo)記成了回滾。
這里就很奇怪了。
按照我的想法,這個異常信息應(yīng)該是Service拋出的自定義異常數(shù)據(jù)版本錯誤才對啊。
想了一下這就說明在Service返回Controller的過程中還發(fā)生了一個新的異常,而把我的異常頂?shù)袅恕?br>
為什么會發(fā)生這種事情?
在網(wǎng)上查了許多大神的資料,Debugger了半天Spring的源碼。
最終總結(jié)出了一個答案
Spring的事務(wù)切面認(rèn)為Service的方法沒有拋出異常,在Service結(jié)束后,打算正常Commit提交事務(wù)!但是這個事務(wù)已經(jīng)被標(biāo)記成了rollback-only狀態(tài),所以提交失敗,拋出上面出乎意料的異常信息!
異常被頂?shù)舻脑蛑懒恕?br>
但是我們Service明明最終拋出了一個自定義異常?。∷艿侥睦锶チ?!
為什么事務(wù)切面認(rèn)為沒有拋出異常呢?!
這就是坑之所在了!
我的自定義異常繼承了Exception而不是RuntimeException
因為知道Spring的事務(wù)默認(rèn)回滾是發(fā)生RuntimeException所以特地配置了rollbackFor = Exception.class
按理來說不應(yīng)該Commit而是正常rollback。
不過問題范圍縮小了,又針對性的搜索了好一陣兒。
原因:
在try{}catch(){}這樣的代碼塊中,最后必須拋出一個RuntimeException,Spring事務(wù)切面才會認(rèn)為你的方法有異常出現(xiàn)!即使配置了rollbackFor = Exception.class也不管用!
解決方法:把自定義異常改成繼承RuntimeException就OK了!