Spring的事务传播机制

Spring的事务传播机制

编码文章call10242025-02-24 15:12:3316A+A-

一、理解事务传播机制的概念?

定义:多个事务方法相互调用时,事务方法之间是如何传播的

举个例子,方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。

二、Spring中事务的传播机制有几种?

定义:主要是通过枚举类Propagation来实现的,并且定义了7种事务传播类型,大概总结为4种支持事务,3种是不支持事务的。

  1. Required 默认的事务传播机制,如果当前有事务的话,就加入当前的事务,反之自己创建一个事务
  2. Supports 如果当前有事务的话,就加入当前的事务,反之以非事务的方式提交.
  3. Mandatory 如果当前有事务的话,就加入当前的事务,反之就抛出异常
  4. Nested 如果当前有事务的话,就会嵌套一个子事务和父事务一起执行,反之和required一样
  5. Required_new 如果当前有事务的话,挂起当前的事务,创建一个新事务执行
  6. Not_supports 如果当前有事务的话,挂起当前的事务,以非事务的方式执行
  7. Never 如果当前有事务的话,则抛除异常,反之以非事务的方式执行

三、事务失效的几种场景?

1.注解@Transactional配置的方法是非public权限修饰

2.注解修饰的方法被类内部方法调用

这种失效场景是我们日常开发中最常踩坑的地方;在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。

为什么会失效呢?其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。

@Service
public class ClassServiceImpl implements ClassService {
    @Autowired
    private ClassMapper classMapper;
    public void insertClass(ClassDo classDo) throws CustomException {
        insertClassByException(classDo);
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void insertClassByException(ClassDo classDo) throws CustomException {
        classMapper.insertClass(classDo);
        throw new RuntimeException();
    }
}
测试用例:
@Test
    public void insertInnerExceptionTest() throws CustomException {
       classDo.setClassId(2);
       classDo.setClassName("java_2");
       classDo.setClassNo("java_2");
       classService.insertClass(classDo);
    }

解决方案

类内部使用其代理类调用事务方法:以上方法略作改动

public void insertClass(ClassDo classDo) throws CustomException {
//         insertClassByException(classDo);
        ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
    }

3.异常类型非RuntimeException

解决方案:

@Transactional注解修饰的方法,加上rollbackfor属性值,指定回滚异常类型:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)

4.捕获异常后,却未抛出异常

在事务方法中使用try-catch,导致异常无法抛出,自然会导致事务失效。

@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) {
        classMapper.insertClass(classDo);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

解决方案:捕获异常并抛出异常

@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void insertClassByException(ClassDo classDo) {
        classMapper.insertClass(classDo);
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

5.数据库存储引擎不支持事务

以MySQL关系型数据为例,如果其存储引擎设置为 MyISAM,则事务失效,因为MyISMA 引擎是不支持事务操作的,故若要事务生效,则需要设置存储引擎为InnoDB ;目前 MySQL 从5.5.5版本开始默认存储引擎是:InnoDB;

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4