Я просмотрел доступные статьи: 1 , 2 , 3 .
Все статьи прибавляются к следующим параметрам:
- Регистрация пользовательских
PostgreSQL95Dialect
с jsonb
типом - Реализация интерфейса
UserType
Hibernate с настраиваемым отображением - Аннотирование
Entity
с @TypeDef
из Пользовательская реализация - Определить в application.properties пользовательский диалект
Если все вышеперечисленное выполнено, код должен работать. В моем случае я сталкиваюсь с таинственным 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
, который я не понимаю, как отлаживать дальше.
Мой 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")
}
Что я делаю не так? Должен ли jsonb
пользовательский тип быть создан по-другому в Kotlin
?