对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

事务传播

本节描述了 Spring 中事务传播的一些语义。请注意,本节并非事务传播的正式入门介绍,而是详细说明了 Spring 中有关事务传播的部分语义。spring-doc.cadn.net.cn

在Spring管理的事务中,请注意物理事务和逻辑事务之间的区别,以及传播设置如何应用于这种差异。spring-doc.cadn.net.cn

理解PROPAGATION_REQUIRED

tx prop required

PROPAGATION_REQUIRED 强制使用一个物理事务:如果当前作用域尚未存在事务,则在本地为当前作用域创建一个事务;如果已存在一个为更大作用域定义的“外部”事务,则加入该事务。在同一线程内的常见调用栈结构中(例如,一个服务门面委托给多个仓库方法,且所有底层资源都必须参与服务级别的事务),这是一个合适的默认行为。spring-doc.cadn.net.cn

默认情况下,参与的事务会加入外部作用域的特性, 静默忽略本地的隔离级别、超时值或只读标志(如果有的话)。 如果你希望在参与一个具有不同隔离级别的现有事务时拒绝隔离级别声明, 请考虑将事务管理器上的 validateExistingTransactions 标志设置为 true。 这种严格模式还会拒绝只读属性不匹配的情况(例如,一个内部的读写事务试图参与一个只读的外部作用域)。

当传播设置为 PROPAGATION_REQUIRED 时,会对应用该设置的每个方法创建一个逻辑事务作用域。每个这样的逻辑事务作用域可以独立地决定是否仅回滚(rollback-only),其中外层事务作用域在逻辑上独立于内层事务作用域。在标准的 PROPAGATION_REQUIRED 行为下,所有这些作用域都映射到同一个物理事务。因此,在内层事务作用域中设置的仅回滚标记会影响外层事务实际提交的可能性。spring-doc.cadn.net.cn

然而,当内部事务作用域设置了回滚标记(rollback-only marker)时,外部事务本身尚未决定要回滚,因此由内部事务作用域静默触发的回滚是出乎意料的。此时会抛出相应的UnexpectedRollbackException异常。这是预期的行为,目的是确保事务的调用方永远不会被误导,误以为事务已提交,而实际上并未提交。因此,如果一个内部事务(外部调用方并不知晓其存在)静默地将事务标记为仅回滚(rollback-only),而外部调用方仍然调用了提交(commit)操作,那么外部调用方必须收到一个UnexpectedRollbackException异常,以明确表明实际执行的是回滚操作而非提交。spring-doc.cadn.net.cn

理解PROPAGATION_REQUIRES_NEW

tx prop requires new

PROPAGATION_REQUIRES_NEWPROPAGATION_REQUIRED 不同,它始终为每个受影响的事务作用域使用一个独立的物理事务,从不参与外部作用域的现有事务。在这种安排下,底层资源事务是不同的,因此可以独立地提交或回滚:外部事务不受内部事务回滚状态的影响,且内部事务的锁在其完成后会立即释放。 这种独立的内部事务还可以声明自己的隔离级别、超时时间和只读设置,而不会继承外部事务的特性。spring-doc.cadn.net.cn

外部事务所绑定的资源将保持不变,而内部事务会获取自己的资源,例如一个新的数据库连接。 如果多个线程同时持有活跃的外部事务,并等待为各自的内部事务获取新连接, 而连接池已无法再提供任何此类内部连接,就可能导致连接池耗尽,甚至可能引发死锁。 除非你的连接池大小设置得当(至少比并发线程数多1),否则不要使用 PROPAGATION_REQUIRES_NEW

理解PROPAGATION_NESTED

PROPAGATION_NESTED 使用单个物理事务并包含多个可回滚到的保存点。这种部分回滚允许内部事务范围触发其自身的回滚,而外部事务即使某些操作已被回滚,仍能继续执行物理事务。此设置通常映射到 JDBC 保存点,因此仅适用于基于 JDBC 资源的事务。请参阅 Spring 的 DataSourceTransactionManagerspring-doc.cadn.net.cn