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

如何用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做映射即可:

  1. 给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;
    }
}
  1. 或者在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:19:50