spring事务声明的几种传播特性

本文深入探讨Spring框架中事务的传播特性,包括PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED和PROPAGATION_NEVER等几种模式下的行为表现,以及PROPAGATION_NESTED与PROPAGATION_REQUIRES_NEW的区别。

 最近遇到了一个spring事务导致的问题,所以写了几个小程序了解了一下事务的传播特性,下面分别举例子分别看看事务的传播特性。

 

        事务的几种传播特性

1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
Java代码   收藏代码
  1. /** 
  2.  * TransactionTestService     test1和test2配有事务(PROPAGATION_REQUIRED) */  
  3. public interface TransactionTestService {  
  4.     //事务属性 PROPAGATION_REQUIRED  
  5.     public void test1() throws Exception{  
  6.         avInfoTaskTunnel.insertAvInfoTask();  
  7.         test2();  
  8.     }  
  9.     //事务属性 PROPAGATION_REQUIRED  
  10.     public void test2() throws Exception{  
  11.         avRequestTunnel.insertAvRequest();  
  12.         throw new Exception();  
  13.     }  
  14. }  
  15.    
  16. /** 
  17.  * main 
  18.  */  
  19. public class TransactionTestMain {  
  20.    
  21.     public static void main(String[] args) throws Exception{  
  22.         TransactionTestService transactionTestService = (TransactionTestService)context.getBean("transactionTestService");  
  23.         try {  
  24.             transactionTestService.test1();  
  25.         } catch (Exception e) {  
  26.         }  
  27.     }  
  28. }  

        上述代码中test1()和test2()都配有PROPAGATION_REQUIRED事务属性,test1()内部调用test2(),这样test1()和test2()方法将都处于同一事务之中,当在test2()中抛出异常,会导致test1()和test2()方法中的事务都回滚。

       但是,如果test1()方法对调用test2()时捕获异常,结果会是怎样的呢? test1应该能正常写入没问题,那么test2呢?

Java代码   收藏代码
  1. //test1()中捕获test2()抛出的异常  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.         try {  
  5.             test2();  
  6.         }catch (Exception e) {  
  7.         }  
  8.    
  9.     }  

       最后的结果test2()也将会正常的写入。其实,上面情况中如果只是test1()配有PROPAGATION_REQUIRED事务属性,test2()不配置任何事务属性,发生的结果也是一致的。上面的情形相当于:

Java代码   收藏代码
  1. public static void main(String[] args) throws Exception{  
  2.         Connection con = null;  
  3.         try {  
  4.             con=getConnection();  
  5.             con.setAutoCommit(false);  
  6.             transactionTestService.test1(); //test1()和test2()处于同一事务之中  
  7.             con.commit();  
  8.         } catch (Exception e) {  
  9.             con.rollback();  
  10.         } finally {  
  11.             closeCon();  
  12.         }  
  13.     }  

         上述test1()和test2()是同一个类的两个方法,那么要是它们处于不同类呢?

Java代码   收藏代码
  1. //main函数不变,test1()中调用test2()地方换成调用另一个类中配有PROPAGATION_REQUIRED事务属性的方法  
  2.     //不对otherService.test2()捕获异常的情形就没必要说了,必定都回滚。  
  3.     public void test1() throws Exception{  
  4.         avInfoTaskTunnel.insertAvInfoTask();  
  5.         try {  
  6.             otherService.test2();  //PROPAGATION_REQUIRED事务属性  
  7.         } catch (Exception e) {  
  8.         }  
  9.     }  
  10.    
  11.     //otherService.test2()  
  12.     public void test2() throws Exception{  
  13.         avRequestTunnel.insertAvRequest();  
  14.         throw new Exception();  
  15.     }  

         上述同样捕获了异常,但是结果会怎样呢? 结果有点出乎意料,与之前test1(),test2()处于同一类的情形不同,这个时候,两个方法都将回滚,并且在调用test1()的地方会抛出下面异常。这是由于子事务在回滚的时候已经将主事务标记成了rollback-only,这样导致主事务在提交的时候就会抛出下面这个异常。 另外:如果otherService.test2()没有配置任何事务属性,那么test2()抛出异常的时候,将导致test1()和test2()都回滚。

