JPA:构造函数表达式导致1 + N选择

时间:2015-01-19 16:07:01

标签: java hibernate jpa

按照此处列出的建议:JPA: How to count child records without loading a lazy loaded set实施了以下查询:

SELECT new x.y.z.TreeInfo(t, count(l.id))
FROM Tree t LEFT JOIN t.branches b LEFT JOIN b.leaves l
GROUP BY t.id

目标是在一个查询中获取整个树对象和数据库中的叶子数。现在它在第一个查询中获取计数和树ID,然后它发出一个额外的查询来加载每个树。有没有办法告诉它在第一个查询中获取整个树对象? JPA是错误的工具吗?

我想要做的等价SQL是:

select *, (select count(*) 
          from leaf l join branch b on l.parentid = b.id
          where b.parentid = t.id)
from tree t

1 个答案:

答案 0 :(得分:0)

在您展示更多代码和映射之前,很难看到问题所在。问题可能是,例如,通过获取树(select new TreeInfo (t, ... ) from Tree t...),您最终通过急切地获取对象图来加载整个树。

通常,从数据库中提取树需要多个查询,因为您不知道最长分支包含多少个节点,因此您需要查询子节点需要多少次。 JPA在这里不是问题。

有多种策略可以解决这个问题:

  1. 您可以将树建模为一个单独的实体(您似乎已经这样做了)并且在树中的每个节点(而不仅仅是根节点)中具有树的外键。现在,您可以一次性获取树的所有节点,并通过加载父节点并点击会话缓存来使hibernate构造树结构。
  2. 您可以在每个节点中冗余地存储每个节点的路径(作为一串ID)。这样可以轻松获取子树。当发生结构变化时,更新很困难,但这通常不是必需的。现在,您可以通过选择具有以根节点的ID开头的路径的所有节点来选择树中的所有节点。您必须标记叶节点并对标志进行过滤以进行计数或在内存中进行计数。
  3. 您会发现执行所需操作的唯一方法是向节点添加冗余数据。


    无论哪种方式,要在SQL中计算树中叶子的数量,您可以这样做:

    select count(*) from node n left outer join node c on c.parent_id = n.id where c.id is null