Spring 测试对服务事务的影响
我在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,这是预期的。
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 withREQUIRES_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.默认情况下(
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 businessadd
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.@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, thetestNumberTransaction()
). The commit/rollback decision will be taken at the demarcation point, that is when returning from your test method. Regardless of whether you specifyREQUIRES
orREQUIRES_NEW
propagation, the actual transaction unit is the same, yourtestNumberTransaction()
method so the twoservice.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 anadd(...)
method that is annotated with @Transactional(propagation = Propagation.REQUIRES_NEW) and call the wrappedservice
instanceadd(...)
method from there. OR you could add some declarative transaction management in your spring test context that adds transaction advise for yourservice.add(...)
method. Read more in the spring documentation on how to add declarative transaction support using the<tx:XXX>
tags.