如何自动将jOOQ记录映射到Java Records?附Sakila库示例
问题背景与需求
本文采用Sakila示例数据库,假设已定义如下Film类型的Java Record:
public record Film(UShort filmId, String title) { }
从jOOQ读取影片数据时,可通过以下方式轻松映射到Film:
List<Film> films = this.context.select(Tables.FILM.FILM_ID, Tables.FILM.TITLE) .from(Tables.FILM) .limit(10) .fetch(Records.mapping(Film::new));
若要查询影片及其关联演员,可使用multiset实现:
Set<SelectField<?>> readFields = Set.of( Tables.FILM.FILM_ID, Tables.FILM.TITLE, DSL.multiset( DSL.select(Tables.FILM_ACTOR.actor().FIRST_NAME, Tables.FILM_ACTOR.actor().LAST_NAME) .from(Tables.FILM_ACTOR) .where(Tables.FILM_ACTOR.FILM_ID.eq(Tables.FILM.FILM_ID)) ) .as("actors") ); var result = this.context.select(readFields) .from(Tables.FILM) .limit(10) .fetch();
现在希望将上述查询结果映射到如下FilmActors类型的Java Record:
public record FilmActors(UShort filmId, String title, List<Actor> actors) { public record Actor(String firstName, String lastName) { } }
请问是否可自动完成该映射,或借助ModelMapper这类库实现?我希望尽量避免自定义RecordMapper。此前已尝试过jOOQ内置的Records类,并确认ModelMapper是否支持Records。
可行方案
一、jOOQ内置映射(无需自定义RecordMapper)
jOOQ的Records.mapping()支持嵌套映射,直接调整查询代码即可完成转换,无需额外依赖:
List<FilmActors> result = this.context.select( Tables.FILM.FILM_ID, Tables.FILM.TITLE, DSL.multiset( DSL.select( Tables.FILM_ACTOR.actor().FIRST_NAME, Tables.FILM_ACTOR.actor().LAST_NAME ) .from(Tables.FILM_ACTOR) .where(Tables.FILM_ACTOR.FILM_ID.eq(Tables.FILM.FILM_ID)) ).convertFrom(r -> r.map(Records.mapping(FilmActors.Actor::new))) ) .from(Tables.FILM) .limit(10) .fetch(Records.mapping(FilmActors::new));
关键改动说明:
- 对
multiset的查询结果调用convertFrom(),通过Records.mapping(FilmActors.Actor::new)将内部记录批量转换为Actor列表 - 外层直接用
Records.mapping(FilmActors::new)完成整体映射,全程无需自定义RecordMapper
二、借助ModelMapper实现映射
ModelMapper 2.4.0及以上版本原生支持Java Record映射,步骤如下:
- 配置ModelMapper(字段名完全匹配时可省略显式映射):
ModelMapper modelMapper = new ModelMapper(); // 显式映射确保字段匹配准确 modelMapper.typeMap(Record3.class, FilmActors.class) .addMapping(r -> r.value1(), FilmActors::filmId) .addMapping(r -> r.value2(), FilmActors::title) .addMapping(r -> r.value3(), FilmActors::actors); modelMapper.typeMap(Record2.class, FilmActors.Actor.class) .addMapping(r -> r.value1(), FilmActors.Actor::firstName) .addMapping(r -> r.value2(), FilmActors.Actor::lastName);
- 执行查询与映射:
var jooqResult = this.context.select(readFields) .from(Tables.FILM) .limit(10) .fetch(); List<FilmActors> filmActorsList = jooqResult.stream() .map(record -> modelMapper.map(record, FilmActors.class)) .collect(Collectors.toList());
内容的提问来源于stack exchange,提问作者JavaStan
相关产品推荐
相关产品推荐

