Package org.hibernate.usertype

Interface UserType<J>

  • All Known Subinterfaces:
    EnhancedUserType<J>,UserVersionType<T>
    All Known Implementing Classes:
    BaseUserTypeSupport,EnumType,RevisionTypeType,StaticUserTypeSupport,UserTypeLegacyBridge,UserTypeSupport

    public interfaceUserType<J>
    This interface should be implemented by user-defined custom types that extend the set oftypes defined inorg.hibernate.type.

    A custom type isnot an actual persistent attribute type, rather it is a class responsible for serializing instances of some other class to and from JDBC. This other class should have "value" semantics, since its identity is lost as part of this serialization process.

    A custom type may be applied to an attribute of an entity either:

    For example, thisUserType persistsPeriod to columns of typevarchar:

     public class PeriodType implements UserType<Period> {     @Override     public int getSqlType() {         return VARCHAR;     }     @Override     public Class<Period> returnedClass() {         return Period.class;     }     @Override     public boolean equals(Period x, Period y) {         return Objects.equals(x, y);     }     @Override     public int hashCode(Period x) {         return x.hashCode();     }     @Override     public Period nullSafeGet(ResultSet rs, int position,                               SharedSessionContractImplementor session, Object owner)                 throws SQLException {         String string = rs.getString( position );         return rs.wasNull() ? null : Period.parse(string);     }     @Override     public void nullSafeSet(PreparedStatement st, Period value, int index,                             SharedSessionContractImplementor session)                 throws SQLException {         if ( value == null ) {             st.setNull(index, VARCHAR);         }         else {             st.setString(index, value.toString());         }     }     @Override     public boolean isMutable() {         return false;     }     @Override     public Period deepCopy(Period value) {         return value; //Period is immutable     }     @Override     public Serializable disassemble(Period period) {         return period; //Period is immutable     }     @Override     public Period assemble(Serializable cached, Object owner) {         return (Period) cached; //Period is immutable     } }

    And it may be used like this:

    @Type(PeriodType.class) Period period;

    We could even use@Type as a meta-annotation:

     @Type(PeriodType.class) @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface TimePeriod {}

    And then use the@TimePeriod annotation to apply ourUserType:

    @TimePeriod Period period;

    Finally, we could ask for our custom type to be used by default:

    @TypeRegistration(basicClass = Period.class, userType = PeriodType.class)

    Which completely relieves us of the need to annotate the field explicitly:

    Period period;

    But on the other hand, ourUserType is overkill. Like most immutable classes,Period is much easier to handle using a JPA attribute converter:

     @Converter public class PeriodToStringConverter implements AttributeConverter<Period,String> {     @Override     public String convertToDatabaseColumn(Period period) {         return period.toString();     }    @Override    public Period convertToEntityAttribute(String string) {         return Period.parse(string);    } }

    AUserType is much more useful when the persistent attribute type is mutable. For example:

     public class BitSetUserType implements UserType<BitSet> {     @Override     public int getSqlType() {         return Types.VARCHAR;     }     @Override     public Class<BitSet> returnedClass() {         return BitSet.class;     }     @Override     public boolean equals(BitSet x, BitSet y) {         return Objects.equals(x, y);     }     @Override     public int hashCode(BitSet x) {         return x.hashCode();     }     @Override     public BitSet nullSafeGet(ResultSet rs, int position,                               SharedSessionContractImplementor session, Object owner)                 throws SQLException {         String string = rs.getString(position);         return rs.wasNull()? null : parseBitSet(columnValue);     }     @Override     public void nullSafeSet(PreparedStatement st, BitSet bitSet, int index,                             SharedSessionContractImplementor session)                 throws SQLException {         if (bitSet == null) {             st.setNull(index, VARCHAR);         }         else {             st.setString(index, formatBitSet(bitSet));         }     }     @Override     public BitSet deepCopy(BitSet value) {         return bitSet == null ? null : (BitSet) bitSet.clone();     }     @Override     public boolean isMutable() {         return true;     }     @Override     public Serializable disassemble(BitSet value) {         return deepCopy(value);     }     @Override     public BitSet assemble(Serializable cached, Object owner)             throws HibernateException {         return deepCopy((BitSet) cached);     } }

    Every implementor ofUserType must be immutable and must declare a public default constructor.

    A custom type implemented as aUserType is treated as a non-composite value, and does not have persistent attributes which may be used in queries. If a custom type does have attributes, and can be thought of as something more like an embeddable object, it might be better to implementCompositeUserType.

    See Also:
    Type,CustomType,Type,TypeRegistration
    API Note:
    This interface:
    • abstracts user code away from changes to the internal interfaceType,
    • simplifies the implementation of custom types, and
    • hides certain SPI interfaces from user code.

    The classCustomType automatically adapts betweenUserType andType. In principle, a custom type could implementType directly, or extend one of the abstract classes inorg.hibernate.type. But this approach risks breakage resulting from future incompatible changes to classes or interfaces in that package, and is therefore discouraged.

    • Method Detail

      • getSqlType

        int getSqlType()
        The JDBC/SQL type code for the database column mapped by this custom type.

        The type code is usually one of the standard type codes declared bySqlTypes, but it could be a database-specific code.

        See Also:
        SqlTypes
      • returnedClass

        Class<J> returnedClass()
        The class returned bynullSafeGet().
        Returns:
        Class
      • equals

        boolean equals​(J x,J y)
        Compare two instances of the Java class mapped by this custom type for persistence "equality", that is, equality of their persistent state.
      • hashCode

        int hashCode​(J x)
        Get a hash code for the given instance of the Java class mapped by this custom type, consistent with the definition ofpersistence "equality" for this custom type.
      • deepCopy

        J deepCopy​(J value)
        Return a clone of the given instance of the Java class mapped by this custom type.
        • It's not necessary to clone immutable objects. If the Java class mapped by this custom type is an immutable class, this method may safely just return its argument.
        • For mutable objects, it's necessary to deep copy persistent state, stopping at associations to other entities, and at persistent collections.
        • If the argument is a reference to an entity, just return the argument.
        • Finally, if the argument is null, just return null.
        Parameters:
        value - the object to be cloned, which may be null
        Returns:
        a clone
      • isMutable

        boolean isMutable()
        Are instances of the Java class mapped by this custom type mutable or immutable?
        Returns:
        true if instances are mutable
      • disassemble

        Serializable disassemble​(J value)
        Transform the given value into a destructured representation, suitable for storage in thesecond-level cache. This method is called only during the process of writing the properties of an entity to the second-level cache.

        If the value is mutable then, at the very least, this method should perform a deep copy. That may not be enough for some types, however. For example, associations must be cached as identifier values.

        This is an optional operation, but, if left unimplemented, this type will not be cacheable in the second-level cache.

        Parameters:
        value - the object to be cached
        Returns:
        a cacheable representation of the object
        See Also:
        Cache
      • assemble

        J assemble​(Serializable cached,Object owner)
        Reconstruct a value from its destructured representation, during the process of reading the properties of an entity from thesecond-level cache.

        If the value is mutable then, at the very least, this method should perform a deep copy. That may not be enough for some types, however. For example, associations must be cached as identifier values.

        This is an optional operation, but, if left unimplemented, this type will not be cacheable in the second-level cache.

        Parameters:
        cached - the object to be cached
        owner - the owner of the cached object
        Returns:
        a reconstructed object from the cacheable representation
        See Also:
        Cache
      • replace

        default J replace​(J detached,J managed,Object owner)
        During merge, replace the existing (target) value in the managed entity we are merging to with a new (original) value from the detached entity we are merging.
        • For immutable objects, or null values, it's safe to simply return the first argument.
        • For mutable objects, it's enough to return a copy of the first argument.
        • For objects with component values, it might make sense to recursively replace component values.
        Parameters:
        detached - the value from the detached entity being merged
        managed - the value in the managed entity
        Returns:
        the value to be merged
        See Also:
        Session.merge(Object)
      • getDefaultSqlLength

        default long getDefaultSqlLength​(Dialect dialect,JdbcType jdbcType)
        The default column length, for use in DDL generation.
      • getDefaultSqlPrecision

        default int getDefaultSqlPrecision​(Dialect dialect,JdbcType jdbcType)
        The default column precision, for use in DDL generation.
      • getDefaultSqlScale

        default int getDefaultSqlScale​(Dialect dialect,JdbcType jdbcType)
        The default column scale, for use in DDL generation.
      • getValueConverter

        @Incubatingdefault BasicValueConverter<J,​Object> getValueConverter()
        Returns the converter that this custom type uses for transforming from the domain type to the relational type, ornull if there is no conversion.

        Note that it is vital to provide a converter if a column should be mapped to multiple domain types, as Hibernate will only select a column once and materialize values as instances of the Java type given byJdbcMapping.getJdbcJavaType(). Support for multiple domain type representations works by converting objects of that type to the domain type.