JPA/Hibernate查询:使用子查询进行及早加载

3

情况: "父"实体有多个"子"实体(@OneToMany, @Lazy) - 双向关系。实体上没有外键("Child#parentId")字段。

目标: 通过使用子查询检索完全加载的父集合来避免N+1问题。如果我理解子查询的理论,这是我的目标(2个结果SQL查询):

select * from Parent ...;
select * from Child where parent_id in ...;


问题1: 如何最好实现这个目标? 你能提供JPQL/HSQL和Criteria的示例吗?

问题2(额外题): API是否可以将第二个查询分成“批次” - 例如将批次限制为500:如果第一个查询加载了1000个Parents,则2a.加载500个Parents的Children,2b.加载下一个500个。


我已尝试过: 两者都会产生SQL JOINs,似乎我不能在没有JOIN的情况下使用Child的外键。

// 2nd query:
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id")
        .in(parentCriteria.setProjection(Projections.property("id"))))
.list();

// 2nd query (manual):
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id").in(parentIdList))
.list();

更新 (2015-04-05)

我检查了这个方法,并通过提示确定它确实可以在EclipseLink中工作:

query.setHint("eclipselink.batch.type", "EXISTS");

这个链接http://blog.ringerc.id.au/2012/06/jpa2-is-very-inflexible-with-eagerlazy.html表明在Hibernate中无法实现此功能,建议手动获取。但是,我无法理解如何通过HQL或Criteria实现,特别是如何获取仅存在于数据库上的child.parent_id列而不在Entity上的值,即避免由child.parent.id引起的JOIN。

1个回答

0
为了避免N+1查询,您可以使用注释关系。
@BatchFetch(BatchFetchType.JOIN)  //in eclipselink or
@BatchSize //in hibernate.

在查询中,您可以将fetch添加到join子句中:

select p from Parent p join fetch p.children c where ...

您还可以添加查询提示

query.setHint("eclipselink.batch", "p.children");

或者使用EntityGraphs。


嗨,感谢您提供这些信息,但这并不是我正在寻找的。实体映射无法更改,至于联接获取,它可以工作,但我正试图在此处明确避免任何SQL JOIN。 - Vaidas.S
然后你应该尝试使用queryhints。最后,没有理由害怕连接操作。我花了一些时间来克服这个问题并进行了一些测试,但是父/子连接是索引连接,因此它们很快,在实际情况下,数据库调用相同的代码并访问与限制为父ID的单独查询相同的数据。因此,通常连接查询将比两个单独的“优化查询”更快。自己测试一下吧。 - Zielu
避免使用连接的其他原因包括:急切获取多个关系会导致笛卡尔积,而这可能会很快变得非常大。此外,连接与分页不兼容。 - oillio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接