Spring 事务传播行为(Propagation Behavior)定义了一个事务方法被另一个事务方法调用时事务的边界和行为。这篇文章,我们将深度分析它们的原理以及对比它们之间的差异。
一、事务传播行为概述
在 Spring 中,Propagation 枚举类型定义了七种主要的事务传播行为:
- REQUIRED
- REQUIRES_NEW
- SUPPORTS
- NOT_SUPPORTED
- MANDATORY
- NEVER
- NESTED
此外,NESTED 传播行为在某些数据源(如支持嵌套事务的数据库)下可用。
二、原理分析
事务传播行为主要决定了在一个事务方法被调用时,当前存在的事务(如果有)应该如何被处理。以下是其原理分析:
- 事务的存在检查:当一个方法(被调用者)执行时,它会检查调用它的方法(调用者)是否存在活动事务。
- 决定是否挂起、重用或创建新事务:根据传播行为的不同,调用者可能会被挂起,或者调用者的方法可能会加入到现有事务中,或者开启一个全新的事务。
- 事务的边界控制:传播行为还控制了事务的提交和回滚边界,确保在复杂调用链中事务的一致性与完整性。
三、各种传播行为的示例分析
1. REQUIRED
定义:支持当前事务。如果没有事务,就新建一个事务。
原理:调用者存在事务,方法加入到这个事务中;否则,开启新事务。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// 执行数据库操作
}
分析:methodA 开启事务,methodB 加入到同一个事务中。如果 methodA 回滚,methodB 也会回滚。
2. REQUIRES_NEW
定义:新建事务,如果存在事务,就将当前事务挂起。
原理:无论调用者是否存在事务,方法都会开启一个独立的新事务。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 执行数据库操作
}
分析:methodA 和 methodB 各自有独立的事务。如果 methodB 回滚,不会影响 methodA 的事务。
3. SUPPORTS
定义:支持当前事务,如果存在事务,就加入;否则,以非事务方式执行。
原理:依赖调用者是否有事务,调用者有则参与事务,无则不使用事务。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// 执行数据库操作
}
分析:如果 methodA 调用 methodB,methodB 会参与 methodA 的事务。如果 methodB 被独立调用,则以非事务方式执行。
4. NOT_SUPPORTED
定义:以非事务方式执行操作,如果存在事务,就将其挂起。
原理:方法不支持事务,即使调用者有事务,也不会参与。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
// 执行数据库操作
}
分析:methodA 的事务会在执行 methodB 时被挂起,methodB 以非事务方式执行,执行完毕后恢复 methodA 的事务。
5. MANDATORY
定义:支持当前事务,必须存在一个事务,否则抛出异常。
原理:如果调用者有事务,方法参与;否则,抛出 IllegalTransactionStateException。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
// 执行数据库操作
}
分析:当 methodA 调用 methodB 时,由于 methodA 有事务,methodB 可以正常参与事务。如果 methodB 被独立调用,无事务,会抛出异常。
6. NEVER
定义:以非事务方式执行,如果存在事务,则抛出异常。
原理:方法绝不支持事务,确保其执行不在事务上下文中。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}
@Transactional(propagation = Propagation.NEVER)
public void methodB() {
// 执行数据库操作
}
分析:当 methodA 调用 methodB 时,由于 methodA 有事务,调用 methodB 会抛出异常。如果 methodB 被独立调用,无事务,则正常执行。
7. NESTED
定义:如果存在事务,则在嵌套事务内执行;否则,类似于 REQUIRED。
原理:基于底层数据库的保存点(savepoint),允许在嵌套事务中回滚到保存点,而不影响外部事务。
示例:
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
// 继续执行
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
// 执行数据库操作
}
分析:如果 methodB 出现异常且事务被回滚,只会回滚到 methodB 的开始,而 methodA 的事务仍然可以继续或回滚。
四、传播行为对比
传播行为 | 是否必须存在事务 | 是否开启新事务 | 是否挂起现有事务 | 适用场景 |
REQUIRED | 否 | 否 | 否 | 默认行为,绝大多数场景使用,如服务层方法。 |
REQUIRES_NEW | 否 | 是 | 是 | 需要独立事务,如日志记录,不受外部事务影响。 |
SUPPORTS | 否 | 否 | 否 | 可选事务性,读操作等,既能在事务中也能在非事务中运行。 |
NOT_SUPPORTED | 否 | 否 | 是 | 非事务操作,如与事务无关的外部系统交互。 |
MANDATORY | 是 | 否 | 否 | 强制要求存在事务的操作,确保方法调用在事务环境中。 |
NEVER | 否 | 否 | 是 | 确保方法在非事务环境中执行,避免事务上下文。 |
NESTED | 否 | 基于底层支持 | 否 | 需要在事务内进行部分回滚的场景,如复杂业务操作。 |
关键差异点:
- 是否开启新事务:REQUIRES_NEW 和 NESTED 可以开启新事务,其他大部分属于参与现有事务或非事务。
- 是否必须存在事务:MANDATORY 和 NEVER 针对事务的存在有严格要求。
- 是否挂起现有事务:REQUIRES_NEW 和 NOT_SUPPORTED 会挂起当前事务。
- 嵌套事务支持:NESTED 依赖于底层数据库的 savepoint 支持,允许在同一事务中进行局部回滚。
五、实际应用中的选择
- 大多数场景:使用 REQUIRED 是最合适的选择,因为它简化了事务管理,并且大多数情况下方法需要参与到调用者的事务中。
- 独立事务需求:如日志记录、发送通知等,需要与主事务独立的操作,可以使用 REQUIRES_NEW。
- 可选事务:对于既能在事务中运行也能在非事务中运行的操作,可以使用 SUPPORTS。
- 确保非事务执行:若某操作必须在非事务环境下执行,如一些特定的外部系统调用,可以使用 NOT_SUPPORTED 或 NEVER。
- 强制要求事务:在某些关键业务逻辑中,确保方法只能在事务中调用,可以使用 MANDATORY。
- 局部回滚需求:在复杂业务场景下,需要对某部分操作进行局部回滚,可以考虑使用 NESTED,但需确保底层数据库支持。
六、注意事项
- 数据库支持:部分传播行为(如 NESTED)依赖于底层数据库的支持,使用前需要确认数据库和事务管理器的兼容性。
- 事务管理器配置:正确配置 Spring 事务管理器(如 PlatformTransactionManager)对于事务传播行为的正常工作至关重要。
- 事务边界设计:合理设计事务边界,避免过长的事务导致资源占用和性能问题。
七、总结
本文,我们详细分析了 Spring 事务传播行为,掌握它们对于构建健壮的事务管理策略至关重要。通过合理选择合适的传播行为,可以确保在复杂的业务逻辑和调用链中,数据的一致性和系统的稳定性。开发人员应根据具体的业务需求和应用场景,灵活运用不同的传播行为,以达到最佳的事务管理效果。