我一直在尝试找出如何从 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 连接的平面结果集并不容易实现,其中不相关实体之间的笛卡尔积(在您的情况下:在 Teacher
和 Book
之间)并不罕见。
使用 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 #3884或this 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/