事务传播
本节介绍了 Spring 中事务传播的一些语义。请注意,本节并不是对事务传播的正式介绍,而是详细说明了 Spring 中有关事务传播的一些语义。
在 Spring 管理的事务中,需要注意物理事务和逻辑事务之间的区别,以及传播设置如何应用于这种区别。
理解 PROPAGATION_REQUIRED
PROPAGATION_REQUIRED
强制执行一个物理事务,如果当前作用域内还不存在事务,则在本地的当前作用域中创建一个新的事务;或者参与到一个已存在的、为更大作用域定义的“外部”事务中。在同一个线程内的常见调用栈安排中(例如,一个服务门面委托给多个仓库方法,所有底层资源都必须参与到服务级别的事务中),这是一个很好的默认设置。
默认情况下,参与的事务会继承外部作用域的特性,静默地忽略本地的隔离级别、超时值或只读标志(如果有的话)。如果你希望在参与具有不同隔离级别的现有事务时拒绝隔离级别声明,请考虑将事务管理器上的 validateExistingTransactions
标志切换为 true
。这种非宽松模式还会拒绝只读不匹配的情况(即,内部读写事务试图参与只读外部作用域)。
当传播设置为 PROPAGATION_REQUIRED
时,会为应用了该设置的每个方法创建一个逻辑事务范围。每个这样的逻辑事务范围可以独立确定仅回滚状态,外部事务范围在逻辑上与内部事务范围独立。在标准的 PROPAGATION_REQUIRED
行为下,所有这些范围都映射到同一个物理事务。因此,内部事务范围设置的仅回滚标记确实会影响外部事务实际提交的机会。
然而,在内部事务范围设置了仅回滚标记的情况下,外部事务本身并未决定回滚,因此由内部事务范围静默触发的回滚是意料之外的。此时会抛出相应的 UnexpectedRollbackException
。这是预期的行为,以便事务的调用者永远不会被误导,错误地认为已经执行了提交操作,而实际上并未执行。因此,如果内部事务(外部调用者并不知情)静默地将事务标记为仅回滚,外部调用者仍然会调用提交。外部调用者需要接收到 UnexpectedRollbackException
,以明确表示执行了回滚操作。
理解 PROPAGATION_REQUIRES_NEW
与 PROPAGATION_REQUIRED
不同,PROPAGATION_REQUIRES_NEW
总是为每个受影响的事务范围使用一个独立的物理事务,并且从不参与外部范围的现有事务。在这种安排下,底层资源事务是不同的,因此可以独立提交或回滚,外部事务不受内部事务回滚状态的影响,并且内部事务的锁在其完成后立即释放。这种独立的内部事务还可以声明自己的隔离级别、超时和只读设置,而不会继承外部事务的特性。
附加到外部事务的资源将保持绑定状态,而内部事务会获取其自己的资源,例如新的数据库连接。如果多个线程拥有活动的外部事务并等待为其内部事务获取新连接,而连接池无法再分配任何此类内部连接,这可能会导致连接池耗尽,并可能引发死锁。除非您的连接池大小适当,至少超过并发线程数 1 个,否则不要使用 PROPAGATION_REQUIRES_NEW
。
理解 PROPAGATION_NESTED
PROPAGATION_NESTED
使用一个物理事务,并在其中创建多个保存点,以便可以回滚到这些保存点。这种部分回滚允许内部事务范围触发其范围内的回滚,而外部事务可以继续物理事务,尽管某些操作已被回滚。此设置通常映射到 JDBC 保存点,因此它仅适用于 JDBC 资源事务。请参阅 Spring 的 DataSourceTransactionManager。