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

使用jOOQ将关联查询结果映射到含Set集合的DTO

jOOQ关联查询映射含集合的自定义DTO方案

针对你需要将关联查询结果映射到包含Set<Table1Record>的自定义DTO场景,下面提供两种实用的实现方案,假设关联关系为:

  • Table1与Table2是一对一关联(Table1.table2_id = Table2.id)
  • Table1与子Table1通过关联表Table3实现一对多关联(Table3.main_table1_id = Table1.id、Table3.sub_table1_id = Table1.id)
  • Table1与Table4是一对一关联(Table1.table4_id = Table4.id)

你的DTO结构示例:

public class CustomDTO {
    private Table1Record mainTable1;
    private Table2Record table2;
    private Set<Table1Record> subTable1s;
    private Table4Record table4;

    // 全参构造器、Getter/Setter
    public CustomDTO(Table1Record mainTable1, Table2Record table2, Set<Table1Record> subTable1s, Table4Record table4) {
        this.mainTable1 = mainTable1;
        this.table2 = table2;
        this.subTable1s = subTable1s;
        this.table4 = table4;
    }
}

方案一:手动分组组装(通用兼容所有数据库)

先查询所有关联数据,再按主Table1的主键分组,手动提取各关联实体并组装DTO。

1. 构建关联查询

注意给子Table1的字段加别名,避免和主Table1字段冲突:

Result<Record> result = DSL.using(configuration)
    .select(
        TABLE1.fields(),
        TABLE2.fields(),
        // 给子Table1字段加别名前缀
        TABLE1.as("SUB_TABLE1").fields().stream()
            .map(f -> f.as("sub_" + f.getName()))
            .toArray(Field[]::new),
        TABLE4.fields()
    )
    .from(TABLE1)
    .join(TABLE2).on(TABLE1.TABLE2_ID.eq(TABLE2.ID))
    .leftJoin(TABLE3).on(TABLE1.ID.eq(TABLE3.MAIN_TABLE1_ID))
    .leftJoin(TABLE1.as("SUB_TABLE1")).on(TABLE3.SUB_TABLE1_ID.eq(TABLE1.as("SUB_TABLE1").ID))
    .join(TABLE4).on(TABLE1.TABLE4_ID.eq(TABLE4.ID))
    .fetch();

2. 分组并组装DTO

// 按主Table1Record分组,聚合相同主记录的所有关联行
Map<Table1Record, List<Record>> recordGroups = result.fetchGroups(r -> r.into(TABLE1));

List<CustomDTO> dtos = new ArrayList<>();
for (Map.Entry<Table1Record, List<Record>> entry : recordGroups.entrySet()) {
    Table1Record mainTable1 = entry.getKey();
    List<Record> groupRecords = entry.getValue();

    // 提取一对一关联的Table2、Table4(取分组内第一条即可)
    Table2Record table2 = groupRecords.get(0).into(TABLE2);
    Table4Record table4 = groupRecords.get(0).into(TABLE4);

    // 提取子Table1集合,过滤空值并去重
    Set<Table1Record> subTable1s = groupRecords.stream()
        .map(r -> r.into("SUB_TABLE1", Table1Record.class))
        .filter(Objects::nonNull) // 左连接可能返回空子记录
        .collect(Collectors.toSet());

    dtos.add(new CustomDTO(mainTable1, table2, subTable1s, table4));
}

方案二:利用聚合函数嵌套映射(适合支持聚合的数据库)

如果你的数据库支持array_agg(如PostgreSQL)或类似聚合函数,可以直接在查询中聚合子记录,再映射到DTO,代码更简洁。

1. 带聚合的关联查询

Result<Record4<Table1Record, Table2Record, Set<Table1Record>, Table4Record>> result = DSL.using(configuration)
    .select(
        // 映射主Table1Record
        DSL.row(TABLE1.fields()).convertFrom(r -> r.into(Table1Record.class)),
        // 映射Table2Record
        DSL.row(TABLE2.fields()).convertFrom(r -> r.into(Table2Record.class)),
        // 聚合子Table1Record为Set
        DSL.arrayAgg(DSL.row(TABLE1.as("SUB_TABLE1").fields()))
            .filter(TABLE1.as("SUB_TABLE1").ID.isNotNull())
            .convertFrom(arr -> Arrays.stream(arr)
                .map(r -> r.into(Table1Record.class))
                .collect(Collectors.toSet())),
        // 映射Table4Record
        DSL.row(TABLE4.fields()).convertFrom(r -> r.into(Table4Record.class))
    )
    .from(TABLE1)
    .join(TABLE2).on(TABLE1.TABLE2_ID.eq(TABLE2.ID))
    .leftJoin(TABLE3).on(TABLE1.ID.eq(TABLE3.MAIN_TABLE1_ID))
    .leftJoin(TABLE1.as("SUB_TABLE1")).on(TABLE3.SUB_TABLE1_ID.eq(TABLE1.as("SUB_TABLE1").ID))
    .join(TABLE4).on(TABLE1.TABLE4_ID.eq(TABLE4.ID))
    // 按主表及一对一关联表的主键分组
    .groupBy(TABLE1.ID, TABLE2.ID, TABLE4.ID)
    .fetch();

2. 直接映射到DTO

List<CustomDTO> dtos = result.stream()
    .map(r -> new CustomDTO(
        r.value1(),
        r.value2(),
        r.value3(),
        r.value4()
    ))
    .collect(Collectors.toList());

注意事项

  • 若使用MySQL等不支持array_agg的数据库,可改用JSON_ARRAYAGG聚合为JSON数组,再通过自定义转换器将JSON转换为Set<Table1Record>。
  • 左连接场景下需过滤空子记录,避免Set中混入null。
  • 字段别名必须唯一,防止主表与子表字段冲突导致映射错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:53:17