使用 jOOQ 加载 PostgreSQL 树

Load PostgreSQL tree with jOOQ

我在 PostgreSQL 中有以下结构:

TableA has many TableB has many TableC

这三张表(简体)如下:

create table TableA (
   idA int,
   nameA varchar(100));


create table TableB (
   idA int,
   idB int,
   nameB varchar(100));


create table TableC (
   idA int,
   idB int,
   idC int,
   nameC varchar(100));

我需要 运行 在 PostgreSQL 上使用 jOOQ select of TableA,但它还应该从 TableBTableC 加载任何相关行.有没有办法 运行 单个 jOOQ 语句来加载这棵树?我知道我可以用 joins 做到这一点,但我试图避免 Java.

中的任何循环

jOOQ 3.15 解决方案使用 MULTISET

从 jOOQ 3.15 开始,您可以使用 standard SQL MULTISET operator, which is emulated using SQL/XML or SQL/JSON

嵌套集合
var result =
ctx.select(
      TABLE_A.ID_A, TABLE_A.NAME_A,
      multiset(
        select(
          TABLE_B.ID_B, TABLE_B.NAME_B,
          multiset(
            select(TABLE_C.ID_C, TABLE_C.NAME_C)
            .from(TABLE_C)
            .where(TABLE_C.ID_B.eq(TABLE_B.ID_B))
            .and(TABLE_C.ID_A.eq(TABLE_B.ID_A))
          ).as("c")
        )
        .from(TABLE_B)
        .where(TABLE_B.ID_A.eq(TABLE_A.ID_A))
      ).as("b")
    )
   .from(TABLE_A)
   .fetch();

result 的类型推断为:

Result<Record3<
  Integer,          // TABLE_A.ID_A
  String,           // TABLE_A.NAME_A
  Result<Record3< 
    Integer,        // TABLE_B.ID_B
    String,         // TABLE_B.NAME_B
    Result<Record2<
      Integer,      // TABLE_C.ID_C
      String        // TABLE_C.NAME_C
    >>
  >>
>> result = ...

如果您希望将其映射到某些自定义 DTO 数据结构中,例如

record TableC(int idC, String nameC) {}
record TableB(int idB, String nameB, List<TableC> c) {}
record TableA(int idA, String nameA, List<TableB> b) {}

您可以使用 jOOQ 3.15's new ad-hoc conversion feature 轻松完成此操作:

List<TableA> result =
ctx.select(
      TABLE_A.ID_A, TABLE_A.NAME_A,
      multiset(
        select(
          TABLE_B.ID_B, TABLE_B.NAME_B,
          multiset(
            select(TABLE_C.ID_C, TABLE_C.NAME_C)
            .from(TABLE_C)
            .where(TABLE_C.ID_B.eq(TABLE_B.ID_B))
            .and(TABLE_C.ID_A.eq(TABLE_B.ID_A))
          ).as("c").convertFrom(r -> r.map(Records.mapping(TableC::new)))
        )
        .from(TABLE_B)
        .where(TABLE_B.ID_A.eq(TABLE_A.ID_A))
      ).as("b").convertFrom(r -> r.map(Records.mapping(TableB::new)))
    )
   .from(TABLE_A)
   .fetch(Records.mapping(TableA::new));

所有映射都是类型安全的、编译时检查的和无反射的

jOOQ 3.14 解决方案使用 SQL/XML 或 SQL/JSON

您可以使用 jOOQ 3.14 的 SQL/XML 或 SQL/JSON 支持,see this blog post here

如果您在 class 路径上有 Gson 或 Jackson,它们可用于将 XML 或 JSON 结构映射回您的 Java class 层次结构。 manual's page about ConverterProvider

中给出了一个例子

本质上:

ctx.select(
     jsonObject(
       key("idA").value(TABLE_A.ID_A),
       key("nameA").value(TABLE_A.NAME_A),
       key("b").value(
         select(jsonArrayAgg(jsonObject(
           key("idB").value(TABLE_B.ID_B),
           key("nameB").value(TABLE_B.NAME_B),
           key("c").value(
             select(jsonArrayAgg(jsonObject(
               key("idC").value(TABLE_C.ID_C),
               key("nameC").value(TABLE_C.NAME_C)
             )))
             .from(TABLE_C)
             .where(TABLE_C.ID_B.eq(TABLE_B.ID_B))
           )
         )))
         .from(TABLE_B)
         .where(TABLE_B.ID_A.eq(TABLE_A.ID_A))
       )
     )
   )
   .from(TABLE_A)
   .fetch();

另请参阅此处的相关问题:

  • jOOQ - nested object with many to many relationship
  • JOOQ pojos with one-to-many and many-to-many relations

请注意,JSON_ARRAYAGG() 将空集聚合到 NULL,而不是空集 []