Spring事务管理中关于传播行为的学习总结

具体上次写事务管理这块内容已经过去好几个月了,昨天打了个草稿,这次学习事务管理相当于是复习,也相当于是巩固这个知识点。关于事务和缓存,在Spring中都有专门的管理机制,当下的开发趋势中,关于Annotation的表达方式越来越常用,之前的事务管理文章中所举例是采用了配置文件的方式,这次就采用注解的方式来巩固下Spring事务管理的传播行为吧。

事务管理:https://blog.csdn.net/Nerver_77/article/details/79876040

Spring中的事务管理:https://blog.csdn.net/Nerver_77/article/details/79896351

以上两篇是以往所总结的内容,这篇就不赘述,直接开始正题:事务管理中的传播行为!

当事务方法被另一个事务方法所调用时,须指定事务应该如何传播。

这里将搭建一个测试事务传播行为的测试用例,在此之前,先搭建所需要的环境。

1. 建立测试数据表:test

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `value` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;

2. Dao层搭建:Mybatis-plus代码生成器

关于Mybatis-plus的代码生成器详情可见文档:http://mp.baomidou.com/#/generate-code

类比Mybatis的逆向工程,都是用来生成Dao层代码的,Mybatis-plus基于的BaseMapper,实现了基本的CURD操作的封装,我们只需要生成代码即可,无需更改。当然如果有其他复杂查询需要自定义。

针对test表,生成对应的pojo对象:Test.java,Dao接口:TestMapper.java,mapping文件:TestMapper.xml

生成的Dao接口,继承了BaseMapper,基本的CURD都有的,这里测试用例无需复杂查询,这里就不写了。

public interface TestMapper extends BaseMapper<Test> {

}

3. Service层搭建:接口 + 实现类方式

接口:这样相同的接口设立了三个,改个名字就好。

public interface TestService1 {
    void test1();
}

实现类: Test1的实现类如下,Test2、Test3的实现类一样。

@Service
public class TestServiceImpl1 implements TestService1{

    @Autowired
    private TestMapper testMapper;

    @Autowired
    private TestService2 testService2;

    @Autowired
    private TestService3 testService3;

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
    public void test1() {

        Test test1 = new Test();
        test1.setValue("test1事务测试");
        testMapper.insert(test1);

        try {
            testService2.test2();
        } catch (Exception e) {
            System.err.println("test2有异常");
            e.printStackTrace();
        }

        testService3.test3();
    }
}
@Service
public class TestServiceImpl2 implements TestService2{

    @Autowired
    private TestMapper testMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void test2() {
        Test test2 = new Test();
        test2.setValue("test2事务测试");
        testMapper.insert(test2);
//        int i = 1/0;
    }
}

这里需要说明下:实现类中的方法都是调用testMapper进行数据添加,test1方法中嵌套了test2、test3的数据添加业务。牵扯到事务的传播行为,必须要存在事务方法的嵌套,我们在下述进行传播行为测试的时候,先采用test1和test2。

采用Junit创建测试类:test1为测试入口。

public class TransactionTest extends BaseJunit {

    @Autowired
    private TestService1 testService1;

    @Test
    public void test(){
        testService1.test1();
    }

}

4. 开启测试:下面会根据事务的每个传播行为进行测试。

提示:自制异常:1/0 /by zero ,事务挂起:当前事务中的方法也是处于执行状态 , pass:控制台结果正确,这里不重复上图。

方法开启事务:采用 @Transactional注解方式声明。@Transactional(propagation = Propagation.REQUIRED)

详细的注解声明问题在第一个测试中进行详细介绍,后面的测试大同小异,就是更改propagation参数而已。

以下传播行为的测试默认都采用test1、test2,只有PROPAGATION_NESTED时候会使用test3。

 PROPAGATION_REQUIRED--表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
            test1:propagation = Propagation.REQUIRED

           
            test2:propagation = Propagation.REQUIRED

            
            测试:对test1而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,会正常执行。
            数据库结果:test1事务测试 + test2事务测试(ID字段自增长,无需在意)

            
            控制台结果:pass

         

            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.REQUIRED + 自制异常
            测试:对test1而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,但是test2中存在异常,基于事务1的一致性,事务1会进行回滚。
            数据库结果:无记录
            控制台结果:出现异常,事务1中的方法执行结果回滚。


       

