【问题标题】:HQL recursion, how do I do this?HQL递归,我该怎么做?
【发布时间】:2010-03-21 10:47:47
【问题描述】:

我有一个树结构,其中每个Node 都有一个父级和一个Set<Node> children。每个节点都有一个String title,我想在选择Set<String> titles 的地方进行查询,这是该节点和所有父节点的标题。如何编写此查询?

单个标题的查询是这样的,但就像我说的,我希望它扩展到父母的整个分支。

SELECT node.title FROM Node node WHERE node.id = :id

干杯

尼克

【问题讨论】:

    标签: java hibernate orm recursion hql


    【解决方案1】:

    您不能使用 HQL 进行递归查询。 See this。正如那里所说,它甚至不是标准的 SQL。你有两个选择:

    • 编写特定于供应商的递归原生SQL query
    • 进行多个查询。例如:

      // obtain the first node using your query
      while (currentNode.parent != null) {
         Query q = //create the query
         q.setParameter("id", currentNode.getParentId());
         Node currentNode = (Node) q.getSingleResult();
         nodes.add(currentNode); // this is the Set
      }
      

    我肯定会选择第二个选项。

    【讨论】:

    • 我知道 SQL 不是递归的,我认为 HQL 在设计时考虑到了这一限制。
    • 如果树不太深或不太复杂,我也会选择 Bozho 的第二个解决方案。但是如果你的树有十个“查询”深度,或者你有很多节点,你想获得这个完整的标题,你最终可能会遇到很多查询和性能问题。根据您的情况,解决此问题的一种方法是在保存记录时将完整标题写入数据库,从而节省检索时间。
    • 这确实是我的问题,树可能很深,这就是为什么我希望 Hibernate 负责优化与数据库交互的方式。我希望它知道智能解决方案,并可能导致数据库转换为可以处理此类问题的数据库。
    • 我认为答案是:HQL 没有递归,使用其他优化策略。我要找的是“物化路径”。非常感谢您的帮助:-)
    • 其实Common Table Expressions are part of the SQL:1999 standard,所以有办法做递归SQL,但是MySQL不支持,Hibernate/JPA也不支持。
    【解决方案2】:

    虽然无法编写您要求的递归查询,但可以使用 HQL 急切获取层次结构;这样做至少可以让您在内存中遍历树,而无需访问每个级别的数据库。

    select n from Node n
    left join fetch n.Children
    

    【讨论】:

      【解决方案3】:

      我知道这个问题已经过时了,但由于它与另一个问题相关联,因此我想对此进行更新,因为 Blaze-Persistence 支持在 JPA 模型之上使用递归 CTE。

      Blaze-Persistence 是基于 JPA 的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。要对 CTE 或递归 CTE 建模,这是您在此处需要的,您首先需要引入一个 CTE 实体,该实体对 CTE 的结果类型进行建模。

      @CTE
      @Entity
      public class NodeCTE {
        @Id Integer id;
      }
      

      您的示例查询可能如下所示

      List<String> titles = criteriaBuilderFactory.create(entityManager, String.class)
        .withRecursive(NodeCTE.class)
          .from(Node.class, "n1")
          .bind("id").select("n1.id")
          .where("n1.id").eq(nodeId)
        .unionAll()
          .from(Node.class, "n2")
          .innerJoinOn(NodeCTE.class, "cte")
            .on("cte.id").eq("n2.parent.id")
          .end()
          .bind("id").select("n2.id")
        .end()
        .from(Node.class, "n")
        .select("n.title")
        .where("n.id").in()
          .from(NodeCTE.class, "c")
          .select("c.id")
        .end()
        .getResultList();
      

      这呈现给 SQL,如下所示

      WITH RECURSIVE NodeCTE(id) AS (
          SELECT n1.id
          FROM Node n1
          WHERE n1.parent_id = :id
        UNION ALL
          SELECT n2.id
          FROM Node n2
          INNER JOIN NodeCTE cte ON n2.parent_id = cte.id
      )
      SELECT n.title
      FROM Node n
      WHERE n.id IN (
        SELECT c.id
        FROM NodeCTE c
      )
      

      您可以在文档中找到有关递归 CTE 的更多信息:https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#recursive-ctes

      【讨论】:

        猜你喜欢
        • 2011-12-22
        • 2018-01-16
        • 1970-01-01
        • 2012-05-26
        • 1970-01-01
        • 2014-01-23
        • 2011-05-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多