一、什么是spring事务传播行为?

事务的传播行为就是在一个方法中嵌套一个或者多个事务,这个(些)嵌套的事务就涉及到了事务的传播行为,对于事务传播行为类型的设置是在被调用的方法上进行。例如:

public void methodParentA() {
    methodA();
    methodB();
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodParentB() {
    methodA();
    methodB();
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    System.out.println("methodA");
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    System.out.println("methodB");
}

如上代码,无论是在methodParentA还是在methodParentB中,都涉及到了事务的传播行为,其中propagation设置的类型不一样,会是不同的事务传播行为。

 

二、七种事务传播行为

在spring中有七种事务传播行为类型,分别是

PROPAGATION_REQUIRED

如果一个事务已开启,使用当前事务;如果没有事务开启,则开启自己的事务

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行

PROPAGATION_MANDATORY

使用当前事务,如果没有当前事务则抛出异常

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起

PROPAGATION_NOT_SUPPORTED

以非事务方式执行,如果当前存在事务,将当前事务挂起

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常

PROPAGATION_NESTED

如果当前存在事务,在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

 

三、以PROPAGATION_REQUIRED类型为例详解事务传播行为

1.在这里用到了两张测试表,两张表结构完全一样,仅名字不同。

CREATE TABLE `animal1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(25) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

CREATE TABLE `animal2` (
  `id` int(11) NOT NULL DEFAULT '0',
  `name` varchar(25) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

2.两张表所对应的各自的业务层代码

@Service
public class Animal1ServiceImpl implements Animal1Service {
    @Autowired
    private Animal1Mapper animal1Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void add(Animal1 animal1) {
        animal1Mapper.insert(animal1);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addWithException(Animal1 animal1) {
        animal1Mapper.insert(animal1);
        throw new RuntimeException();
    }
}
@Service
public class Animal2ServiceImpl implements Animal2Service {
    @Autowired
    private Animal2Mapper animal2Mapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void add(Animal2 animal2) {
        animal2Mapper.insert(animal2);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addWithException(Animal2 animal2) {
        animal2Mapper.insert(animal2);
        throw new RuntimeException();
    }
}

3.以PROPAGATION_REQUIRED类型为例,在五种种情况下分别测试,并记录测试结果

/**
     * 1.外层没有开启事务,但最后一行抛出异常
     * 2.内层两个事务传播行为均为REQURED,其中一个有抛出异常
     * 3.结果为animal1表插入了“狮子1”,animal2表未插入成功
     */
    public  void testRequiredNoTransactionalThrowException() {
        Animal1 animal1 = new Animal1();
        animal1.setName("狮子1");
        animal1Service.add(animal1);

        Animal2 animal2 = new Animal2();
        animal2.setName("狮子2");
        animal2Service.addWithException(animal2);

        throw new RuntimeException();
    }

    /**
     * 1.外层没有开启事务,且没有抛出异常
     * 2.内层两个事务传播行为均为REQURED,其中一个有抛出异常
     * 3.结果为animal1表插入了“狮子1”,animal2表未插入成功
     */
    @Override
    public void testRequiredNoTransactional() {
        Animal1 animal1 = new Animal1();
        animal1.setName("狮子1");
        animal1Service.add(animal1);

        Animal2 animal2 = new Animal2();
        animal2.setName("狮子2");
        animal2Service.addWithException(animal2);
    }

    /**
     * 1.外层开启事务,且没有抛出异常
     * 2.内层两个事务传播行为均为REQURED,其中一个有抛出异常
     * 3.结果为内层两个事务都发生了回滚,没有插入成功
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void testRequiredTransactional() {
        Animal1 animal1 = new Animal1();
        animal1.setName("狮子1");
        animal1Service.add(animal1);

        Animal2 animal2 = new Animal2();
        animal2.setName("狮子2");
        animal2Service.addWithException(animal2);
    }

    /**
     * 1.外层开启事务,且抛出异常
     * 2.内层两个事务传播行为均为REQURED,且都正常执行
     * 3.结果为内层两个事务都发生了回滚,没有插入成功
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void testRequiredTransactionalThrowException() {
        Animal1 animal1 = new Animal1();
        animal1.setName("狮子1");
        animal1Service.add(animal1);

        Animal2 animal2 = new Animal2();
        animal2.setName("狮子2");
        animal2Service.add(animal2);

        throw new RuntimeException();
    }

    /**
     * 1.内层两个事务传播行为均为REQURED,其中一个有抛出异常
     * 2.外层捕获异常,不主动抛出异常
     * 3.结果为内层两个事务都发生了回滚,没有插入成功
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void testRequiredTransactionalTryException() {
        Animal1 animal1 = new Animal1();
        animal1.setName("狮子1");
        animal1Service.add(animal1);

        Animal2 animal2 = new Animal2();
        animal2.setName("狮子2");
        try {
            animal2Service.addWithException(animal2);
        } catch (Exception e) {
            System.out.println("捕获异常,回滚");
        }
    }

4.结论

(1)在外层方法中没有开启事务的情况下,内层事务传播类型为 Propagation.REQUIRED的事务会各自开启新的事务,并且相互之间独立,互不干扰;

(2)在外层方法中开启事务的情况下,在内层事务传播类型为Propagation.REQUIRED的事务会加入外层的事务,也就是它们同属于一个事务,只要有其中一个逻辑发生了回滚,则外层方法所包含的内容全部回滚。

5.其他

在这里,我以事务传播类型Propagation.REQUIRED为例,测试了七种事务传播类型中的一种。本文的目的主要是在解释spring的事务传播类型是什么,而其他六种事务传播类型的测试结果就不在此赘述了。