所以我有一个课:
@Service
public class MyService {
@Autowired
private RepositoryA repoA;
@Autowired
private RepositoryB repoB;
@Transactional
public void storeEntity(SomeEntity e) {
repoA.save(e);
OtherEntity o = doSomethingWithEntity(e);
repoB.save(o);
}
}
我的方法storeEntity
将两次保存到两个不同的数据源。我希望如果repoB的保存失败或doSomethingWithEntity
失败,repoA.save(e)
将被回滚。
我想写一个小的测试来确保这种行为:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceForTransactionTest {
@Autowired
private MyService subject;
@Autowired
private RepositoryA repoA;
@MockBean
private RepositoryB repoB;
@Test
public void repoBShouldNotHaveEntries() {
// given
when(repoB.save(any())).thenThrow(new IllegalStateException("Something wrong with db"));
assertThat(repoB.count()).isEqualTo(0);
// when
SomeEntity e = ...
subject.storeEntity(e);
// then
assertThat(repoA.count()).isEqualTo(0);
}
}
这将不起作用,因为引发了异常并且测试失败。当我用try / catch包围呼叫时,断言失败,并显示一条消息,说repoA有1个条目。该如何解决?
我也尝试过:
@Test
public void repoBShouldNotHaveEntries() {
// given
when(repoB.save(any())).thenThrow(new IllegalStateException("Something wrong with db"));
assertThat(repoB.count()).isEqualTo(0);
// when
SomeEntity e = ...
try {
subject.storeEntity(e);
} catch (Exception e) {
// some output here
}
// then
assertThat(repoA.count()).isEqualTo(0);
}
断言失败。我也尝试过这个:
@Test
public void repoBShouldNotHaveEntries() {
// given
when(repoB.save(any())).thenThrow(new IllegalStateException("Something wrong with db"));
assertThat(repoB.count()).isEqualTo(0);
// when
SomeEntity e = ...
subject.storeEntity(e);
}
@After
public void tearDown() {
// then
assertThat(repoA.count()).isEqualTo(0);
}
}
也失败。找到1条记录,但我希望应该回滚@Transactional
。
答案 0 :(得分:1)
通过Spring管理事务时,实际上是在使用抽象。对于单个数据源(通常称为资源本地事务),事务开始/提交/回滚将按预期工作,但是如果使用两个不同的数据源,则您将需要一个能够执行诸如Bitronix或{{ 3}}(在开源世界中)。在EE应用程序服务器环境中,此功能由服务器本身提供。 Atomikos Essentials是一本非常老的文章,值得一读(它使用一个数据库和一个消息传递代理来参与分布式事务,但是概念是相同的-关键是多种资源)。有关Bitrionix和Spring的示例配置,请查看Here。