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