Java 递归JPA查询?

Java 递归JPA查询?,java,orm,jpa-2.0,jpql,recursive-query,Java,Orm,Jpa 2.0,Jpql,Recursive Query,JPA2有运行递归查询的机制吗 我的情况是:我有一个实体E,它包含一个整数字段x。它也可能有E类型的子级,通过@OneToMany映射。我想做的是通过主键找到一个E,然后得到它的x值,以及它所有子代的x值。有没有办法在一个查询中实现这一点 我使用的是Hibernate 3.5.3,但我不希望对Hibernate API有任何明确的依赖关系 编辑:根据条目,Hibernate没有这个功能,或者至少在三月份没有。因此,JPA似乎不太可能拥有它,但我想确保。使用简单的邻接模型,其中每一行都包含对其父

JPA2有运行递归查询的机制吗

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

我使用的是Hibernate 3.5.3,但我不希望对Hibernate API有任何明确的依赖关系



编辑:根据条目,Hibernate没有这个功能,或者至少在三月份没有。因此,JPA似乎不太可能拥有它,但我想确保。

使用简单的邻接模型,其中每一行都包含对其父行的引用,而父行将引用同一表中的另一行,这与JPA不太配合。这是因为JPA不支持使用Oracle CONNECT BY子句或SQL标准WITH语句生成查询。如果没有这两个子句中的任何一个,邻接模型就不可能真正有用

然而,有两种其他方法可以用来模拟这个问题,可以应用于这个问题。第一个是物质化路径模型。在这里,节点的完整路径被展平为一列。表定义的扩展如下:

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不会为您这样做。例如,如果将节点移动到不同的父对象,则必须更新父对象引用并根据新父对象确定新路径值

另一种方法称为嵌套集模型,它稍微复杂一些。最好是由它的发起者(而不是由我逐字添加)

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


关于这个问题的更完整的解释,请参见本手册第7章

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

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


即使您拥有的数据量很大,您也很可能将问题细分为独立的递归树批,然后一次处理一个,而不需要一次加载所有数据。

我遇到过这样的问题,从一个表中查询菜单节点, 我创立的方式是: 假设我们有一个名为Node的类,创建了一个单向的一对多关联,如下所示:

    @OneToMany(  fetch = FetchType.EAGER)
    @JoinColumn(name = "parent_id", referencedColumnName = "id")
    private List<Node> subNodeList;
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name=“parent\u id”,referencedColumnName=“id”)
私有列表子节点列表;
在entity中还有一个名为boolean isRoot的字段,如果该节点是根菜单项, 然后,通过查询isRoot为true的节点,我们只得到顶部节点,并且由于
FetchType.EAGER
,我们还得到列表中的子节点。
这将导致多个查询,但对于类似于小菜单的内容,这将是正常的。

我知道这个问题很老,但由于它在另一个问题中链接,我想对此进行更新,因为它提供了对在JPA模型上使用递归CTE的支持

Blaze Persistence是JPA之上的查询生成器,它支持JPA模型之上的许多高级DBMS功能。要对CTE或递归CTE进行建模(这是您在这里需要的),首先需要引入一个CTE实体,该实体对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();
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
)

您可以在文档中找到有关递归CTE的更多信息:

这比我要查找的内容要复杂得多,但感谢您提供了信息丰富的回复。对于物化路径模型,还有一种更简单的替代方法——让每个节点存储它所属的树的根节点的ID。这意味着您只能使用根节点作为查询的起点,但在我的特定情况下,这不是问题,所以我可能会这样做。您可以使用通过JPA管理的邻接列表,但使用本机查询来使用递归SQL功能。它会有点恶心,而且不便于携带,但它将是一个两全其美的东西。嵌套集是可移植的,但非常灵活,对于一些相当常见的查询,它的性能不是很好。。。例如,我使用了一个本地NamedQuery在我的查询中有一个“connectby”子句。我试过了,它确实返回了孩子们,但不是以层次列表的形式。。。只是些平庸的孩子。有什么想法吗?我将使用JPA持久性上下文,它需要一个假的rootHey。您能更新
嵌套集模型的链接吗?