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

Quarkus Panache Reactive问题:Postgres NULL JSONB列无法映射为JsonArray

解决Postgres JSONB空值映射到Hibernate JsonArray字段的报错问题

当Postgres表的JSONB列为NULL时,使用@JdbcTypeCode(SqlTypes.JSON_ARRAY)映射的JsonArray字段会抛出Cannot set Tuple$1 to: [my.package.Class.myColumn] (setter) of type JsonArray错误,核心原因是Hibernate的JSON_ARRAY类型处理器未正确将数据库空值映射为Java的null,而是返回了空Tuple对象。

以下是几种可行的解决方案:

解决方案1:改用JsonNode类型(最简便)

将字段类型从JsonArray改为JsonNode,并调整@JdbcTypeCode为SqlTypes.JSON。JsonNode可以自然兼容null值,同时支持JSON数组和对象的映射:

@Column(name = "my_column")
@JdbcTypeCode(SqlTypes.JSON)
private JsonNode myColumn;

如果业务中需要将JsonNode转为JsonArray,可在代码中强制转换(确保字段实际存储的是数组时):

JsonArray jsonArray = (JsonArray) myColumn;

解决方案2:自定义类型处理器处理空值

如果必须保留JsonArray类型,可以自定义Hibernate类型处理器,显式处理数据库空值返回Java null:

1. 实现自定义类型相关类

// 处理SQL层面的空值映射
public class JsonArraySqlTypeDescriptor implements SqlTypeDescriptor {
    public static final JsonArraySqlTypeDescriptor INSTANCE = new JsonArraySqlTypeDescriptor();

    @Override
    public int getSqlType() {
        return Types.OTHER;
    }

    @Override
    public boolean canBeRemapped() {
        return true;
    }

    @Override
    public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicBinder<>(javaTypeDescriptor, this) {
            @Override
            protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                if (value == null) {
                    st.setNull(index, Types.OTHER);
                } else {
                    st.setObject(index, javaTypeDescriptor.unwrap(value, String.class, options), Types.OTHER);
                }
            }
        };
    }

    @Override
    public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
        return new BasicExtractor<>(javaTypeDescriptor, this) {
            @Override
            protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                Object object = rs.getObject(name);
                if (rs.wasNull() || object == null) {
                    return null;
                }
                return javaTypeDescriptor.wrap(object.toString(), options);
            }
        };
    }
}

// 处理Java层面的JsonArray转换
public class JsonArrayJavaTypeDescriptor extends AbstractTypeDescriptor<JsonArray> {
    public static final JsonArrayJavaTypeDescriptor INSTANCE = new JsonArrayJavaTypeDescriptor();

    public JsonArrayJavaTypeDescriptor() {
        super(JsonArray.class, ImmutableMutabilityPlan.INSTANCE);
    }

    @Override
    public String toString(JsonArray value) {
        return value.toString();
    }

    @Override
    public JsonArray fromString(String string) {
        // 此处以Jakarta JSON API为例,可根据实际使用的JSON库调整解析逻辑
        return Json.createReader(new StringReader(string)).readArray();
    }

    @Override
    public <X> X unwrap(JsonArray value, Class<X> type, WrapperOptions options) {
        if (value == null) {
            return null;
        }
        if (String.class.isAssignableFrom(type)) {
            return (X) value.toString();
        }
        throw unknownUnwrap(type);
    }

    @Override
    public <X> JsonArray wrap(X value, WrapperOptions options) {
        if (value == null) {
            return null;
        }
        if (String.class.isInstance(value)) {
            return fromString((String) value);
        }
        throw unknownWrap(value.getClass());
    }
}

// 自定义Hibernate类型
public class NullableJsonArrayType extends AbstractSingleColumnStandardBasicType<JsonArray> {
    public NullableJsonArrayType() {
        super(JsonArraySqlTypeDescriptor.INSTANCE, JsonArrayJavaTypeDescriptor.INSTANCE);
    }

    @Override
    public String getName() {
        return "nullable-json-array";
    }
}

2. 在实体字段上使用自定义类型

@Column(name = "my_column")
@Type(type = "my.package.NullableJsonArrayType")
private JsonArray myColumn;

解决方案3:添加@Nullable注解(部分场景有效)

尝试为字段添加jakarta.annotation.Nullable注解,明确告知Hibernate该字段允许为null:

@Column(name = "my_column")
@JdbcTypeCode(SqlTypes.JSON_ARRAY)
@Nullable
private JsonArray myColumn;

注意:该方法的有效性取决于Hibernate版本和Quarkus Panache的实现,部分场景可能无法解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:17:04