这对于学校作业来说是一个棘手的问题,因为从历史上看,这一直是 jOOQ 最缺失的功能之一 :)
使用 MULTISET 的 jOOQ 3.15+ 解决方案
除了以下基于 SQL/XML 或 SQL/JSON 的解决方案之外,jOOQ 3.15 现在支持标准 SQL MULTISET value constructor 运算符以及合成 MULTISET_AGG aggregate function,可以像这样使用:
List<School> schools =
ctx.select(
SCHOOL.NAME,
SCHOOL.ID,
multisetAgg(
TEACHER.NAME,
TEACHER.ID,
multiset(
select(SUBJECT.NAME, SUBJECT.ID)
.from(SUBJECT)
.where(SUBJECT.TEACHER_ID.eq(TEACHER.ID))
).as("subjects").convertFrom(r -> r.map(Records.mapping(Subject::new))),
multiset(
select(CLASS.NAME, CLASS.ID)
.from(CLASS)
.where(CLASS.TEACHER_ID.eq(TEACHER.ID))
).as("classes").convertFrom(r -> r.map(Records.mapping(Classes::new)))
).as("teachers").convertFrom(r -> r.map(Records.mapping(Teachers::new)))
)
.from(SCHOOL)
.join(TEACHER).on(TEACHER.SCHOOL_ID.eq(SCHOOL.ID))
.groupBy(SCHOOL.NAME, SCHOOL.ID)
.fetch(Records.mapping(School::new));
上述使用各种Records.mapping() 重载和ad-hoc data type conversion 的方法假定存在不可变的构造函数,例如,如果您的类是Java 16 记录,您会得到:
record Subject (String name, long id) {}
使用 SQL/XML 或 SQL/JSON 的 jOOQ 3.14+ 解决方案
从jOOQ 3.14 and the new SQL/XML and SQL/JSON support开始,这将相对容易实现。本质上,您将使用 RDBMS 的原生 XML 或 JSON 支持直接在 SQL 中嵌套集合。 (正如您所注意到的,所有其他使用连接并尝试将重复数据删除和鞋拔扁平结果集放入嵌套数据结构的方法都不能很好地工作)
您可以编写这样的查询(假设您使用代码生成器,并假设您对顶部带有 School 的树结构感兴趣):
List<School> schools =
ctx.select(jsonObject(
jsonEntry("name", SCHOOL.NAME),
jsonEntry("id", SCHOOL.ID),
jsonEntry("teachers", jsonArrayAgg(jsonObject(
jsonEntry("name", TEACHER.NAME),
jsonEntry("id", TEACHER.ID),
jsonEntry("subjects", field(
select(jsonArrayAgg(jsonObject(SUBJECT.NAME, SUBJECT.ID)))
.from(SUBJECT)
.where(SUBJECT.TEACHER_ID.eq(TEACHER.ID))
)),
jsonEntry("classes", field(
select(jsonArrayAgg(jsonObject(CLASS.NAME, CLASS.ID)))
.from(CLASS)
.where(CLASS.TEACHER_ID.eq(TEACHER.ID))
))
)))
))
.from(SCHOOL)
.join(TEACHER).on(TEACHER.SCHOOL_ID.eq(SCHOOL.ID))
.groupBy(SCHOOL.NAME, SCHOOL.ID)
.fetchInto(School.class);
此解决方案基于您的架构假设,即SUBJECT -> TEACHER 和CLASS -> TEACHER 之间存在一对一关系。
另外,你可以看到我仍然使用TEACHER 加入SCHOOL 组,使用JSON_ARRAYAGG() 聚合教师。这是一种选择,对于 SUBJECT 和 CLASS 查询的另一个相关子查询也是可能的。
使用SQL Server's FOR JSON clause 可能有一个更简单的解决方案,可以在其他方言中模拟。