欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

手把手带你实战下Spring的七种事务传播行为

程序员文章站 2022-10-28 08:13:39
本文介绍Spring的七种事务传播行为并通过代码演示下。 [TOC] 一、什么是事务传播行为? 事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。 例如:methodA方法调用methodB方法时,methodB是继续在调 ......

目录

本文介绍spring的七种事务传播行为并通过代码演示下。

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

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。

例如:methoda方法调用methodb方法时,methodb是继续在调用者methoda的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodb的事务传播行为决定的。

二、事务的7种传播行为

spring在transactiondefinition接口中规定了7种类型的事务传播行为。事务传播行为是spring框架独有的事务增强特性。这是spring为我们提供的强大的工具箱,使用事务传播行为可以为我们的开发工作提供许多便利。

7种事务传播行为如下:

1.propagation_required

如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是spring默认的事务传播行为。

2.propagation_supports

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

3.propagation_mandatory

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4.propagation_requires_new

创建新事务,无论当前存不存在事务,都创建新事务。

5.propagation_not_supported

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6.propagation_never

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

7.propagation_nested

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按required属性执行。

其实这7中我也没看懂,不过不急,咱们接下来直接看效果。

三、7种传播行为实战

演示前先建两个表,用户表和用户角色表,一开始两个表里没有数据。

需要注意下,为了数据更直观,每次执行代码时 先清空下user和user_role表的数据。

user表:

create table `user` (
  `id` int(11) not null auto_increment,
  `name` varchar(255) default null,
  `password` varchar(255) default null,
  `sex` int(11) default null,
  `des` varchar(255) default null,
  primary key (`id`)
) engine=innodb default charset=utf8;

user_role表:

create table `user_role` (
  `id` int(11) not null auto_increment,
  `user_id` int(11) default null,
  `role_id` int(11) default null,
  primary key (`id`)
) engine=innodb default charset=utf8;

1.propagation_required测试

如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是spring默认的事务传播行为。

场景一:

此场景外围方法没有开启事务。

1.验证方法

两个实现类userserviceimpl和userroleserviceimpl制定事物传播行为propagation=propagation.required,然后在测试方法中同时调用两个方法并在调用结束后抛出异常。

2.主要代码

外层调用方法代码:

/**
     * 测试 propagation_required
     *
     * @author: java_suisui
     */
    @test
    void test_propagation_required() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

userserviceimpl代码:

/**
     * 增加用户
     */
    @transactional(propagation = propagation.required)
    @override
    public int add(user user) {
        return usermapper.add(user);
    }

userroleserviceimpl代码:

    /**
     * 增加用户角色
     */
    @transactional(propagation = propagation.required)
    @override
    public int add(userrole userrole) {
        return userrolemapper.add(userrole);
    }

3.代码执行后数据库截图

两张表数据都新增成功,截图如下:

手把手带你实战下Spring的七种事务传播行为

4.结果分析

外围方法未开启事务,插入用户表和用户角色表的方法在自己的事务中独立运行,外围方法异常不影响内部插入,所以两条记录都新增成功。

场景二:

此场景外围方法开启事务。

1.主要代码

测试方法代码如下:

/**
     * 测试 propagation_required
     *
     * @author: java_suisui
     */
    @transactional
    @test
    void test_propagation_required() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

2.代码执行后数据库截图

两张表数据都为空,截图如下:

手把手带你实战下Spring的七种事务传播行为

3.结果分析

外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法也要回滚,所以两个记录都插入失败。

结论:以上结果证明在外围方法开启事务的情况下propagation.required修饰的内部方法会加入到外围方法的事务中,所以propagation.required修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。

2.propagation_supports测试

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

场景一:

此场景外围方法没有开启事务。

1.验证方法

两个实现类userserviceimpl和userroleserviceimpl制定事物传播行为propagation=propagation.supports,然后在测试方法中同时调用两个方法并在调用结束后抛出异常。

2.主要代码

外层调用方法代码:

    /**
     * 测试 propagation_supports
     *
     * @author: java_suisui
     */
    @test
    void test_propagation_supports() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

userserviceimpl代码:

/**
     * 增加用户
     */
    @transactional(propagation = propagation.supports)
    @override
    public int add(user user) {
        return usermapper.add(user);
    }

userroleserviceimpl代码:

    /**
     * 增加用户角色
     */
    @transactional(propagation = propagation.supports)
    @override
    public int add(userrole userrole) {
        return userrolemapper.add(userrole);
    }

3.代码执行后数据库截图

两张表数据都新增成功,截图如下:

手把手带你实战下Spring的七种事务传播行为

4.结果分析

外围方法未开启事务,插入用户表和用户角色表的方法以非事务的方式独立运行,外围方法异常不影响内部插入,所以两条记录都新增成功。

场景二:

此场景外围方法开启事务。

1.主要代码

test_propagation_supports方法添加注解@transactional即可。

2.代码执行后数据库截图

两张表数据都为空,截图如下:

手把手带你实战下Spring的七种事务传播行为

3.结果分析

外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法也要回滚,所以两个记录都插入失败。

结论:以上结果证明在外围方法开启事务的情况下propagation.supports修饰的内部方法会加入到外围方法的事务中,所以propagation.supports修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。

3.propagation_mandatory测试

支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

通过上面的测试,“支持当前事务,如果当前存在事务,就加入该事务”,这句话已经验证了,外层添加@transactional注解后两条记录都新增失败,所以这个传播行为只测试下外层没有开始事务的场景。

场景一:

此场景外围方法没有开启事务。

1.验证方法

