JPA无额外注解实现关联查询:获取部门及学生去重层级列表
适配嵌套结构的JPA实现方案
问题根因
你现在拿到平铺二元组返回的核心原因,是直接做行级关联查询——每个匹配的「部门-学生」记录都会生成一行独立结果,自然无法把同部门下的多个Level值嵌套聚合到同一行部门记录里。
要在不引入额外自定义注解、自定义DTO的前提下拿到目标嵌套结构,只要从聚合查询逻辑和实体关联补全两个点调整即可。
具体调整步骤
- 补全实体的双向关联(这是JPA关联映射的标准写法,不属于额外自定义注解)
你现在只在Student侧定义了指向Department的多对一关联,Department实体里缺少反向的学生集合映射,直接关联查询会取不到部门下的学生列表。在Department类里补充标准的一对多映射即可:@OneToMany(mappedBy = "dp") private List<Student> students; - 替换Repository里的JPQL查询语句
用JPA原生支持的分组聚合逻辑,把同部门的去重Level值直接聚合成集合,对应查询写法:
这里的几个关键细节:@RestResource(path="/myquery") @Query("SELECT d.name, COLLECT(DISTINCT s.Level) FROM Department d JOIN d.students s GROUP BY d.id, d.name") List<Object[]> getLevel();COLLECT(DISTINCT s.Level)是JPA规范内置的聚合函数,会把当前分组下所有学生的Level值去重后聚合成整数列表,不需要手动处理行拼接- 分组条件同时写
d.id和d.name,是为了兼容开启了only_full_group_by的MySQL等数据库的SQL校验规则,避免查询报错 - 从
Department实体出发做关联查询,能避免从Student出发查询时漏过没有学生的空部门的场景
最终返回结构匹配验证
Spring Data JPA 在序列化List<Object[]>类型的返回值时,会严格按照查询字段的顺序生成嵌套结构:
- 数组第一个元素为字符串类型的部门名称
- 数组第二个元素为聚合生成的Level值列表
最终输出的JSON结构完全匹配你的预期:
[ [ "Dep1", [1, 2, 3] ], [ "Dep2", [1] ] ]
常见踩坑提醒
- 不要直接写
SELECT d.name, s.Level FROM ... GROUP BY d.name, s.Level,这种写法还是会把每个Level值单独生成一行,回到最初的平铺二元组结果 - 不要遗漏
DISTINCT关键字,否则聚合出来的Level列表会包含重复值,不符合去重的要求
内容的提问来源于stack exchange,提问作者Nova
相关产品推荐
相关产品推荐

