首页 > 其他分享 >Spring事务的传播机制

Spring事务的传播机制

时间:2023-02-04 13:22:53浏览次数:36  
标签:事务 调用者 Spring 传播 Propagation Role user role

1. 什么是事务的传播机制

事务的传播机制简单的说就是一个事务方法在调用另一个事务方法的时候,事务是如何在这些方法中进行传播的。比如说方法A调用了方法B,那么方法A有无事务,方法B有无事务以及事务的传播机制是什么都会对执行的结果产生不同的影响。

2. 事务的传播机制都有哪些以及介绍

Spring事务的传播机制总共有其中,由@Transactional注解的propagation属性来设置。

传播机制 描述
Propagation.REQUIRED 默认的事务传播级别,表示如果当前存在事务则加入该事务,如果当前不存在事务则创建一个新的事务
Propagation.SUPPORTS 支持事务,表示如果当前存在事务则加入该事务,如果当前不存在事务则以非事务的方式运行
Propagation.MANDATORY 强制型,如果当前存在事务则以事务的方式运行,如果不存在事务则报异常
Propagation.REQUIRES_NEW 无论外部调用是否存在事务,都创建一个新的事务 ,如果外部调用存在事务则将事务挂起,创建新的事务,且两个事务之间互不干扰
Propagation.NOT_SUPPORTED 不支持事务,即以非事务的方式运行,如果外部存在事务则将事务挂起
Propagation.NEVER 不能存在事务,以非事务的方式运行,如果外部存在事务,则抛出异常
Propagation.NESTED 嵌套事务,如果当前调用方法存在一个事务,则会创建一个新的事务当作当前事务的嵌套事务来运行,如果当前不存在事务,则该机制与Propagation.REQUIRED机制一样

样例:
Propagation.REQUIRED:
1.

    // 调用类方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //throw new RuntimeException("测试异常");
    }
    
    // 被调用类方法
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上图可以看到role表和user表中都没有增加数据,这是因为Propagation.REQUIRED机制,在没有事务的情况下会创建一个新的事务,当已存在事务的情况下会加入当前事务,所以这两个方法从根本上是在同一个事务中,当被调用类的testTransaction方法抛出异常的时候事务进行回滚,调用类同样会进行回滚。
2.

    // 调用类方法
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //throw new RuntimeException("测试异常");
    }
    
    // 被调用类方法
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
可以看到user表中新增了一条数据,而role表中则没有进行新增数据,这是因为Propagation.REQUIRED机制,在调用者方法上没有事务,被调用方法上有事务的时候会创建一个新的事务,当新的事务中发生异常回滚,并不会对调用者方法产生任何影响,所以调用者方法的数据插入成功了,而被调用者方法则没有插入成功。
Propagation.SUPPORTS:

    // 调用者方法
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //throw new RuntimeException("测试异常");
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上图的执行结果可以看出role表中增加了一条数据,user表中同样增加了一条数据。这是因为Propagation.SUPPORTS机制在调用者方法没有事务的时候被调用者方法也会以非事务的方式运行,在调用者方法有事务的时候被调用者方法则会加入当前事务,所以这里user表的数据与role表的管理员1数据能够插入成功,管理员2的数据则没有插入。
PS:当调用者方法上存在事务时,其执行情况与Propagation.REQUIRED机制第一种的时候的情况是一样的,这里就不举例说了。
Propagation.MANDATORY:
1.

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-MANDATORY");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //throw new RuntimeException("测试异常");
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-MANDATORY");
        insertRole(role);

        //// 这里设置个异常
        //int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-MANDATORY");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
可以看到role表中的两条数据都插入成功了,user表中的一条数据也插入成功了。这是因为Propagation.MANDATORY机制,当调用者使用事务的时候,被调用者就直接加入当前事务,也就是一个事务要么全部成功,要么全部失败,当你把抛出除0异常的代码注释去掉的时候可以发现这三条数据都会插入不成功。
2

    // 调用者类
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-MANDATORY");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //throw new RuntimeException("测试异常");
    }
    
    // 被调用者类
    @Override
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-MANDATORY");
        insertRole(role);

        //// 这里设置个异常
        //int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-MANDATORY");
        insertRole(role1);
    }

