Spring事务传播行为总结

Spring事务传播行为总结


1、概念

事务传播行为(propagation behavior):指的就是当一个事务方法被另一个事务方法调用时,这个事务与事务应该如何运行。
事务传播行为是Spring框架独有的事务增强特性。这是Spring为我们提供的强大的工具箱,使用事务传播行为可以为我们的开发工作提供许多便利。


2、Spring的七种事务传播行为类型

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为:

事务注解源码枚举:
Spring事务传播行为总结_第1张图片

总结:

事务传播行为类型 对应说明 验证结果
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(spring 默认的类型) 外围方法未开启事务:内部方法各自执行,走各自的事务,互不影响;在外围方法开启事务的情况下,内部方法会加入到外围方法的事务中,所有内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 外围开启SUPPORTS,且内部也是SUPPORTS情况下,相当于没有事务;外围开启SUPPORTS,内部方法开启别的事务,内部方法按开启的事务类型执行,互不影响;
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 外围方法开启MANDATORY事务或者没有开启事务,不管内部方法任何事务类型或者内部方法均不开启事务,都会抛出异常;外围开启别的事务,内部方法会加入到外围方法的事务中一起执行
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 内部方法声明REQUIRES_NEW类型后会新建独立事务执行,与外围方法不走同一个事务,内部方法之间事务互不影响
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 内部方法声明后就是以非事务执行,外围方法对其try catch的情况下,内部方法的异常不会导致外围方法回滚
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 以非事务执行,如果加入到的外围方法中包含事务就会报错
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 外围方法在对内部方法try catch的情况下,内部子事务可以单独回滚而不影响外围主事务和其他子事务

3、各种行为类型及对应的结果验证

声明:
user方法 person方法 为不同的两个表的方法,外围方法同时调用 user 和 person;

user方法:

    @Override
    public void addUser(User user) {
        userMapper.addUser(user);
    }

person方法:

    @Override
    public void addPerson(Person person) {
        personMapper.addPerson(person);
    }

person含异常方法:

    @Override
    public void addPersonException(Person person) {
        personMapper.addPerson(person);
        int i = 1/0;
    }

3.1、PROPAGATION_REQUIRED类型

@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)

场景3.1.1:外围方法不开启事务,User方法,Person方法均开启事务,且外围方法包含异常

外围方法:

    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPerson(person);
        
        //异常
        int i = 1/0;
    }

运行结果:

user表插入成功:
在这里插入图片描述

person表插入成功:
Spring事务传播行为总结_第2张图片


场景3.1.2:外围方法不开启事务,User方法,Person方法均开启事务,且Person方法包含异常

外围方法:

    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPersonException(person);
    }

运行结果:

user表插入成功:
在这里插入图片描述

person表插入失败:
在这里插入图片描述


场景3.1.3 外围方法开启事务,User方法,Person方法均开启事务,且外围方法包含异常

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {
        
        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPerson(person);
        
        //异常
        int i = 1/0;
    }

运行结果:

user表插入失败:
Spring事务传播行为总结_第3张图片
person表插入失败:
Spring事务传播行为总结_第4张图片


场景3.1.4 外围方法开启事务,User方法,Person方法均开启事务,且Person方法包含异常

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPersonException(person);
    }

运行结果:

user表插入失败:
在这里插入图片描述

person表插入失败:
在这里插入图片描述


场景3.1.5 外围方法开启事务,User方法,Person方法均开启事务,且Person方法包含的异常被try catch捕获

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        
        try{
            personService.addPersonException(person);
        }catch (Exception e){
            System.out.println("捕获person异常方法!");
        }
    }

运行结果:

user表插入失败:
在这里插入图片描述

person表插入失败:
在这里插入图片描述


PROPAGATION_REQUIRED类型事务总结:

对应场景 外围是否开启事务 外围方法是否包含异常 外围是否捕获异常 person方法是否包含异常 最终执行结果
3.1.1 × × × 均成功
3.1.2 × × × user成功,person失败
3.1.3 × × 均失败
3.1.4 × × 均失败
3.1.5 捕获person的异常 均失败

PROPAGATION_REQUIRED类型事务结论:
在外围方法开启事务的情况下,内部方法会加入到外围方法的事务中,所有内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。


3.2 PROPAGATION_SUPPORTS类型

@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)

PROPAGATION_SUPPORTS类型事务总结:

经验证:
外围开启SUPPORTS,且内部也是SUPPORTS情况下,相当于没有事务;
外围开启SUPPORTS,内部方法开启别的事务,内部方法按开启的事务类型执行,互不影响;


3.3 PROPAGATION_MANDATORY类型

