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

使用jOOQ Multiset Select映射一对多关系时触发MappingException

解决jOOQ Multiset映射POJO的Converter异常问题

问题原因

你遇到的DataTypeException: No Converter found for types org.jooq.Converters$UnknownType and java.util.List异常,本质是jOOQ 3.15.x版本的默认RecordMapper无法自动识别Multiset查询转换后的List类型,导致无法将taskDates字段映射到TaskDetails的List<TaskDate>属性。

解决方案

针对jOOQ 3.15.1版本,有两种可行方案:

方案1:手动指定整体映射逻辑(推荐,无需升级版本)

放弃使用fetchOneInto(TaskDetails.class)的自动映射,改用fetchOne(mapping(...))手动绑定查询字段到TaskDetails的Builder,绕过自动映射器的缺陷。

修改后的查询代码如下:

public Optional<TaskDetails> findTaskDetailsById(UUID taskId) {
    return dslContext.select(
                    TASK.ID,
                    TASK.TENANT_ID,
                    TASK.TITLE,
                    TASK.DESCRIPTION,
                    TASK.CREATED_AT,
                    TASK.PRIORITY,
                    TASK.NUMERIC_ID,
                    TASK.ASSIGNEE_ID,
                    TASK.STATUS_ID,
                    TASK.REPORTER_ID,
                    TASK.DELETED,
                    TASK.MODIFIED_AT,
                    TASK.MODIFIED_BY,
                    multiset(
                        select(TASK_DATE.ID,
                               TASK_DATE.TASK_ID,
                               TASK_DATE.TYPE,
                               TASK_DATE.DATE,
                               TASK_DATE.CREATED_AT,
                               TASK_DATE.CREATED_BY,
                               TASK_DATE.MODIFIED_BY,
                               TASK_DATE.MODIFIED_AT)
                        .from(TASK_DATE)
                        .where(TASK_DATE.TASK_ID.eq(TASK.ID))
                    ).convertFrom(r -> r.map(mapping(this::mapTaskDate)))
            )
            .from(TASK)
            .where(TASK.ID.eq(taskId)) // 单个UUID用eq替代in更合理
            .fetchOne(mapping(
                (id, tenantId, title, description, createdAt, priority, numericId, assigneeId, statusId, reporterId, deleted, modifiedAt, modifiedBy, taskDates) -> 
                    TaskDetails.builder()
                        .id(id)
                        .tenantId(tenantId)
                        .title(title)
                        .description(description)
                        .createdAt(createdAt)
                        .priority(priority)
                        .numericId(numericId)
                        .assigneeId(assigneeId)
                        .statusId(statusId)
                        .reporterId(reporterId)
                        .deleted(deleted)
                        .modifiedAt(modifiedAt)
                        .modifiedBy(modifiedBy)
                        .taskDates(taskDates)
                        .build()
            ));
}

private TaskDate mapTaskDate(UUID id, UUID taskId, String type, LocalDate taskDate, LocalDateTime createdAt, String createdBy, String modifiedBy, LocalDateTime modifiedAt) {
    return TaskDate.builder()
            .id(id)
            .taskId(taskId)
            .type(type)
            .date(taskDate)
            .createdAt(createdAt)
            .createdBy(createdBy)
            .modifiedBy(modifiedBy)
            .modifiedAt(modifiedAt)
            .build();
}

方案2:升级jOOQ版本到3.16+

jOOQ 3.16版本开始对Multiset的自动映射做了大幅优化,默认RecordMapper可以正确识别Multiset转换后的List类型。升级后,你原有的fetchOneInto(TaskDetails.class)代码可以直接使用(注意确保multiset的convertFrom逻辑正确)。

升级时需要同步调整spring-boot-starter-jooq的依赖版本,确保和jOOQ版本兼容(Spring Boot 2.6.x可以兼容jOOQ 3.16.x)。

关键细节说明

  • 原代码中TASK.ID.in(taskId):因为taskId是单个UUID,使用eq替代in更符合语义,也能避免不必要的查询开销。
  • multiset的convertFrom逻辑:你原有的r.map(mapping(this::mapTaskDate))是正确的,它负责将子查询的记录转换为TaskDate对象列表,问题出在顶层的自动映射无法识别这个列表类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:05:36