Java 我们如何测试N+;JPA/Hibernate中的1个问题?
我有一个N+1问题,我想写一些自动回归测试,因为它对性能影响很大 我考虑监视EntityManager并验证其方法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”)
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))))
}
}