Java代码   收藏代码
  1. org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only  

        上述情况网上查询到一种解决方式是在transactionManager中将globalRollbackOnParticipationFailure 设置为false(默认是true)。但是这样又带来另一个问题,子事务也给一并提交了(这个时候子事务产生异常,不想提交),具体的解决方式还没找到,但是我觉得不应该将这两个配置在同一事务中。

Xml代码   收藏代码
  1. <bean id="transactionManager"      
  2.         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  3.         <property name="dataSource">  
  4.             <ref local="dataSource" />  
  5.         </property>  
  6.         <property name="globalRollbackOnParticipationFailure" value="false" />   
  7.     </bean>  

 

2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
Java代码   收藏代码
  1. //TransactionTestService     test1()配有事务(PROPAGATION_SUPPORTS)  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.         throw new Exception();  
  5.     }  
  6.    
  7.    
  8. /** 
  9.  * main 
  10.  */  
  11.     public class TransactionTestMain {  
  12.    
  13.         public static void main(String[] args) throws Exception{  
  14.             TransactionTestService transactionTestService = (TransactionTestService)context.getBean("transactionTestService");  
  15.             try {  
  16.                 transactionTestService.test1();  
  17.             } catch (Exception e) {  
  18.             }  
  19.        }  
  20.     }  

         TransactionTestService的test1()配有PROPAGATION_SUPPORTS事务属性,我们知道这种情形下如果配的是PROPAGATION_REQUIRED事务属性,那么test1()将新建事务运行,但是PROPAGATION_SUPPORTS属性不会新建,这种情形下,test1()方法将正常提交。那如果是外层事务配有事务呢?如下所示,此种情形test2()将处于事务之中了。

Java代码   收藏代码
  1. //PROPAGATION_REQUIRED事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.         test2();  
  5.     }  
  6.     //PROPAGATION_SUPPORTS事务属性  
  7.     public void test2() throws Exception{  
  8.         avRequestTunnel.insertAvRequest();  
  9.         throw new Exception();  
  10.     }  

 

  3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

 

Java代码   收藏代码
  1. //情形1:PROPAGATION_REQUIRED事务属性  情形2:不配任何事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.         otherService.test2();  
  5.     }  
  6.     //otherService的test2()配置PROPAGATION_MANDATORY事务属性  
  7.     public void test2() throws Exception {  
  8.         avRequestTunnel.insertAvRequest();  
  9.         throw new Exception();  
  10.     }  
  11.    
  12. //test1()处于情形2时抛出异常,test2()不能够提交,但是test1()却是可以成功提交的  
  13. 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: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
Java代码   收藏代码
  1. //情形1:PROPAGATION_REQUIRED事务属性  情形2:不配任何事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.         otherService.test2();  
  5.     }  
  6.     //otherService的test2()配置PROPAGATION_REQUIRES_NEW事务属性  
  7.     public void test2() throws Exception {  
  8.         avRequestTunnel.insertAvRequest();  
  9.         throw new Exception();  
  10.     }  

 上述情况,test1()处于情形2时test2()新建事务,这点没有问题。那如果test1()处于情形1呢?也就是说test1()已经有事务了。结果是test2()处于新的事务中,怎么确定是处于新的事务中呢?看下面代码:

Java代码   收藏代码
  1. //test1配置PROPAGATION_REQUIRED事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.             otherService.test2();  //PROPAGATION_REQUIRES_NEW事务属性  
  5.     }  
  6.    
  7.     //otherService.test2()  
  8.     public void test2() throws Exception{  
  9.         avRequestTunnel.insertAvRequest();  
  10.         throw new Exception();  
  11.     }  

         如果是在同一事务中,那么情形将同于讲述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: 总是非事务地执行,并挂起任何存在的事务。
