Как создать собственный jsonb mapper для PostgreSQL и Hibernate в Kotlin? - PullRequest
0 голосов
/ 29 января 2020

Я просмотрел доступные статьи: 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?

...