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

标签 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 应该回滚。

最佳答案

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

关于java - 为什么@Transactional 在另一项提交失败时不回滚一项提交?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58195292/

相关文章:

java - 编码无符号整数

java - 无事务获取数据

java - Spring Integration 中的事务感知 POJO

java - 正则表达式 - 如何通过它的类查找 HTML <a> 标记内容?

java - 使用实现接口(interface)的抽象类 (Java)

java - 使用 spring-test-dbunit 转义保留的数据库关键字

java - 模拟 ModelMapper 中的 map() 方法返回 null 值

go - 为什么要推迟回滚?

java - MappingException : Could not get constructor for org. hibernate.persister.entity.SingleTableEntityPersister

junit - Eclipse JUnit @Before注解问题