使用JPA递归查询列以查找对象的后代

时间:2011-11-13 01:02:05

标签: java sql jpa

假设我有一个名为Person的表,其中包含列:

id, name, parent_id

让我说我有一些这样的数据:

1, Bob, null
2, Mary, 1
3, Tim, 1
4, Sally, 3

所以鲍勃有两个孩子:玛丽和蒂姆。 蒂姆有一个孩子:莎莉(祖父母是鲍勃)

编写JPA查询的最简单方法是什么,以便找到Bob的所有后代? (结果将归还Mary,Tim和Sally)

4 个答案:

答案 0 :(得分:3)

我认为你不能用普通的JPA做到这一点。

但是如果你可以像这样修改你的表

id, name, parent_path(string)

,您的数据将如下所示

1, Bob, null
2, Mary, 1
3, Tim, 1
4, Sally, 1/3
5, John, 1/3/4
6, Huge, 1/3/4/5

然后您可以使用like子句查询所有后代。 例如:

select p from Person p where p.parentPath like '1/%'

这里1 - 是parentPath + entityId,因此Bob like子句看起来像

like '1/%'

因为Bob的parentPath为null而Bob的id为1。

对于Sally,查询将如下所示

select p from Person p where p.parentPath like '1/3/4/%'

因为Sally的parentPath是'1/3'而Sally的id是4。

如果你必须添加一个新的孩子,你只需要将其parent_path设置为

parent.parentPath + '/' + parent.id

答案 1 :(得分:2)

大多数现代DBMS使用递归公用表表达式支持分层查询。如果您可以将纯SQL语句传递给JPA层,则可以使用类似于以下内容的单个语句轻松完成此操作:

WITH RECURSIVE people_tree (id, name, parent) as 
(
  SELECT id, name, parent_id
  FROM people
  WHERE parent name = 'Bob'

  UNION ALL

  SELECT p2.id, p2.name, p2.parent_id
  FROM people p2 
    INNER JOIN people_tree ON people_tree.id = p2.parent_id
)
SELECT * 
FROM people_tree
ORDER BY name;

答案 2 :(得分:1)

我使用嵌套集来解决同样的问题。请参阅我的问题并回答:How to show tree-view replies to message? Java & Hibernate

我认为这是查询关系数据库中的层次数据的最佳解决方案。

答案 3 :(得分:0)

在SQL Server 2008中,以下查询用于检索每个元素所在的级别,假设您的表名为Hierarchical_Test1,并包含字段“id,name,parent_id”:

with family_tree (parent_id, id, name, level) AS
(
-- Anchor member definition
select f.parent_id, f.id, f.name, 0 as level
from dbo.Hierarchical_Test1 f
where f.parent_id=0
union all
-- Recursive member definition
select f2.parent_id, f2.id, f2.name, level + 1
from dbo.Hierarchical_Test1 as f2
inner join family_tree as f3
on f2.parent_id = f3.id
)
-- Statement that executes the CTE
select parent_id, id, name, level
from family_tree

这个可以让你获得元素的所有祖先:

with family_tree (parent_id, id, name, level, parent_path) AS
(
-- Anchor member definition
select f.parent_id, f.id, f.name, 0 as level, 
            CAST('/' as varchar(255)) as parent_path
from dbo.Hierarchical_Test1 f
where f.parent_id=0
union all
-- Recursive member definition
select f2.parent_id, f2.id, f2.name, level + 1, 
            CAST(
                CAST('/' as varchar(255)) 
                + CAST(f2.parent_id as varchar(255)) 
                + CAST(f3.parent_path as varchar(255))
            as varchar(255))
from dbo.Hierarchical_Test1 as f2
inner join family_tree as f3
on f2.parent_id = f3.id
)
-- Statement that executes the CTE
select parent_id, id, name, level, parent_path
from family_tree