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
相关产品推荐
相关产品推荐

