一、try catch 對 Spring 事務的影響
當 try catch 捕獲了異常,事務不會回滾。如果非得在 service 層寫 try catch,需要 catch 后 throw new RuntimeException 讓事務回滾。如果使用 try catch 捕獲拋出的unchecked exception
后沒有在 catch 塊中采用頁面硬編碼的方式使用 Spring api 對事務做顯式的回滾,則事務不會回滾。將異常捕獲,而未在 catch 塊中對事務做顯式回滾等同于生吞掉異常。
1??catch 只打印異常,不拋出異常
try {
數據庫新增訂單邏輯;
int a=5/0;
數據庫減少庫存邏輯;
}catch (Exception e){
e.printStackTrace();
}
該方法會影響事務,此時數據庫中訂單數據會插入成功!因為 Spring 的事務的標準是 RuntimeException。
2??catch 打印異常,并拋出異常
try {
數據庫新增訂單邏輯;
int a=5/0;
數據庫減少庫存邏輯;
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException();
}
該方法不會影響事務,因為拋出了 RuntimeException。
二、Spring 事務的默認機制
1??不可查的異常(unchecked exceptions):RuntimeException 及其子類和錯誤(Error)
~~~回滾
2??可查的異常(checked exceptions):Exception 下除 RuntimeException 之外的異常。即需要 try catch 或 向上拋出的 異常
~~~不回滾
3??可以配置所有異常回滾:@Transactional(rollbackFor = {Exception.class})
說明:
Spring 的 AOP 即聲明式事務管理,默認是針對unchecked exception
回滾,也就是默認對RuntimeException()
或是其子類進行事務回滾。
要想捕獲非運行時異常則需要如下配置:
1??在針對事務的類中拋出 RuntimeException,而不是拋出 Exception。
2??在 txAdive 中增加 rollback-for,里面寫自己的 exception:
<tx:advice id='txAdvice' transaction-manager='transactionManager'>
<tx:attributes>
<tx:method name='*' rollback-for='com.cn.untils.exception.XyzException'/>
</tx:attributes>
</tx:advice>
或者定義不回滾的異常:
<tx:advice id='txAdvice'>
<tx:attributes>
<tx:method name='update*' no-rollback-for='IOException'/>
<tx:method name='*'/>
</tx:attributes>
</tx:advice>
在 @Transactional 中如果不配置 rollbackFor 屬性,那么事務只會在遇到 RuntimeException 的時候才會回滾。加上 rollbackFor=Exception.class,可以讓事務在遇到非運行時異常也回滾。
如果不對運行時異常進行處理,那么出現運行時異常之后,要么是線程中止,要么是主程序終止。 如果不想終止,則必須捕獲所有的運行時異常,決不讓這個處理線程退出。隊列里面出現異常數據了,正常的處理應該是把異常數據舍棄,然后記錄日志。不應該由于異常數據而影響下面對正常數據的處理。
非運行時異常是 RuntimeException 以外的異常,類型上都屬于 Exception 類及其子類。如 IOException、SQLException 等以及自定義的 Exception。對于這種異常,Java 編譯器強制要求必須對出現的這些異常進行 catch 并處理,否則程序就不能編譯通過。所以,面對這種異常不管是否愿意,必須寫一大堆 catch 塊去處理可能的異常。三、Spring 的事務默認取決于是否拋出 RuntimeException
Spring 的事務邊界是在調用業務方法之前開始的,業務方法執行完畢之后來執行 commit or rollback。如果拋出 RuntimeException 并在業務方法中沒有 catch 到的話,事務會回滾。一般不需要在業務方法中 catch 異常,如果非要 catch,在做完想做的工作后(比如關閉文件等)一定要拋出 RuntimeException,否則 Spring 會將操作 commit,這樣就會產生臟數據。所以這樣的 catch 代碼是畫蛇添足。如:
try {
//bisiness logic code
} catch(Exception e) {
//handle the exception
}
由此,在 Spring 中如果某個業務方法被 try catch 整個包裹起來,則這個業務方法也就等于脫離了 Spring 事務的管理,因為沒有任何異常會從業務方法中拋出!全被捕獲并吞掉,導致 Spring 異常拋出觸發事務回滾策略失效。不過,如果在 catch 代碼塊中采用頁面硬編碼的方式使用 Spring api 對事務做顯式的回滾,這樣也未嘗不可。