JPA方法可以在没有1 + n + n * m个查询的情况下延迟加载整个实体图?

时间:2018-11-16 12:15:54

标签: java hibernate jpa

假设我有这些实体类(省略了JPA批注):

class TableA { Long id; List<TableB> tableBs; }
class TableB { Long id; List<TableC> tableCs; }
class TableC { Long id; List<TableD> tableDs; }
class TableD { Long id; int foo; }

这给了我们这个实体“图” /“依赖项”:

TableA ---OneToMany--> TableB ---OneToMany--> TableC ---OneToMany--> TableD

如果我想深入加载一个TableA对象的所有子实体,子子实体和子子实体,JPA将产生以下查询:

  • 使用1个查询获得一个TableA:当然可以
  • 1个查询以延迟加载tableA.getTableBs():也可以=>我们得到n个TableB实体
  • n个查询以延迟加载所有tableA.getTableBs()[1..n] .getTableCs()=>我们每个TableB实体获得m个TableC实体
  • n * m个查询以延迟加载所有tableA.getTableBs()[1..n] .getTableCs()[1..m] .getTableDs()

我想避免这种1 + n *(m + 1)查询来延迟加载TableA对象的所有子子实体。

如果我必须手动进行查询,则只需要 4 个查询:

  • 相同:1个查询即可获得一个TableA
  • 相同:1个查询到懒加载tableA.getTableBs():很好
  • 更好:1个查询以获取所有TableC WHERE ID IN(tableA.getTableBs()[1..n] .getId())//使用Java计算“ IN”子句,我执行一个SQL查询,并且然后从TableC {id,parentTableBId,...}结果中,我用Java填充每个TableB.getTableC()列表
  • 更好的方法:1个查询以获取所有TableD WHERE id IN(tableA.getTableBs()[1..n] .getTableCs()[1..m] .getId())//相同的子句计算和树遍历,将所有TableD子代分配给每个TableC父代

我想打电话给

  • JpaMagicUtils.deeplyLoad(tableA); //并且执行“ IN”子句的构建(可能分为2或3个查询,以使“ IN” id过多)+树形子代分配本身,或者
  • JpaMagicUtils.deeplyLoad(tableA,“ getTablesBs()。getTableCs()”); JpaMagicUtils.deeplyLoad(tableA,“ getTableBs()。getTableCs()。getTableDs()”); //一次填充一个级别,并具有更好的粒度,可以批量加载哪些字段,以及不加载哪些字段。

我认为JPA无法解决这个问题。

  • 或者JPA有办法吗?
  • 还是作为非JPA标准的方式,但是也许与Hibernate一起使用?或其他JPA实施?
  • 还是有一个库我们可以用来做到这一点(在任何JPA实现之上,特别是在一个实现之上)?

1 个答案:

答案 0 :(得分:1)

  

如果我必须手动进行查询,我只需要4个查询

那么,为什么不将一个查询连接到四个表呢?

但是,如果要限制查询的数量,我会首先尝试Hibernate的@Fetch(FetchMode.JOIN)(不确定其他JPA提供程序是否有类似的注释)。这是一个提示,告诉Hibernate使用联接来加载子实体(而不是发出单独的查询)。它并不总是适用于嵌套的一对多关联,但是我会尝试在层次结构的最深层定义它,然后逐步进行,直到找到可接受的性能(或禁止的结果集大小)为止。 。

如果您正在寻找通用解决方案,那么令人遗憾的是,我不知道任何将遵循您描述的算法的JPA提供程序,既不是一般功能也不是选择加入的功能。这是一个非常特定的用例,我想由于库未包含针对特殊情况的优化而变得健壮的代价。

注意:如果您想在一个用例中急于加载一个实体层次结构,但在一般情况下却使这些实体延迟加载,则需要查找JPA实体图。您可能还需要使用FETCH JOIN编写自定义查询,但我认为通常不支持嵌套FETCH JOINS