事务传播

本节描述了 Spring 中事务传播的一些语义。请注意,本节不是对事务传播的正式介绍。相反,它详细介绍了 Spring 中关于事务传播的一些语义。

在 Spring 管理的事务中,请注意物理事务和逻辑事务之间的区别,以及传播设置如何应用于这种区别。

理解 PROPAGATION_REQUIRED

tx prop required

PROPAGATION_REQUIRED 强制使用一个物理事务,如果当前作用域尚无事务,则在本地使用;如果存在为更大作用域定义的“外部”事务,则参与其中。在同一线程内的常见调用栈安排中(例如,一个将任务委托给多个仓库方法的 Service Facade,其中所有底层资源都必须参与 Service 级别的事务),这是一个不错的默认设置。

默认情况下,参与事务会继承外部作用域的特性,默默忽略本地的隔离级别、超时值或只读标志(如果有的话)。如果您希望在参与不同隔离级别的现有事务时拒绝隔离级别声明,请考虑将事务管理器上的 validateExistingTransactions 标志切换为 true。这种非宽松模式也会拒绝只读不匹配(即尝试参与只读外部作用域的内部读写事务)。

当传播设置为 PROPAGATION_REQUIRED 时,对于应用了该设置的每个方法都会创建一个逻辑事务作用域。每个这样的逻辑事务作用域都可以独立确定仅回滚状态,而外部事务作用域在逻辑上独立于内部事务作用域。在标准的 PROPAGATION_REQUIRED 行为下,所有这些作用域都映射到同一个物理事务。因此,在内部事务作用域中设置的仅回滚标记确实会影响外部事务实际提交的机会。

然而,如果在内部事务作用域设置了仅回滚标记的情况下,外部事务本身尚未决定回滚,因此回滚(由内部事务作用域悄然触发)是意外的。此时会抛出相应的 UnexpectedRollbackException。这是预期行为,以确保事务的调用者永远不会被误导,以为事务已提交而实际上并未提交。因此,如果一个内部事务(外部调用者不知道)悄然将事务标记为仅回滚,外部调用者仍然会调用提交。外部调用者需要接收一个 UnexpectedRollbackException 来清楚地表明发生了回滚。

理解 PROPAGATION_REQUIRES_NEW

tx prop requires new

PROPAGATION_REQUIRED 相反,PROPAGATION_REQUIRES_NEW 始终为每个受影响的事务作用域使用独立的物理事务,绝不参与外部作用域的现有事务。在这种安排下,底层资源事务是不同的,因此可以独立提交或回滚,外部事务不受内部事务回滚状态的影响,并且内部事务的锁在完成后立即释放。这种独立的内部事务也可以声明自己的隔离级别、超时和只读设置,而不继承外部事务的特性。

附加到外部事务的资源将保持绑定状态,而内部事务获取自己的资源,例如新的数据库连接。如果多个线程具有活动的外部事务并等待为其内部事务获取新连接,而连接池无法再提供此类内部连接,这可能导致连接池耗尽,并可能导致死锁。除非您的连接池大小合适,且至少比并发线程数多 1,否则不要使用 PROPAGATION_REQUIRES_NEW

理解 PROPAGATION_NESTED

PROPAGATION_NESTED 使用单个物理事务和多个可以回滚到的保存点。这种部分回滚允许内部事务作用域触发其作用域的回滚,而外部事务尽管部分操作已回滚,仍能继续物理事务。此设置通常映射到 JDBC 保存点,因此仅适用于 JDBC 资源事务。请参阅 Spring 的 DataSourceTransactionManager