两个实现类userserviceimpl和userroleserviceimpl制定事物传播行为propagation = propagation.mandatory,主要代码如下。

2.主要代码

外层调用方法代码:

    /**
     * 测试 propagation_mandatory
     *
     * @author: java_suisui
     */
    @test
    void test_propagation_mandatory() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

userserviceimpl代码:

/**
     * 增加用户
     */
    @transactional(propagation = propagation.mandatory)
    @override
    public int add(user user) {
        return usermapper.add(user);
    }

userroleserviceimpl代码:

    /**
     * 增加用户角色
     */
    @transactional(propagation = propagation.mandatory)
    @override
    public int add(userrole userrole) {
        return userrolemapper.add(userrole);
    }

3.代码执行后数据库截图

两张表数据都为空,截图如下:

手把手带你实战下Spring的七种事务传播行为

4.结果分析

运行日志如下,可以发现在调用userservice.add()时候已经报错了,所以两个表都没有新增数据,验证了“如果当前不存在事务,就抛出异常”。

at com.example.springboot.mybatisannotation.service.impl.userserviceimpl$$enhancerbyspringcglib$$50090f18.add(<generated>)
    at com.example.springboot.mybatisannotation.springbootmybatisannotationapplicationtests.test_propagation_mandatory(springbootmybatisannotationapplicationtests.java:78)
    at sun.reflect.nativemethodaccessorimpl.invoke0(native method)
    at sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)
    at sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43)
    at java.lang.reflect.method.invoke(method.java:498)

4.propagation_requires_new测试

创建新事务,无论当前存不存在事务,都创建新事务。

这种情况每次都创建事务,所以我们验证一种情况即可。

场景一:

此场景外围方法开启事务。

1.验证方法

两个实现类userserviceimpl和userroleserviceimpl制定事物传播行为propagation = propagation.requires_new,主要代码如下。

2.主要代码

外层调用方法代码:

    /**
     * 测试 requires_new
     *
     * @author: java_suisui
     */
    @test
    @transactional
    void test_requires_new() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

userserviceimpl代码:

/**
     * 增加用户
     */
    @transactional(propagation = propagation.requires_new)
    @override
    public int add(user user) {
        return usermapper.add(user);
    }

userroleserviceimpl代码:

    /**
     * 增加用户角色
     */
    @transactional(propagation = propagation.requires_new)
    @override
    public int add(userrole userrole) {
        return userrolemapper.add(userrole);
    }

3.代码执行后数据库截图

两张表数据都新增成功,截图如下:

手把手带你实战下Spring的七种事务传播行为

4.结果分析

无论当前存不存在事务,都创建新事务,所以两个数据新增成功。

5.propagation_not_supported测试

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

场景一:

此场景外围方法不开启事务。

1.验证方法

两个实现类userserviceimpl和userroleserviceimpl制定事物传播行为propagation = propagation.not_supported,主要代码如下。

2.主要代码

外层调用方法代码:

    /**
     * 测试 propagation_not_supported
     *
     * @author: java_suisui
     */
    @test
    void test_propagation_not_supported() {
        // 增加用户表
        user user = new user();
        user.setname("java碎碎念");
        user.setpassword("123456");
        userservice.add(user);
        // 增加用户角色表
        userrole userrole = new userrole();
        userrole.setuserid(user.getid());
        userrole.setroleid(200);
        userroleservice.add(userrole);
        //抛异常
        throw new runtimeexception();
    }

userserviceimpl代码:

/**
     * 增加用户
     */
    @transactional(propagation = propagation.not_supported)
    @override
    public int add(user user) {
        return usermapper.add(user);
    }

userroleserviceimpl代码:

    /**
     * 增加用户角色
     */
    @transactional(propagation = propagation.not_supported)
    @override
    public int add(userrole userrole) {
        return userrolemapper.add(userrole);
    }

3.代码执行后数据库截图

两张表数据都新增成功,截图如下:

手把手带你实战下Spring的七种事务传播行为

4.结果分析

以非事务方式执行,所以两个数据新增成功。

场景二:

此场景外围方法开启事务。

1.主要代码

test_propagation_not_supported方法添加注解@transactional即可。

2.代码执行后数据库截图

两张表数据都新增成功,截图如下:

手把手带你实战下Spring的七种事务传播行为

3.结果分析

如果当前存在事务,就把当前事务挂起,相当于以非事务方式执行,所以两个数据新增成功。

6.propagation_never测试

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

上面已经有类似情况,外层没有事务会以非事务的方式运行,两个表新增成功;有事务则抛出异常,两个表都都没有新增数据。

7.propagation_nested测试

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按required属性执行。

上面已经有类似情况,外层没有事务会以required属性的方式运行,两个表新增成功;有事务但是用的是一个事务,方法最后抛出了异常导致回滚,两个表都都没有新增数据。

到此spring的7种事务传播行为已经全部介绍完成了,有问题欢迎留言沟通哦!

完整源码地址: https://github.com/suisui2019/springboot-study

推荐阅读

1.springboot系列-整合mybatis(注解方式)
2.springboot系列-整合mybatis(xml配置方式)
3.java中打印日志,这4点很重要!
4.springboot集成jwt实现权限认证
5.一分钟带你了解jwt认证!


限时领取免费java相关资料,涵盖了java、redis、mongodb、mysql、zookeeper、spring cloud、dubbo/kafka、hadoop、hbase、flink等高并发分布式、大数据、机器学习等技术。
关注下方公众号即可免费领取:

手把手带你实战下Spring的七种事务传播行为