如何使用Hibernate向H2数据库插入JSON数据并兼容PostgreSQL的jsonb类型?
H2内存数据库兼容PostgreSQL jsonb类型JPA转换问题解决方案
根因分析
- PostgreSQL的JDBC驱动会自动将
jsonb类型的返回值转换为字符串,匹配你自定义Jackson Converter的入参类型,因此生产环境运行正常 - 将
jsonb映射为H2的JSON类型后,H2查询返回的是内置的org.h2.value.ValueJson类型对象,不是字符串,Converter反序列化时会抛出类型转换异常
可选修复方案
方案1:调整Converter兼容多类型返回值(最简改动,无额外依赖)
直接修改自定义Converter的读取逻辑,兼容PostgreSQL返回的字符串和H2返回的ValueJson类型即可,生产环境逻辑不受影响:
import org.h2.value.ValueJson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; @Converter public class PersonalInfoConverter implements AttributeConverter<PersonalInfo, String> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(PersonalInfo attribute) { try { return objectMapper.writeValueAsString(attribute); } catch (JsonProcessingException e) { throw new IllegalArgumentException("PersonalInfo序列化失败", e); } } // 将入参类型从String修改为Object,兼容不同数据库返回类型 @Override public PersonalInfo convertToEntityAttribute(Object dbData) { if (dbData == null) { return null; } String jsonContent; if (dbData instanceof ValueJson) { jsonContent = ((ValueJson) dbData).getString(); } else if (dbData instanceof String) { jsonContent = (String) dbData; } else { throw new IllegalArgumentException("不支持的数据库返回类型:" + dbData.getClass().getName()); } try { return objectMapper.readValue(jsonContent, PersonalInfo.class); } catch (JsonProcessingException e) { throw new IllegalArgumentException("PersonalInfo反序列化失败", e); } } }
方案2:使用Hibernate JSON类型扩展(规范方案,适合多JSON字段场景)
如果项目中存在大量JSON类型字段,推荐引入Hypersistence Utils库(原Hibernate Types),自动适配多数据库的JSON类型,不需要单独写Converter:
- 引入对应版本的依赖,以Hibernate 6.2为例,Maven配置如下:
<dependency> <groupId>io.hypersistence</groupId> <artifactId>hypersistence-utils-hibernate-62</artifactId> <version>3.7.0</version> </dependency>
- 修改实体类注解,替换自定义Converter:
import io.hypersistence.utils.hibernate.type.json.JsonType; import org.hibernate.annotations.Type; import jakarta.persistence.*; @Entity public class Record { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(columnDefinition = "jsonb") @Type(JsonType.class) private PersonalInfo personalInfo; // 省略构造方法、Getter/Setter }
- 保留
jsonb映射为H2JSON类型的别名配置即可,Hibernate会自动处理两种数据库的JSON序列化/反序列化逻辑。
方案3:保留现有text别名配置(临时方案)
你当前使用的CREATE TYPE "JSONB" as text;方案可以正常使用,H2会将JSON内容作为字符串存储和返回,和PostgreSQL行为一致,适合快速验证测试逻辑使用,缺点是H2不会对写入内容做JSON格式校验。
内容的提问来源于stack exchange,提问作者Psycho Punch
相关产品推荐
相关产品推荐

