zoukankan      html  css  js  c++  java
  • 我的事务为什么会失效

    我的事务为啥会失效?

    在用Spring的时候,我们经常用过使用@Transactional声明式事务,但是有些时候,@Transactional声明的事务却是没有生效。

    一个例子。

    • 环境

    数据库为innodb,代码为基于spring的一个demo。

    Demo代码如下:

    package jfound.demo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    public class DemoService {
        @Autowired
        private DemoMapper demoMapper;
        public void methodA() {
            this.methodB(i);
        }
        @Transactional
        public void methodB() {//发生异常的方法B
            User user = new User();
            user.setId(1);
            user.setName("Tester");
            user.setAge(10);
            demoMapper.insertUser(user);//往user表插入一条用户记录
            int a = 1 / 0;//抛出ArithmeticException异常
        }
    }
    
    • 代码说明

    代码中methodB声明式事务注解 @Transactional,在B中,插入一个user对象后,再伪造一个ArithmeticException异常然后抛出;而methodA中的方法是没有声明式事务的,methodA调用了方法methodB

    • 执行例子

    注:调用方都是DemoService 外的Component,如controller、其他service等。

    demoService.methodA();
    

    当外部一个bean调用方法 methodA时候,methodA 会接收到一个异常,但user已经能成功插入数据库,也就间接说明事务是没有回滚的。有人说在抛异常的时候,methodA没有对methodB进行异常捕捉并手动回滚,然后试下手动回滚。

    public void methodA() {
    	try{
    		this.methodB(i);
    	}catch(Exception e){
        e.printStackTrace();
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }    
    }
    

    当添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();手动回滚的时候,返回methodA中抛出了一下异常:

    org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
        at org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(TransactionAspectSupport.java:122)
        ...
    

    从抛出的异常描述来看,也就是没有异常,怎么回事?明明是有@Transactional注解声明了事务的。

    当调用方直接调用methodB的时候。

    demoService.methodB();
    

    虽然调用方接收到了异常,但是user却没有插入到数据库,也就是说明,事务是生效的。

    @Transactional 工作原理

    • @Transactional 被外部类的代码调用时,Spring在运行时为方法所在类生产一个AOP对象
    • AOP对象根据@Transactional的属性,决定是否由事务拦截器对此方法进行事务拦截
    • 在进行事务拦截时,会先开始事务,然后执行业务代码,根据执行结果是否抛出异常,然后通过事务管理器来进行rollback还是commit

    也可以阅读下上一篇 Spring注入的对象到底是什么类型 来理解下spring注入的对象是什么类型的对象。

    简单说,spring 的声明式事务是通过aop来管理的,是用动态代理对象来实现事务管理,所以调用方所注入的对象一定是代理对象才能使事务生效,当然,当spring ioc 检查到需要aop的时候,是会生成代理对象。然而需要注意的是,所在类的方法之间调用时,@Transactional注解是不会生效的。

    @Async等注解也是同样的道理。

    解决方法

    • 最外层被调用的声明@Transactional注解,例如在例子中的methodA上添加注解@Transactional
    • 使用TransactionTemplate来手动管理实务

    注意事项

    事务失效问题排查

    • 数据库引擎是否支持事务

    Mysql的Innodb和bdb引擎是事务引擎,支持事务,如需要事务控制,必须使用事务引擎

    • 声明式注解是否被加载成bean

    @Transactional注解所在对象必须是被spring ioc管理的,也就是一个 bean,自己手动new出来的对象没被spring 管理的话,注解是失效的。

    • 注解所在的方法是被public修饰,并非自调

    注解所在的方法必须被public修饰,而且调用方必须是其他bean,不能是类本身的其他方法调用

    总结

    在使用 @Transactional声明式事务的时候,要注意一定是外部的bean来调用,@Transactional的功能是通过AOP动态代理类来实现事务管理功能,类自身调用的没有效果的。

    微信关注我,发现更多java领域知识
    JFound

  • 相关阅读:
    Elasticsearch 架构解析与最佳实践
    Redis一键安装脚本
    Android——GridView(网格视图)相关知识总结贴
    Android——TabHost(标签容器)相关知识总结贴
    Android——RatingBar(评价条)相关知识总结贴
    Android——SeekBar(拖动条)相关知识总结贴
    Android——SQLite/数据库 相关知识总结贴
    Android——媒体库 相关知识总结贴
    Android——Broadcast Receive 相关知识总结贴
    Android——进程通信/ AIDL/Message相关知识总结贴
  • 原文地址:https://www.cnblogs.com/jfound/p/12914740.html
Copyright © 2011-2022 走看看