Spring事务传播机制和隔离机制

本文详细解析了Spring框架中的事务管理机制,包括默认的回滚策略、传播行为及嵌套事务的概念。并通过具体示例介绍了不同传播属性的应用场景。

事务的嵌套概念

所谓事务的嵌套就是两个事务方法之间相互调用。spring事务开启 ,或者是基于接口的或者是基于类的代理被创建( 注意一定要是代理,不能手动new 一个对象,并且此类(有无接口都行)一定要被代理——spring中的bean只要纳入了IOC管理都是被代理的 )。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的

###
Spring默认情况下会对运行期例外(RunTimeException),即uncheck异常,进行事务回滚。
如果遇到checked异常就不回滚。
如何改变默认规则:

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)


2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)


3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
上面三种方式也可在xml配置

spring事务传播属性

 在 spring的 TransactionDefinition接口中一共定义了六种事务传播属性

PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。 
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。 
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。 
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。 
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。 
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager) 


举例浅析Spring嵌套事务
ServiceA#methodA(我们称之为外部事务),ServiceB#methodB(我们称之为外部事务)
 
[java]  view plain  copy
  1. ServiceA {  
  2.          
  3.      void methodA() {  
  4.          ServiceB.methodB();  
  5.      }  
  6.     
  7. }  
  8.     
  9. ServiceB {  
  10.          
  11.      void methodB() {  
  12.      }  
  13.          
  14. }  

PROPAGATION_REQUIRED

假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候
  1、如果ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。这时只有外部事务并且他们是共用的,所以这时ServiceA.methodA或者ServiceB.methodB无论哪个发生异常methodA和methodB作为一个整体都将一起回滚。
  2、如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。这样,在ServiceA.methodA中是没有事务控制的。只是在ServiceB.methodB内的任何地方出现异常,ServiceB.methodB将会被回滚,不会引起ServiceA.methodA的回滚

PROPAGATION_SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行 

PROPAGATION_MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

PROPAGATION_REQUIRES_NEW

启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 
 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。

使用场景:
不管业务逻辑的service是否有异常,Log Service都应该能够记录成功,所以Log Service的传播属性可以配为此属性。最下面将会贴出配置代码。

PROPAGATION_NOT_SUPPORTED

当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

PROPAGATION_NEVER

不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。 

PROPAGATION_NESTED

开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 

比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_NESTED,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的子事务并设置savepoint,等待ServiceB.methodB的事务完成以后,他才继续执行。。因为ServiceB.methodB是外部事务的子事务,那么
1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB也将回滚。
2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理,ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是:
PROPAGATION_REQUIRES_NEW 完全是一个新的事务,它与外部事务相互独立; 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back. 

在 spring 中使用 PROPAGATION_NESTED的前提:

1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!! 
2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+ 
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0 

确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. 


##############################################################################

Log Service配置事务传播

