文档补充:
PROPAGATION_NESTED如何实现的?
PROPAGATION_NESTED 是 Spring 事务管理中的一个传播行为选项,用于控制事务在方法调用之间的传播方式。当事务属性设置为 PROPAGATION_NESTED 时,如果当前已经存在一个活动事务(即外部事务),新的事务将会作为嵌套事务执行。如果没有现有的事务,那么其行为将与 PROPAGATION_REQUIRED 类似,即创建一个新的事务。
PROPAGATION_NESTED 的实现机制依赖于底层数据库对保存点(Savepoint)的支持。以下是其大致的工作流程:
- 检测事务环境:当带有
PROPAGATION_NESTED属性的方法被调用时,Spring 首先检查是否存在一个活动的事务。如果存在,进入下一步;如果不存在,则启动一个新的事务,就像PROPAGATION_REQUIRED一样。 - 创建保存点:如果外部事务存在,Spring 使用 JDBC 的
Savepoint功能在当前事务中设置一个保存点。这个保存点就像是事务中的一个快照,允许你将来回滚到这个点,而不是直接回滚整个事务。 - 执行嵌套事务:之后,嵌套的方法在该保存点之上执行。这意味着,如果嵌套事务中的任何操作失败并导致回滚,Spring 将指示数据库回滚到之前设置的保存点,而不是回滚整个外部事务。这样,只有嵌套事务内的更改会被撤销,外部事务可以继续进行或在最终决定时独立回滚。
- 事务结束处理:如果嵌套事务成功完成(即没有抛出异常),那么对保存点的操作通常是忽略的,因为外部事务将在其正常结束时提交所有更改。如果外部事务最终需要回滚,那么它将包括嵌套事务的所有更改,因为它们实际上是同一事务上下文的一部分。
需要注意的是,并非所有的数据库都支持保存点,因此 PROPAGATION_NESTED 的可用性依赖于所使用的数据库系统。此外,使用嵌套事务可能会影响性能,因为它涉及到额外的数据库交互来管理保存点。在设计时应权衡其带来的细粒度控制和潜在的性能影响。
PROPAGATION_REQUIRES_NEW是如何实现的?
PROPAGATION_REQUIRES_NEW 是 Spring 事务管理中的另一个传播行为选项,它提供了与 PROPAGATION_NESTED 不同的事务处理策略。当事务属性设置为 PROPAGATION_REQUIRES_NEW 时,Spring 保证该方法将在一个全新的事务中执行,无论是否已经存在一个活动事务。具体实现机制如下:
- 检测事务环境:当带有
PROPAGATION_REQUIRES_NEW属性的方法即将被执行时,Spring 首先检查当前线程是否已经关联了一个事务。 - 挂起现有事务:如果当前存在一个事务,Spring 会将这个事务挂起。这意味着它会保存当前事务的状态(如连接和会话信息),然后暂停它,而不是像
PROPAGATION_NESTED那样使用保存点。 - 创建新事务:接下来,Spring 创建一个新的事务上下文,并开始一个新的数据库事务。这个新事务是完全独立于外部事务的,即使外部事务最终回滚,新事务的提交也不会受到影响。
- 执行方法:在新事务中执行方法,期间所做的任何数据库修改都会在这个新的事务范围内进行管理。
- 提交或回滚新事务:如果方法执行成功,新事务会被提交。如果方法执行过程中抛出了未检查异常(继承自
RuntimeException的异常)或者已检查异常(通过@Transactional注解的rollbackFor指定的异常),新事务会被回滚。 - 恢复原有事务:一旦新事务完成(提交或回滚),Spring 会恢复先前挂起的事务(如果有的话),继续在那个事务的上下文中执行后续的操作。
通过这种方式,PROPAGATION_REQUIRES_NEW 确保了被注解的方法总是在一个单独的事务中执行,为需要独立于外部事务处理逻辑的特定操作提供了隔离。这在需要确保某些操作(如记录日志、发送消息等)即使主事务失败也要持久化的情况下非常有用。