4

I'm using eclipselink 2.6 as a persistence provider of spring data jpa, that in my understanding, now allows you to serialize a subtree of an entity as json using the internal moxy serializer.

So I'm trying to mix this to migrate from embedded element collections to a serialized json using the json datatype of postgres. I have an entity named Product, and this entity have the following mapped property:

@Convert(Convert.JSON) 
private List<MetadataIndex> indexes=new ArrayList<MetadataIndex> ();

In which metadata index is a simple class with a few string properties. I would like to convert this list of object into a json and store it into a column of json data type in postgres.

I thought that the above code should suffice, but it does not. The application crashes on boot (can't create entitymanager factory - npe somwhere inside eclipselink).

If I change the converter to @Convert(Convert.SERIALIZED) it works. It creates a field on the table Products named indexes of type bytea and store the serialized list in it. Is this an eclipselink bug or I'm missing something?

Thank you.

fer.marino
  • 497
  • 1
  • 5
  • 20

1 Answers1

4

well, I've used a custom eclipselink converter to convert my classes into json objects, then store them into the db using directly the postgres driver. This is the converter.

import fr.gael.dhus.database.jpa.domain.MetadataIndex;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.sessions.Session;
import org.postgresql.util.PGobject;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

/**
 * Created by fmarino on 20/03/2015.
 */
@Converter
public class JsonConverter implements org.eclipse.persistence.mappings.converters.Converter {

    private static ObjectMapper mapper =  new ObjectMapper();

    @Override
    public Object convertObjectValueToDataValue(Object objectValue, Session session) {
        try {
            PGobject out = new PGobject();
            out.setType("jsonb");
            out.setValue( mapper.writerWithType( new TypeReference<Collection<MetadataIndex>>() {} )
                    .writeValueAsString(objectValue) );
            return out;
        } catch (IOException e) {
            throw new IllegalArgumentException("Unable to serialize to json field ", e);
        } catch (SQLException e) {
            throw new IllegalArgumentException("Unable to serialize to json field ", e);
        }
    }

    @Override
    public Object convertDataValueToObjectValue(Object dataValue, Session session) {
        try {
            if(dataValue instanceof PGobject && ((PGobject) dataValue).getType().equals("jsonb"))
                return mapper.reader( new TypeReference<Collection<MetadataIndex>>() {}).readValue(((PGobject) dataValue).getValue());
            return "-";
        } catch (IOException e) {
            throw new IllegalArgumentException("Unable to deserialize to json field ", e);
        }
    }

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

    @Override
    public void initialize(DatabaseMapping mapping, Session session) {

    }

}

as you can see I use jackson for serialization, and specify the datatype as Collection. You can use the type you want here.

Inside my classes, I've mapped my field with this:

@Convert(converter = JsonConverter.class)
@Column (nullable = true, columnDefinition = "jsonb")

adding also this annotation to the class:

@Converter(converterClass = JsonConverter.class, name = "jsonConverter")

To make things works properly with jackson I've also added to my MetadataIndex class this annotation, on the class element:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")

I personally like using directly the postgres driver to store those kind of special datatype. I didn't manage to achieve the same with hibernate. As for the converter, I've would preferred a more general solution, but jackson forced me to state the object type I want to convert. If you find a better way to do it, let me know.

With a similar approach, I've also manage to use the hstore datatype of postgres.

fer.marino
  • 497
  • 1
  • 5
  • 20
  • Thanks for the answer, How to handle convertDataValueToObjectValue(), If the Metadata is of abstract class and I have 2 other classes(Eg: MetadataA, MetadataB) which extends Metadata class. I am unable to deserialize from json to collection in this situation. – Pavan Sokke Nagaraj Oct 03 '16 at 13:30
  • 1
    this is more related to how jackson deal with deserialization. See http://stackoverflow.com/questions/16800896/java-unmarshilling-json-data-containg-abstract-type – fer.marino Oct 04 '16 at 07:14
  • Yup, I realized it later. Thanks a lot. Could you just share how can I achieve the same with Hstore. THanks – Pavan Sokke Nagaraj Oct 04 '16 at 14:56
  • How to handle the mapping exception when Collection is null? – Pavan Sokke Nagaraj Nov 02 '16 at 21:35