从并发问题衍生出的Spring的七种事务传播行为
最近在处理一个BPM流程时,遇到了并发问题,原因是事务粒度太大了,导致等待lock超时。今天刚好借此机会分享下Spring框架中提供的7种事务传播行为。
在 Spring中,@Transactional 注解支持配置事务的传播行为,用于指定当一个事务方法被另一个事务方法调用时,如何处理事务边界。Spring 定义了 7 种传播行为。
1. Propagation.REQUIRED (默认值)
这是最常用的传播行为,也是 @Transactional 注解的默认设置。
它保证了方法总是在一个事务中执行。如果内部方法加入外部方法的事务,它们将共享同一个事务上下文,内部方法的失败会导致整个事务回滚。
@Service
public class MyService {// 默认 REQUIRED,一组操作要么同时成功,要么同时失败。@Transactionalpublic void defaultTx() { … }
}
2. Propagation.SUPPORTS
这个方法不强制需要事务。如果调用它的上下文有事务,它就利用这个事务;如果没有,它也不创建新的事务。
@Service
public class MyService {// 支持事务,无则非事务执行@Transactional(propagation = Propagation.SUPPORTS)public void supportsTx() { … }
}
3. Propagation.MANDATORY
强制要求方法必须在一个已存在的事务中执行。如果调用者没有启动事务,调用该方法会失败。
@Service
public class MyService {// 必须存在事务,否则抛异常@Transactional(propagation = Propagation.MANDATORY)public void mandatoryTx() { … }
}
4. Propagation.REQUIRES_NEW
内部方法的事务与外部方法的事务是完全独立的。
内部事务的提交或回滚不影响外部事务。同样,外部事务的回滚也不会影响已提交的内部事务。
@Service
public class MyService {// 新建事务,挂起外层事务@Transactional(propagation = Propagation.REQUIRES_NEW)public void requiresNewTx() { … }
}
5. Propagation.NOT_SUPPORTED
明确表示该方法不应在事务中运行。
如果调用它时存在事务,该事务会被暂停,直到该方法执行完毕。
@Service
public class MyService {// 不支持事务,存在事务时挂起,适用于数据批量导出、日志写入操作@Transactional(propagation = Propagation.NOT_SUPPORTED)public void notSupportedTx() { … }
}
6. Propagation.NEVER
强制要求方法不能在任何事务中执行。如果检测到当前存在事务,会立即报错。
@Service
public class MyService {// 不支持事务,存在事务时抛异常@Transactional(propagation = Propagation.NEVER)public void neverTx() { … }
}
7. Propagation.NESTED
嵌套事务是外部事务的一个子事务。它有自己的保存点 (savepoint)。
内部嵌套事务可以独立于外部事务进行回滚(回滚到保存点),但它的最终提交依赖于外部事务的成功提交。如果外部事务回滚,则嵌套事务的所有更改也会被回滚。
@Service
public class MyService {// 嵌套事务,依赖外层事务@Transactional(propagation = Propagation.NESTED)public void nestedTx() { … }
}
与REQUIRES_NEW 不同的地方在于,前者创建的是完全独立的事务,而 NESTED 创建的是依赖于外部事务的子事务(使用保存点)。
NESTED 行为依赖于底层 DataSource 和事务管理器(PlatformTransactionManager)是否支持保存点。并非所有数据库和 JDBC 驱动都支持。