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

Spring Boot Java API存储JSON数组至PostgreSQL jsonb列报错

解决R2DBC PostgreSQL jsonb字段序列化/反序列化异常

问题场景

请求对象与POJO包含JSON数组字段,需要存入PostgreSQL的jsonb类型列,请求示例如下:

{
  "productId": "1",
  "product": [
    {
      "name": "PENCIL",
      "quantity": "5"
    }
  ]
}

通过Postman调用接口时触发异常,异常信息翻译后如下:

类型定义错误: [简单类型,类 io.r2dbc.postgresql.codec.Json]; 嵌套异常为 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 无法构造io.r2dbc.postgresql.codec.Json的实例(不存在默认构造器等创建器): 抽象类型需要映射到具体类型、使用自定义反序列化器,或包含额外类型信息
来源: (io.netty.buffer.ByteBufInputStream);

异常原因

io.r2dbc.postgresql.codec.Json是抽象类,Jackson默认无法实例化抽象类——因为抽象类没有可直接调用的构造器(比如无参构造),导致接口接收请求时,Jackson无法将JSON字符串反序列化为该抽象类型的对象。

解决方案

方案1:POJO改用具体数据类型(推荐)

不在POJO中直接使用Json抽象类,改用List<自定义对象>或String类型,存入数据库时再转换为Json实例。

示例代码

  1. 定义POJO和子对象:
// 请求POJO
public class ProductRequest {
    private String productId;
    private List<ProductItem> product; // 用List替代Json抽象类

    // Getter、Setter
}

// 子对象
public class ProductItem {
    private String name;
    private String quantity;

    // Getter、Setter
}
  1. 保存时转换为Json存入数据库:
@Autowired
private ObjectMapper objectMapper;

public Mono<Void> saveProduct(ProductRequest request) {
    try {
        // 将List转为JSON字符串,再创建Json实例
        String productJsonStr = objectMapper.writeValueAsString(request.getProduct());
        Json productJson = Json.create(productJsonStr);
        
        // 构建数据库实体并保存
        ProductEntity entity = new ProductEntity(request.getProductId(), productJson);
        return productRepository.save(entity).then();
    } catch (JsonProcessingException e) {
        return Mono.error(new IllegalArgumentException("JSON转换失败", e));
    }
}

方案2:自定义Jackson序列化/反序列化器

如果必须在POJO中使用Json类型,可为其编写自定义的序列化/反序列化逻辑,让Jackson知道如何处理该抽象类。

示例代码

  1. 编写序列化器:
public class JsonSerializer extends StdSerializer<Json> {
    public JsonSerializer() {
        super(Json.class);
    }

    @Override
    public void serialize(Json value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // 将Json对象转为原始JSON字符串输出
        gen.writeRawValue(value.asString());
    }
}
  1. 编写反序列化器:
public class JsonDeserializer extends StdDeserializer<Json> {
    public JsonDeserializer() {
        super(Json.class);
    }

    @Override
    public Json deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        // 读取JSON字符串并创建Json实例
        String jsonStr = p.readValueAsTree().toString();
        return Json.create(jsonStr);
    }
}
  1. 在POJO字段上添加注解:
public class ProductRequest {
    private String productId;
    
    @JsonSerialize(using = JsonSerializer.class)
    @JsonDeserialize(using = JsonDeserializer.class)
    private Json product;

    // Getter、Setter
}

方案3:注册Spring Data R2DBC自定义转换器

通过Spring的类型转换器,自动完成List/自定义对象与Json类型的转换,无需在业务代码中手动处理。

示例代码

@Configuration
public class R2dbcConfig {
    @Bean
    public R2dbcCustomConversions r2dbcCustomConversions(ObjectMapper objectMapper) {
        List<Converter<?, ?>> converters = new ArrayList<>();

        // 自定义:List转Json
        converters.add(new Converter<List<?>, Json>() {
            @Override
            public Json convert(List<?> source) {
                try {
                    return Json.create(objectMapper.writeValueAsString(source));
                } catch (JsonProcessingException e) {
                    throw new IllegalArgumentException("List转JSON失败", e);
                }
            }
        });

        // 自定义:Json转List<ProductItem>
        converters.add(new Converter<Json, List<ProductItem>>() {
            @Override
            public List<ProductItem> convert(Json source) {
                try {
                    return objectMapper.readValue(
                        source.asString(),
                        new TypeReference<List<ProductItem>>() {}
                    );
                } catch (JsonProcessingException e) {
                    throw new IllegalArgumentException("JSON转List失败", e);
                }
            }
        });

        return R2dbcCustomConversions.of(PostgresDialect.INSTANCE, converters);
    }
}

配置完成后,POJO可直接使用List<ProductItem>类型,Spring会自动在数据库交互时完成与Json的转换。

内容的提问来源于stack exchange,提问作者developer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 16:25:09