Spring 测试对服务事务的影响

发布于 2024-11-30 21:56:22 字数 658 浏览 1 评论 0原文

我在 JUnit 测试中使用 @Transactional(主要优点是回滚一个测试中的更改),但我有一个小问题,这会影响我的服务事务。例如,这是

我的服务:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class ServiceImpl

我的单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
@Transactional
public class TestService 

@Test
public void testNumberTransaction() {
Entity a = new Entity();
Entity b = new Entity();
service.add(a);
service.add(b);
}

所以我天真地期望 service.add() 有两个单独的事务,但是除非我在测试方法上使用 @nontransactional ,否则它会在一个事务中运行(但随后它不会回滚)测试后)。

这是预期的吗?我可以通过一些配置来改变它吗?

谢谢

I am using @Transactional for my JUnit tests (main advantage is rollback of changes within one test) but I have small problem that this influence my service transactions. So for example this is

my service :

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class ServiceImpl

my unit test :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
@Transactional
public class TestService 

@Test
public void testNumberTransaction() {
Entity a = new Entity();
Entity b = new Entity();
service.add(a);
service.add(b);
}

So naively I expected to have two separate transactions for service.add() but unless I use @nontransactional on the test method it runs inside one transaction (but then it does not roll back after test).

Is this expected?Can I alter it with some configuration?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

游魂 2024-12-07 21:56:22

是的,这是预期的。 Propagation.REQUIRED(默认)表示:如果现有事务存在,则在现有事务中执行。否则,创建一个事务并在方法结束时提交它。

所以是的,如果整个测试方法是事务性的,则两个服务调用都将在测试事务的上下文中执行。

请注意,由于该服务带有 REQUIRED 注释,因此如果事务已存在,它应该可以工作。这使得测试有效:它在现有事务的上下文中测试您的服务。如果您希望某个服务在其自己的专用事务中执行,则应使用 REQUIRES_NEW 对其进行注释。但当然,如果是这种情况,您将无法通过在事务中执行测试来回滚服务事务。

Yes, it's expected. Propagation.REQUIRED (which is the default) means: execute in the existing transaction if it exists. Else, create a transaction and commit it at the end of the method.

So yes, if the whole test method is transactional, both service calls will execute in the context of the test transaction.

Note that since the service is annotated with REQUIRED, it's supposed to work if a transaction already exists. This makes the test valid: it tests your service in the context of an existing transaction. If you want a service to execute in its own dedicated transaction, it should be annotated with REQUIRES_NEW. But of course, if it's the case, you won't be able to rollback the service transaction by executing the test in a transaction.

年少掌心 2024-12-07 21:56:22

默认情况下(REQUIRES 是默认的传播行为)如果事务已经存在,您的服务方法将不会创建新的事务,而是加入到已存在的事务中。在您的情况下,它是由 Spring 测试框架创建的事务(将回滚的事务)。

您可以通过将传播设置为 REQUIRES_NEW 来更改此行为。但是,因为现在您的业务 add 方法在新事务中运行,一旦您离开此方法,事务将被提交而不是回滚。默认情况下,由于所有事务都加入到 JUnit 测试事务中,因此对数据库所做的所有更改都会回滚。

By default (REQUIRES is the default propagation behavior) if the transaction already exists, your service method will not create new one but rather join to the one that exists. In your case it is the transaction created by the Spring test framework (the one that will be rolled back).

You can alter this behavior by setting propagation to REQUIRES_NEW. However because now you business add method runs within a new transaction, once you leave this method the transaction will be committed rather than rolled back. By default, since all transactions are joined in the JUnit test transaction, all changes made to the database are rolled back.

泪冰清 2024-12-07 21:56:22

@Transactional 定义了分界线。注释类相当于注释类的每个公共方法,分界线仍然是方法(在您的例子中,是 testNumberTransaction())。提交/回滚决定将在分界点做出,即从测试方法返回时。无论您指定 REQUIRES 还是 REQUIRES_NEW 传播,实际的事务单元都是相同的,您的 testNumberTransaction() 方法因此两个 service.add(...) 调用将始终在同一事务中执行。

如果您的目标是在测试后始终回滚事务,那么只需删除 @Transactional 注释(或如您提到的那样放置 @nontransactional)就可以了。

另一方面,如果您想为每个 service.add(...) 调用强制执行一个新事务,您可以为您的服务类创建一个包装器,其中您有一个 使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 注解的 add(...) 方法并调用包装的 service 实例 add(...)方法从那里开始。或者您可以在 Spring 测试上下文中添加一些声明性事务管理,为您的 service.add(...) 方法添加事务建议。阅读 spring 文档了解更多信息如何使用 标签添加声明性事务支持。

The @Transactional defines the demarcation line. Annotating the class is equivalent with annotating each public method of the class, the line of demarcation is still the method (in your case, the testNumberTransaction()). The commit/rollback decision will be taken at the demarcation point, that is when returning from your test method. Regardless of whether you specify REQUIRES or REQUIRES_NEW propagation, the actual transaction unit is the same, your testNumberTransaction() method so the two service.add(...) calls will always be executed in the same transaction.

If your goal is to always roll back your transactions after the test, then simply removing the @Transactional annotation (or putting @nontransactional as you mentioned) should do.

If, on the other hand, you want to FORCE a new transaction for each service.add(...) invocation, you could either create a wrapper for your service class where you have an add(...) method that is annotated with @Transactional(propagation = Propagation.REQUIRES_NEW) and call the wrapped service instance add(...) method from there. OR you could add some declarative transaction management in your spring test context that adds transaction advise for your service.add(...) method. Read more in the spring documentation on how to add declarative transaction support using the <tx:XXX> tags.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文