为什么@Transactional在另一个提交失败时不回滚一个提交?

时间:2019-10-02 04:38:10

标签: java junit transactions spring-transactions

所以我有一个课:

@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

1 个答案:

答案 0 :(得分:1)

通过Spring管理事务时,实际上是在使用抽象。对于单个数据源(通常称为资源本地事务),事务开始/提交/回滚将按预期工作,但是如果使用两个不同的数据源,则您将需要一个能够执行诸如Bitronix或{{ 3}}(在开源世界中)。在EE应用程序服务器环境中,此功能由服务器本身提供。 Atomikos Essentials是一本非常老的文章,值得一读(它使用一个数据库和一个消息传递代理来参与分布式事务,但是概念是相同的-关键是多种资源)。有关Bitrionix和Spring的示例配置,请查看Here