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

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类型:

  1. 修改父类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;
}
  1. 定义基础属性接口及子类实现:
public interface BaseProperty {}

record PropertyTypeA(String propertyA) implements BaseProperty {}
record PropertyTypeB(String propertyB) implements BaseProperty {}
  1. 修改数据库插入语句,在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 02:01:13