0

I have an entity with a value object LatLng position which should be embedded in one field. My LatLng class is Serializable and by default a BLOB is used and it's serialized via Object*Stream which works so far.

My requirement is to be able to manipulate those value objects, for example in phpmyadmin in which BLOB is not suitable nor is the format human readable.

Thus I would like to use JSON, but I cannot find any approaches to solve my problem.

Any ideas? Thank you!

// edit 2:

Since I cannot use converter I had go one level deeper to hibernate. I've implemented a new UserType.

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
import org.springframework.util.ObjectUtils;

public class JsonSerializedUserType implements UserType {

    private final Class<?> returnedClass;

    public JsonSerializedUserType(Class<?> returnedClass) {
        this.returnedClass = returnedClass;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{
            Types.CLOB
        };
    }

    @Override
    @SuppressWarnings("rawtypes")
    public Class returnedClass() {
        return returnedClass;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return ObjectUtils.nullSafeEquals(x, y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return ObjectUtils.nullSafeHashCode(x);
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
            throws HibernateException, SQLException {

        final String json = (String) StringType.INSTANCE.nullSafeGet(rs, names, session, owner);
        return fromJson(json);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
            throws HibernateException, SQLException {

        StringType.INSTANCE.nullSafeSet(st, toJson(value), index, session);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {

        try {
            // first try copy constructor
            final Class<?> objClass = value.getClass();
            final Constructor<?> ctor = objClass.getConstructor(objClass);

            return ctor.newInstance(value);
        } catch (
                NoSuchMethodException
                | SecurityException
                | InvocationTargetException
                | IllegalAccessException
                | InstantiationException
                e
        ) {

        }

        // we expect object to be serializable via gson thus this is should be a deep copy
        return fromJson(toJson(value));
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return toJson(value);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        assert cached instanceof String;
        return fromJson((String)cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

    private String toJson(Object object) {
        return GsonProvider.get().toJson(object);
    }

    private Object fromJson(String json) {
        return GsonProvider.get().fromJson(json, returnedClass);
    }

}

Where do I need to add this code in spring and how do I get the Configuration object?

    cfg.registerTypeOverride(
            new JsonSerializedUserType(LatLng.class),
            new String[] {"latlng"}
    );
Aitch
  • 1,617
  • 13
  • 24
  • 1
    Sorry, but that's not clear to me: do you want it still to be BLOB or you want to make it CLOB containing JSON? What DB do you use? I'm asking because PostreSQL for instance supports JSON natively. – MirMasej Oct 04 '15 at 10:45
  • Which type the table column will be ? I Assume `String` or your database has a `JSON` type ? – Gaël J Oct 04 '15 at 10:46
  • yes it should be CLOB/TEXT and JSON – Aitch Oct 04 '15 at 10:47
  • Check this question: [JPA map JSON column to Java Object](http://stackoverflow.com/questions/25738569/jpa-map-json-column-to-java-object). – MirMasej Oct 04 '15 at 10:53
  • again my comment might not be clear enough, sorry. I want CLOB/TEXT as field type, storing JSON. No native JSON support necessary since I just read and write it, just text. I'm using MySQL which would be TEXT as type. – Aitch Oct 04 '15 at 10:56
  • I updated my question. – Aitch Oct 04 '15 at 11:44
  • Question updated again – Aitch Oct 07 '15 at 15:24
  • Why you cannot use converter? It seemed to be the right solution to your problem. – MirMasej Oct 07 '15 at 19:25
  • Because a converter is built from default constructor and the gson implementation expects the class as parameter to be able to unserialize it. That means 1. I have to create a `public class LatLngConverter extends GsonConverter { public LatLngConverter() { super(LatLng.class); } }` and create as many converters as types I have and 2. annotate all fields although it is clear, that every `LatLng` field should be serialized. – Aitch Oct 11 '15 at 07:07

0 Answers0