【问题标题】:JPA/Hibernate query: load eagerly with subselectJPA/Hibernate 查询:使用子选择热切加载
【发布时间】:2015-06-03 23:51:19
【问题描述】:

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

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

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


问题 1:实现这一目标的最佳做法是什么?您能否提供 JPQL/HSQL 和 Criteria 的示例?

问题 2(奖励):API 能否将第二个查询划分为“批次” - 例如将批次限制为 500:如果第一个查询加载 1000 个父母,则为 2a。为 500 位父母加载儿童,2b。加载下一个 500。


我试过了: 两者都会导致 SQL JOIN,看来我不能在没有 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 列。也就是说,避免由 child.parent.id 导致的 JOIN。

【问题讨论】:

    标签: java hibernate jpa eclipselink jpa-2.0


    【解决方案1】:

    为避免 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。
    • 那你应该试试查询提示。最后,没有理由害怕加入。我花了一段时间来克服它并进行了一些测试,但是父/子联接是索引联接,因此它们速度很快,实际上 db 调用相同的代码并访问与限制为父 ID 的单独查询相同的数据。因此,通常连接查询会比两个单独的“优化查询”更快。自己测试一下。
    • 还有其他原因要避免加入。例如,急切地获取多个关系会导致笛卡尔积很快变大。此外,连接不能很好地配合分页。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    • 2019-02-17
    • 2015-10-02
    • 2021-12-14
    • 1970-01-01
    • 2010-09-20
    • 2014-11-19
    相关资源
    最近更新 更多