Spring事务传播行为总结

Spring事务传播行为总结


1、概念

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


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

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

事务注解源码枚举:
在这里插入图片描述

总结:

事务传播行为类型对应说明验证结果
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表插入成功:
在这里插入图片描述


场景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表插入失败:
在这里插入图片描述
person表插入失败:
在这里插入图片描述


场景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表插入失败:

在这里插入图片描述


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;
    }

运行结果:

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

在这里插入图片描述

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("存入第三人异常!");
        }

    }

运行结果:

第三条失败,其他成功

在这里插入图片描述


PROPAGATION_REQUIRES_NEW类型事务总结:

对应场景外围方法是否开启事务及事务类型内部方法事务类型是否有异常最终执行结果
3.4.1×均为REQUIRES_NEW外围方法包含异常均成功
3.4.2×均为REQUIRES_NEW内部person方法包含异常user成功,person失败
3.4.3REQUIRED均为REQUIRES_NEW外围方法包含异常均成功
3.4.4REQUIRED1为REQUIRED, 2 3为REQUIRES_NEW外围方法包含异常1失败,2 3成功
3.4.5REQUIRED1为REQUIRED, 2 3为REQUIRES_NEW3方法包含异常2成功,1 3失败
3.4.6REQUIRED1为REQUIRED, 2 3为REQUIRES_NEW3方法包含异常且在外围方法中被try catch3失败,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表插入成功:

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

在这里插入图片描述


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表插入失败:

在这里插入图片描述
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表插入失败:

在这里插入图片描述
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表插入成功:

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

在这里插入图片描述


PROPAGATION_NESTED类型事务总结:

对应场景外围方法是否开启事务内部方法事务类型是否包含异常最终执行结果
3.7.1×NESTED类型外围方法包含异常均成功
3.7.2×NESTED类型person包含异常user成功,person失败
3.7.3REQUIREDNESTED类型外围方法包含异常均失败
3.7.4REQUIREDNESTED类型person包含异常均失败
3.7.5REQUIREDNESTED类型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){
            //省略...
        }



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值