zoukankan      html  css  js  c++  java
  • Spring事务异常回滚,捕获异常不抛出就不会回滚(转载) 解决了我一年前的问题

    最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......  
      为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志。但是这边情况来了,当这个方法异常时候 日志是打印了,但是加的事务却没有回滚。

      例:  
       类似这样的方法不会回滚 (一个方法出错,另一个方法不会回滚) :  

    if(userSave){        
        try {       
            userDao.save(user);        
            userCapabilityQuotaDao.save(capabilityQuota);       
         } catch (Exception e) {        
            logger.info("能力开通接口,开户异常,异常信息:"+e);       
         }       
     }

    下面的方法回滚(一个方法出错,另一个方法会回滚):

    if(userSave){         
         try {          
            userDao.save(user);          
            userCapabilityQuotaDao.save(capabilityQuota);         
           } catch (Exception e) {         
            logger.info("能力开通接口,开户异常,异常信息:"+e);          
            throw new RuntimeException();         
         }          
    }

    或者:

    if(userSave){          
        try {          
            userDao.save(user);          
            userCapabilityQuotaDao.save(capabilityQuota);          
        } catch (Exception e) {          
            logger.info("能力开通接口,开户异常,异常信息:"+e);          
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();         
        }         
     }  

    为什么不会滚呢??是对Spring的事务机制就不明白。!! 
       默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。  
       spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过  
        
    配置来捕获特定的异常并回滚  
      换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
      解决方案: 
      方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
      方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)

    参考博客  Spring事务管理只对出现运行期异常进行回滚   http://blog.csdn.net/abc19900828/article/details/39497631

    spring事物传播机制和隔离级别    http://blog.csdn.net/hijiankang/article/details/9174461

    spring事务处理详解    http://blog.csdn.net/u013628152/article/details/47980811

    1:Java包含两种异常:checked异常和unchecked异常。

    checked和unchecked异常之间的区别是: 
    Checked异常必须被显式地捕获try-catch-finally,而unchecked异常则可以不必捕获或抛出。 
    Checked异常继承java.lang.Exception类。Unchecked异常继承自java.lang.RuntimeException类。

    2:Mysql 存储引擎中InnoDB与Myisam的主要区别

    事务处理方面:innodb 支持事务功能,myisam 不支持。 
    Myisam 的执行速度更快,性能更好。

    engine = innodb 和 engine = myisam 
    可使用下述语句之一检查表的表类型: 
    SHOW TABLE STATUS LIKE ‘tbl_name’; 
    SHOW CREATE TABLE tbl_name;

    使用下述语句,可检查mysqld服务器支持的存储引擎: 
    SHOW ENGINES;

    3:spring事务处理

    throw new RuntimeException(“xxxxxxxxxxxx”); 默认事务回滚 
    throw new Exception(“xxxxxxxxxxxx”); 默认事务不回滚 
    Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。也就是默认对RuntimeException()异常或是其子类进行事务回滚; 
    checked异常,throw new Exception默认不会回滚

    若想让checked异常或者自定义异常回滚 
    (1)配置文件方式

    <tx:advice id="txAdvice">
        <tx:attributes>
           <tx:method name="update*" no-rollback-for="IOException"/>
           <tx:method name="*"/>
        </tx:attributes>
     </tx:advice>
    
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
         <tx:method name="*" rollback-for="com.cn.untils.exception.MyException"/>
       </tx:attributes>
     </tx:advice>
    

    (2)注解方式

    @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = MyException.class)

    不起作用:

    @Transactional
    public void addAdmin(Admin admin) throws Exception {
        try {
            this.adminDao.add(admin);
            admin.setUsername("123456789012345");
            this.adminDao.update(admin);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("测试!");
        }
    
    }

    起作用

    @Transactional(rollbackFor = Exception.class)
    public void addAdmin(Admin admin) throws Exception {
        try {
            this.adminDao.add(admin);
            admin.setUsername("123456789012345");
            this.adminDao.update(admin);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("测试!");
        }
    
    }

    起作用

    @Transactional
    public void addAdmin(Admin admin) {
        try {
            this.adminDao.add(admin);
            admin.setUsername("123456789012345");
            this.adminDao.update(admin);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("测试!");
        }
    
    }

    完整写法:

    @Transactional(rollbackFor = RuntimeException.class)
    public void addAdmin(Admin admin) {
        try {
            this.adminDao.add(admin);
            admin.setUsername("123456789012345");
            this.adminDao.update(admin);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("测试!");
        }
  • 相关阅读:
    JVM 综述
    看 Netty 在 Dubbo 中如何应用
    Netty 心跳服务之 IdleStateHandler 源码分析
    Netty 高性能之道
    Netty 解码器抽象父类 ByteToMessageDecoder 源码解析
    Netty 源码剖析之 unSafe.write 方法
    Netty 出站缓冲区 ChannelOutboundBuffer 源码解析(isWritable 属性的重要性)
    Netty 源码剖析之 unSafe.read 方法
    Netty 内存回收之 noCleaner 策略
    Netty 源码阅读的思考------耗时业务到底该如何处理
  • 原文地址:https://www.cnblogs.com/chengpeng15/p/5898534.html
Copyright © 2011-2022 走看看