事务传播
本节描述了Spring中事务传播的一些语义。请注意,本节不是关于事务传播的适当介绍。相反,它详细说明了Spring中事务传播的一些语义。
在Spring管理的事务中,要注意物理事务和逻辑事务之间的区别,以及传播设置如何应用于这种区别。
理解 PROPAGATION_REQUIRED
![tx prop required](https://docs.spring.io/spring-framework/reference/_images/tx_prop_required.png)
PROPAGATION_REQUIRED
强制执行一个物理事务,如果当前范围中尚不存在事务,则在本地创建一个,或者参与到已定义在更大范围中的“外部”事务中。在同一线程内的常见调用堆栈安排中,这是一个很好的默认设置(例如,一个服务外观代理到多个存储库方法,其中所有底层资源都必须参与到服务级事务中)。
默认情况下,参与的事务会加入外部范围的特性,默默地忽略本地隔离级别、超时值或只读标志(如果有)。如果希望在参与具有不同隔离级别的现有事务时拒绝隔离级别声明,请考虑在事务管理器上将 validateExistingTransactions 标志切换为 true 。这种非宽容模式还会拒绝只读不匹配(即,内部读写事务尝试参与只读外部范围)。 |
当传播设置为 PROPAGATION_REQUIRED
时,对应用该设置的每个方法都会创建一个逻辑事务范围。每个这样的逻辑事务范围可以单独确定是否仅回滚状态,外部事务范围与内部事务范围在逻辑上是独立的。在标准 PROPAGATION_REQUIRED
行为的情况下,所有这些范围都映射到同一个物理事务。因此,在内部事务范围中设置的仅回滚标记会影响外部事务实际提交的机会。
然而,在内部事务范围设置了仅回滚标记的情况下,外部事务尚未决定是否回滚,因此回滚(由内部事务范围默默触发)是意外的。在这一点上会抛出相应的 UnexpectedRollbackException
。这是预期的行为,以便事务的调用者永远不会被误导以为已执行提交,而实际上并没有。因此,如果内部事务(外部调用者不知道的事务)默默地将事务标记为仅回滚,则外部调用者仍会调用提交。外部调用者需要收到 UnexpectedRollbackException
来清楚地指示实际上执行了回滚而不是提交。
理解 PROPAGATION_REQUIRES_NEW
![tx prop requires new](https://docs.spring.io/spring-framework/reference/_images/tx_prop_requires_new.png)
PROPAGATION_REQUIRES_NEW
与 PROPAGATION_REQUIRED
相反,始终为每个受影响的事务范围使用独立的物理事务,永远不会参与外部范围的现有事务。在这种安排中,底层资源事务是不同的,因此可以独立提交或回滚,外部事务不受内部事务回滚状态的影响,并且内部事务完成后立即释放锁。这样一个独立的内部事务也可以声明自己的隔离级别、超时和只读设置,并且不会继承外部事务的特性。
与外部事务绑定的资源将保持在那里,而内部事务将获取自己的资源,例如新的数据库连接。这可能导致连接池耗尽,并且可能导致死锁,如果有几个线程有一个活动的外部事务并等待获取内部事务的新连接,而连接池无法再分配任何这样的内部连接。除非连接池的大小适当,至少超过并发线程数1个,否则不要使用 PROPAGATION_REQUIRES_NEW 。 |
理解 PROPAGATION_NESTED
PROPAGATION_NESTED
使用一个具有多个保存点的单个物理事务,可以回滚到这些保存点。这种部分回滚允许内部事务范围触发其范围的回滚,而外部事务可以继续进行物理事务,尽管某些操作已经回滚。此设置通常映射到JDBC保存点,因此仅适用于JDBC资源事务。请参阅Spring的 DataSourceTransactionManager
。