一文搞通Spring事务的七种传播机制(通俗易懂)

前言:最近阅读了网上很多关于讲解Spring事务的七种传播机制的文章,很少有通过结合实际代码进行讲解的,对于小白来说干巴巴的文字解释略微抽象,没有一个具体形象化的概念,下面我就通过理论+实践的方式来对Spring事务的每一种传播机制进行剖析,这样大家也容易记得住,毕竟实践才是检验真理的唯一标准!每一个字和每一行代码都是博主纯手打的!

目录

一、REQUIRED(默认)

1.1、基础概念

1.2、代码详解

二、SUPPORTS

2.1、基础概念

2.2、代码详解

2.2.1、不加@Transactional注解

2.2.2、加@Transactional注解

三、MANDATORY

3.1、基础概念

3.2、代码详解

3.2.1、不加@Transactional注解

3.2.2、加@Transactional注解

四、REQUIRES_NEW

4.1、基础概念

4.2、代码详解

五、NOT_SUPPORTED

5.1、基础概念

5.2、代码详解

5.2.1、不加@Transactional注解

5.2.2、加@Transactional注解

六、NEVER

6.1、基础概念

6.2、代码详解

6.2.1、不加@Transactional注解

6.2.2、加@Transactional注解

七、NESTED

7.1、基础概念

7.2、代码详解

7.2.1、不加@Transactional注解

7.2.2、加@Transactional注解

八、总结


一、REQUIRED(默认)

1.1、基础概念

官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

如果A、B两个方法都加了@Transactional注解,默认是REQUIRED传播行为。那么如果A方法调用B方法,它们会共用一个事务,因为默认会使用同一条连接,相当于一个事务里执行。

1.2、代码详解

例如,在插入一条用户信息以后会紧接着插入一条角色信息,最后我手动写了个除数不能为0的异常,以下是关键代码:

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但事务都进行了回滚操作! 

二、SUPPORTS

2.1、基础概念

官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

如果A方法没有事务(没有加@Transactional注解),B方法配置了SUPPORTS传播行为,那么B方法也会以非事务方式执行。

如果A方法存在事务(加了@Transactional注解),B方法配置了SUPPORTS传播行为,那么B方法会挂起自己的事务,加入到A方法的事务来执行。

2.2、代码详解

整体代码逻辑依旧不变。

2.2.1、不加@Transactional注解

在insertUser方法上面不加@Transactional注解。

UserService关键代码:

    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码: 

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,这是因为insertUser方法没有配置事务,那么insertRole方法也会以非事务的方式进行运行。

2.2.2、加@Transactional注解

仅仅需要在insertUser方法上面加上@Transactional注解。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService代码不动! 

运行结果如下: 

虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都进行了回滚,这是因为insertRole方法配置了SUPPORTS传播行为,那么insertRole方法会挂起自己的事务,加入到inserUser方法的事务来执行。 

三、MANDATORY

3.1、基础概念

官方解释:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

如果A方法没有事务(没有加@Transactional注解),B方法配置了MANDATORY传播行为,那么B方法将会抛出异常。

如果A方法存在事务(加@Transactional注解),B方法配置了MANDATORY传播行为,那么B方法将加入到该存在的事务来执行。

3.2、代码详解

整体代码逻辑依旧不变。

3.2.1、不加@Transactional注解

在insertUser方法上面不加@Transactional注解。

UserService关键代码:

    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

程序会抛出org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'的异常信息,数据库这边User信息入库了,但是Role信息没有,因为insertUser方法不存在事务,才导致出现这个异常并且数据成功入库。

3.2.2、加@Transactional注解

仅仅需要在insertUser方法上面加上@Transactional注解。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService代码不动!

运行结果如下:

虽然程序抛出:java.lang.ArithmeticException: / by zero的异常信息,但是2条数据都进行了回滚,上面的org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'的异常信息也没有了,所以这种事务的传播机制必须需要存在一个活动的事务。

四、REQUIRES_NEW

4.1、基础概念

官方解释:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

如果A、B方法都有事务,但B配置了REQUIRES_NEW,那么B会起一个新的事务,暂停A的事务。等B事务结束,才恢复A的事务。

4.2、代码详解

整体代码逻辑不变。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

