使用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
相关产品推荐
相关产品推荐

