Hibernate + 使用泛型的自定义用户类型

Hibernate + custom usertypes using generics

我是休眠新手。我正在使用 postgres 作为数据库。

我有一个 table 用户。其中包含一个名为 metadata 的列,类型为 jsonb。

我想将此列映射到对象(可序列化)元数据。我阅读了一些教程,了解到我们需要实现自定义 userType 来实现这一点。

所以我实现了 MyMetadataType。现在我有另一个名为 settings 的 jsonb 类型的列。要将此列映射到其相应的对象,我需要另一个 userType 实现。

是否可以有一个像下面这样的通用 class。所有这些列只有一个 class?

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

如果是,那么我将如何在实体定义中使用它?

@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;

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

}

通过查找以前的 SO 问题,我得出以下 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 };
    }

}

我只需要知道如何在用户中使用此自定义类型 class。谁能帮帮我?

使用类型的完全限定名称:

@Type(type = "package.MyCustomType")

您还需要使用 ParameterizedType :

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

在您的 UserType 中,您必须添加此方法(实现 ParameterizedType 接口):

void setParameterValues(Properties parameters);

您将在其中获取参数,条目:class=package.Metadata.class

您只需将其存储在一个字段中,然后修改标准 UserType 方法以更改基于它的行为。