Hibernate 6单表继承JSONB字段反序列化异常求助
Hibernate 6.1.5.Final单表继承JSON字段反序列化异常问题
升级至Hibernate 6.1.5.Final版本后,单表继承结构下查询子类EntityB时抛出JSON反序列化异常,EntityA查询正常。具体场景如下:
代码结构
父类CommonEntity
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "table_test") @DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) public abstract class CommonEntity { @Id protected Long id; }
子类EntityA
@Data @Entity @DiscriminatorValue("A") public class EntityA extends CommonEntity { @JdbcTypeCode(SqlTypes.JSON) @Column(name = "property", columnDefinition = "jsonb") private PropertyTypeA property; record PropertyTypeA(String propertyA){} }
子类EntityB
@Data @Entity @DiscriminatorValue("B") public class EntityB extends CommonEntity { @JdbcTypeCode(SqlTypes.JSON) @Column(name = "property", columnDefinition = "jsonb") private PropertyTypeB property; record PropertyTypeB(String propertyB){} }
Repository
@Repository public interface CommonEntityRepository extends JpaRepository<CommonEntity, Long> { }
测试代码
@Autowired CommonEntityRepository repository; @Test void findEntities() { repository.findById(1L).ifPresent(System.out::println); // 正常运行 repository.findById(2L).ifPresent(System.out::println); // 抛出异常 }
数据库插入语句
INSERT INTO public.table_test (id, type, property) VALUES( '1', 'A', '{ "propertyA": "valueA" }'::jsonb); INSERT INTO public.table_test (id, type, property) VALUES( '2', 'B', '{ "propertyB": "valueB" }'::jsonb);
抛出的异常信息
Unable to extract JDBC value for position `3` org.springframework.orm.jpa.JpaSystemException: Unable to extract JDBC value for position `3` Caused by: java.lang.IllegalArgumentException: Could not deserialize string to java type: BasicJavaType(com.example.jpa.model.EntityA$PropertyTypeA) at org.hibernate.type.jackson.JacksonJsonFormatMapper.fromString(JacksonJsonFormatMapper.java:42) at org.hibernate.type.descriptor.jdbc.JsonJdbcType$2.getObject(JsonJdbcType.java:101) at org.hibernate.type.descriptor.jdbc.JsonJdbcType$2.doExtract(JsonJdbcType.java:84) at org.hibernate.type.descriptor.jdbc.BasicExtractor.extract(BasicExtractor.java:44) at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.readCurrentRowValues(JdbcValuesResultSetImpl.java:298) ... 151 more Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "propertyB" (class com.example.jpa.model.EntityA$PropertyTypeA), not marked as ignorable (one known property: "propertyA"]) at [Source: (String)"{"propertyB": "valueB"}"; line: 1, column: 24] (through reference chain: com.example.jpa.model.EntityA$PropertyTypeA["propertyB"])
问题原因
Hibernate处理单表继承结果集时,会先尝试按父类关联的字段元数据解析所有列,再根据鉴别器列判断具体子类。但此处两个子类的property字段映射到同一数据库列,Hibernate错误地用EntityA的PropertyTypeA去反序列化EntityB行的JSON数据,导致Jackson因识别到未定义属性而抛出异常。
解决方案
方案1:父类定义多态JSON字段(推荐)
将property字段移到父类,通过Jackson的多态反序列化注解区分不同子类的JSON类型:
- 修改父类
CommonEntity:
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "table_test") @DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) public abstract class CommonEntity { @Id protected Long id; @JdbcTypeCode(SqlTypes.JSON) @Column(name = "property", columnDefinition = "jsonb") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type") @JsonSubTypes({ @JsonSubTypes.Type(value = PropertyTypeA.class, name = "A"), @JsonSubTypes.Type(value = PropertyTypeB.class, name = "B") }) protected BaseProperty property; }
- 定义基础属性接口及子类实现:
public interface BaseProperty {} record PropertyTypeA(String propertyA) implements BaseProperty {} record PropertyTypeB(String propertyB) implements BaseProperty {}
- 修改数据库插入语句,在JSON中加入
@type标记字段:
INSERT INTO public.table_test (id, type, property) VALUES( '1', 'A', '{ "@type": "A", "propertyA": "valueA" }'::jsonb); INSERT INTO public.table_test (id, type, property) VALUES( '2', 'B', '{ "@type": "B", "propertyB": "valueB" }'::jsonb);
方案2:显式指定子类字段的类型处理
如果无法修改父类结构,可以尝试为每个子类的property字段显式指定Hibernate类型,确保反序列化时使用正确的类型:
@Data @Entity @DiscriminatorValue("B") public class EntityB extends CommonEntity { @JdbcTypeCode(SqlTypes.JSON) @Column(name = "property", columnDefinition = "jsonb") @Type(JsonType.class) // 显式指定类型,确保Hibernate使用正确的反序列化器 private PropertyTypeB property; record PropertyTypeB(String propertyB){} }
注:此方案可能因Hibernate的结果集解析逻辑仍存在问题,不如方案1可靠。
内容的提问来源于stack exchange,提问作者San Matus
相关产品推荐
相关产品推荐

