You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套类场景下如何用QueryDSL转换查询结果生成层级DTO?

解决方案

首先确认你已经生成了QueryDSL对应的Q实体类QStudent、QSubject、QGrade,按照以下步骤实现嵌套分组转换即可:

第一步:给DTO补充构造方法

QueryDSL的构造器映射需要DTO有匹配参数顺序的全参构造方法:

// 补充StudentTransient全参构造
public class StudentTransient {
  private Long studentId;
  private String name;
  private List<SubjectTansient> subjects;

  public StudentTransient(Long studentId, String name, List<SubjectTansient> subjects) {
    this.studentId = studentId;
    this.name = name;
    this.subjects = subjects;
  }
  //  getter、setter按需补充
}

// 补充SubjectTansient全参构造
public class SubjectTansient {
  private Long subjectId;
  private List<BigDecimal> grades;

  public SubjectTansient(Long subjectId, List<BigDecimal> grades) {
    this.subjectId = subjectId;
    this.grades = grades;
  }
  //  getter、setter按需补充
}

第二步:编写QueryDSL查询逻辑

核心是使用嵌套GroupBy实现两层分组,先按学生分组,再按学生下的科目分组,最后收集对应成绩:

import static com.querydsl.core.group.GroupBy.*;
import com.querydsl.jpa.impl.JPAQueryFactory;
import 你的包路径.QStudent;
import 你的包路径.QSubject;
import 你的包路径.QGrade;

// 初始化Q类
QStudent qStudent = QStudent.student;
QSubject qSubject = QSubject.subject;
QGrade qGrade = QGrade.grade;

// 注入JPA的entityManager初始化查询工厂
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);

List<StudentTransient> result = queryFactory
    .select(qStudent.id, qStudent.name, qSubject.id, qGrade.value)
    .from(qStudent)
    .leftJoin(qStudent.subjects, qSubject)
    .leftJoin(qSubject.grades, qGrade)
    // 第一层按学生ID分组,构造StudentTransient
    .transform(
        groupBy(qStudent.id).list(
            Projections.constructor(
                StudentTransient.class,
                qStudent.id,
                qStudent.name,
                // 第二层按科目ID分组,构造SubjectTansient
                groupBy(qSubject.id).list(
                    Projections.constructor(
                        SubjectTansient.class,
                        qSubject.id,
                        list(qGrade.value) // 收集当前科目下所有成绩的value字段
                    )
                )
            )
        )
    );

注意事项

  • 如果你不想给DTO加构造方法,可以把Projections.constructor替换为Projections.fields,直接按字段名映射,无需构造参数匹配:
    Projections.fields(
        StudentTransient.class,
        qStudent.id.as("studentId"), // 别名必须和DTO字段名完全一致
        qStudent.name.as("name"),
        // 内层同样替换为fields即可
        groupBy(qSubject.id).list(
            Projections.fields(
                SubjectTansient.class,
                qSubject.id.as("subjectId"),
                list(qGrade.value).as("grades")
            )
        )
    )
    
  • 关联查询的join方式可以根据业务需求调整,比如不需要无科目/无成绩的学生可以把leftJoin改成innerJoin。
  • 你示例中的SubjectTansient属于拼写错误(少了字母r,正确拼写应为SubjectTransient),如果需要修正请同步调整构造方法/字段映射的逻辑。

内容的提问来源于stack exchange,提问作者Diego Macario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 07:27:01