Wednesday, September 14, 2016

Hibernate JSON Types in Kotlin: A TypeCasting puzzler and generic collections

Trying to persist JSON types in Postgresql using Hibernate I came across the excellent post from Vlad Mihalcea How to map JSON objects using generic Hibernate Types.

I just wanted to have it as Kotlin code, as mostly all of my project is coded in Kotlin. So I used the Java-to-Kotlin conversion which as usual left me with some puzzlers, one of them I'd like to mention:

class JsonBinarySqlTypeDescriptor : AbstractJsonSqlTypeDescriptor() {

    override fun <X : Any> getBinder(
            javaTypeDescriptor: JavaTypeDescriptor<X>): ValueBinder<X> {

        return object : BasicBinder<X>(javaTypeDescriptor, this) {
            @Throws(SQLException::class)
            override fun doBind(
                    st: PreparedStatement,
                    value: X,
                    index: Int,
                    options: WrapperOptions) {
                st.setObject(index,
                        javaTypeDescriptor.unwrap<X>(
                                value,
                                (JsonNode::class.java as Class<X>),
                                options),
                        getSqlType())
            }

The most puzzling research was up, when it came to passing the JsonNode type to

protected abstract void doBind(PreparedStatement st, J value, ...
in BasicBinder. I had to cast it to Class<X>, it's still an unchecked conversion, but at last it works.


Ome thing I would wish I had a starting idea for is how to persist generic collections,  Let's start with passing in the TypeReference Jackson needs as a Hibernate Type Parameter (String only...)

private lateinit var jsonObjectClass: Class<*>
private var jsonTypeReference: TypeReference<*>? = null
override fun setParameterValues(parameters: Properties) {
    jsonObjectClass = (parameters.get(
            DynamicParameterizedType.PARAMETER_TYPE)

            as DynamicParameterizedType.ParameterType)
            .getReturnedClass()
    val jsonTypeParam = parameters.get("json.typereference") as String?
    if (jsonTypeParam != null) {
        jsonTypeReference = Class.forName(jsonTypeParam)
                .getConstructor().newInstance() as TypeReference<*>
    }
}

Now, when we set this on a Collection like this:

@Type(type = "jsonb",
 parameters = arrayOf(Parameter(name = "json.typereference",
 value = "de.eiswind.xino.datalayer.entities.PermissionTypeReference")))
var permissions: MutableList<Permission> = ArrayList<Permission>()
We can pass in any custom TypeReferene instance to Jackson to deserialize generic collections!
class PermissionTypeReference :
        TypeReference<MutableList<Permission>>() {
}
At last we need to make the proper call to Jackson for the deserialization:
override fun fromString(string: String): Any {
    if (jsonTypeReference == null) {
        return JacksonUtil.fromString(string, jsonObjectClass)
    } else {
        return JacksonUtil.fromString(string,
                jsonTypeReference as TypeReference<*>)
    }
}
JacksonUtil has the corresponding overloaded methods:
fun <T> fromString(string: String, clazz: Class<T>): T {
    try {
        return OBJECT_MAPPER.readValue(string, clazz)
    } catch (e: IOException) { ...
fun <T> fromString(string: String, reference: TypeReference<T>): T {
    try {
        return OBJECT_MAPPER.readValue(string, reference)
    } catch (e: IOException) { ...
There's one thing that hit me terrible in the next morning hours, we have to think about clone()!
fun <T : Any> clone(value: T): T {
    return when (value) {
        is ArrayList<*> -> {
            val newList = ArrayList<Any?>()
            for (elem in value) {
                newList.add(fromString(
                        toString(elem), elem.javaClass))
            }
            newList as T        }
        else ->
            fromString(toString(value), value.javaClass)
    }
}

Friday, January 22, 2016

Consuming p2 Repositories from Maven - Update

I always have been struggling with p2. Tycho somewhat made things easier. But I always tried to find ways to use the eclipse p2 repositories with maven and bndtools.

Today I installed Package Drone to see if this solves my troubles, and believe it, it does. I uploaded the p2-repository zip from RAP and I was able to consume the databinding-bundles with plain maven and bnd-tools. Strike!


Using Mapstruct with Protobuf3