虽然程序抛出:java.lang.ArithmeticException: / by zero的异常信息,但是Role信息插入成功了,这是因为insertUser和insertRole方法公用的不是同一个事务,也可以说是公用的不是同一个数据库链接。

五、NOT_SUPPORTED

5.1、基础概念

官方解释:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

如果A方法没有事务(没有加@Transactional注解),B方法配置了NOT_SUPPORTED传播行为,那么B方法也会以非事务方式执行。

如果A方法存在事务(加了@Transactional注解),B方法配置了NOT_SUPPORTED传播行为,那么B方法会挂起自己的事务,以非事务方式加入到A方法的事务来执行。

5.2、代码详解

整体代码逻辑依旧不变。

5.2.1、不加@Transactional注解

在insertUser方法上面不加@Transactional注解。

UserService关键代码:

    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下: 

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,虽然insertUser方法没有配置事务,但是insertRole方法配置了NOT_SUPPORTED传播行为,那么inserRole方法也会以非事务方式执行。

5.2.2、加@Transactional注解

仅仅需要在insertUser方法上面加上@Transactional注解。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService代码不动!

运行结果如下:

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是Role信息成功入库了,因为insertUser方法配置了事务,但是insertRole方法配置了NOT_SUPPORTED传播行为,那么insertRole方法会挂起自己的事务,以非事务方式加入到insertUser方法的事务来执行。

六、NEVER

6.1、基础概念

官方解释:以非事务方式运行,如果当前存在事务,则抛出异常。

如果A方法没有事务(没有加@Transactional注解),B方法配置了NEVER传播行为,那么B方法会正常以非事务方式执行。

如果A方法存在事务(加了@Transactional注解),B方法配置了NEVER传播行为,那么B方法将会抛出异常。

6.2、代码详解

整体代码逻辑依旧不变。

6.2.1、不加@Transactional注解

在insertUser方法上面不加@Transactional注解。

UserService关键代码:

    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但2条数据都成功入库了,这是因为insertUser方法没有配置事务,insertRole方法配置了NOT_SUPPORTED传播行为,那么insertRole方法会以非事务方式执行。

6.2.2、加@Transactional注解

仅仅需要在insertUser方法上面加上@Transactional注解。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService代码不动! 

运行结果如下:

程序会抛出org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' 的异常信息,同时2条数据都没有成功入库!因为insertUser方法配置了事务,insertRole方法配置了NEVER传播行为,那么insertRole方法抛出了异常信息。

七、NESTED

7.1、基础概念

官方解释:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED

如果A方法没有事务(没有加@Transactional注解),B方法配置了NESTED传播行为,那么B方法将启动一个新的嵌套事务执行。

如果A方法存在事务(加@Transactional注解),B方法配置了NESTED传播行为,那么B方法将会在嵌套事务内执行。

7.2、代码详解

整体代码逻辑依旧不变。

7.2.1、不加@Transactional注解

在insertUser方法上面不加@Transactional注解。

UserService关键代码:

    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService关键代码:

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
    public void insertRole(){
        Role role = new Role();
        role.setId(UUID.randomUUID().toString());
        role.setName("管理员");
        roleMapper.insert(role);
    }

运行结果如下:

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都成功入库了。因为insertUser方法没有配置事务,insertRole方法配置了NESTED传播行为,那么insertRole方法将启动一个新的嵌套事务执行。

7.2.2、加@Transactional注解

仅仅需要在insertUser方法上面加上@Transactional注解。

UserService关键代码:

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(){
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("张三");
        user.setPassword("123456");
        userMapper.insert(user);
        roleService.insertRole();
        int a = 1/0;
    }

RoleService代码不动! 

运行结果如下:

可以很明显的看出,虽然程序中会抛出java.lang.ArithmeticException: / by zero的异常,但是2条数据都进行了回滚。 这是因为insertUser方法配置了事务,insertRole方法配置了NESTED传播行为,那么insertRole方法将会在嵌套事务内执行。

八、总结

以上就是我总结的Spring事务的7种传播机制,都是博主纯手打的,通过理论+实践的方式通俗易懂的讲解每一种事务的传播机制,让大家更好的去理解,如有问题,欢迎评论区讨论!

猜你喜欢

转载自blog.csdn.net/HJW_233/article/details/131993937