1

I am new to hibernate. I am using postgres as the DB.

I have a table User. which contains a column called metadata which is of type jsonb.

I would like to map this column to an object (serializable) Metadata. I read through some tutorials and came to know that we need to implement custom userType to achieve this.

So I implemented MyMetadataType. Now I have another column called settings again of jsonb type. To map this column to its corresponding Object I need another userType implementation.

Is it possible to have a generic class like the following.. Just one class for all such columns?

class MyCustomType<T> implements UserType
{
...
...
}

if yes then how will I use it in the Entity definition?

@Entity
@Table(name = "user")
@TypeDefs({ @TypeDef(name = "MyCustomType", typeClass = MyCustomType<Metadata>.class) })
public class User extends BaseEntity implements Serializable
{

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "metadata")
    @Type(type = "MyCustomeType")
    private Metadata metadata;

    .........
    .........
    .........

}

By looking up previous SO questions, I came up with the following class:

public class MyCustomType<T> implements UserType
{
    protected static Conversion conversion = new JsonDataConversionImpl();
    private static final Logger logger = LogManager.getLogger(MyCustomType.class.getCanonicalName());

    @SuppressWarnings("unchecked")
    private Class<T> genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), MyCustomType.class);

    /**
     * Reconstruct an object from the cacheable representation. At the very least this method should
     * perform a deep copy if the type is mutable. (optional operation)
     *
     * @param cached
     *            the object to be cached
     * @param owner
     *            the owner of the cached object
     * @return a reconstructed object from the cachable representation
     * @throws HibernateException
     */
    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException
    {
        return this.deepCopy(cached);
    }

    /**
     * Return a deep copy of the persistent state, stopping at entities and st collections. It is
     * not necessary to copy immutable objects, or null values, in which case it is safe to simple
     * return the argument.
     *
     * @param value
     *            the object to be cloned, which may be null
     * @return object a copy
     * @throws HibernateException
     */
    @Override
    public Object deepCopy(Object value) throws HibernateException
    {
        return value;
    }

    /**
     * Transform the object into its cacheable representation. At the very least this method should
     * perform a deep copy if the type is mutable. That may not be enough for some implementations,
     * however; for example, associations must be cached as identifier values. (optional operation)
     *
     * @param value
     *            the object to be cached
     * @return a cachable representation of the object
     * @throws HibernateException
     */
    @Override
    public Serializable disassemble(Object value) throws HibernateException
    {
        return (String) this.deepCopy(value);
    }

    /**
     * Compare two instances of the class mapped by this type for persistence "equality". Equality
     * of the persistence state.
     *
     * @param x
     * @param y
     * @return boolean
     * @throws HibernateException
     */
    @Override
    public boolean equals(Object x, Object y) throws HibernateException
    {

        if (x == null)
        {
            return y == null;
        }
        return x.equals(y);
    }

    /**
     * Get a hashcode for the instance, consistent with persistence "equality".
     */
    @Override
    public int hashCode(Object x) throws HibernateException
    {
        return x.hashCode();
    }

    /**
     * Are objects of this type mutable?
     *
     * @return boolean
     */
    @Override
    public boolean isMutable()
    {
        return true;
    }

    /**
     * Retrieve an instance of the mapped class from a JDBC resultset. Implementors should handle
     * possibility of null values.
     *
     * @param rs
     *            a JDBC result set
     * @param names
     *            the column names
     * @param session
     * @param owner
     *            the containing entity
     * @return
     * @throws HibernateException
     * @throws SQLException
     */
    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException
    {
        T t = null;
        try
        {

            if (rs.getString(names[0]) != null)
            {
                t = conversion.getObject(rs.getString(names[0]), genericType);
            }
        }
        catch (MyException e)
        {
            logger.error("Error while reading data type", e);
        }

        return t;
    }

    /**
     * Write an instance of the mapped class to a prepared statement. Implementors should handle
     * possibility of null values. A multi-column type should be written to parameters starting from
     * <tt>index</tt>
     *
     * @param st
     *            a JDBC prepared statement
     * @param value
     *            the object to write
     * @param index
     *            statement parameter index
     * @param session
     * @throws HibernateException
     * @throws SQLException
     */
    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException
    {

        if (value == null)
        {
            st.setNull(index, Types.OTHER);
            return;
        }

        st.setObject(index, value, Types.OTHER);
    }

    /**
     * During merge, replace the existing (target) values in the entity we are merging to with a new
     * (original) value from the detched entity we are merging. For immutable objects, or null
     * values, it is safe to return a copy of the first parameter. For the objects with component
     * values, it might make sense to recursively replace component values
     *
     * @param original
     *            the value from the detched entity being merged
     * @param target
     *            the value in the managed entity
     * @param owner
     * @return the value to be merged
     * @throws HibernateException
     */
    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException
    {
        return original;
    }

    /**
     * The class returned by <tt>nullSafeGet()</tt>
     *
     * @return Class
     */
    @Override
    public Class returnedClass()
    {
        return String.class;
    }

    /**
     * Returns the SQL type codes for the columns mapped by this type. The codes are defined on
     * <tt>java.sql.Types</tt>
     *
     * @return int[] the typecodes
     * @see java.sql.Types
     */
    @Override
    public int[] sqlTypes()
    {
        return new int[] { Types.JAVA_OBJECT };
    }

}

I just need to know how to use this custom type in the User class. Can anyone help me out?

1 Answer 1

3

Use the fully qualified name of the Type :

@Type(type = "package.MyCustomType")

You also need to use a ParameterizedType :

@Type(type = "package.MyCustomType",
      parameters = { @Parameter(
                           name = "class", value = "package.Metadata.class") })

In your UserType, you'll have to add this method (implements ParameterizedType interface):

void setParameterValues(Properties parameters);

where you'll get in parameters, the entry : class=package.Metadata.class

you'll just have to store this in a field, and modify the standard UserType methods to change behavior base on it.

Sign up to request clarification or add additional context in comments.

4 Comments

But how will hibernate know that the type is MyCustomType<Metadata> and not something else?
Ok, i did not see you were looking for this info to. I'm updating my answer
Firstly Thanks for the quick response. Also, if possible can you point me to some working example that you know?
@iwekesi, were you able to find a working example for this? I am trying to do the same with Ids and hit a road block, and there aren't any examples I could find

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.