Java代码   收藏代码
  1. //test1配置PROPAGATION_REQUIRED事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.             otherService.test2();  //PROPAGATION_NOT_SUPPORTED事务属性  
  5.     }  
  6.    
  7.     //otherService.test2()  
  8.     public void test2() throws Exception{  
  9.         avRequestTunnel.insertAvRequest();  
  10.         throw new Exception();  
  11.     }  

         如果otherService.test2() 没有配置事务属性,那么test2()抛出异常时,test1()和test2()都回滚。但是现在test2()配置了PROPAGATION_NOT_SUPPORTED事务属性,那么test2()将以非事务运行,而test1()继续运行在事务中,也就是说,此时,test1()回滚,test2()正常提交。注意:如果test1()和test2()在同一类中,那么test2()的PROPAGATION_NOT_SUPPORTED失效。

 

6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
Java代码   收藏代码
  1. //test1配置PROPAGATION_REQUIRED事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.             otherService.test2();  //PROPAGATION_NEVER事务属性  
  5.     }  
  6.    
  7.     //otherService.test2()  
  8.     public void test2() throws Exception{  
  9.         avRequestTunnel.insertAvRequest();  
  10.         throw new Exception();  
  11.     }  
  12. //test1()配置PROPAGATION_REQUIRED事务属性, otherService.test2()配置PROPAGATION_NEVER事务属性,将抛下面异常:  
  13. 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.

Java代码   收藏代码
  1. //test1配置PROPAGATION_REQUIRED事务属性  
  2.     public void test1() throws Exception{  
  3.         avInfoTaskTunnel.insertAvInfoTask();  
  4.             otherService.test2();  //PROPAGATION_NESTED事务属性  
  5.     }  
  6.    
  7.     //otherService.test2()  
  8.     public void test2() throws Exception{  
  9.         avRequestTunnel.insertAvRequest();  
  10.     }  

         上述情况,如果otherService.test2()配置PROPAGATION_REQUIRES_NEW事务属性,这样test1()回滚,但是test2()正常提交,因为这是两个事务。但是如果otherService.test2()配置PROPAGATION_NESTED事务属性,那么test1()和test2()都将回滚。

