2010-09-03 233 views
10

JPA 2是否有任何运行递归查询的机制?递归JPA查询?

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

我使用的是Hibernate 3.5.3,但我不希望在Hibernate API上有明确的依赖关系。


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

回答

21

使用简单的邻接模型其中每行包含对其父代的引用,这将引用同一表中的另一行,但与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' 

因此,要获得节点'及其所有孩子的查询是:

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

要将此映射到JPA,只需将'path'列设置为持久对象的属性即可。然而,您必须做好记录,以保持“路径”字段保持最新。 JPA/Hibernate不会为你做这件事。例如。如果将节点移动到其他父级,则必须更新父级引用并从新父级对象中确定新路径值。

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

还有第三种方法称为嵌套间隔模型,但是这严重依赖存储过程来实现。

The Art of SQL的第7章描述了对这个问题更完整的解释。

+0

这比我所寻找的要复杂得多,但谢谢你提供的信息。对物化路径模型还有一个更简单的替代方法 - 让每个节点存储它所属树的根节点的ID。这意味着你只能使用根节点作为查询的起点,但在我的具体情况下这不是问题 - 所以这可能是我会做的。 – 2010-09-03 22:08:12

+0

您可以使用通过JPA管理的邻接列表,但使用本机查询来使用递归SQL功能。这会有点恶心,而且不便携,但它会是两全其美的。嵌套集合是可移植的,但很烦琐,而且对于一些相当常见的查询来说效果不佳。 – 2010-09-04 00:11:14

+0

有什么更改JPA 2.0 ...例如,我已经使用本地NamedQuery在我的查询中有一个'CONNECT BY'子句。我已经尝试过了,它确实会返回孩子,但不是以hierchical列表形式返回......只是平坦的孩子。有任何想法吗? – SoftwareSavant 2012-06-27 15:00:25

4

在这篇文章中的最佳答案看起来像一个巨大的解决方法黑客给我。我已经不得不处理数据模型,辉煌的工程师们认为将数据库字段中的Tree Hiarchies编码为文本如“欧洲|英国| Shop1 | John”以及这些表中海量数据是一个好主意。不要惊慌,MyHackedTreeField LIKE'parentHierharchy%'这个形式的查询表现在哪里杀手。 解决这类问题最终需要在树状结构的内存缓存中创建以及许多其他类型......

如果您需要运行递归查询并且您的数据量不是很大......让您的生活变得简单,并简单地加载您需要运行计划的数据库字段。并用java编写你的递归代码。 除非你有充分的理由去做,否则不要在数据库中创建它。

即使您的数据量很大,您很可能会将您的问题细分为独立的递归树形批处理,并且无需一次加载所有数据。