1. 程式人生 > >運用@Transactional,自己丟擲異常時不會回滾的原因

運用@Transactional,自己丟擲異常時不會回滾的原因

問題

        使用spring的配置事物註解@Transactional,在測試的時候發現不起作用。

環境

         配置檔案

  1. <beanid="studentMGDataSource"class="org.apache.commons.dbcp.BasicDataSource"
  2.     destroy-method="close">
  3.     <propertyname="driverClassName"value="${student_MG_jdbc.driver}"/>
  4.     <propertyname="url"value=
    "${student_MG_jdbc.url}"/>
  5.     <propertyname="username"value="${student_MG_jdbc.username}"/>
  6.     <propertyname="password"value="${student_MG_jdbc.password}"/>
  7.     <propertyname="initialSize"value="${student_MG_jdbc.initialSize}"/>
  8.     <propertyname="maxActive"value="${student_MG_jdbc.maxActive}"
    />
  9.     <propertyname="maxIdle"value="${student_MG_jdbc.maxIdle}"/>
  10.     <propertyname="maxWait"value="${student_MG_jdbc.maxWait}"/>
  11.     <propertyname="defaultAutoCommit"value="${student_MG_jdbc.defaultAutoCommit}"/>
  12. </bean>
  13. <beanid="studentMGSqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"
    >
  14.     <propertyname="configLocation"value="classpath:mybatis/mybatis-studentMG-config.xml"/>
  15.     <propertyname="dataSource"ref="studentMGDataSource"/>
  16. </bean>
  17. <beanid="studentMGSqlSession"class="org.mybatis.spring.SqlSessionTemplate">
  18.     <constructor-argindex="0"ref="studentMGSqlSessionFactory"/>
  19. </bean>
  20. <beanid="studentMGTxManager"
  21.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  22.     <propertyname="dataSource"ref="studentMGDataSource"/>
  23. </bean>
  24. <tx:annotation-drivenproxy-target-class="true"transaction-manager="studentMGTxManager"/>

        Java程式碼

  1. @Transactional(value="studentMGTxManager",rollbackFor=java.lang.Exception.class)  
  2. publicvoid saveStudentDto(List<StudentDto> dtoList, String classId) {  
  3. }  

原因

        資料庫使用的儲存引擎是MyISam,MyISam不支援事物,應該用InnoDB引擎

TIPS

        @Transactional註解事務不起作用的解決
         可能的原因:
        1.資料庫引擎要支援事務
        如果是MySQL,注意表要使用支援事務的引擎,比如innodb,如果是myisam,事務是不起作用的

        2.是否開啟了對註解的解析

        配置檔案必須加<tx:annotation-driven />,否則不解析@Transactional

假如讀者是在整合j2EE的三大框架式遇到這個問題,那應該就是下面這個問題:

    我記得當時我遇到這個問題的時候,是因為資料庫的表,不支援事務!如果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變數的值。

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

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

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


在業務程式碼中,有如下兩種情況,比如:
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回滾


spring的配置檔案中,如果資料來源的defaultAutoCommit設定為True了,那麼方法中如果自己捕獲了異常,事務是不會回滾的,如果沒有自己捕獲異常則事務會回滾,如下例
比如配置檔案裡有這麼條記錄:

[html] view plain copy  print?
  1. <beanid="dataSource"class="xxx">
  2.    <propertyname="xxx"value="xxx"/>
  3.    <propertyname="xxx"value="xxx"/>
  4.                      ....  
  5.      <propertyname="defaultAutoCommit"value="true"/>
  6. </bean>

那麼現在有兩個情況
情況1:如果沒有在程式中手動捕獲異常
[java] view plain copy  print?
  1. @Transactional(rollbackFor = { Exception.class })  
  2. publicvoid test() throws Exception {  
  3.      doDbStuff1();  
  4.      doDbStuff2();//假如這個操作資料庫的方法會丟擲異常,現在方法doDbStuff1()對資料庫的操作   會回滾。
  5. }  

情況2:如果在程式中自己捕獲了異常
[java] view plain copy  print?
  1. @Transactional(rollbackFor = { Exception.class })  
  2. publicvoid test() {  
  3.      try {  
  4.         doDbStuff1();  
  5.         doDbStuff2();//假如這個操作資料庫的方法會丟擲異常,現在方法doDbStuff1()對資料庫的操作  不會回滾。
  6.      } catch (Exception e) {  
  7.            e.printStackTrace();     
  8.      }  
  9. }  

現在如果我們需要手動捕獲異常,並且也希望拋異常的時候能回滾腫麼辦呢?
下面這樣寫就好了,手動回滾事務:
[java] view plain copy  print?
  1. @Transactional(rollbackFor = { Exception.class })  
  2. publicvoid test() {  
  3.      try {  
  4.         doDbStuff1();  
  5.         doDbStuff2();  
  6.      } catch (Exception e) {  
  7.           e.printStackTrace();     
  8.           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是這一句了,加上之後,如果doDbStuff2()拋了異常,                                                                                       //doDbStuff1()是會回滾的
  9.      }  
  10. }  



相關推薦

運用@Transactional自己異常原因

問題         使用spring的配置事物註解@Transactional,在測試的時候發現不起作用。 環境          配置檔案 <beanid="studentMGDataSource"class="org.apache.common

程式碼異常後進行事務的兩種方式(Spring @Transactional註解)