@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)

  1. 如果外围方法和内部方法,均开启MANDATORY,则会报以下异常;
  2. 外围开启MANDATORY,内部其中一个开启SUPPORTS类型,另一个开启REQUIRED类型也会报以下异常,因为SUPPORTS默认就是无事务;
  3. 外围开启MANDATORY,内部均开启REQUIRED类型,也会抛如下异常;
  4. 外围开启MANDATORY,内部均不开启事务,也是抛如下异常;
  5. 外围开启别的事务,则内部方法都会加入到外围的事务中一起执行;
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:572) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:360) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.6.RELEASE.jar:5.2.6.RELEASE]

3.4 PROPAGATION_REQUIRES_NEW类型

3.4.1 外围不开启事务,user,person方法均开启REQUIRES_NEW类型事务,且外围方法有异常

外围方法:

    @Override
    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPerson(person);
        
        //异常
        int i = 1/0;
    }

运行结果:

user表插入成功:
在这里插入图片描述
person表插入成功:

在这里插入图片描述


3.4.2 外围不开启事务,user,person方法均开启REQUIRES_NEW类型事务,且person方法有异常

外围方法:

    @Override
    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPersonException(person);
    }

运行结果:

user表插入成功:
在这里插入图片描述
person表插入失败:

Spring事务传播行为总结_第5张图片


3.4.3 外围开启REQUIRED事务,user,person方法均开启REQUIRES_NEW类型事务,且外围方法有异常

外围代码:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("addPerson1");
        personService.addPerson(person);
        
        //异常
        int i = 1/0;
    }

运行结果:

user表插入成功:
在这里插入图片描述
person表插入成功:
在这里插入图片描述


3.4.4 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,且外围方法有异常

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        Person person1 = new Person();
        person1.setId(1);
        person1.setName("第一人");
        personService.addPersonRequired(person1);

        Person person2 = new Person();
        person2.setId(2);
        person2.setName("第二人");
        personService.addPersonRequiredNew(person2);

        Person person3 = new Person();
        person3.setId(3);
        person3.setName("第三人");
        personService.addPersonRequiredNew(person3);

        //异常
        int i= 1/0;
    }

运行结果:

第一条插入失败,其他成功

Spring事务传播行为总结_第6张图片

3.4.5 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,addPersonExceptionRequiredNew方法开启REQUIRES_NEW事务且有异常

外围代码:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        Person person1 = new Person();
        person1.setId(1);
        person1.setName("第一人");
        personService.addPersonRequired(person1);

        Person person2 = new Person();
        person2.setId(2);
        person2.setName("第二人");
        personService.addPersonRequiredNew(person2);

        Person person3 = new Person();
        person3.setId(3);
        person3.setName("第三人");
        personService.addPersonExceptionRequiredNew(person3);

    }

运行结果:

第二条成功,其他失败

在这里插入图片描述


3.4.6 外围开启REQUIRED事务,addPersonRequired方法开启REQUIRED事务,addPersonRequiredNew开启REQUIRES_NEW事务,addPersonExceptionRequiredNew方法开启REQUIRES_NEW事务且有异常,被try catch

外围代码:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        Person person1 = new Person();
        person1.setId(1);
        person1.setName("第一人");
        personService.addPersonRequired(person1);

        Person person2 = new Person();
        person2.setId(2);
        person2.setName("第二人");
        personService.addPersonRequiredNew(person2);

        Person person3 = new Person();
        person3.setId(3);
        person3.setName("第三人");
        try{
            personService.addPersonExceptionRequiredNew(person3);
        }catch (Exception e){
            System.out.println("存入第三人异常!");
        }

    }

运行结果:

第三条失败,其他成功

Spring事务传播行为总结_第7张图片


PROPAGATION_REQUIRES_NEW类型事务总结:

对应场景 外围方法是否开启事务及事务类型 内部方法事务类型 是否有异常 最终执行结果
3.4.1 × 均为REQUIRES_NEW 外围方法包含异常 均成功
3.4.2 × 均为REQUIRES_NEW 内部person方法包含异常 user成功,person失败
3.4.3 REQUIRED 均为REQUIRES_NEW 外围方法包含异常 均成功
3.4.4 REQUIRED 1为REQUIRED, 2 3为REQUIRES_NEW 外围方法包含异常 1失败,2 3成功
3.4.5 REQUIRED 1为REQUIRED, 2 3为REQUIRES_NEW 3方法包含异常 2成功,1 3失败
3.4.6 REQUIRED 1为REQUIRED, 2 3为REQUIRES_NEW 3方法包含异常且在外围方法中被try catch 3失败,1 2成功

PROPAGATION_REQUIRES_NEW类型事务结论:

(1) 3.4.1,3.4.2和3.4.3说明:
REQUIRES_NEW类型修复的方法会新建事务,内部方法的事务各自执行,互不影响,且内部方法事务不会加入到外围方法事务;

