如何用@JdbcTypeCode将PostgreSQL JSONB列转为Java的MultiValueMap
问题
从PostgreSQL数据库读取JSONB列时,使用@JdbcTypeCode(SqlTypes.JSON)注解实现类型转换,自定义实体类CustomClass字段转换正常,但MultiValueMap<String, String>类型的headers字段报错:
org.springframework.dao.InvalidDataAccessApiUsageException: Could not deserialize string to java type: JsonJavaType(org.springframework.util.MultiValueMap<java.lang.String, java.lang.String>)
实体类代码如下:
@Table(name = "test") @Data @AllArgsConstructor @NoArgsConstructor @Builder public class TestEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", columnDefinition = "INT") private Integer id; @JdbcTypeCode(SqlTypes.JSON) @Column(name = "custom", columnDefinition = "JSONB") private CustomClass custom; @JdbcTypeCode(SqlTypes.JSON) @Column(name = "headers", columnDefinition = "JSONB") private MultiValueMap<String, String> headers; }
此前通过实现AttributeConverter接口编写自定义转换器配合@Convert注解可正常转换,但希望通过@JdbcTypeCode或类似注解实现自动转换,无需手动编写转换器。
解决方案
1. 替换为具体的MultiValueMap实现类
MultiValueMap是接口,Hibernate的JSON转换器无法自动推断要实例化的实现类,直接将字段类型改为LinkedMultiValueMap(Spring提供的默认实现)即可:
@JdbcTypeCode(SqlTypes.JSON) @Column(name = "headers", columnDefinition = "JSONB") private LinkedMultiValueMap<String, String> headers;
2. 配置Jackson支持MultiValueMap接口
如果不想修改字段类型,需要让Jackson识别MultiValueMap接口,可通过以下两种方式实现:
方式一:注册Spring Jackson模块到全局ObjectMapper
在Spring Boot中配置全局ObjectMapper,注册Spring提供的SpringModule,该模块包含了MultiValueMap的序列化/反序列化逻辑:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new SpringModule()); return objectMapper; } }
配置后,原实体类的@JdbcTypeCode(SqlTypes.JSON)注解即可正常工作。
方式二:使用@JsonDeserialize指定实现类
在字段上添加@JsonDeserialize注解,指定MultiValueMap的具体实现类,同时确保Spring Jackson模块已引入(Spring Boot默认已包含):
@JdbcTypeCode(SqlTypes.JSON) @Column(name = "headers", columnDefinition = "JSONB") @JsonDeserialize(as = LinkedMultiValueMap.class) private MultiValueMap<String, String> headers;
3. 自定义Hibernate JsonType
如果需要更精细的控制,可以自定义Hibernate的JsonType,传入带有Spring模块的ObjectMapper:
public class SpringJsonType extends JsonType { public SpringJsonType() { super( JsonJavaType.of(MultiValueMap.class, JsonTypeFactory.instance.constructType(MultiValueMap.class)), new ObjectMapper().registerModule(new SpringModule()) ); } }
然后在实体类字段上使用自定义类型:
@Type(SpringJsonType.class) @Column(name = "headers", columnDefinition = "JSONB") private MultiValueMap<String, String> headers;
内容的提问来源于stack exchange,提问作者user27316257

