如何用JOOQ将JSONB数组映射为Java POJO列表?
问题描述
我有一个存储JSONB数组数据的数据库列,数据格式如下:
[ { "date": X1, "value": "Y1" }, { "date": X2, "value": "Y2" }, { "date": X3, "value": "Y3" } ]
希望将其提取并转换为以下POJO的列表:
public record Point(LocalDate date, Double value)
当前我用ObjectMapper手动转换的实现如下:
public Mono<List<Point>> getPoints(int id) { return Mono.from( dsl.select(MY_TABLE.POINTS) .from(MY_TABLE) .where(MY_TABLE.ID.eq(id))) .map(record -> { try { return Arrays.stream(objectMapper.readValue(record.component1().data(), Point[].class)).toList(); } catch (JsonProcessingException e) { throw new RuntimeException(e); } }); }
想找更简洁的方案,比如用JOOQ默认映射器直接把记录转成Point列表。尝试创建了PointWrapper:
public record PointWrapper(List<Point> points)
然后用.map(record -> record.into(PointWrapper.class)),但得到的是LinkedHashMap列表,运行时提取元素会触发ClassCastException。
解决方案
方法1:简化ObjectMapper直接映射
不用数组转列表,直接用TypeReference指定List<Point>类型,代码更简洁:
public Mono<List<Point>> getPoints(int id) { return Mono.from(dsl.select(MY_TABLE.POINTS) .from(MY_TABLE) .where(MY_TABLE.ID.eq(id))) .map(record -> { try { JSONB pointsJson = record.get(MY_TABLE.POINTS); return objectMapper.readValue(pointsJson.data(), new TypeReference<List<Point>>() {}); } catch (JsonProcessingException e) { throw new RuntimeException(e); } }); }
方法2:修复PointWrapper的映射问题
你之前的问题是JOOQ默认不知道如何解析嵌套的Point对象,给PointWrapper配置Jackson注解,或者让JOOQ用你的ObjectMapper做映射即可:
- 给
PointWrapper添加Jackson注解:
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public record PointWrapper(List<Point> points) { @JsonCreator public PointWrapper(@JsonProperty("points") List<Point> points) { this.points = points; } }
- 或者在JOOQ配置中指定使用自定义ObjectMapper:
DSLContext dsl = DSL.using(configuration.set(new DefaultRecordMapperProvider( new JacksonRecordMapperProvider(objectMapper) )));
之后再调用record.into(PointWrapper.class)就能得到正确的List<Point>了。
方法3:用JOOQ原生JSONB操作直接映射
直接在SQL层面完成JSON到Point的转换,完全避免手动解析:
public Mono<List<Point>> getPoints(int id) { return Mono.from(dsl.select( jsonbArrayAgg( jsonbObject( key("date").value(MY_TABLE.POINTS.value("date").cast(LocalDate.class)), key("value").value(MY_TABLE.POINTS.value("value").cast(Double.class)) ).mapping(Point.class) ) ) .from(MY_TABLE) .where(MY_TABLE.ID.eq(id))) .map(record -> record.get(0, new TypeReference<List<Point>>() {})); }
内容的提问来源于stack exchange,提问作者SGiux
相关产品推荐
相关产品推荐