不管业务逻辑的service是否有异常,Log Service都应该能够记录成功,通常有异常的调用更是用户关心的。Log Service如果沿用业务逻辑Service的事务的话在抛出异常时将没有办法记录日志(事实上是回滚了)。所以希望Log Service能够有独立的事务。日志和普通的服务应该具有不同的策略。Spring 配置文件transaction.xml:
[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
  4.     xmlns:tx="http://www.springframework.org/schema/tx"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">  
  6.     <!-- configure transaction -->  
  7.   
  8.     <tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">  
  9.         <tx:attributes>  
  10.             <tx:method name="get*" read-only="true" />  
  11.             <tx:method name="query*" read-only="true" />  
  12.             <tx:method name="find*" read-only="true" />  
  13.             <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />  
  14.         </tx:attributes>  
  15.     </tx:advice>  
  16.   
  17.     <tx:advice id="logTxAdvice" transaction-manager="transactionManager">  
  18.         <tx:attributes>  
  19.             <tx:method name="get*" read-only="true" />  
  20.             <tx:method name="query*" read-only="true" />  
  21.             <tx:method name="find*" read-only="true" />  
  22.             <tx:method name="*" propagation="REQUIRES_NEW"  
  23.                 rollback-for="java.lang.Exception" />  
  24.         </tx:attributes>  
  25.     </tx:advice>  
  26.   
  27.     <aop:config>  
  28.         <aop:pointcut id="defaultOperation"  
  29.             expression="@within(com.homent.util.DefaultTransaction)" />  
  30.         <aop:pointcut id="logServiceOperation"  
  31.             expression="execution(* com.homent.service.LogService.*(..))" />  
  32.               
  33.         <aop:advisor advice-ref="defaultTxAdvice" pointcut-ref="defaultOperation" />  
  34.         <aop:advisor advice-ref="logTxAdvice" pointcut-ref="logServiceOperation" />  
  35.     </aop:config>  
  36. </beans>  

 如上面的Spring配置文件所示,日志服务的事务策略配置为propagation="REQUIRES_NEW",告诉Spring不管上下文是否有事务,Log Service被调用时都要求一个完全新的只属于Log Service自己的事务。通过该事务策略,Log Service可以独立的记录日志信息,不再受到业务逻辑事务的干扰。










<think>嗯,用户这次想了解Spring事务传播机制隔离级别的详细解释。之前用户提到了线性表,但那已经是另一个话题了,现在需要专注在Spring事务上。用户可能正在开发企业级Java应用,遇到了事务管理的问题。从ta特意要求“详细解释”来看,应该不是初学者,可能是想深入理解原理来解决实际项目中的事务问题。之前提供的两个技术引用很有价值,特别是PlatformTransactionManager接口XML配置示例。关于传播行为,需要讲清楚七种类型的区别,特别是PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW这种容易混淆的。隔离级别部分要强调脏读、不可重复读、幻读的具体表现,以及不同隔离级别的适用场景。注意到用户提供的引用里提到事务配置方式,所以在回答中需要区分注解配置XML配置的不同语法。还要解释清楚核心接口PlatformTransactionManagerTransactionDefinition的关系。对于传播机制,准备用场景对话的方式来说明每种行为的使用时机。比如PROPAGATION_SUPPORTS适合查询方法,PROPAGATION_MANDATORY用于必须存在事务的场景等。隔离级别部分考虑用表格对比,但也要说明Spring的默认隔离级别是数据库决定的这个重要知识点。脏读、不可重复读、幻读这些概念需要具体化,最好举例说明。最后的相关问题设计要针对可能产生的疑问,比如嵌套事务异常处理、隔离级别性能影响等实用话题。这样用户可以根据需要进一步探索。</think> ### Spring 事务传播机制详解 Spring 事务传播行为定义了**多个事务方法相互调用时事务如何传播**,由`Propagation`枚举类(实现`TransactionDefinition`接口)定义。以下是所有传播行为及其场景说明: | **传播行为类型** | **枚举值** | **行为描述** | **典型场景** | |----------------------------|---------------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **必需事务** | `REQUIRED`(默认) | 若当前无事务则开启新事务;若存在事务则加入 | 大多数写操作(如订单创建)[^1] | | **支持事务** | `SUPPORTS` | 存在事务则加入;否则以非事务运行 | 查询操作(可兼容事务环境)[^2] | | **强制要求事务** | `MANDATORY` | 必须在事务中运行,否则抛出异常 | 金额结算(必须与其他操作同事务)[^1] | | **新建事务** | `REQUIRES_NEW` | 无论当前有无事务,都开启独立新事务(挂起原事务) | 日志记录(需独立提交)[^1] | | **非事务运行** | `NOT_SUPPORTED` | 以非事务方式执行,挂起当前事务 | 外部接口调用(不受事务管理) | | **禁止事务** | `NEVER` | 必须在非事务环境运行,否则抛出异常 | 静态数据加载 | | **嵌套事务** | `NESTED` | 当前有事务时创建嵌套子事务(可独立回滚);无事务时行为同`REQUIRED` | 批量处理子任务(允许部分失败回滚)[^1] | --- ### Spring 事务隔离级别详解 事务隔离级别解决**并发操作导致的数据一致性问题**,由`Isolation`枚举类定义。Spring 支持以下五级隔离: | **隔离级别** | **枚举值** | **脏读** | **不可重复读** | **幻读** | **实现基础** | |----------------------------|-----------------------------|----------|----------------|----------|------------------------------| | **默认隔离级** | `DEFAULT` | ❌ | ❌ | ❌ | 使用数据库默认级别 | | **读未提交** | `READ_UNCOMMITTED` | ✔️ | ✔️ | ✔️ | 最低隔离性,高并发性能 | | **读已提交** | `READ_COMMITTED`(常用) | ❌ | ✔️ | ✔️ | Oracle/SQL Server 默认 | | **可重复读** | `REPEATABLE_READ` | ❌ | ❌ | ✔️ | MySQL 默认 | | **串行化** | `SERIALIZABLE` | ❌ | ❌ | ❌ | 最高隔离性,低并发性能 | #### 关键概念说明 1. **脏读(Dirty Read)** 读取到其他事务未提交的数据(如事务A修改数据未提交,事务B读到修改值后A又回滚)。 2. **不可重复读(Non-Repeatable Read)** 同一事务内多次读取结果不同(如事务A两次读取间事务B修改并提交了数据)。 3. **幻读(Phantom Read)** 同一查询返回不同行数(如事务A统计用户数时事务B新增用户并提交)。 --- ### 配置示例 #### 1. 注解声明事务(推荐) ```java @Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class ) public void createOrder(Order order) { // 业务逻辑 } ``` #### 2. XML 配置事务管理器(兼容旧系统) ```xml <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="create*" propagation="REQUIRED" isolation="READ_COMMITTED"/> </tx:attributes> </tx:advice> ``` --- ### 事务关键接口说明 ```java public interface PlatformTransactionManager { // 获取事务状态(根据定义) TransactionStatus getTransaction(TransactionDefinition definition); // 提交事务 void commit(TransactionStatus status); // 回滚事务 void rollback(TransactionStatus status); } ``` > **`TransactionDefinition`** 定义了传播行为、隔离级别、超时时间等核心属性[^1]。 --- ### 相关问题 1. **嵌套事务`NESTED`与新建事务`REQUIRES_NEW`有何本质区别?** (答:嵌套事务共享主事务连接,子事务回滚不触发主事务回滚;新建事务完全独立) 2. **为什么MySQL默认隔离级别是可重复读(`REPEATABLE_READ`)而Oracle是读已提交(`READ_COMMITTED`)?** (答:设计差异导致,MySQL通过间隙锁解决幻读问题) 3. **如何在`REQUIRED`传播行为下实现部分操作独立提交?** (答:将需独立提交的方法声明为`REQUIRES_NEW`) 4. **事务隔离级别选择如何影响系统并发性能?** (答:隔离级别越高(如串行化),锁竞争越激烈,并发性能越低) 5. **`@Transactional`注解在类内部方法调用为何失效?** (答:因Spring AOP代理机制限制,需通过代理对象调用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值