java 春季的 PROPAGATION_REQUIRED 事务属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7015607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 18:17:32  来源:igfitidea点击:

PROPAGATION_REQUIRED transaction attribute in spring?

javaspringjakarta-eespring-transactions

提问by M Sach

In first case study given at http://www.vermatech.com/code/SpringTransactionExamples.html, program is calling two methods i.e.

http://www.vermatech.com/code/SpringTransactionExamples.html给出的第一个案例研究中,程序调用了两种方法,即

testModel.deleteAllCountries();
testModel.initializeCountries();

where initializeCountries throws runtime exception. For both methods' transaction definition attribute is PROPAGATION_REQUIRED. Still transaction under deleteAllCountries method gets committed but transaction under initializeCountries are rolled back(as per the logs given in the same case study).

其中 initializeCountries 抛出运行时异常。这两种方法的事务定义属性都是 PROPAGATION_REQUIRED。deleteAllCountries 方法下的事务仍然被提交,但 initializeCountries 下的事务被回滚(根据同一个案例研究中给出的日志)。

As per PROPAGATION_REQUIRED definition is that it Support a current transaction; create a new one if none exists. So my question here is transaction under initializeCountries method should support the transaction under deleteAllCountries method. I mean both method should be treated as single transaction. As per my understanding either complete transaction should be committed or rolled back? Not sure how come logs are treating them separately.

根据 PROPAGATION_REQUIRED 定义,它支持当前事务;如果不存在,则创建一个新的。所以我的问题是 initializeCountries 方法下的事务应该支持 deleteAllCountries 方法下的事务。我的意思是这两种方法都应该被视为单个事务。根据我的理解,完整的事务应该提交还是回滚?不知道日志是如何分别对待它们的。

回答by Nathan Hughes

"Propagation required" is defined as

“需要传播”定义为

Support a current transaction, create a new one if none exists.

支持当前事务,如果不存在则创建一个新事务。

In your case above the deleteAllCountries method executes in a transaction and commits. There is no current transaction when initializeCountries is called, so it executes in a second transaction, and rolling it back has no effect on the changes made by the first method.

在上面的例子中,deleteAllCountries 方法在事务中执行并提交。调用 initializeCountries 时没有当前事务,因此它在第二个事务中执行,并且回滚它对第一个方法所做的更改没有影响。

Propagation applies to nested method calls, not to successive ones. If you look at the documentation:

传播适用于嵌套的方法调用,而不适用于连续的方法调用。如果您查看文档

When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, all these scopes will be mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction's chance to actually commit (as you would expect it to).

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.

当传播设置为 PROPAGATION_REQUIRED 时,将为应用该设置的每个方法创建一个逻辑事务范围。每个这样的逻辑事务范围可以单独确定仅回滚状态,外部事务范围在逻辑上独立于内部事务范围。当然,在标准 PROPAGATION_REQUIRED 行为的情况下,所有这些范围都将映射到同一个物理事务。因此,在内部事务范围内设置的仅回滚标记确实会影响外部事务实际提交的机会(正如您所期望的那样)。

但是,如果内部事务作用域设置了仅回滚标记,外部事务本身并没有决定回滚,因此回滚(由内部事务作用域静默触发)是意外的。此时会抛出相应的 UnexpectedRollbackException。这是预期的行为,因此事务的调用者永远不会被误导,认为提交已执行,而实际上并没有执行。因此,如果内部事务(外部调用者不知道)默默地将事务标记为仅回滚,外部调用者仍会调用 commit。外部调用者需要接收一个 UnexpectedRollbackException 来清楚地表明执行了回滚。

then you can see all of this is about inner- and outer-, none of it mentions successive calls. In your case the call to deleteAllCountries is the outermost transactional method, so when it finishes successfully then Spring commits the transaction immediately. Then your call to initializeCountries has to be executed within a separate transaction, where it is the outermost method.

那么你可以看到所有这些都是关于内部和外部的,没有提到连续调用。在您的情况下,对 deleteAllCountries 的调用是最外层的事务方法,因此当它成功完成时,Spring 会立即提交事务。然后您对 initializeCountries 的调用必须在一个单独的事务中执行,它是最外层的方法。

Your assumption seems to be that Spring will hold the transaction open after the first method finishes, but that's not how it works. In order to get the effect you want, you could create another method on testModel that wraps the calls to deleteAllCountries and initializeCountries, make that method transactional and give it the attribute PROPAGATION_REQUIRED. That way a rollback of the second method will cause the first method's changes to be rolled back too, because the wrapping method is grouping them together. Otherwise nothing is telling Spring these things should be part of the same transaction.

您的假设似乎是 Spring 将在第一个方法完成后保持事务打开,但这不是它的工作原理。为了获得您想要的效果,您可以在 testModel 上创建另一个方法,该方法包装对 deleteAllCountries 和 initializeCountries 的调用,使该方法具有事务性并为其赋予属性 PROPAGATION_REQUIRED。这样,第二个方法的回滚将导致第一个方法的更改也被回滚,因为包装方法将它们组合在一起。否则没有什么告诉 Spring 这些东西应该是同一个事务的一部分。