最近遇到了一个spring事务导致的问题,所以写了几个小程序了解了一下事务的传播特性,下面分别举例子分别看看事务的传播特性。
事务的几种传播特性
1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
/** * TransactionTestService test1和test2配有事务(PROPAGATION_REQUIRED) */ public interface TransactionTestService { //事务属性 PROPAGATION_REQUIRED public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); test2(); } //事务属性 PROPAGATION_REQUIRED public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); } } /** * main */ public class TransactionTestMain { public static void main(String[] args) throws Exception{ TransactionTestService transactionTestService = (TransactionTestService)context.getBean("transactionTestService"); try { transactionTestService.test1(); } catch (Exception e) { } } }
上述代码中test1()和test2()都配有PROPAGATION_REQUIRED事务属性,test1()内部调用test2(),这样test1()和test2()方法将都处于同一事务之中,当在test2()中抛出异常,会导致test1()和test2()方法中的事务都回滚。
但是,如果test1()方法对调用test2()时捕获异常,结果会是怎样的呢? test1应该能正常写入没问题,那么test2呢?
//test1()中捕获test2()抛出的异常 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); try { test2(); }catch (Exception e) { } }
最后的结果test2()也将会正常的写入。其实,上面情况中如果只是test1()配有PROPAGATION_REQUIRED事务属性,test2()不配置任何事务属性,发生的结果也是一致的。上面的情形相当于:
public static void main(String[] args) throws Exception{ Connection con = null; try { con=getConnection(); con.setAutoCommit(false); transactionTestService.test1(); //test1()和test2()处于同一事务之中 con.commit(); } catch (Exception e) { con.rollback(); } finally { closeCon(); } }
上述test1()和test2()是同一个类的两个方法,那么要是它们处于不同类呢?
//main函数不变,test1()中调用test2()地方换成调用另一个类中配有PROPAGATION_REQUIRED事务属性的方法 //不对otherService.test2()捕获异常的情形就没必要说了,必定都回滚。 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); try { otherService.test2(); //PROPAGATION_REQUIRED事务属性 } catch (Exception e) { } } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }
上述同样捕获了异常,但是结果会怎样呢? 结果有点出乎意料,与之前test1(),test2()处于同一类的情形不同,这个时候,两个方法都将回滚,并且在调用test1()的地方会抛出下面异常。这是由于子事务在回滚的时候已经将主事务标记成了rollback-only,这样导致主事务在提交的时候就会抛出下面这个异常。 另外:如果otherService.test2()没有配置任何事务属性,那么test2()抛出异常的时候,将导致test1()和test2()都回滚。
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
上述情况网上查询到一种解决方式是在transactionManager中将globalRollbackOnParticipationFailure 设置为false(默认是true)。但是这样又带来另一个问题,子事务也给一并提交了(这个时候子事务产生异常,不想提交),具体的解决方式还没找到,但是我觉得不应该将这两个配置在同一事务中。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="globalRollbackOnParticipationFailure" value="false" /> </bean>
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
//TransactionTestService test1()配有事务(PROPAGATION_SUPPORTS) public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); throw new Exception(); } /** * main */ public class TransactionTestMain { public static void main(String[] args) throws Exception{ TransactionTestService transactionTestService = (TransactionTestService)context.getBean("transactionTestService"); try { transactionTestService.test1(); } catch (Exception e) { } } }
TransactionTestService的test1()配有PROPAGATION_SUPPORTS事务属性,我们知道这种情形下如果配的是PROPAGATION_REQUIRED事务属性,那么test1()将新建事务运行,但是PROPAGATION_SUPPORTS属性不会新建,这种情形下,test1()方法将正常提交。那如果是外层事务配有事务呢?如下所示,此种情形test2()将处于事务之中了。
//PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); test2(); } //PROPAGATION_SUPPORTS事务属性 public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }
3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
//情形1:PROPAGATION_REQUIRED事务属性 情形2:不配任何事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); } //otherService的test2()配置PROPAGATION_MANDATORY事务属性 public void test2() throws Exception { avRequestTunnel.insertAvRequest(); throw new Exception(); } //test1()处于情形2时抛出异常,test2()不能够提交,但是test1()却是可以成功提交的 org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
上述情况,当test1()处于情形1时,会是的test1()和test2()都处于事务当中;test1()处于情形2时,就会抛异常,但是这个时候test2()不能够提交,但是test1()却是可以成功提交的。此外,还有一点,注意上述test2()是处于另一个类中的,如果是处于同一个类,那么PROPAGATION_MANDATORY不会因为外层是否有事务而抛异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
//情形1:PROPAGATION_REQUIRED事务属性 情形2:不配任何事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); } //otherService的test2()配置PROPAGATION_REQUIRES_NEW事务属性 public void test2() throws Exception { avRequestTunnel.insertAvRequest(); throw new Exception(); }
上述情况,test1()处于情形2时test2()新建事务,这点没有问题。那如果test1()处于情形1呢?也就是说test1()已经有事务了。结果是test2()处于新的事务中,怎么确定是处于新的事务中呢?看下面代码:
//test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_REQUIRES_NEW事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }
如果是在同一事务中,那么情形将同于讲述PROPAGATION_REQUIRED属性时的情形,test1()和test2()都将回滚,并且抛出异常事务被打上rollback-only标记的异常。但是这里,结果就是test2()回滚,test1正常提交,所以otherService.test2()处于新的事务中。注意:上述情况test2()也是另一个类的方法,如果属于同一类,也就是test1()和test2()处于同一个类,test1()中调用test2(),那么配置的PROPAGATION_REQUIRES_NEW将无效(跟test2()什么事务属性都没配置一样)。但是如果是main函数中直接调用test2(),那么还是会起一个新的事务。
另外一种证明test1()和test2()处于不同事务的方式是,在test2()不抛出异常,然后再test1()调用了test2()之后,抛出异常,最后结果是()回滚,test2()正常提交。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
//test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_NOT_SUPPORTED事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }
如果otherService.test2() 没有配置事务属性,那么test2()抛出异常时,test1()和test2()都回滚。但是现在test2()配置了PROPAGATION_NOT_SUPPORTED事务属性,那么test2()将以非事务运行,而test1()继续运行在事务中,也就是说,此时,test1()回滚,test2()正常提交。注意:如果test1()和test2()在同一类中,那么test2()的PROPAGATION_NOT_SUPPORTED失效。
6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
//test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_NEVER事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); } //test1()配置PROPAGATION_REQUIRED事务属性, otherService.test2()配置PROPAGATION_NEVER事务属性,将抛下面异常: org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
当然上述情况,如果是test1()和test2()在同一类中,那么PROPAGATION_NEVER也将失效。
7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED。PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint.。嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.
//test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_NESTED事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); }
上述情况,如果otherService.test2()配置PROPAGATION_REQUIRES_NEW事务属性,这样test1()回滚,但是test2()正常提交,因为这是两个事务。但是如果otherService.test2()配置PROPAGATION_NESTED事务属性,那么test1()和test2()都将回滚。
相关推荐
#### 三、详细解释每种传播特性 - **PROPAGATION_REQUIRED**:这是最常用的事务传播行为,如果当前存在事务,则当前方法将在该事务中运行;如果当前不存在事务,则会创建一个新的事务,并在该事务中运行当前方法。...
### Spring事务的传播特性和隔离级别 #### 一、事务的基本概念 在计算机科学中,事务(transaction)是指一系列操作的集合,这些操作要么全部成功完成,要么全部失败回滚,确保数据的一致性和完整性。事务具备四个...
下面详细介绍 Spring 的几种事务传播特性: 1. **PROPAGATION_REQUIRED** - **定义**:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 - **应用场景**:这是最常用的一种事务传播特性...
通过理解这七种传播行为的含义及其适用场景,开发者可以在Spring应用中更灵活地管理事务,从而提高系统的可靠性和性能。在实际开发过程中,根据具体的需求选择适当的传播行为是非常关键的一步。
在Spring事务管理Demo中,通常会包含以下几个步骤: 1. 配置事务管理器:在Spring的XML配置文件中,根据数据库类型(如JDBC、Hibernate、MyBatis等)配置相应的事务管理器。 2. 开启事务:使用`@Transactional`注解...
此外,Spring 事务管理还涉及到事务隔离级别(如 READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE)和事务超时设置等高级特性。这些都可以通过配置或者注解进行定制,以满足不同业务场景的需求。 ...
### Spring事务与数据库操作 #### 一、Spring的声明式事务管理 在现代软件开发中,事务处理是非常关键的一部分,特别是在涉及多个数据操作时。Spring框架提供了强大的事务管理能力,可以方便地集成到应用程序中。...
本文将深入探讨Spring中的几种事务配置方式,帮助开发者更好地理解和运用。 1. **编程式事务管理** 编程式事务管理是在代码中显式调用事务API来控制事务的开始、提交、回滚等操作。这种方式直接在业务逻辑代码中...
在讨论了代理模式、异常分类、方法权限后,文章还提到了Spring事务管理中事务的传播机制和隔离机制。事务的传播机制定义了事务的行为,例如是否在当前事务中执行或者创建一个新的事务。隔离机制定义了事务之间的隔离...
### Spring事务的七大传播行为 在深入探讨Spring框架下的事务管理之前,我们首先明确事务的概念。事务是指一组操作作为一个整体,要么全部成功,要么全部失败。Spring提供了多种方式来管理和控制事务,其中一种重要...
1. **Spring事务管理器(Transaction Manager)**: - Spring支持多种事务管理器,如DataSourceTransactionManager(用于JDBC事务)和HibernateTransactionManager(用于Hibernate)。事务管理器是负责处理事务的...
声明式事务管理是Spring中最常用的事务管理方式,它通过AOP(面向切面编程)来实现。在配置文件中,我们可以通过`<tx:advice>`元素定义事务行为,并使用`<aop:config>`或`@AspectJ`注解来指定哪些方法应该在事务中...
本文主要探讨Spring声明式事务管理的配置,这是Spring提供的一种简便的事务管理方式,允许开发者在不编写任何事务管理代码的情况下实现事务控制。这种方式极大地提高了代码的可维护性和可读性。 首先,我们要理解...
接下来,我们将通过以下几个步骤来搭建Spring事务操作环境: 1. **引入依赖**:在项目中添加Spring框架和数据库驱动的相关依赖,如Maven的pom.xml文件。 2. **配置数据源**:在Spring的配置文件中,配置数据源,如...
在IT行业中,尤其是在企业级应用开发中,声明式事务控制是一种常见的事务管理方式。它允许开发者通过配置,而不是代码来管理事务,使得事务处理更加简洁、易于维护。本主题聚焦于"声明式事务控制"在Spring 2.5与...
以下是常见的几种传播行为: - **PROPAGATION_REQUIRED**:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 - **PROPAGATION_MANDATORY**:如果当前存在事务,则加入该事务;如果当前没有...
首先,Spring的事务管理分为两种模式:编程式事务管理和声明式事务管理。编程式事务管理通过`PlatformTransactionManager`接口及其实现类(如`DataSourceTransactionManager`)进行手动控制,而声明式事务管理则更加...
Spring 提供了一种抽象化的事务管理方式,使得开发者可以更加专注于业务逻辑而不是底层事务处理的细节。 ##### 1. 核心接口 Spring 的事务管理涉及到几个核心接口,其中最重要的是 `PlatformTransactionManager` ...