`

Spring事务传递性探讨

 
阅读更多

本篇主要讨论下面几点:

一: Spring 事务的传递性介绍

二: 第三方调用含有事务的Service抛异常方法探讨

 

一: Spring 事务的传递性介绍

    事务传播行为,所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

    TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

    TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

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

    TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

    TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

    TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

    TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价TransactionDefinition.PROPAGATION_REQUIRED。

 

    这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

 

二: 第三方调用含有事务的Service抛异常方法探讨

    原始数据:

   

 

   

    假设术语如下:

    a)正常情况

      调用方为Conumer,  调用ServiceA的methodA, methodA调用ServiceA的 methodB和

ServiceA的methodC。

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        updateCommodityCatalog2(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
    }
 

   运行结果为:

    
   
 

 

 

 

    b)事务中一个方法跑出异常

      ServiceA的methodA 调用ServiceA的methodB和ServiceA的methodC,代码如下:

     

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        updateCommodityCatalog2(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("222");
    }
   运行结果:

    
 

      c)捕获异常的情况
   ServiceA的methodA 调用ServiceA的methodB和ServiceA的methodC,代码如下:
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        try {
            updateCommodityCatalog2(commodity);
        } catch (Exception e) {
            
        }
       
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("222");
    }
 
    运行结果:
 
    
 
 
  d)跨服务事务
  ServiceA的methodA 调用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD,代码如下:
 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        updateCommodityCatalog2(commodity);
        updateCommodityCatalog3(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
    }
    
    //另一个Service InnerService的方法
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateCommodityCatalog3(Commodity commodity) {
        commodity.setDescription("desc333");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("333");
    }
    
    运行结果:
    
    假设MethodD抛出异常,则Consumer调用MethodA会发生怎样的情形? 

    发现Consumer调用MethodA的时候出现了运行时异常,UnexpectedRollbackException: “Transaction rolled back because it has been marked as rollback-only”。这是为什么呢?

 

    网上搜索了下,终于发现了一个合理的解释。当MethodA调用MethodD的时候,且两个方法都为required属性,根据事务传播级别,则methodA和methodD共享一个事务,当methodD抛出了异常,则共享事务回滚,但是被MethodA catch了,而MethodA又没有及时抛出异常,则MethodA正常执行到最后的时候,则会做提交事务的操作,但是事务已经被回滚了,所以才出现了上面的异常。

 

    既然这样,小弟就开始YY了一下,哪些情况会使调用方没有这个异常呢?经过与小伙伴们的思维碰撞,发现有一下几个方法。

   1) MethodA 不加事务,所以执行到最后就不会commit,SUPPORTS和NOT_SUPPORTED都可以实现这种功能。

   2) MethodD设置不共享事务,拥有自己单独的事务。验证发现,REQUIRES_NEW可以实现这种功能。

   

 

   e)

   代码如下:

   

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        updateCommodityCatalog2(commodity);
        try {
	    commodityInnerService.updateCommodityCatalog3(commodity);
        } catch (Exception e) {
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
    }
    
    //另一个Service InnerService的方法
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void updateCommodityCatalog3(Commodity commodity) {
        commodity.setDescription("desc333");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("333");
    }

 

 结果如下:



 

 

 

 

   然后又YY了下,既然一个回滚的事务不能提交了,那么这个回滚的事务可以重复回滚吗?

   f)ServiceA的methodA 调用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD和ServiceB的methodE。代码如下:

   

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void modifyCommodityInfo(Commodity commodity) {
        updateCommodityCatalog1(commodity);
        updateCommodityCatalog2(commodity);
        try {
	    ommodityInnerService.updateCommodityCatalog3(commodity);
        } catch (Exception e) {
        }
         try {
	    commodityInnerService.updateCommodityCatalog4(commodity);
        } catch (Exception e) {
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog1(Commodity commodity) {
        commodity.setCatalog("catalog222222222");
        commodityDao.updateCommodity(commodity);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    private void updateCommodityCatalog2(Commodity commodity) {
        commodity.setName("name222");
        commodityDao.updateCommodity(commodity);
    }
    
    //另一个Service InnerService的方法
    @Transactional(propagation = Propagation.REQUIRES, rollbackFor = Exception.class)
    public void updateCommodityCatalog3(Commodity commodity) {
        commodity.setDescription("desc333");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("333");
    }
    
     //另一个Service InnerService的方法
    @Transactional(propagation = Propagation.REQUIRES, rollbackFor = Exception.class)
    public void updateCommodityCatalog4(Commodity commodity) {
        commodity.setDescription("desc333");
        commodityDao.updateCommodity(commodity);
        throw new RuntimeException("333");
    }
     

运行结果:

   发现还是只抛出了一个Transaction rolled back because it has been marked as rollback-only

所以猜测一个被共享的事务抛出多个异常的时候只是标记下rollback-only,而在方法结束的时候判断是执行事务还是回滚事务。

 

   总结:

    1) 单个ServiceA 内部调用不存在事务传播,相当于把methodB和methodC的代码嵌套到methodA的代码中,即使定义了也无法生效。

    2) 跨Service调用存在事务传播级别,需要考虑共享事务,还是新事务调用,即跨Service的调用是否需要需要回滚本Servcie的代码。

   

 

 参考:https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/

 

 

 

 

 

 

 

 

 

  • 大小: 7.8 KB
  • 大小: 3.5 KB
  • 大小: 3.3 KB
  • 大小: 9.8 KB
  • 大小: 8.3 KB
  • 大小: 13 KB
  • 大小: 14.8 KB
分享到:
评论

相关推荐

    Spring事务相关类Uml图

    本文将深入探讨Spring事务管理的相关类及其UML图,帮助理解这些类之间的关系和作用。 首先,Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理通过调用`PlatformTransactionManager`...

    spring_tx编程式事务代码

    总结来说,`Spring_tx编程式事务代码`主要涉及Spring的`TransactionTemplate`类,它是Spring事务管理的一种便捷工具,可以帮助开发者以编程的方式更加方便地控制事务的生命周期,同时保持代码的简洁和可维护性。...

    springCloud-rabbitmq分布事物实现.zip

    在这个实例中,我们将探讨如何利用Spring Cloud和RabbitMQ来实现分布式事务,以及相关的技术细节。 首先,RabbitMQ是一个开源的消息代理和队列服务器,广泛用于处理异步任务和实现解耦。它支持多种协议,包括AMQP...

    Spring整合JMS(四)——事务管理

    本文将深入探讨如何在Spring中整合JMS并实现事务管理,以确保消息传递的可靠性和一致性。 首先,我们需要理解Spring的声明式事务管理。Spring提供了一种强大的声明式事务管理机制,它允许我们在不编写任何事务控制...

    Spring-4.3源码

    现在,我们将深入探讨Spring 4.3中的核心概念和技术细节。 首先,让我们关注依赖注入(DI)。这是Spring的核心特性之一,允许我们通过配置或注解将对象之间的依赖关系明确地定义出来,而不是硬编码在代码中。在...

    Spring4_3_2必须jar包

    本篇文章将详细探讨Spring 4.3.2中的核心知识点,以及与之相关的jar包。 首先,Spring框架的核心在于依赖注入(Dependency Injection,简称DI)。这一特性允许对象之间的关系在运行时动态配置,提高了代码的可测试...

    spring-lib Spring 核心jar包

    让我们深入探讨一下Spring框架的核心组件及其重要性。 1. **IoC(Inversion of Control)容器**:Spring的核心特性之一是依赖注入(Dependency Injection),也称为控制反转。IoC容器负责管理对象的生命周期和对象...

    spring完整demo实例

    在这个"spring完整demo实例"中,我们将深入探讨Spring的主要功能和如何在实际项目中应用它们。 首先,Spring的核心特性——依赖注入,是它简化软件开发的关键。依赖注入允许我们解耦组件,使得代码更加模块化,易于...

    基于Springboot+ActiveMQ事务==本地事件表+activemq.zip

    为了保证事务的原子性,Springboot可以集成Spring Transactions,使用@Transactional注解来开启事务管理。在服务端,当接收到消息后,本地事务会开始,执行业务逻辑。如果一切顺利,事务提交,消息确认;否则,事务...

    spring recipe 英文版

    事务管理是保证数据一致性的重要手段,Spring 提供了全面的事务管理功能,可以方便地应用于不同的业务场景中。 #### 12. Spring Batch 对于需要处理大量数据的任务,Spring Batch 提供了一种可靠的批量处理解决...

    SpringRecipes

    - **声明式事务管理**:探讨了如何利用Spring的声明式事务管理简化事务逻辑的编写,提高代码的可读性和可维护性。 - **JDBC模板**:详细说明了如何使用Spring的JDBC模板类来简化数据库访问代码,减少错误和重复...

    spring技术手册demo

    此外,还会讲解如何在Spring中实现事务管理,以确保数据的一致性和完整性。 通过这五个章节的学习,你将对Spring有一个全面的认识,能够熟练地使用Spring框架进行开发,无论是基本的Bean管理,还是更高级的AOP和...

    rest+spring+hibernate

    4. **Spring事务管理**:Spring的Transaction Management提供了声明式事务管理,通过@Transactional注解在方法级别控制事务边界。当发生异常时,Spring会自动回滚事务,确保数据的一致性。 5. **O2O_v1.0**:这个...

    Spring mvc + Spring + Spring jdbc 整合 demo

    在本项目中,我们主要探讨的是如何将Spring MVC、Spring框架和Spring JDBC这三大核心组件进行整合,构建一个完整的Java Web应用程序。这个整合Demo旨在帮助开发者理解这些技术的协同工作方式,以及如何在实际开发中...

    Java学习SpringCloud - 整合Seata实现分布式事务

    本篇文章将详细探讨如何在Spring Cloud项目中整合Seata,实现分布式事务的处理。 首先,我们需要了解分布式事务的基本概念。在单体应用中,事务管理相对简单,但在分布式系统中,由于多个服务间的交互,确保数据的...

    spring 资料spring 资料spring 资料spring 资料

    下面将深入探讨Spring框架的核心组件和关键特性。 1. **依赖注入(Dependency Injection, DI)**:DI是Spring的核心特性,它允许开发者在运行时动态地将依赖关系注入到对象中,而不是在代码中硬编码这些依赖。这样...

    Spring与JDBC整合

    同时,Spring的事务管理确保了数据的一致性,提高了代码的可维护性和可测试性。 总之,Spring与JDBC的整合是Java开发中的常见实践,它降低了数据库操作的复杂性,提升了代码质量。理解并熟练掌握这一技术,对于提升...

    spring 整合全部jar包

    接下来,我们将深入探讨这些模块及其重要性。 1. **Spring IOC**: Spring IOC容器是Spring框架的核心,它负责管理对象的生命周期和依赖关系。通过使用IOC,开发者不再需要手动创建和管理对象,而是将这些工作交给...

    spring-source-4.2.4源码

    通过源码,我们可以看到Spring如何协调数据库事务,确保数据的一致性和完整性。 `spring-messaging-4.2.4.RELEASE-sources`包含了对消息传递系统的支持,如JMS、AMQP等。源码中展示了Spring如何处理异步通信和消息...

Global site tag (gtag.js) - Google Analytics