如何在Kotlin中为PostgreSQL和Hibernate创建自定义jsonb映射器? [英] How to create custom jsonb mapper for PostgreSQL and Hibernate in Kotlin?
问题描述
I went through available articles: 1, 2, 3.
所有文章均涉及以下选项:
All of the articles nail down to the following options:
- 注册自定义
PostgreSQL95Dialect
具有jsonb
类型 - 使用自定义映射实现Hibernate的
UserType
接口 - 使用自定义实现的
@TypeDef
注释实体
- 在application.properties自定义方言中定义
- Register custom
PostgreSQL95Dialect
which hasjsonb
type - Implement Hibernate's
UserType
interface with custom mapping - Annotate
Entity
with@TypeDef
of custom implementation - Define in application.properties custom dialect
如果上述所有操作均已完成,则代码应该可以正常工作。就我而言,我遇到了一个神秘的无法建立Hibernate SessionFactory的问题。嵌套的异常是org.hibernate.MappingException:属性映射的列数错误:com.example.Book.header类型:com.example.hibernate.BookHeaderType
我不知道如何进一步调试。
If all of the above is done, code is supposed to work. In my case I bump into mysterious Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: property mapping has wrong number of columns: com.example.Book.header type: com.example.hibernate.BookHeaderType
which I don't understand how to debug further.
我的 JsonbType
抽象类:
abstract class JsonbType : UserType {
override fun hashCode(p0: Any?): Int {
return p0!!.hashCode()
}
override fun deepCopy(p0: Any?): Any {
return try {
val bos = ByteArrayOutputStream()
val oos = ObjectOutputStream(bos)
oos.writeObject(p0)
oos.flush()
oos.close()
bos.close()
val bais = ByteArrayInputStream(bos.toByteArray())
ObjectInputStream(bais).readObject()
} catch (ex: ClassNotFoundException) {
throw HibernateException(ex)
} catch (ex: IOException) {
throw HibernateException(ex)
}
}
override fun replace(p0: Any?, p1: Any?, p2: Any?): Any {
return deepCopy(p0)
}
override fun equals(p0: Any?, p1: Any?): Boolean {
return p0 == p1
}
override fun assemble(p0: Serializable?, p1: Any?): Any {
return deepCopy(p0)
}
override fun disassemble(p0: Any?): Serializable {
return deepCopy(p0) as Serializable
}
override fun nullSafeSet(p0: PreparedStatement?, p1: Any?, p2: Int, p3: SharedSessionContractImplementor?) {
if (p1 == null) {
p0?.setNull(p2, Types.OTHER)
return
}
try {
val mapper = ObjectMapper()
val w = StringWriter()
mapper.writeValue(w, p1)
w.flush()
p0?.setObject(p2, w.toString(), Types.OTHER)
} catch (ex: java.lang.Exception) {
throw RuntimeException("Failed to convert Jsonb to String: " + ex.message, ex)
}
}
override fun nullSafeGet(p0: ResultSet?, p1: Array<out String>?, p2: SharedSessionContractImplementor?, p3: Any?): Any {
val cellContent = p0?.getString(p1?.get(0))
return try {
val mapper = ObjectMapper()
mapper.readValue(cellContent?.toByteArray(charset("UTF-8")), returnedClass())
} catch (ex: Exception) {
throw RuntimeException("Failed to convert String to Jsonb: " + ex.message, ex)
}
}
override fun isMutable(): Boolean {
return true
}
override fun sqlTypes(): kotlin.IntArray? {
return IntArray(Types.JAVA_OBJECT)
}
}
我的具体类 BookHeaderType
看起来:
class BookHeaderType : JsonbType() {
override fun returnedClass(): Class<BookBody> {
return BookBody::class.java
}
}
CustomPostgreSQLDialect.kt
:
class CustomPostgreSQLDialect : PostgreSQL95Dialect {
constructor(): super() {
this.registerColumnType(Types.JAVA_OBJECT, "jsonb")
}
}
Book.kt
实体:
@Entity
@Table(name = "book")
@TypeDefs(
TypeDef(name = "BookHeaderType", typeClass = BookHeaderType::class)
)
data class Book(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(updatable = false, nullable = false)
val id: Long,
@Column(name = "header", nullable = false, columnDefinition = "jsonb")
@Type(type = "BookHeaderType")
var header: BookHeader
)
BookHeader .kt
实现序列化
@JsonIgnoreProperties(ignoreUnknown = true)
data class BookHeader(
var createdAt: OffsetDateTime,
var createdBy: String
) : Serializable {
constructor() : this(OffsetDateTime.now(), "test")
}
我该怎么办?应该在 Kotlin
中创建 jsonb
自定义类型吗?
What do I do wrong? Should jsonb
custom type be created differently in Kotlin
?
推荐答案
有一个兄弟问题,具有答案
There is a sibling question that has the answer
您需要使用自定义类型:
You would need to use custom types:
pom.xml依赖项:
pom.xml dependency:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.9.9</version>
</dependency>
注册客户PostgreSQL方言:
Register Customer PostgreSQL dialect:
class CustomPostgreSQLDialect : PostgreSQL95Dialect {
constructor() : super() {
this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonStringType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeBinaryType::class.java.name)
this.registerHibernateType(Types.OTHER, JsonNodeStringType::class.java.name)
}
}
选项1:
用<$ c注释实体$ c> jsonb 类型,并将 jsonb
设为 Map< String,Any>
:
import com.example.demo.pojo.SamplePojo
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*
@Entity
@Table(name = "tests")
@TypeDefs(
TypeDef(name = "json", typeClass = JsonStringType::class),
TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleEntity (
@Id @GeneratedValue
val id: Long?,
val name: String?,
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
var data: Map<String, Any>?
) {
/**
* Dependently on use-case this can be done differently:
* https://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
*/
constructor(): this(null, null, null)
}
您需要Pojo到Map序列化器/反序列化器。
You would need Pojo to Map serializer/deserializer.
选项2:
使用 jsonb
类型注释实体并生成 jsonb
作为 JsonNode? code>:
Annotate Entity with jsonb
type and make jsonb
as JsonNode?
:
import com.fasterxml.jackson.databind.JsonNode
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*
@Entity
@Table(name = "tests")
@TypeDefs(
TypeDef(name = "json", typeClass = JsonStringType::class),
TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleJsonNodeEntity (
@Id @GeneratedValue
val id: Long?,
val name: String?,
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
var data: JsonNode?
) {
/**
* Dependently on use-case this can be done differently:
* https://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
*/
constructor(): this(null, null, null)
}
您需要自定义POJO到JsonNode,假设是Jackson序列化器/解串器。
You would need custom POJO to JsonNode, assumingly Jackson serializer/deserializer.
摘要:
如果您的JSON具有一个缩进级别,则第一个选项更好。
1st option is better when you have a big JSON with one level of indentation.
第二个选项更好。
这篇关于如何在Kotlin中为PostgreSQL和Hibernate创建自定义jsonb映射器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!