java - jOOQ - 具有多对多关系的嵌套对象

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

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

我的对象看起来与此类似,学生-教师是 N:N 关系,学生-书本是 1:N。

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

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

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

最佳答案

jOOQ 3.14 之前

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

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

jOOQ 3.14之后

jOOQ 3.14 and the new SQL/XML and SQL/JSON support开始,这将相对容易实现。本质上,您将使用 RDBMS 的 native 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));

关于java - jOOQ - 具有多对多关系的嵌套对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63207665/

相关文章:

java - 在azure Rest API调用中,在同一虚拟机的不同API中获取不同的数据磁盘ID?

java - Java 中的 JIT 错误

python - 过滤满足一组条件的多对多关系

java - 在一对多关系中指定子表的条件

java - 使用具有单个输入字符串/模式的算法生成多个唯一 ID

python - 当模型位于不同应用程序中时,ManytoMany 字段 django 模板

MySQL 多对多设置 - 如何将 bool 表达式转换为 MySQL?

grails - Grails如何与SQL Server数据库正确地一对多映射

hibernate - 删除一对多关系中的子项抛出 ObjectDeletedException

Java ZipInputStream 在读取图像的 ZipEntry 后关闭