【问题标题】:jOOQ - nested object with many to many relationshipjOOQ - 具有多对多关系的嵌套对象
【发布时间】:2020-11-22 05:38:19
【问题描述】:

我一直在试图弄清楚如何从 jOOQ 查询中转换嵌套对象。 我已经看到提到的 RecordMapper 或 java 8 流作为可能的解决方案,但无法弄清楚如何使用它们。

我的对象看起来与此类似,Student-Teacher 是 N:N 关系,Student-Book 是 1:N。

Student {
  List<Teacher> teachers
  List<Book> books;
}

现在我想使用 jooq 查询表并将其转换为包含其他对象的 Student 对象。有没有直接的方法来做到这一点?是否有示例说明如何实现这一目标? intoGroups 可以与类似的模式一起使用吗?如果我使用 intoGroups,我会得到我加入数据库的所有不同类型记录的列表吗?

我还可以进行 2 次查询,这应该不是问题。 谢谢。

【问题讨论】:

    标签: java many-to-many one-to-many jooq


    【解决方案1】:

    在 jOOQ 3.14 之前

    从历史上看,jOOQ 没有为多对多关系提供任何映射解决方案。使用源自 SQL 连接的平面结果集并不容易实现,其中不相关实体之间的笛卡尔积(在您的情况下:TeacherBook 之间)并不少见。

    使用 2 个或更多查询的解决方案是可能的,但有很多需要避免的手写映射代码。

    jOOQ 3.14 之后

    jOOQ 3.14 and the new SQL/XML and SQL/JSON support开始,这将相对容易实现。本质上,您将使用 RDBMS 的原生 XML 或 JSON 支持直接在 SQL 中嵌套集合。

    您可以编写这样的查询(假设您使用代码生成器):

    List<Student> students =
    ctx.select(jsonObject(
         jsonEntry("name", STUDENT.NAME),
         jsonEntry("id", STUDENT.ID),
         jsonEntry("teachers", field(
           select(jsonArrayAgg(jsonObject(TEACHER.NAME, TEACHER.ID)))
           .from(TEACHER)
           .join(STUDENT_TEACHER).on(TEACHER.ID.eq(STUDENT_TEACHER.TEACHER_ID))
           .where(STUDENT_TEACHER.STUDENT_ID.eq(STUDENT.ID))
         )),
         jsonEntry("books", field(
           select(jsonArrayAgg(jsonObject(BOOK.NAME, BOOK.ID)))
           .from(BOOK)
           .join(STUDENT_BOOK).on(BOOK.ID.eq(STUDENT_BOOK.BOOK_ID))
           .where(STUDENT_BOOK.STUDENT_ID.eq(STUDENT.ID))
         ))
       ))
       .from(STUDENT)
       .fetchInto(Student.class);
    

    请注意,JSON_ARRAYAGG() 将空集聚合到 NULL,而不是空集 []If that's a problem, use COALESCE()

    jOOQ 3.15 之后

    jOOQ 终于有了MULTISET support, see #3884this blog post。这允许简化上述 JSON 方法:

    使用反射映射

    List<Student> students =
    ctx.select(
         STUDENT.NAME,
         STUDENT.ID,
         multiset(
           select(TEACHER.NAME, TEACHER.ID)
           .from(TEACHER)
           .join(STUDENT_TEACHER)
             .on(TEACHER.ID.eq(STUDENT_TEACHER.TEACHER_ID))
           .where(STUDENT_TEACHER.STUDENT_ID.eq(STUDENT.ID))
         ).as("teachers").convertFrom(r -> r.map(Teacher.class)),
         multiset(
           select(BOOK.NAME, BOOK.ID)
           .from(BOOK)
           .join(STUDENT_BOOK).on(BOOK.ID.eq(STUDENT_BOOK.BOOK_ID))
           .where(STUDENT_BOOK.STUDENT_ID.eq(STUDENT.ID))
         ).as("books").convertFrom(r -> r.map(Book.class))
       ))
       .from(STUDENT)
       .fetchInto(Student.class);
    

    使用类型安全、无反射的映射

    如果您有不可变的 DTO(例如 Java 16 记录),则比使用反射要好得多,在这种情况下,您可以以类型安全、编译时检查、无反射的方式将 jOOQ 记录直接映射到构造函数引用:

    List<Student> students =
    ctx.select(
         STUDENT.NAME,
         STUDENT.ID,
         multiset(
           select(TEACHER.NAME, TEACHER.ID)
           .from(TEACHER)
           .join(STUDENT_TEACHER)
             .on(TEACHER.ID.eq(STUDENT_TEACHER.TEACHER_ID))
           .where(STUDENT_TEACHER.STUDENT_ID.eq(STUDENT.ID))
         ).as("teachers").convertFrom(r -> r.map(Records.mapping(Teacher::new))),
         multiset(
           select(BOOK.NAME, BOOK.ID)
           .from(BOOK)
           .join(STUDENT_BOOK).on(BOOK.ID.eq(STUDENT_BOOK.BOOK_ID))
           .where(STUDENT_BOOK.STUDENT_ID.eq(STUDENT.ID))
         ).as("books").convertFrom(r -> r.map(Records.mapping(Book::new)))
       ))
       .from(STUDENT)
       .fetch(Records.mapping(Student::new));
    

    【讨论】:

      猜你喜欢
      • 2014-06-13
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      • 2013-02-17
      • 1970-01-01
      • 1970-01-01
      • 2016-02-24
      • 2020-09-03
      相关资源
      最近更新 更多