PROPAGATION_SUPPORTS--表示当前方法不需要事务,但是如果存在当前事务的话,那么该方法会在这个事务中运行,如果当前没有事务,就以非事务方式执行。 
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.SUPPORTS
            测试:对test1而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,会正常执行。
            数据库结果:test1事务测试 + test2事务测试
            控制台结果:pass
            上述情况即:test1、test2都在事务1中执行,任何一方法出现异常就会导致事务回滚。

            test1:未开启事务
            test2:propagation = Propagation.SUPPORTS + 自制异常
            测试:对test2而言,当前方法没有运行事务中,因此test2将以非事务方式执行(遇到异常也不会进行回滚)。
            数据库结果:test1事务测试 + test2事务测试
            控制台结果:test1未处于事务中,正常执行,test2中声明了SUPPORTS并自制异常,非事务方式执行方法,出现异常也不会回滚。

PROPAGATION_MANDATORY--表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
            test1:未开启事务
            test2:propagation = Propagation.MANDATORY
            测试:对test2而言没有处于事务中,会抛出异常。
            数据库结果:test1事务测试
            控制台结果:MANDATORY所声明的事务中为发现事务存在,即外围无事务存在。 当test2外围存在事务时,就会事务执行成功。


           

PROPAGATION_REQUIRES_NEW--表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。 
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.REQUIRED_NEW + 自制异常
            测试:对test1而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,但是test2会重新开启一个新事务2,此时事务1和事务2互不影响。
            数据库结果:test1事务测试
            控制台结果:test1正常 test2回滚

       

PROPAGATION_NOT_SUPPORTED--表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。 
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.REQUIRED + 自制异常
            测试:对test1而言,当前方法运行在事务1中。对test2而言,当前所处与事务1中,但是test2不应该运行在事务中,也就是test2独立出事务1,以非事务方式运行,此时事务1挂起(不影响方法执行),。
            数据库结果:test1事务测试 + test2事务测试
            控制台结果:test2独立出事务以非事务方式运行。出现异常也不会回滚。

       

PROPAGATION_NEVER--表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.NEVER
            测试:对test1而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1中,存在事务运行,抛出异常。
            数据库结果:test1事务测试
            控制台结果:NEVER 表明发现外围存在事务运行,抛出异常。

            test1:未开启事务
            test2:propagation = Propagation.NEVER
            测试:对test2而言不存在事务运行,可以正常执行。
            数据库结果:test1事务测试 + test2事务测试
            控制台结果:pass

PROPAGATION_NESTED--一个事务内部嵌套事务的执行不会影响外部事务,但外部事务的执行要影响内部
            一个事务内部嵌套事务的执行不会影响外部事务:REQUIRES_NEW,开启全新事务,独立外围事务运行。    
            外部事务的执行要影响内部:REQUIRED,外围事务影响内部方法。
            需要把这两者需求结合起来:
            1.外部事物影响内部事务
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.NESTED
            test3:propagation = Propagation.REQUIRED + 自制异常
          测试:对test1、test3而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,并声明传播方式为NESTED,外部事物出现异常,此时事务1会回滚。
            数据库结果:无记录
            控制台结果:test3出现异常,由于外部事物影响内部,所以test1、test2、test3方法均回滚。


            2.内部事务不影响外部事物
            test1:propagation = Propagation.REQUIRED
            test2:propagation = Propagation.NESTED + 自制异常
            test3:propagation = Propagation.REQUIRED
            测试:对test1、test3而言,当前方法运行在事务1中,对test2而言当前事务运行在事务1内部,并声明传播方式为NESTED,test2构建的内部事务出现异常,不影响外部事务。
            数据库结果:test1事务测试 + test3事务测试
            控制台结果:内部事务不影响外部事物,外部事务1正常执行,内部事务出现异常,方法触发回滚。

至此,关于Spring事务管理的传播行为的测试完结,了解事务传播行为可以合理安排业务执行。基于注解方式声明事务这一操作,实质上基于Spring AOP的动态代理机制,相对底层的学习会在以后展开,需要学习的小伙伴可以参考之前两篇文章对概念性进行理解,结合测试用例,有利于理解和学习。

猜你喜欢

转载自blog.csdn.net/Nerver_77/article/details/81556968
今日推荐