如何通过Lateral Join与arrayAgg()将Long[]字段映射至POJO?
如何用Lateral Join结合arrayAgg()将Long[]字段映射到自定义POJO?
问题代码示例
查询方法:
public Optional<TeacherWithStudents> getTeachersWithStudentsForStudentId(long studentId) { return ctx().select( TEACHER.ID, TEACHER.NAME, DSL.field("student.ids", STUDENT_TEACHER_MAPPING.STUDENT_ID.getDataType().getArrayType())) .from(TEACHER) .leftJoin(DSL.lateral( DSL.select(DSL.arrayAgg(STUDENT_TEACHER_MAPPING.STUDENT_ID).as("ids")) .from(STUDENT_TEACHER_MAPPING) .where(STUDENT_TEACHER_MAPPING.TEACHER_ID.eq(TEACHER.ID))).as("student")) .on(DSL.condition(true)) .limit(1) .fetchOptionalInto(TeacherWithStudents.class); }
POJO类:
@Data @AllArgsConstructor public class TeacherWithStudents { long id; String name; Long[] ids; }
已尝试的方案
- 向
DSL.field传入Long[].class,映射到jOOQ Record有效,但无法映射到POJO,报错:org.jooq.exception.MappingException: An error occurred when mapping record to class [Ljava.lang.Long; - 用
DSL.jsonArrayAgg替代arrayAgg,结果得到仅含一个null值的数组[null] - 使用
multiset可行,但需重复关联子查询以满足WHERE条件,不够优雅 - 尝试多种自定义转换器均未生效
- 先获取Record再用Jackson转POJO,可行但效率低下
解决方法
方案1:调整POJO映射规则并明确字段类型
jOOQ对数组类型的POJO映射更适配无参构造器+Setter模式,同时需确保字段类型与SQL数组严格匹配:
- 修改POJO,显式添加无参构造器(配合Lombok注解):
@Data @NoArgsConstructor @AllArgsConstructor public class TeacherWithStudents { long id; String name; Long[] ids; }
- 查询中使用jOOQ原生的数组DataType,避免直接传入
Long[].class:
DataType<Long[]> longArrayType = STUDENT_TEACHER_MAPPING.STUDENT_ID.getDataType().getArrayType(); return ctx().select( TEACHER.ID, TEACHER.NAME, DSL.field("student.ids", longArrayType)) // 其余查询逻辑不变 .fetchOptionalInto(TeacherWithStudents.class);
方案2:自定义数组类型Converter
如果默认转换逻辑不兼容数据库数组实现,可自定义Converter处理SQL数组到Java Long[]的转换:
- 实现jOOQ的
Converter接口(以PostgreSQL为例):
public class SqlArrayToLongArrayConverter implements Converter<Object, Long[]> { @Override public Long[] from(Object databaseObject) { if (databaseObject == null) return new Long[0]; if (databaseObject instanceof PGArray) { try { return (Long[]) ((PGArray) databaseObject).getArray(); } catch (SQLException e) { throw new RuntimeException(e); } } return (Long[]) databaseObject; } @Override public Object to(Long[] userObject) { return userObject; } @Override public Class<Object> fromType() { return Object.class; } @Override public Class<Long[]> toType() { return Long[].class; } }
- 在查询中绑定该Converter:
DataType<Long[]> longArrayType = SQLDataType.BIGINT.getArrayDataType().converter(new SqlArrayToLongArrayConverter()); return ctx().select( TEACHER.ID, TEACHER.NAME, DSL.field("student.ids", longArrayType)) // 其余查询逻辑不变 .fetchOptionalInto(TeacherWithStudents.class);
方案3:用RecordMapper手动映射(高效灵活)
如果上述方案仍不生效,可直接通过Record手动映射,避免POJO自动映射的兼容性问题:
return ctx().select( TEACHER.ID, TEACHER.NAME, DSL.field("student.ids", STUDENT_TEACHER_MAPPING.STUDENT_ID.getDataType().getArrayType())) // 其余查询逻辑不变 .fetchOptional(record -> new TeacherWithStudents( record.get(TEACHER.ID), record.get(TEACHER.NAME), record.get(DSL.field("student.ids", Long[].class)) ));
内容的提问来源于stack exchange,提问作者torta-frita
相关产品推荐
相关产品推荐

