您的位置:首页 > 其它

运用@Transactional,自己抛出异常时不会回滚的原因

2016-11-10 16:15 495 查看


问题

        使用spring的配置事物注解@Transactional,在测试的时候发现不起作用。


环境


       


         配置文件

[html] view
plain copy

 





<bean id="studentMGDataSource" class="org.apache.commons.dbcp.BasicDataSource"  

    destroy-method="close">  

    <property name="driverClassName" value="${student_MG_jdbc.driver}" />  

    <property name="url" value="${student_MG_jdbc.url}" />  

    <property name="username" value="${student_MG_jdbc.username}" />  

    <property name="password" value="${student_MG_jdbc.password}" />  

    <property name="initialSize" value="${student_MG_jdbc.initialSize}" />  

    <property name="maxActive" value="${student_MG_jdbc.maxActive}" />  

    <property name="maxIdle" value="${student_MG_jdbc.maxIdle}" />  

    <property name="maxWait" value="${student_MG_jdbc.maxWait}" />  

    <property name="defaultAutoCommit" value="${student_MG_jdbc.defaultAutoCommit}" />  

</bean>  

  

<bean id="studentMGSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  

    <property name="configLocation" value="classpath:mybatis/mybatis-studentMG-config.xml" />  

    <property name="dataSource" ref="studentMGDataSource" />  

</bean>  

  

<bean id="studentMGSqlSession" class="org.mybatis.spring.SqlSessionTemplate">  

    <constructor-arg index="0" ref="studentMGSqlSessionFactory" />  

</bean>  

  

<bean id="studentMGTxManager"  

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  

    <property name="dataSource" ref="studentMGDataSource" />  

</bean>  

  

<tx:annotation-driven proxy-target-class="true" transaction-manager="studentMGTxManager" />  


        Java代码

[java] view
plain copy

 





@Transactional(value="studentMGTxManager",rollbackFor=java.lang.Exception.class)  

public void saveStudentDto(List<StudentDto> dtoList, String classId) {  

      

}  


原因

        数据库使用的存储引擎是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?

<bean id="dataSource" class="xxx">  

   <property name="xxx" value="xxx"/>  

   <property name="xxx" value="xxx"/>  

                     ....  

     <property name="defaultAutoCommit" value="true" />  

</bean>  

那么现在有两个情况

情况1:如果没有在程序中手动捕获异常

[java] view
plain copy

 print?

@Transactional(rollbackFor = { Exception.class })  

public void test() throws Exception {  

     doDbStuff1();  

     doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作   会回滚。  

}  

情况2:如果在程序中自己捕获了异常

[java] view
plain copy

 print?

@Transactional(rollbackFor = { Exception.class })  

public void test() {  

     try {  

        doDbStuff1();  

        doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作  不会回滚。  

     } catch (Exception e) {  

           e.printStackTrace();     

     }  

}  

现在如果我们需要手动捕获异常,并且也希望抛异常的时候能回滚肿么办呢?

下面这样写就好了,手动回滚事务:

[java] view
plain copy

 print?

@Transactional(rollbackFor = { Exception.class })  

public void test() {  

     try {  

        doDbStuff1();  

        doDbStuff2();  

     } catch (Exception e) {  

          e.printStackTrace();     

          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果doDbStuff2()抛了异常,                                                                                       //doDbStuff1()是会回滚的  

     }  

}  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: