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