Storing a List as JSON in a text field with Hibernate

前端 未结 2 2052
春和景丽
春和景丽 2020-12-17 01:49

I have something like the following model in Hibernate:

class Person {
    String name;
    List
addresses; } class Address { String stre
相关标签:
2条回答
  • 2020-12-17 02:06

    You can create a custom type:

    import com.fasterxml.jackson.databind.JavaType;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SessionImplementor;
    import org.hibernate.usertype.DynamicParameterizedType;
    import org.hibernate.usertype.UserType;
    
    import java.io.IOException;
    import java.io.Serializable;
    import java.io.StringWriter;
    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Types;
    import java.util.*;
    
    public class JsonListType implements UserType, DynamicParameterizedType {
    
        private static final int[] SQL_TYPES = new int[]{Types.LONGVARCHAR};
        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    
        private JavaType valueType = null;
        private Class<?> classType = null;
    
        @Override
        public int[] sqlTypes() {
            return SQL_TYPES;
        }
    
        @Override
        public Class<?> returnedClass() {
            return classType;
        }
    
        @Override
        public boolean equals(Object x, Object y) throws HibernateException {
            return Objects.equals(x, y);
        }
    
        @Override
        public int hashCode(Object x) throws HibernateException {
            return Objects.hashCode(x);
        }
    
        @Override
        public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
            return nullSafeGet(rs, names, owner);
        }
    
        @Override
        public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
            nullSafeSet(st, value, index);
        }
    
        public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
            String value = rs.getString(names[0]);
            Object result = null;
            if (valueType == null) {
                throw new HibernateException("Value type not set.");
            }
            if (value != null && !value.equals("")) {
                try {
                    result = OBJECT_MAPPER.readValue(value, valueType);
                } catch (IOException e) {
                    throw new HibernateException("Exception deserializing value " + value, e);
                }
            }
            return result;
        }
    
        public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
            StringWriter sw = new StringWriter();
            if (value == null) {
                st.setNull(index, Types.VARCHAR);
            } else {
                try {
                    OBJECT_MAPPER.writeValue(sw, value);
                    st.setString(index, sw.toString());
                } catch (IOException e) {
                    throw new HibernateException("Exception serializing value " + value, e);
                }
            }
        }
    
        @Override
        public Object deepCopy(Object value) throws HibernateException {
            if (value == null) {
                return null;
            } else if (valueType.isCollectionLikeType()) {
                try {
                    Object newValue = value.getClass().newInstance();
                    Collection newValueCollection = (Collection) newValue;
                    newValueCollection.addAll((Collection) value);
                    return newValueCollection;
                } catch (InstantiationException e) {
                    throw new HibernateException("Failed to deep copy the collection-like value object.", e);
                } catch (IllegalAccessException e) {
                    throw new HibernateException("Failed to deep copy the collection-like value object.", e);
                }
            }
    
            return null;
        }
    
        @Override
        public boolean isMutable() {
            return true;
        }
    
        @Override
        public Serializable disassemble(Object value) throws HibernateException {
            return (Serializable) deepCopy(value);
        }
    
        @Override
        public Object assemble(Serializable cached, Object owner) throws HibernateException {
            return deepCopy(cached);
        }
    
        @Override
        public Object replace(Object original, Object target, Object owner) throws HibernateException {
            return deepCopy(original);
        }
    
        @Override
        public void setParameterValues(Properties parameters) {
            try {
    
                // Get entity class
                Class<?> entityClass = Class.forName(parameters.getProperty(DynamicParameterizedType.ENTITY));
                Field property = null;
    
                // Find the field
                while(property == null && entityClass != null){
                    try {
                        property = entityClass.getDeclaredField(parameters.getProperty(DynamicParameterizedType.PROPERTY));
                    } catch (NoSuchFieldException e) {
                        entityClass = entityClass.getSuperclass();
                    }
                }
    
                if(property != null){
                    ParameterizedType listType = (ParameterizedType) property.getGenericType();
                    Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
                    valueType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, listClass);
                    classType = List.class;
                }
    
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException(e);
            }
        }
    
    }
    

    And use it like this:

    @Type(type = "com.company.util.JsonListType")
    private List<MyCustomClass> myCustomClasses;
    

    This solution is not DB specific and you can easily extend it to support Maps and custom cloneable entities.

    0 讨论(0)
  • 2020-12-17 02:16

    I haven't tried myself but here is a blog which is worth referring to... https://dzone.com/articles/annotating-custom-types

    Which basically suggests you to add a custom annotation. The annotated class for example call it "AddressJSONParser" should convert your Address object to JSON (using parser's) and returns as String. You should also a think about having a parser which does the reverse, from JSON string back to Address object...

    0 讨论(0)
提交回复
热议问题