需求 在service層的某個方法中,在執行完一個對資料庫的寫方法後,丟擲異常,再執行另一個對資料庫的寫方法,虛擬碼如下: @Transactional public void func() { dao.write(pojo1); throw new Exception("異常"

java事務異常——Spring事務異常捕獲異常

最近遇到了事務不回滾的情況,我還考慮說JPA的事務有bug? 我想多了.......     為了列印清楚日誌,很多方法我都加tyr catch,在catch中列印日誌。但是這邊情況來了,當這個方法異常時候 日誌是列印了,但是加的事務卻沒有回滾。   例:      類

十六、Spring事務異常捕獲異常

最近遇到了事務不回滾的情況,我還考慮說JPA的事務有bug? 我想多了……. 為了列印清楚日誌,很多方法我都加tyr catch,在catch中列印日誌。但是這邊情況來了,當這個方法異常時候 日誌是列印了,但是加的事務卻沒有回滾。 例: 類似這樣的方法不會回滾 (一個方法出錯,另一個方

Spring事務異常捕獲異常

最近遇到了事務不回滾的情況,我還考慮說JPA的事務有bug? 我想多了.......     為了列印清楚日誌,很多方法我都加tyr catch,在catch中列印日誌。但是這邊情況來了,當這個方法異常時候 日誌是列印了,但是加的事務卻沒有回滾。   例:      

Java 原碼、反碼、補碼自定義的異常手動異常

1.byte 型佔一個位元組,正數按原碼儲存,負數按補碼儲存。第一位為符號符,“0”表示正數,“1”表示負數。 0000 0000表示 0 ,1000 0000 表示 -0,由於byte表示的範圍為256,+0,-0對應補碼都為0000000,所以補碼10000000沒有所為

Spring 事物Exception 異常事物沒有

Spring 宣告式事務 只針對  RuntimeException 異常丟擲時才會回滾事物,如果時Exception  丟擲時是不會回滾的。 如果想要讓exception 丟擲時也讓事物回滾 則可以在spring 配置檔案中新增 一個AOP  配置: <tx:adv

是返回錯誤碼還是異常?說說我的選擇

昨晚翻了翻《 松本行弘的程式世界 》這本書,看到他對異常設計原則的講述,覺得頗為贊同。近期的面試,我有時也問類似的問題,但應聘者的回答大都不能令人滿意。有必要理一理,說說我是怎麼理解的,以及在程式設計實踐中如何做出合適的選擇。當然這只是一家之言,未必就是完全正確的。

Springboot中宣告事務@Transactional為何有時候聲明瞭事務報異常資料卻

@Transactional 這個註解相信大家都不陌生,這是事務的註解,什麼是事務,無非就是未保證資料一致性,當出現任何異常時候出現數據回滾 註解是是不需要寫提交事務的。 那麼,最近我發現,這個@Transactional的註解,並不是所有異常都可以進行資料回滾,他只有

Spring事務異常捕獲異常拋出就

actions .info time tpi detail ava ogg ren tool 最近遇到了事務不回滾的情況,我還考慮說JPA的事務有bug? 我想多了....... 為了打印清楚日誌,很多方法我都加tyr catch,在catch中打印日誌。但是這邊情

@Transactional 中使用 try catch之後

採坑記錄:        使用spring的事務管理的時候,建議在service的try catch中丟擲自動異常,然後在controller層做統一的異常處理,再返回給檢視。也可以使用切面捕獲異常,返

問題描述: 在使用mybatis對資料庫執行更新操作parameterType為某個具體的bean而bean中傳入的引數為null異常如下:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.type.TypeException: Could not set parameters for mappin

關於Tomcat啟動專案控制檯各種異常誤以為專案錯誤

1.當在eclipse啟動tomcat,初始化專案時,控制檯會列印啟動日誌:當出現            九月 19, 2017 3:07:35 下午 org.springframework.web.

stack 處理 pop異常

leetcode 20 :https://leetcode.com/problems/valid-parentheses/description/ 如果 stack 為空時,pop()會 丟擲exception, 處理的方法如下: try { char top = st.pop(); if (!

【C#作業】學生成績新增並排序錯誤則異常

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Cons

第四章App4_3懂得了異常 throws Exceptionread為讀取鍵盤輸入數學會了switch迴圈

package 第四章; import java.io.EOFException; //filename:App4_3.javapublic class App4_3 //定義一個公共類App4_3{ public static void main(String[] args) throws Except

自定義的異常異常處理中異常

你可以用raise語句來引發一個異常。異常/錯誤物件必須有一個名字,且它們應是Error或Exception類的子類 下面是一個引發異常的例子: class ShortInputException(Exception): ‘’‘自定義的異常類’’’ def init(self, le

synchronized重入後異常鎖釋放了嗎

synchronized用於同步方法或者程式碼塊,使得多個執行緒在試圖併發執行同一個程式碼塊的時候,序列地執行。以達到執行緒安全的目的。 在多執行緒的時候是這樣的,但是對於單執行緒,是允許重入的,每重入一次,計數器加1,當退出程式碼塊時,計數器減1。 那正常退出時計數器減1,拋異常時計數器也是減1。那如果

java實現階乘的計算異常怎麼使用標號跳轉需要幫解決一下

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

【求解答】service異常為什麼controller捕捉到呢?

RT,直接貼程式碼吧! 這個是我的service,service丟擲異常,controller沒有捕捉到,直接走進原始碼了。 //每日傳送次數 if (null != redisCacheManager.hget(KEY_CACHE_COUNT_SEND_VER