浅析Spring事务传播机制

在这里插入图片描述

基本概念

Spring的事务传播机制有以下七种

PROPAGATION_REQUIRED:Spring的默认传播级别,如果上下文中存在事务则加入当前事务,如果不存在事务则新建事务执行。

PROPAGATION_SUPPORTS:如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。

PROPAGATION_MANDATORY:该传播级别要求上下文中必须存在事务,否则抛出异常。

PROPAGATION_REQUIRES_NEW:该传播级别每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。(子事务的执行结果不影响父事务的执行和回滚)

PROPAGATION_NOT_SUPPORTED:当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复上下文事务。(降低事务大小,将非核心的执行逻辑包裹执行。)

PROPAGATION_NEVER:该传播级别要求上下文中不能存在事务,否则抛出异常。

PROPAGATION_NESTED:嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务。(save point概念)

接下来分析两种 PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW

案例分析

案例一:常规情况

这种也是大家最常见的情况

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void Example1(DemoEntity demoEntity) {
        demoMapper.insert(demoEntity);
        propagationService.required();
    }
	@Transactional
	@Override
	public void required() {
	    throw new NullPointerException("肥朝假装抛出了异常");
	}

测试

	@Test
    public void testExample1() {
        DemoEntity user = new DemoEntity();
        user.setName("batman");
        demoService.Example1(user);
    }

案例二:try-required

开始敲黑板划重点了,这个情况也是大家最常见的情况之一,但是由于这个try起来了,那么,这个insert能否插入数据呢?

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void Example2(DemoEntity demoEntity) {
        demoMapper.insert(demoEntity);
        try {
            propagationService.required();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void requiresNew() {
        throw new NullPointerException("假装抛出了异常");
    }

测试

    @Test
    public void testExample2() {
        DemoEntity user = new DemoEntity();
        user.setName("batman");
        demoService.Example2(user);
    }

案例三:try-requiresNew


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void Example3(DemoEntity demoEntity) {
        demoMapper.insert(demoEntity);
        try {
            propagationService.requiresNew();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void requiresNew() {
        throw new NullPointerException("假装抛出了异常");
    }

测试

    @Test
    public void testExample3() {
        DemoEntity user = new DemoEntity();
        user.setName("batman");
        demoService.Example3(user);
    }

案例四:常规情况

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void Example4(DemoEntity demoEntity) {
        demoMapper.insert(demoEntity);
        propagationService.requiresNew();
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void requiresNew() {
        throw new NullPointerException("假装抛出了异常");
    }
    @Test
    public void testExample4() {
        DemoEntity user = new DemoEntity();
        user.setName("batman");
        demoService.Example4(user);
    }

解密

案例一

这个不用说,稍微有点Java常识的人都知道,异常必然会导致回滚,数据库不会插入数据。

案例二

这个到底会不会插入数据呢?毕竟这个异常被try起来了。这个时候,正常的思维都会认为,能正常插入数据,但是答案是,不会插入数据,并且抛出异常
在这里插入图片描述

案例三

这个和案例二很像,因为有了案例二的阴影,这个时候你就变得不确定了。答案是,能正常插入数据。

案例四

这个和案例一很像,本来你是很确定能不能插入数据的,但是有了案例三的阴影之后,这个时候你又变得不确定了。答案是,不会插入数据。

原理

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

在这里插入图片描述

我们重点解析案例二和案例三的情况。我们再把那两个事务传播机制的意思来解读一下:

PROPAGATION_REQUIRED:Spring的默认传播级别,如果上下文中存在事务则加入当前事务,如果不存在事务则新建事务执行。

PROPAGATION_REQUIRES_NEW:该传播级别每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。

首先来看案例二,执行Example2方法的时候,由上文得知,将开启一个事务,再执行到required方法时,此时,因为用得的默认的隔离级别,因此,这个时候,会加入到刚才的事务之中,然后required方法中,出现了异常,我们来看

completeTransactionAfterThrowing(txInfo, ex);

中的核心方法
在这里插入图片描述

从doSetRollbackOnly(status)这个单词就知道,required的时候,已经把这个事务设置成RollbackOnly,因此,虽然try住了,但是Example2执行完提交的时候,却发现无法提交,所以异常信息如下:

Transaction rolled back because it has been marked as rollback-only

踩坑

@Transactional有很多注意点

  • 在同个类中A方法调用B方法,B方法是不会开启事务,自然也就不会用到事务的传播机制。(因为不会进入aop)!
  • @Transactional默认情况下,只回滚RuntimeException。如果你抛出的异常不是RuntimeException,可能导致在默认情况下和本文有所偏差

github地址: https://github.com/fafeidou/fast-cloud-nacos/tree/master/fast-common/fast-common-mybatis

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章