执行结果:
image
image
image
结果说明:
可以看到role表中一条数据都没有插入成功,user表中的数据插入成功了,同时代码还报了了一个异常。这是因为Propagation.MANDATORY机制是强制性要求事务的,如果没有事务就会报异常,所以role表中两条数据会一条也插入不成功,而user表中的数据插入成功是因为调用者没有开启事务,被调用者抛出异常回滚不会对其产生影响,所以其能插入成功。
Propagation.REQUIRES_NEW:
1.

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-REQUIRES_NEW");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        // 这里设置个异常
        int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-REQUIRES_NEW");
        insertRole(role);

        //// 这里设置个异常
        //int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-REQUIRES_NEW");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上面的结果可以看到role表中的两条数据都插入成功了,user表中的数据没有插入成功。这是因为Propagation.REQUIRES_NEW机制是如果调用者有事务,那么就会将调用者的事务挂起,然后新建了一个事务,所以当调用者的事务发生异常的时候,不会对被调用者的事务产生影响。
2.

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-REQUIRES_NEW2");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        try {
            roleService.testTransaction();
        } catch (Exception e)  {
            System.out.println(e);
        }
        //// 这里设置个异常
        //int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-REQUIRES_NEW2");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-REQUIRES_NEW2");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上面的结果可以知道role表中没有插入新的数据,而user表中插入了一条数据。这同样是因为Propagation.REQUIRES_NEW机制会创建一个新的事务,所以在新的事务中出现异常,只要调用者将异常捕获,并且调用者不会抛出异常,那么调用者的事务就不会发生回滚。因此这边user表能够插入数据成功,而role表插入数据没有成功。
Propagation.NOT_SUPPORTED:

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-NOT_SUPPORTED");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //// 这里设置个异常
        //int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-NOT_SUPPORTED");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-NOT_SUPPORTED");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
从上面的结果中可以看出jrole表中管理员1的数据插入成功了,管理员2的数据由于异常没有插入成功,而user表中的数据则没有插入成功。这是因为Propagation.NOT_SUPPORTED机制,不适用事务,所以虽然这里调用者方法使用的有事务,但是由于被调用者方法不支持使用事务,所以,这里被调用者方法是没有在事务中的,因此管理员1的数据能插入成功,管理员2的数据插入不成功,而调用者方法由于异常发生回滚,所以user表的数据也是没有插入的。
Propagation.NEVER:

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj-NEVER");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //// 这里设置个异常
        //int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.NEVER, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-NEVER");
        insertRole(role);

        //// 这里设置个异常
        //int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-NEVER");
        insertRole(role1);
    }

执行结果:
image
image
image
结果说明:
从上面的结果可以出role表和user表数据都没有插入成功,并且接口报了一个异常。这是因为被调用者方法使用的是Propagation.NEVER机制,这个机制是不允许使用事务的,这里调用者方法使用了事务,所以这里才会报一个异常。
Propagation.NESTED:
1.调用者方法中出现异常

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj1-NESTED");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        // 这里设置个异常
        int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-NESTED");
        insertRole(role);

        //// 这里设置个异常
        //int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-NESTED");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
这里可以看到两个表中都没有插入成功,这是因为Propagation.NESTED机制时嵌套事务,当调用者方法的父事务出现异常时,那么其子事务也会被回滚。
2.子事务中出现异常,并且父事务中没有捕获异常

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj1-NESTED");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        roleService.testTransaction();
        //// 这里设置个异常
        //int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-NESTED");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-NESTED");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上面的结果可以看出两张表中数据都没有插入成功,因此当嵌套事务中的子事务中出现异常,而父事务并没有对异常进行捕获时,那么父子事务都会进行回滚。
