代码之家  ›  专栏  ›  技术社区  ›  Mike Baranczak

递归JPA查询?

  •  14
  • Mike Baranczak  · 技术社区  · 14 年前

    这里是我的情况:我有一个实体E,它包含一个整数字段x。它也可能有E类型的子元素,通过@OneToMany映射。我想做的是通过主键找到一个E,得到它的x值,以及它所有子代的x值。有没有办法在一个查询中实现这一点?

    我使用的是hibernate3.5.3,但我不希望对hibernateapi有任何明确的依赖关系。


    编辑:根据 this 项目,休眠 有这个功能,或者至少在三月份没有。所以JPA似乎不太可能拥有它,但我想确定一下。

    2 回复  |  直到 7 年前
        1
  •  28
  •   Michael Barker    6 年前

    使用简单的

    然而,有几个其他的方法来模拟这个问题,可以应用到这个问题。第一个是 物质化路径模型

    CREATE TABLE node (id INTEGER,
                       path VARCHAR, 
                       parent_id INTEGER REFERENCES node(id));
    

    INSERT INTO node VALUES (1, '1', NULL);  -- Root Node
    INSERT INTO node VALUES (2, '1.2', 1);   -- 1st Child of '1'
    INSERT INTO node VALUES (3, '1.3', 1);   -- 2nd Child of '1'
    INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3'
    

    因此,要获取节点“1”及其所有子节点,查询是:

    SELECT * FROM node WHERE id = 1 OR path LIKE '1.%';
    

    要将其映射到JPA,只需将“path”列作为持久对象的属性。但是,您必须进行簿记,以使“路径”字段保持最新。JPA/Hibernate不会为您这样做。例如,如果将节点移动到不同的父对象,则必须更新父对象引用并从新父对象确定新路径值。

    另一种方法称为 套集模型 ,这有点复杂。可能是最好的 described 由其发起者(而不是由我逐字添加)。

    还有第三种方法称为嵌套区间模型,但是这在很大程度上依赖于存储过程来实现。

    对这个问题的更完整的解释在本书的第7章中描述 The Art of SQL .

        2
  •  9
  •   99Sono    9 年前

    在我看来,这篇文章中最好的答案就像是一个巨大的黑客工程。我已经处理过一些数据模型,在这些模型中,优秀的工程师们认为最好将数据库字段中的树结构编码为文本,例如:“Europe | Uk | Shop1 | John”,并在这些表中包含大量数据。毫不奇怪,MyHackedTreeField表单的查询性能就像“parentHierharchy%”where killers。 解决这类问题最终需要在内存中创建树层次结构缓存和其他许多缓存。。。

    如果您需要运行递归查询,并且您的数据量不是很大。。。使您的生活简单,只需加载运行计划所需的DB字段。并用java编写递归代码。 除非你有充分的理由,否则不要进入数据库。

        3
  •  1
  •   Mehdi Pourrostam    5 年前

    我遇到了这样的问题,从一个表中查询菜单节点, 我创立的方式是: 假设我们有一个名为Node的类,创建了一个 Unidirectional One-to-Many 像这样的联想:

        @OneToMany(  fetch = FetchType.EAGER)
        @JoinColumn(name = "parent_id", referencedColumnName = "id")
        private List<Node> subNodeList;
    

    还有一个名为boolean isRoot in entity的文件,如果这个节点是根菜单项, 然后,通过查询有isRoot为真的节点,我们只会得到顶级节点,因为 FetchType.EAGER 这将导致多个查询,但对于小菜单之类的东西,这将是好的。

        4
  •  0
  •   Christian Beikov    4 年前

    我知道这个问题很老了,但由于它是在一个不同的问题,我想给这个更新,因为 Blaze-Persistence 提供对在JPA模型之上使用递归cte的支持。

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

    List<Group> groups = criteriaBuilderFactory.create(entityManager, Group.class)
      .withRecursive(GroupCTE.class)
        .from(Group.class, "g1")
        .bind("id").select("g1.id")
        .where("g1.parent").isNull()
      .unionAll()
        .from(Group.class, "g2")
        .innerJoinOn(GroupCTE.class, "cte")
          .on("cte.id").eq("g2.parent.id")
        .end()
        .bind("id").select("g2.id")
      .end()
      .from(Group.class, "g")
      .fetch("groups")
      .where("g.id").in()
        .from(GroupCTE.class, "c")
        .select("c.id")
      .end()
      .getResultList();
    

    这将呈现如下所示的SQL

    WITH RECURSIVE GroupCTE(id) AS (
        SELECT g1.id
        FROM Group g1
        WHERE g1.parent_group_id IS NULL
      UNION ALL
        SELECT g2.id
        FROM Group g2
        INNER JOIN GroupCTE cte ON g2.parent_group_id = cte.id
    )
    SELECT *
    FROM Group g
    LEFT JOIN Group gsub ON gsub.parent_group_id = g.id
    WHERE g.id IN (
      SELECT c.id
      FROM GroupCTE c
    )
    

    https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#recursive-ctes