<think>我们被要求回答Spring框架支持的事务传播行为类型。根据引用内容,Spring定义了7种事务传播行为。这些传播行为在TransactionDefinition接口中规定。 根据引用[1]:Spring框架提供了7种事务传播行为。 引用[2]:Spring 提供了多种事务传播行为,包括七种常见的。 引用[3]:Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。 引用[4]:事务传播行为定义了当a()方法(有事务)调用b()方法(也有事务)时,事务如何传递。 因此,我们需要列出这7种事务传播行为,并简要说明每种行为的意义。 根据引用[2]和引用[3]的内容,这7种传播行为分别是: 1. REQUIRED 2. SUPPORTS 3. MANDATORY 4. REQUIRES_NEW 5. NOT_SUPPORTED 6. NEVER 7. NESTED 现在,我们根据引用内容对每一种传播行为进行解释: 1. **REQUIRED(必须的)**: - 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的传播行为[^2][^3]。 2. **SUPPORTS(支持的)**: - 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行[^2][^3]。 3. **MANDATORY(强制的)**: - 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常[^2][^3]。 4. **REQUIRES_NEW(需要新事务)**: - 创建一个新的事务,如果当前存在事务,则把当前事务挂起[^2][^3]。 5. **NOT_SUPPORTED(不支持)**: - 以非事务方式执行操作,如果当前存在事务,则把当前事务挂起[^2][^3]。 6. **NEVER(从不)**: - 以非事务方式执行,如果当前存在事务,则抛出异常[^2][^3]。 7. **NESTED(嵌套的)**: - 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,只有外部事务结束时才会提交。内部事务的回滚不会影响外部事务,但外部事务的回滚会导致内部事务回滚[^2][^3][^4]。 注意:引用[4]中提到了事务传播行为的场景,即一个事务方法调用另一个事务方法时事务如何传递。 因此,我们可以这样组织回答: 首先,介绍事务传播行为的概念,然后列出7种类型并逐一解释。 同时,按照用户要求,在回答末尾生成相关问题。 注意:回答中引用的段落末尾自然地添加引用标识,例如:[^1] 由于引用内容来自多个引用,我们在解释每种传播行为时,可以引用多个来源,例如[^2][^3]。</think>Spring 框架通过 `TransactionDefinition` 接口定义了 **7 种事务传播行为**,用于控制事务方法在嵌套调用时的边界处理逻辑。以下是具体类型及其核心特性: --- ### 📜 事务传播行为类型 1. **REQUIRED (默认)** - 当前存在事务则加入,不存在则创建新事务 - **场景**:适用于大多数业务方法(如订单创建+库存扣减)[^2][^4] ```java @Transactional(propagation = Propagation.REQUIRED) public void methodA() { methodB(); // 共享methodA的事务 } ``` 2. **REQUIRES_NEW** - 始终创建独立新事务,挂起当前事务(若存在) - **场景**:日志记录(需独立提交不受主事务回滚影响)[^3] ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void auditLog() { ... } // 独立事务 ``` 3. **NESTED** - 在当前事务内创建**嵌套子事务**(Savepoint机制) - **特点**:子事务回滚不影响父事务,父事务回滚强制子事务回滚[^1][^3] - **数据库要求**:需支持 Savepoint(如 MySQL InnoDB) 4. **SUPPORTS** - 存在事务则加入,不存在则以非事务方式执行 - **场景**:查询方法(可适应事务或非事务环境)[^2] 5. **NOT_SUPPORTED** - 以非事务方式执行,挂起当前事务(若存在) - **场景**:批量数据处理(避免长事务占用连接)[^3] 6. **MANDATORY** - 必须在已有事务中执行,否则抛出 `IllegalTransactionStateException` - **场景**:强制事务调用的工具方法[^1] 7. **NEVER** - 必须在非事务环境下执行,存在事务则抛出异常 - **场景**:性能敏感操作(禁止事务开销)[^2] --- ### ⚖️ 传播行为对比 | 类型 | 当前有事务 | 当前无事务 | 典型场景 | |---------------|------------|------------------|------------------------| | `REQUIRED` | 加入 | 创建新事务 | 常规业务方法(默认) | | `REQUIRES_NEW`| 挂起并新建 | 创建新事务 | 独立日志/审计 | | `NESTED` | 嵌套子事务 | 创建新事务 | 可部分回滚的业务单元 | | `SUPPORTS` | 加入 | 非事务执行 | 查询方法 | | `NOT_SUPPORTED`| 挂起 | 非事务执行 | 大数据处理 | | `MANDATORY` | 加入 | **抛出异常** | 强制事务依赖的工具类 | | `NEVER` | **抛出异常** | 非事务执行 | 高性能非事务操作 | --- ### 💡 核心价值 1. **事务边界控制**:通过 `REQUIRES_NEW` 实现关键操作的独立提交[^1] 2. **部分回滚能力**:`NESTED` 提供类似保存点的细粒度控制[^3] 3. **灵活适配**:`SUPPORTS` 使方法能同时适应事务/非事务环境[^4] 4. **强约束保障**:`MANDATORY` 确保方法必须在事务上下文中调用[^2] > 📌 **设计建议**:默认使用 `REQUIRED`,对需独立提交的操作使用 `REQUIRES_NEW`,对可部分回滚的子任务使用 `NESTED`[^1][^3]。 --- ### 示例场景 ```java @Service public class OrderService { @Transactional(propagation = Propagation.REQUIRED) public void placeOrder(Order order) { inventoryService.deductStock(order); // REQUIRED (加入事务) auditService.logOperation(); // REQUIRES_NEW (独立事务) couponService.applyCoupon(order); // NESTED (嵌套子事务) } } ``` > 当库存扣减失败时: > - `deductStock` 回滚导致主事务回滚 > - `logOperation` 独立提交不受影响 > - `applyCoupon` 因嵌套在主事务中自动回滚[^3][^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值