2016-11-22 5 views
0

Ich bin neu im Winterschlaf. Ich benutze Postgres als DB.Hibernate + benutzerdefinierte usertypes mit Generics

Ich habe eine Tabelle Benutzer. Das enthält eine Spalte namens metadata, die vom Typ jsonb ist.

Ich möchte diese Spalte zu einem Objekt (serialisierbar) Metadaten zuordnen. Ich habe einige Tutorials gelesen und festgestellt, dass wir einen benutzerdefinierten userType implementieren müssen, um dies zu erreichen.

Also habe ich MyMetadataType implementiert. Jetzt habe ich noch eine Spalte namens Einstellungen des JSONB-Typs. Um diese Spalte dem entsprechenden Objekt zuzuordnen, benötige ich eine andere userType-Implementierung.

Ist es möglich, eine generische Klasse wie die folgende zu haben. Nur eine Klasse für alle solche Spalten?

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

wenn ja dann wie werde ich es in der Entity-Definition verwenden?

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

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

} 

Durch vorherige SO Fragen nach oben, kam ich mit der folgenden Klasse bis:

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

} 

Ich muß nur wissen, wie die User-Klasse diesen benutzerdefinierten Typen zu verwenden. Kann mir jemand helfen?

Antwort

1

Verwenden Sie den vollständig qualifizierten Namen des Typ:

@Type(type = "package.MyCustomType")

Sie müssen auch ein ParameterizedType verwenden:

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

In Ihrem UserType, werden Sie diese Methode hinzufügen müssen (implementiert ParameterizedType Schnittstelle):

void setParameterValues(Properties parameters); 

, wo Sie in den Parametern erhalten werden, wird der Eintrag: class = package.Metadata.class

Sie nur diese in einem Feld speichern haben, und ändern Sie die Standard UserType Methoden Verhalten Basis auf, es zu ändern.

+0

Aber wie Hibernate wissen, dass der Typ MyCustomType ist und nicht etwas anderes? – iwekesi

+0

Ok, ich habe nicht gesehen, dass du nach dieser Information gesucht hast. Ich aktualisiere meine Antwort – Thierry

+0

Erstens Danke für die schnelle Antwort. Kannst du mich, wenn möglich, auch auf ein funktionierendes Beispiel hinweisen, das du kennst? – iwekesi