2011-08-22 173 views
2

我使用@Transactional我的JUnit测试(主要优势是一个测试中改变的回滚),但我有个小问题,这影响我的服务交易。因此,例如这是春季测试inpact上服务交易

我的服务:

@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()两个独立的交易记录,但除非我用@它在一个事务内部运行的测试方法上是非事务性的(但在测试之后它不会回滚)。

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

谢谢

回答

4

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

所以,是的,如果整个测试方法是事务性的,既服务电话将在测试事务的上下文中执行。

注意,由于服务与REQUIRED注解,它应该如果事务已经存在的工作。这使测试有效:它在现有事务的上下文中测试您的服务。如果您希望服务在自己的专用交易中执行,则应该使用REQUIRES_NEW注释。但是,当然,如果是这种情况,您将无法通过在事务中执行测试来回滚服务事务。

2

默认情况下(REQUIRES是默认的传播行为)如果交易已经存在,你的服务方法不会产生新的,而是加盟存在的一个。在你的情况下,它是由Spring测试框架(将回滚的)创建的事务。

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

1

@Transactional定义了分界线。注释该类相当于注释了该类的每个公共方法,但分界线仍然是方法(在您的案例中,为testNumberTransaction())。提交/回滚决定将在分界点进行,即从测试方法返回时。无论您指定REQUIRESREQUIRES_NEW传播,实际成交单位是一样的,你testNumberTransaction()方法,这样两个service.add(...)调用将始终在同一事务执行。

如果您的目标是在测试后始终回滚您的交易,那么只需删除@Transactional注释(或者像您提到的那样放置@nontransactional)即可。

如果,另一方面,要强制一个新的交易,每个service.add(...)调用,您既可以创建服务类的包装,你有一个add(...)方法标注有@Transactional(传播=传播。 REQUIRES_NEW)并从那里调用包装的service实例add(...)方法。或者您可以在您的弹簧测试环境中添加一些声明式事务管理,为您的service.add(...)方法添加事务通知。有关如何使用<tx:XXX>标签添加声明性事务支持,请参阅spring documentation

+1

从测试方法中删除@Transactional不会回退事务:测试不会有任何事务。事务将在服务方法被调用时开始,并在服务方法返回时提交。 –

+0

真实,笨拙的表述。删除@Transactional注释将会阻止事务管理器在java层创建一个事务。但是,大多数数据库将始终为每个会话创建一个隐式事务,并且如果将自动提交设置为关闭(默认为IIRC),则没有事务的行为与始终回滚的事务的行为相同。如果我错了,请纠正我。 – pap

+0

错误,没有。从* test *方法中移除Transactional将使测试在任何事务上下文之外运行,但Spring将仍然为每个服务方法调用启动事务,因为* service *方法使用Transactional注释。由服务完成的实际事务工作(可能更新某个数据库)将在事务中完成并提交,除非服务本身抛出运行时异常。 –