Java 我们如何测试N+;JPA/Hibernate中的1个问题?

Java 我们如何测试N+;JPA/Hibernate中的1个问题?,java,hibernate,jpa,regression-testing,Java,Hibernate,Jpa,Regression Testing,我有一个N+1问题,我想写一些自动回归测试,因为它对性能影响很大 我考虑监视EntityManager并验证其方法createQuery()只调用一次,但Hibernate不使用它初始化惰性关系,因此它不起作用。我也可以尝试关闭我的存储库和服务之间的JPA事务(或者分离我的实体)并查找异常,但这确实是一个丑陋的想法 为了给我们一个框架,假设我们有一个非常简单的父子模型: @实体 公共类父类{ … @OneToMany(fetch=FetchType.LAZY,mappedBy=“parent”)

我有一个N+1问题,我想写一些自动回归测试,因为它对性能影响很大

我考虑监视EntityManager并验证其方法
createQuery()
只调用一次,但Hibernate不使用它初始化惰性关系,因此它不起作用。我也可以尝试关闭我的存储库和服务之间的JPA事务(或者分离我的实体)并查找异常,但这确实是一个丑陋的想法

为了给我们一个框架,假设我们有一个非常简单的父子模型:

@实体
公共类父类{
…
@OneToMany(fetch=FetchType.LAZY,mappedBy=“parent”)
私人收藏儿童;
}
@实体
公营儿童{
…
@许多酮
私人家长;
}
还有一个非常简单的服务:

公共类MyService{
…
公共无效doSomething(长parentId){
父项父项=…;/*从数据库检索父项*/
doSomeOtherThing(parent.getChildren());
}
}
从数据库检索父级可以使用以下两个查询:

SELECT parent FROM Parent parent WHERE parent.id = :id;
SELECT parent FROM Parent parent JOIN FETCH parent.children WHERE parent.id = :id;

当我使用第一个查询而不是第二个查询检索父实体时,如何编写崩溃的测试?

作为选项,您可以验证测试中的查询计数(获取、更新、插入)

 repository.findById(10L);

 SessionFactory sf = em.getEntityManagerFactory().unwrap(SessionFactory.class);
 Statistics statistics = sf.getStatistics();

 assertEquals(2L, statistics.getQueryExecutionCount());

请参阅以下解决方案,它依赖于包装您的
数据源

我想“回归测试”是指可能由JUnit启动的实际测试

在单元测试中处理该问题的一般方法可以是:

  • 将hibernate.show_sql配置为true
  • 截取日志消息,如中所述
  • 扫描日志文件以查找
    • 特定的查询,您希望避免
    • 类似查询的数量

在运行查询以检索“父”实体后,使用,您可以断言“子”已加载或未加载:

PersistenceUnitUtil pu = em.getEntityManagerFactory().getPersistenceUnitUtil();
assertTrue(pu.isLoaded(parent, "children"));

您可以使用的另一个选项是在初始获取之后,但在引用实体上任何可能延迟加载的字段之前,清除
EntityManager
。这将有效地断开代理的连接以执行延迟加载,并且如果初始查询中未使用
JOIN FETCH
,则会导致测试引发异常

您的测试结果如下(用Kotlin编写)


Duplicate@Sambit我不是在寻求N+1问题的解决方案,而是寻求一种自动检测它的方法。我已经阅读了您所指的问题,但在那里找不到答案。您可以尝试检查
getChildren()
是否实际返回
PersistentCollection
,如果是,则检查
wasninitialized()
是否返回true。如果未初始化该持久集合,则任何访问都可能导致延迟加载(在某些情况下,这可能不会发生,例如,在调用
size()
时,并且取决于您的映射,例如,它是否特别延迟,但很有可能)。
class MyRepositoryTest @Autowired constructor(
    myRepository: MyRepository,
    entityManager: EntityManager
) {
    @Test
    fun `When load children will eagerly fetch`() {
        val parent = myRepository.loadParent()

        entityManager.clear()

        // This line should throw if children are being lazily loaded
        assertThat(parent?.children, equalTo(listOf(Child(1), Child(2))))
    }
}