Quarkus迁移至Hibernate Reactive时JSONB字段映射异常问题
Hibernate Reactive JSONB字段映射问题处理方案
问题本质
这不是Bug,而是Hibernate Reactive对自定义类型的处理有特殊要求。传统Hibernate ORM基于JDBC驱动,而Hibernate Reactive依赖Vert.x异步SQL客户端,两者的类型绑定、转换机制完全不同。你使用的quarkus-hibernate-types:0.2.0中的JsonBinaryType仅适配阻塞式JDBC场景,没有实现Hibernate Reactive所需的异步类型处理接口,导致参数无法被Vert.x SQL客户端正确解析。
解决方法
方案1:自定义适配Reactive的JSON类型
实现Hibernate Reactive要求的ReactiveType接口,让JSON类型能适配Vert.x的异步参数处理逻辑:
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.type.descriptor.WrapperOptions import org.hibernate.type.descriptor.java.JavaTypeDescriptor import org.hibernate.type.descriptor.sql.SqlTypeDescriptor import org.hibernate.type.descriptor.sql.ValueBinder import org.hibernate.type.descriptor.sql.ValueExtractor import org.hibernate.type.json.JsonBinaryType import org.hibernate.reactive.type.ReactiveType import java.sql.* import java.sql.Types.JAVA_OBJECT class ReactiveJsonBinaryType : JsonBinaryType(), ReactiveType<JsonNode> { private val objectMapper = ObjectMapper() override fun getReactiveJavaTypeDescriptor(): JavaTypeDescriptor<JsonNode> { return object : JavaTypeDescriptor<JsonNode> { override fun getJavaType(): Class<JsonNode> = JsonNode::class.java override fun unwrap(value: JsonNode, context: WrapperOptions): Any { // 将JsonNode转为Vert.x能处理的字符串 return objectMapper.writeValueAsString(value) } override fun wrap(value: Any, context: WrapperOptions): JsonNode { // 将数据库返回的对象转回JsonNode return objectMapper.readTree(value.toString()) } } } override fun getReactiveSqlTypeDescriptor(): SqlTypeDescriptor { return object : SqlTypeDescriptor { override fun getSqlType(): Int = JAVA_OBJECT override fun canBeRemapped(): Boolean = true override fun getBinder(bindingType: BindingType): ValueBinder<*> { return object : ValueBinder<JsonNode> { override fun bind( st: PreparedStatement, value: JsonNode, index: Int, options: WrapperOptions ) { st.setObject(index, unwrap(value, options), Types.JSON) } override fun bind( st: CallableStatement, value: JsonNode, name: String, options: WrapperOptions ) { st.setObject(name, unwrap(value, options), Types.JSON) } } } override fun getExtractor(extractionType: ExtractionType): ValueExtractor<*> { return object : ValueExtractor<JsonNode> { override fun extract( rs: ResultSet, name: String, options: WrapperOptions ): JsonNode? { val value = rs.getObject(name) return value?.let { wrap(it, options) } } override fun extract( rs: ResultSet, index: Int, options: WrapperOptions ): JsonNode? { val value = rs.getObject(index) return value?.let { wrap(it, options) } } override fun extract( cs: CallableStatement, index: Int, options: WrapperOptions ): JsonNode? { val value = cs.getObject(index) return value?.let { wrap(it, options) } } override fun extract( cs: CallableStatement, name: String, options: WrapperOptions ): JsonNode? { val value = cs.getObject(name) return value?.let { wrap(it, options) } } } } } } }
更新实体类的类型定义:
@Entity @TypeDef(name = JsonTypes.JSON_BIN, typeClass = ReactiveJsonBinaryType::class) class MyEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "myEntityIdGenerator") @SequenceGenerator(name = "myEntityIdGenerator", sequenceName = "my_entity_id_seq", allocationSize = 10) var id: Long? = null @Type(type = JsonTypes.JSON_BIN) @NotNull lateinit var jsonData: JsonNode }
方案2:利用Hibernate Reactive原生JSON支持(PostgreSQL场景)
如果你的数据库是PostgreSQL,可以直接依赖Hibernate Reactive对JSONB的原生支持,无需第三方类型库:
- 调整实体类字段标注:
@Entity class MyEntity { // ... 其他字段 @Column(columnDefinition = "jsonb") @NotNull lateinit var jsonData: JsonNode }
- 确保项目依赖
quarkus-hibernate-reactive和对应数据库驱动扩展(如quarkus-reactive-pg-client),Hibernate Reactive会自动通过Jackson处理JsonNode的序列化与反序列化。
总结
Hibernate Reactive的异步模型与传统ORM的阻塞JDBC模型差异较大,自定义类型必须实现ReactiveType接口才能适配其底层的Vert.x SQL客户端。你遇到的问题属于场景适配问题,而非Bug。
内容的提问来源于stack exchange,提问作者Ultranium
相关产品推荐
相关产品推荐

