【问题标题】:Recursively query column using JPA to find descendants of an object使用 JPA 递归查询列以查找对象的后代
【发布时间】:2011-11-13 01:02:05
【问题描述】:

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

id, name, parent_id

假设我有一些这样的数据:

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

所以 Bob 有 2 个孩子:Mary 和 Tim。 Tim 有 1 个孩子:Sally(祖父母是 Bob)

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

【问题讨论】:

    标签: java sql jpa


    【解决方案1】:

    我认为你不能用普通的 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
    

    【讨论】:

    • 路径的想法一般都不错,但需要注意“like”; “parentPath 像 '1/3/4%' 会选择 '1/3/42' 和 '1/3/4/2'。通常我已经通过做类似 '1/3/ 的事情来解决这个问题4/%'。
    • 感谢您关注此事。
    【解决方案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;
    

    【讨论】:

      【解决方案3】:

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

      我相信这是在关系数据库中查询分层数据的最佳解决方案。

      【讨论】:

        【解决方案4】:

        在 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
        

        【讨论】:

          猜你喜欢
          • 2011-04-07
          • 2015-03-14
          • 1970-01-01
          • 2019-01-31
          • 2022-10-26
          • 1970-01-01
          • 1970-01-01
          • 2013-11-04
          • 2013-11-18
          相关资源
          最近更新 更多