【问题标题】:Adding @Transactional is breaking UnitTests by stopping JPA from fetching the foreign keys添加 @Transactional 通过阻止 JPA 获取外键来破坏 UnitTests
【发布时间】:2019-08-25 07:58:31
【问题描述】:

我正在尝试提高 flyway 数据库测试的速度。最初的方法是在每次测试运行之间运行flyway.clear()flyway.migrate()。但问题是,现在我们有一些可能长时间运行的 flyway 迁移脚本,我们不想在每次测试之间重新运行这些脚本。

我一直在尝试使用spring的@Transactional注解在每次测试后回滚数据库,那么我们只需要运行一次flyway。

问题是,一旦我在测试 JPA 周围添加事务,就会停止获取外键。我假设这是因为嵌套事务的行为不符合我的预期,但我无法弄清楚我配置错了什么。

我在这里发布了一个示例项目:ExampleSpringJpaHibernatePostgresqltest 有问题(请参阅ExampleTest.java)但总结如下:

我的实体具有如下外键(我已删除不必要的内容以使其更易于阅读):

@Entity
@Table(name = "parent")
public class ParentEntity {
    @Id
    @Column
    private Integer id;

    @Column
    private String name;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "fk_parent", referencedColumnName = "id")
    private Set<ChildEntity> children;
}
@Entity
@Table(name = "child")
public class ChildEntity {
    @Id
    @Column
    private Integer id;

    @Column
    private String name;

    @Column(name = "fk_parent")
    private Integer fkParent;
}

然后我有一个使用parentRepository.findAll() 的测试。如果我使用@Transactional 包围该测试,那么测试将失败,因为parentEntity.getChildren() 为空。但是,如果没有事务,并且在每个测试之间运行 flyway clear/migrate,一切都会按预期工作。

    @Test
    @Transactional
    public void testReadWithDependencies() {
        ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
        childRepo.save(new ChildEntity("child1", savedParent1.getId()));

        Set<ChildEntity> acutalChildren = parentRepo.findAll().get(0).getChildren();

        // XXX When the tests are run with @Transactional, this assertion fails.
        assertThat(acutalChildren).isNotNull();
    }

【问题讨论】:

  • 主键是如何生成的?
  • 它们是使用休眠序列生成器生成的,看看我发布到 github 的示例,我刚刚删除了该代码以保持帖子易于阅读。 ParentEntity.java

标签: java postgresql hibernate jpa transactional


【解决方案1】:

parentRepo.findAll 不会刷新数据库中的数据,并且您不会将子代添加到父代中的子代中。

因此没有分配给父实体的子实体。

所以如果你刷新实体(我注入了 EntityManger)你的测试是成功的:

@Test
@Transactional
public void testReadWithDependencies() {
    assertThat(parentRepo.findAll()).isEmpty();
    assertThat(childRepo.findAll()).isEmpty();

    ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
    childRepo.save(new ChildEntity("child1", savedParent1.getId()));

    List<ParentEntity> allParents = parentRepo.findAll();
    assertThat(allParents).hasSize(1);

    // Refresh
    ParentEntity parentEntity = allParents.get(0);
    entityManager.refresh(parentEntity);

    Set<ChildEntity> acutalChildren = parentEntity.getChildren();

    // XXX When the tests are run with @Transactional, this assertion fails.
    assertThat(acutalChildren).isNotNull();

    assertThat(acutalChildren).hasSize(1);
    assertThat(acutalChildren.iterator().next().getName()).isEqualTo("child1");
}

【讨论】:

  • 当然!谢谢你。如此简单,令人尴尬。如果一个额外的问题没问题,有没有办法我们可以在查询时自动刷新实体?我只会将其应用于测试配置文件。
  • 据我所知没有。但是您应该正确设置测试中的所有依赖项以避免此类问题
猜你喜欢
  • 2016-01-23
  • 1970-01-01
  • 1970-01-01
  • 2014-07-04
  • 2011-05-27
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多