(2) 3.4.4说明:
外围REQUIRED 1为REQUIRED ,所以1和外围方法会在同一个事务,外围方法有异常,所以1必然会回滚,2 3为REQUIRES_NEW,所以2 3会各自建立新的事务,不加入到外围方法事务,所以2 3成功;

(3) 3.4.5说明:
2 3为REQUIRES_NEW会新建事务,虽然不加到外围事务,但是外围是REQUIRED,内部的事务执行异常会传导到外围方法,外围方法发现异常直接回滚,3本身又包含异常,所以3失败,导致外围失败,1方法加入外围事务,所以1也失败,2是内部新建的独立事务,所以成功;

(4) 3.4.6说明:
2 3是独立新建事务,而3有异常,所以2会成功,3失败;而3的异常被外围方法捕获,所以外围不会回滚,正常执行,所以加入到外围事务的1也会成功;


3.5 PROPAGATION_NOT_SUPPORTED类型事务


3.6 PROPAGATION_NEVER类型事务


3.7 PROPAGATION_NESTED类型事务

3.7.1 外围不开启事务,user,person方法均开启NESTED类型事务,且外围方法有异常

外围代码:

    @Override
    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("person");
        personService.addPerson(person);

        int i = 1/0;
    }

执行结果:

user表插入成功:

在这里插入图片描述
person表插入成功:

在这里插入图片描述


3.7.2 外围不开启事务,user,person方法均开启NESTED类型事务,且person方法有异常

外围方法:

    @Override
    public void noTransactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("person");
        personService.addPersonException(person);
    }

执行结果:
user表插入成功:

Spring事务传播行为总结_第8张图片
person表插入失败:

Spring事务传播行为总结_第9张图片


3.7.3 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且外围方法有异常

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("person");
        personService.addPerson(person);

        int i = 1/0;
    }

运行结果:

user表插入失败:

Spring事务传播行为总结_第10张图片
person表插入失败:

在这里插入图片描述


3.7.4 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且person方法有异常

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("person");
        personService.addPersonException(person);

    }

运行结果:

user表插入失败:

Spring事务传播行为总结_第11张图片
person表插入失败:

在这里插入图片描述


3.7.5 外围开启REQUIRED类型事务,user,person方法均开启NESTED类型事务,且person方法有异常在外围方法中捕获

外围方法:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void transactional() {

        User user = new User();
        user.setId(1);
        user.setName("addUser1");
        user.setPwd("123456");
        userService.addUser(user);

        Person person = new Person();
        person.setId(1);
        person.setName("person");
        try{
            personService.addPersonException(person);
        }catch (Exception e){
            System.out.println("异常捕获!");
        }
    }

运行结果:

user表插入成功:

Spring事务传播行为总结_第12张图片
person表插入失败:

在这里插入图片描述


PROPAGATION_NESTED类型事务总结:

对应场景 外围方法是否开启事务 内部方法事务类型 是否包含异常 最终执行结果
3.7.1 × NESTED类型 外围方法包含异常 均成功
3.7.2 × NESTED类型 person包含异常 user成功,person失败
3.7.3 REQUIRED NESTED类型 外围方法包含异常 均失败
3.7.4 REQUIRED NESTED类型 person包含异常 均失败
3.7.5 REQUIRED NESTED类型 person包含异常且在外围方法捕获 user成功,person失败

结论:
(1)在外围方法未开启事务的情况下NESTEDREQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
(2)在外围方法开启REQUIRED事务的情况下,NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务,这是与REQUIRED的区别;


4. 关于REQUIRED,REQUIRES_NEW,NESTED异同

  1. NESTEDREQUIRED 修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回

滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法

事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方

法的事务。

  1. NESTEDREQUIRES_NEW 都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事

务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部

事务和外围事务是两个事务,外围事务回滚不会影响内部事务。


5. 事务传播特性的应用场景

如: 系统在用户注册账号会赠送积分。

那我们是期望注册成功,才会赠送积分,注册失败的话,赠送的积分也要回滚;但是积分赠送失败,不能影响注册的执行流程;另外,假设我们要记录日志,那我们期望不管注册,还是赠送积分是否有异常,都不要回滚日志;

那结合事务传播特性,我们可以这样写:

注册方法:

        @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
        public void register(User user){
            //省略...       
            try {
                //添加积分
                pointService.addPoint(Point point);
            } catch (Exception e) {
               //省略...
            }
            //省略...
        }

添加积分方法:

        @Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
        public void addPoint(Point point){
            //省略...     
            try {
                recordService.addLog(Record record);
            } catch (Exception e) {
               //省略...
            }
            //省略...
        }
        //省略...

记录日志方法:

        @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
        public void addLog(Record record){
            //省略...
        }



你可能感兴趣的:(spring,事务传播行为,事务特性,事务)