Spring中@Transcation原理、注意事项

一、原理

spring事务有两种实现方式,基于jdk动态代理实现、基于cglib实现。spring默认基于jdk动态代理,springboot貌似2.x以后默认是使用cglib,当然选择使用哪种方式都是可配置的。无论使用哪种方式其原理简单来说就是
运行时动态生成代理类、加载该类、执行增强后的代理类的方法。

  • jdk动态代理是基于接口生成接口的实现类,接口没有的方法或者private、protected方法是无法被代理的
  • cglib代理是基于继承生成被代理类的子类

在这里插入图片描述

二、简单使用

Spring事务管理涉及的接口的联系如下:
在这里插入图片描述
我们常用的mybatis使用的是DataSourceTransactionManager类
编程式事务这里不多说,这里只讲基于注解的声明式事务。
相关配置这里不详细举例,配置好后使用很简单,一般是在方法上加上@Transactinal注解即可。

    @Override
    @Transactional
    public int update() {
        userService.update2();
        return userMapper.update();
    }

三、回滚策略

@Transcation 默认只回滚 抛出RuntimeException 及Error异常的事务,因此默认情况下如果抛出SQLException、IOException不进行处理的话是无法回滚事务的。
可以通过设置rollbackFor的方式指定哪些异常回滚

//这样指定了,error也一样会回滚
@Transactional(rollbackFor = Exception.class)

error是一定会回滚的
三点结论:

  1. 当我们抛出的异常为RunTime及其子类或者Error和其子类的时候。不论rollbackFor的异常是啥,都会进行事务的回滚。
  2. 当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,必须根据rollbackfor进行回滚。比如rollbackfor=RuntimeException,而抛出IOException时候,事务是不进行回滚的。
  3. 当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,如果嵌套事务中,只要有一个rollbackfor允许回滚,则整个事务回滚。

注意下面这种方式,在使用spring整合mybatis的项目中,user表添加了唯一索引模拟sql异常,最后打印

====异常类型:class org.springframework.dao.DuplicateKeyException

SQL异常被spring封装成了Runtime异常,如果大家想测试上面的结论直接手动throw相应的异常就可以了

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            int insert2 = userMapper.insert(user);
            return insert;
        } catch (Exception e){
            System.out.println("====异常类型:"+e.getClass());
            throw e;
        }
    }

在这里插入图片描述

四、嵌套事务的回滚策略

事务嵌套回滚策略由事务的传播行为决定,可以通过如下方式指定:

    @Transactional(propagation = Propagation.REQUIRED)
事务传播行为类型 说明 外围方法不开启事务 外围方法开启事务
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。外围方法不会加入到任一事务 外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 - -
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 - -
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 同第一排PROPAGATION_REQUIRED 开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 - -
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 - -
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 同第一排PROPAGATION_REQUIRED 外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务

五、事务方法调用自己类下面的其它方法

如下:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            this.update();//调用本类中其它添加了事务注解的方法,导致update事务不生效
            return insert;
        } catch (Exception e){
            throw new RuntimeException();
        }
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int update() {
        try {
            return userMapper.update();
        } catch (Exception e){
            throw new RuntimeException();
        }
    }

update事务失效是因为调用的动态代理类的增强方法,该方法内调用实际被增强类的方法,方法内this.xxx();,并不是使用的代理类。
在这里插入图片描述
解决方法:
使用增强后的代理类调用该方法,使用@Autowired或从spring容器获取bean,如果该Service加了事务注解,拿到的bean类型其实就是$ProxyXX等代理类并不是我们自己写的Service
如果使用的是springBoot,在启动类添加:@EnableAspectJAutoProxy(ExposeProxy=true) 在通过
AopContext.currentProxy()可以拿到当前类的代理类;

    @Autowired
    private UserService userService;
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int insert(User user) {
        try {
            int insert = userMapper.insert(user);
            //this.update();//调用本类中其它添加了事务注解的方法,导致update事务不生效
            userService.update();//替换为调用增强类的方法
            return insert;
        } catch (Exception e){
            throw new RuntimeException();
        }
    }

六、事务注解的timeout

一般我们使用默认值,默认为-1等待事务执行结果永不超时。
大家可以参考另一篇博客:spring事务超时时间测试
spring事务的超时时间 = 事务中最后一条sql执行完毕的时间 - 事务开始时间。

发布了52 篇原创文章 · 获赞 7 · 访问量 3792

猜你喜欢

转载自blog.csdn.net/maomaoqiukqq/article/details/103941157