天天看點

運用@Transactional,自己抛出異常時不會復原的原因

[b]一、 當你讀這篇文章的時候,假如讀者是在整合j2EE的三大架構式遇到這個問題,那應該就是下面這個問題:[/b]

    我記得當時我遇到這個問題的時候,是因為資料庫的表,不支援事務!如果mysql不支援存儲引擎,它将以MyISAM表建立表,這是非事務性表。一般修改成InnoDB.

    假如有興趣了解 mysql中 " engine=innodb " 以及 " engine=innodb 和engine=myisam的差別 ",可以讀讀這篇文章,或許對讀者有幫助:http://blog.sina.com.cn/s/blog_6ac4c6cb01018pb1.html

    可使用下述語句之一檢查表的标類型: 

    SHOW TABLE STATUS LIKE 'tbl_name';

    SHOW CREATE TABLE tbl_name;

  使用下述語句,可檢查mysqld伺服器支援的存儲引擎: 

    SHOW ENGINES;

    也可以使用下述語句,檢查與你感興趣的存儲引擎有關的變量值: 

    SHOW VARIABLES LIKE 'have_%';

  例如,要想确定InnoDB存儲引擎是否可用,可檢查have_innodb變量的值。

[b] 二 、假如讀者不是上述情況,那請研讀下面這段:[/b]

--------------------------------------------------------------------------------------------------

近日測試用例,發現這樣一個現象:

在業務代碼中,有如下兩種情況,比如:

throw new RuntimeException("xxxxxxxxxxxx"); 事務復原

throw new Exception("xxxxxxxxxxxx"); 事務沒有復原

自以為很了解事務,或許時間久遠的緣故,沒分析出來何故,遂查閱了下資料,寫下了如下的内容,供參考:

1).Spring的AOP即聲明式事務管理預設是針對unchecked exception復原。也就是預設對RuntimeException()異常或是其子類進行事務復原;checked異常,即Exception可try{}捕獲的不會復原,如果使用try-catch捕獲抛出的unchecked異常後沒有在catch塊中采用頁面寫死的方式使用spring api對事務做顯式的復原,則事務不會復原, “将異常捕獲,并且在catch塊中不對事務做顯式送出=生吞掉異常” ,要想捕獲非運作時異常則需要如下配置:

解決辦法:

1.在針對事務的類中抛出RuntimeException異常,而不是抛出Exception。

2.在txAdive中增加rollback-for,裡面寫自己的exception,例如自己寫的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>

2).spring的事務邊界是在調用業務方法之前開始的,業務方法執行完畢之後來執行commit or rollback(Spring預設取決于是否抛出runtime異常).

 如果抛出runtime exception 并在你的業務方法中沒有catch到的話,事務會復原。 

 一般不需要在業務方法中catch異常,如果非要catch,在做完你想做的工作後(比如關閉檔案等)一定要抛出runtime exception,否則spring會将你的操作commit,這樣就會産生髒資料.是以你的catch代碼是畫蛇添足。

如:

try {  

    //bisiness logic code  

} catch(Exception e) {  

    //handle the exception  

}  

 由此可以推知,在spring中如果某個業務方法被一個 整個包裹起來,則這個業務方法也就等于脫離了spring事務的管理,因為沒有任何異常會從業務方法中抛出!全被捕獲并吞掉,導緻spring異常抛出觸發事務復原政策失效。

 不過,如果在catch代碼塊中采用頁面寫死的方式使用spring api對事務做顯式的復原,這樣寫也未嘗不可。

 3).基于注解的事務:

 Transactional的異常控制,預設是Check Exception 不復原,unCheck Exception復原

 如果配置了rollbackFor 和 noRollbackFor 且兩個都是用同樣的異常,那麼遇到該異常,還是復原

 rollbackFor 和noRollbackFor 配置也許不會含蓋所有異常,對于遺漏的按照Check Exception 不復原,unCheck Exception復原

該博文轉自:[url]http://blog.sina.com.cn/s/blog_6ac4c6cb01018pbl.html[/url]