Spring Boot 3.2.4中PostgreSQL JSONb与JsonNode映射启动报错求助
问题排查与解决办法
问题根源
Spring Boot 3.2.4 依赖的是 Hibernate 6.4.x,这个版本对类型系统做了彻底重构,旧的UserType接口已经被标记为过时,而且Hibernate 6的类型解析逻辑和旧版完全不一样。你自定义的JsonNodeUserType是基于旧的UserType实现的,没法被Hibernate 6正确识别,导致系统找不到对应的JDBC类型(也就是PostgreSQL的jsonb),所以才抛出这个错误。
解决办法
方案1:用Hibernate原生JSON类型支持(优先推荐)
Hibernate 6已经原生支持把Jackson的JsonNode直接映射到PostgreSQL的jsonb列,完全不用自己写自定义类型,步骤很简单:
修改实体类的字段注解
删掉之前自定义类型的配置,改用Hibernate的@JdbcTypeCode注解指定JSON类型代码,同时保留columnDefinition确保数据库里的列是jsonb类型:import com.fasterxml.jackson.databind.JsonNode; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; @Entity public class MapEntity { @Id private Long id; @Column(columnDefinition = "jsonb") @JdbcTypeCode(SqlTypes.JSON) private JsonNode data; // 构造器、getter、setter自行补充 }清理旧的自定义类型相关代码
- 删除你编写的
JsonNodeUserType类 - 移除
application.properties里注册Hibernate类型的配置(比如类似spring.jpa.properties.hibernate.type_contributors这类配置)
- 删除你编写的
方案2:适配Hibernate 6重构自定义类型(仅需兼容旧代码时使用)
如果一定要保留自定义类型的逻辑,就得改用Hibernate 6的CompositeUserType接口来实现,示例代码如下:
实现CompositeUserType接口
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.CompositeUserType; import org.hibernate.type.SqlTypes; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Objects; public class JsonNodeUserType implements CompositeUserType<JsonNode> { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @Override public String[] getPropertyNames() { return new String[]{"data"}; } @Override public org.hibernate.type.Type[] getPropertyTypes() { return new org.hibernate.type.Type[]{org.hibernate.type.JsonType.INSTANCE}; } @Override public Object getPropertyValue(JsonNode component, int property) { return component; } @Override public void setPropertyValue(JsonNode component, int property, Object value) { // JsonNode是不可变类型,无需处理 } @Override public Class<JsonNode> returnedClass() { return JsonNode.class; } @Override public boolean equals(JsonNode x, JsonNode y) { return Objects.equals(x, y); } @Override public int hashCode(JsonNode x) { return Objects.hashCode(x); } @Override public JsonNode nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws SQLException { String json = rs.getString(names[0]); if (json == null) { return null; } try { return OBJECT_MAPPER.readTree(json); } catch (Exception e) { throw new SQLException("解析JSON失败", e); } } @Override public void nullSafeSet(PreparedStatement st, JsonNode value, int index, SharedSessionContractImplementor session) throws SQLException { if (value == null) { st.setNull(index, Types.OTHER); } else { try { String json = OBJECT_MAPPER.writeValueAsString(value); st.setObject(index, json, SqlTypes.JSON); } catch (Exception e) { throw new SQLException("序列化JSON失败", e); } } } @Override public JsonNode deepCopy(JsonNode value) { return value == null ? null : value.deepCopy(); } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(JsonNode value) { return deepCopy(value); } @Override public JsonNode assemble(Serializable cached, Object owner) { return deepCopy((JsonNode) cached); } @Override public JsonNode replace(JsonNode original, JsonNode target, Object owner) { return deepCopy(original); } }注册自定义类型
在application.properties中添加类型贡献者配置:spring.jpa.properties.hibernate.type_contributors=你的包路径.JsonNodeTypeContributor然后实现
TypeContributor接口:import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributor; import org.hibernate.service.ServiceRegistry; public class JsonNodeTypeContributor implements TypeContributor { @Override public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { typeContributions.contributeType(new JsonNodeUserType()); } }修改实体类字段注解
指定使用自定义类型:@Column(columnDefinition = "jsonb") @Type(JsonNodeUserType.class) private JsonNode data;
验证
启动应用后,尝试执行插入或查询操作,确认JsonNode数据能正常存入PostgreSQL的jsonb列,且无类型相关错误。
内容的提问来源于stack exchange,提问作者Serge
相关产品推荐
相关产品推荐

