【问题标题】:Hierarchical Data Fetch in Spring and HibernateSpring 和 Hibernate 中的分层数据获取
【发布时间】:2020-09-09 18:45:46
【问题描述】:

我有 2 个表 Account 和 Group 都包含层次结构中的数据。

示例 - (仅供参考,我使用的是 PostgresSQL)

|------|----------|-------------------|
|  id  |   name   |  parent_group_id  |
|------|----------|-------------------|
|  1   |  Group1  |  null             |
|  2   |  Group2  |  1                |
|  3   |  Group3  |  2                |
|  4   |  Group4  |  1                |
|------|----------|-------------------|

Account
|----|----------|----------|
| id | name     | group_id |
|----|----------|----------|
| 1  | Account1 | 1        |
| 2  | Account2 | 1        |
| 3  | Account3 | 2        |
| 4  | Account4 | 3        |
| 4  | Account5 | 4        |
-----|----------|-----------

此帐户和组层次结构可以有很多层次。我想使用 Spring 和 Hibernate 以一种有效的方式获取所有组和帐户。

我希望输出像 -

{"name":"Group1","groups":[{"name":"Group4","groups":[],"accounts":[{"name":"Account5"}]},{"name":"Group2","groups":[{"name":"Group3","groups":[],"accounts":[{"name":"Account4"}]}],"accounts":[{"name":"Account3"}]}],"accounts":[{"name":"Account2"},{"name":"Account1"}]}

我检查了一些文章,但它们不是递归的(意味着组内的组等等)。

【问题讨论】:

标签: spring hibernate spring-boot


【解决方案1】:

这是Blaze-Persistence 的完美用例。

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").eqExpression("g2.parent.id")
    .end()
    .bind("id").select("g2.id")
  .end()
  .from(Group.class, "g")
  .fetch("accounts", "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 Account a ON a.group_id = g.id
LEFT JOIN Group gsub ON gsub.parent_group_id = g.id
WHERE g.id IN (
  SELECT c.id
  FROM GroupCTE c
)

您可以在文档中找到有关递归 CTE 的更多信息:https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#recursive-ctes

【讨论】:

  • 我试过了,但在Parameter value [g2.parent.id] did not match expected type [java.lang.Integer (n/a)] 出现错误我在java中的所有id都是Long,在postgres中是bigint
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-06
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多