3.子事务中出现异常,父事务中捕获异常

    // 调用者方法
    @Transactional(rollbackFor = ArithmeticException.class)
    @Override
    public void testTransaction() {
        User user = new User();
        user.setUsername("mcj1-NESTED");
        user.setPassword("123456");
        user.setEmail("[email protected]");
        userMapper.insertUser(user);
        try {
            roleService.testTransaction();
        } catch (ArithmeticException e) {

        }
        //// 这里设置个异常
        //int i = 1 / 0;
    }
    
    // 被调用者方法
    @Override
    @Transactional(propagation = Propagation.NESTED, rollbackFor = ArithmeticException.class)
    public void testTransaction() {
        Role role = new Role();
        role.setRoleName("管理员1-NESTED");
        insertRole(role);

        // 这里设置个异常
        int i = 1 / 0;

        Role role1 = new Role();
        role1.setRoleName("管理员2-NESTED");
        insertRole(role1);
    }

执行结果:
image
image
结果说明:
由上面的结果可以看出,user表中的数据添加成功,而role表中的数据并没有成功。因此当嵌套事务中的子事务中出现异常,而父事务对异常进行捕获,那么父子事务不会进行回滚。

3. 总结

  • Propagation.REQUIRED机制是事务传播机制的默认机制,当事务没有设置传播机制的时候默认就是该机制
  • 这七种事务传播机制中最常用的是Propagation.REQUIREDPropagation.REQUIRES_NEW这两种
  • Propagation.NESTEDPropagation.REQUIRES_NEW这两种机制在被调用方法抛出异常时他们的结果一样,但是当调用者的方法抛出异常时他们的结果就不一样了。
  • Propagation.NESTED机制的特性是:当调用者方法事务回滚时,被调用者方法事务也跟着回滚。当被调用者方法事务发送回滚时,调用者方法事务是否回滚取决于是否捕获异常。如果捕获了异常,那么就不回滚,否则回滚

标签:事务,调用者,Spring,传播,Propagation,Role,user,role
From: https://www.cnblogs.com/mcj123/p/17080780.html

相关文章

  • Spring基础知识一(概念和模块)
    一、Spring的概念和优点1.基本概念:Spring是一个轻量级、非侵入性的开源框架,通过基于POJO对象的编程模型,提供了以前的EJB才能提供的企业及服务。2.主要优点:(1).Spring通......
  • springcloud(一) - 核心组件
    Eureka各个微服务都会注册到Eureka,服务调用时候也会调用Eureka获取服务注册表,功能类似zk。 Feign动态代理的方式,简化请求其他服务的开发成本,更好得对请求地址组装、......
  • Spring 5(三)AOP
    三.AOP1.基本概念1.1什么是AOP(1)面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效......
  • Spring6 DI 依赖注入--Bean属性赋值
    Spring6基于XML实现Bean管理(属性赋值)IOC和DI有什么区别:DI是IOC中的具体实现,DI表示依赖注入或注入属性,注入属性要在创建对象的基础之上完成依赖注入方法bean属性赋值方......
  • Spring组成及其拓展
         ......
  • Spring Boot 集成 Redis 实现数据缓存
    SpringBoot集成Redis实现缓存主要分为以下三步:加入Redis依赖加入Redis配置演示Redis缓存加入依赖首先创建一个项目,在项目中加入Redis依赖,项目依赖如下......
  • 创建springboot项目
    有两种方式1.maven创建2.忘了今天采用maven创建       2.在pom.xml里面添加依赖3. 在resources下创建配置文件application.yam 4.Main就是启动......
  • springboot2.5.6集成swagger3
    引入依赖<dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3......
  • day01-SpringMVC基本介绍-01
    SpringMVC介绍-011.离线文档解压spring-5.3.8-dist.zip文件。位置:spring-framework-5.3.8/docs/reference/html/web.html#spring-web2.SpringMVC基本介绍2.1SpringM......
  • 1、Spring源码环境搭建
    本文目的完成SpringFramework5.x的源码构建 准备官网:SpringFramework使用5.x版本源码包构建项目管理工具gradle(没学过的先去找资料学习) 说明Spring框架是......