Interface UserType<J>
- All Known Subinterfaces:
EnhancedUserType<J>,UserVersionType<T>
- All Known Implementing Classes:
BaseUserTypeSupport,RevisionTypeType,UserTypeLegacyBridge,UserTypeSupport
org.hibernate.type.
A custom type is not 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:
- explicitly, using
@Type, or - implicitly, using
@TypeRegistration.
For example, this UserType persists Period
to columns of type varchar:
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, WrapperOptions options)
throws SQLException {
String string = rs.getString(position);
return rs.wasNull() ? null : Period.parse(string);
}
@Override
public void nullSafeSet(PreparedStatement st, Period value, int index, WrapperOptions options)
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 our UserType:
@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, our UserType 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);
}
}
A UserType 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, WrapperOptions options)
throws SQLException {
String string = rs.getString(position);
return rs.wasNull()? null : parseBitSet(columnValue);
}
@Override
public void nullSafeSet(PreparedStatement st, BitSet bitSet, int index, WrapperOptions options)
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 of UserType must be immutable and must
declare a public default constructor.
A custom type implemented as a UserType 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 implement CompositeUserType.
- See Also:
- API Note:
- This interface:
- abstracts user code away from changes to the internal interface
Type, - simplifies the implementation of custom types, and
- hides certain SPI interfaces from user code.
The class
CustomTypeautomatically adapts betweenUserTypeandType. In principle, a custom type could implementTypedirectly, 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. - abstracts user code away from changes to the internal interface
-
Method Summary
Modifier and TypeMethodDescriptiondefault Jassemble(Serializable cached, Object owner) Reconstruct a value from its destructured representation, during the process of reading the properties of an entity from the second-level cache.Return a clone of the given instance of the Java class mapped by this custom type.default Serializabledisassemble(J value) Transform the given value into a destructured representation, suitable for storage in the second-level cache.default booleanCompare two instances of the Java class mapped by this custom type for persistence "equality", that is, equality of their persistent state.default longThe default column length, for use in DDL generation.default longgetDefaultSqlLength(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker.default intThe default column precision, for use in DDL generation.default intgetDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker.default intThe default column scale, for use in DDL generation.default intgetDefaultSqlScale(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker.intThe JDBC/SQL type code for the database column mapped by this custom type.default AttributeConverter<J, ?> Returns the converter that this custom type uses for transforming from the domain type to the relational type, ornullif there is no conversion.default intGet a hash code for the given instance of the Java class mapped by this custom type, consistent with the definition of persistence "equality" for this custom type.default BooleanIs this a comparable type in HQL? A type is comparable if it may be used with comparison operators like>=, and withmax()andmin().booleanAre instances of the Java class mapped by this custom type mutable or immutable?default JnullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) Deprecated, for removal: This API element is subject to removal in a future version.default JnullSafeGet(ResultSet rs, int position, WrapperOptions options) Read an instance of the Java class mapped by this custom type from the given JDBCResultSet.default voidnullSafeSet(PreparedStatement st, J value, int position, SharedSessionContractImplementor session) Deprecated, for removal: This API element is subject to removal in a future version.default voidnullSafeSet(PreparedStatement st, J value, int position, WrapperOptions options) Write an instance of the Java class mapped by this custom type to the given JDBCPreparedStatement.default JDuring 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.The class returned bynullSafeGet().
-
Method Details
-
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 by
SqlTypes, but it could be a database-specific code.- See Also:
-
returnedClass
The class returned bynullSafeGet().- Returns:
- Class
-
equals
Compare two instances of the Java class mapped by this custom type for persistence "equality", that is, equality of their persistent state.- Implementation Note:
- The default implementation calls
Objects.equals(java.lang.Object, java.lang.Object).
-
hashCode
Get a hash code for the given instance of the Java class mapped by this custom type, consistent with the definition of persistence "equality" for this custom type.- Implementation Note:
- The default implementation calls
Objects.hashCode(java.lang.Object).
-
nullSafeGet
Read an instance of the Java class mapped by this custom type from the given JDBCResultSet. Implementors must handle null column values.- Throws:
SQLException- Since:
- 7.0
- Implementation Note:
- The default implementation calls
ResultSet.getObject(int, Class)with the givenpositionand with the returned class.
-
nullSafeSet
default void nullSafeSet(PreparedStatement st, J value, int position, WrapperOptions options) throws SQLException Write an instance of the Java class mapped by this custom type to the given JDBCPreparedStatement. Implementors must handle null values of the Java class. A multi-column type should be written to parameters starting fromindex.- Throws:
SQLException- Since:
- 7.0
- Implementation Note:
- The default implementation calls
PreparedStatement.setObject(int, Object, int)with the givenpositionandvalueand with the SQL type.
-
deepCopy
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 if the argument is mutable, or the argument if it's an immutable object
-
isMutable
boolean isMutable()Are instances of the Java class mapped by this custom type mutable or immutable?- Returns:
trueif instances are mutable
-
disassemble
Transform the given value into a destructured representation, suitable for storage in the second-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
- Throws:
UnsupportedOperationException- if this type cannot be cached in the second-level cache.- See Also:
- Implementation Note:
- The default implementation calls
deepCopy(J)and then casts the result toSerializable.
-
assemble
Reconstruct a value from its destructured representation, during the process of reading the properties of an entity from 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:
cached- the object to be cachedowner- the owner of the cached object- Returns:
- a reconstructed object from the cacheable representation
- Throws:
UnsupportedOperationException- if this type cannot be cached in the second-level cache.- See Also:
- Implementation Note:
- The default implementation calls
deepCopy(J).
-
replace
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 mergedmanaged- the value in the managed entity- Returns:
- the value to be merged
- See Also:
-
getDefaultSqlLength
default long getDefaultSqlLength()The default column length, for use in DDL generation.- Since:
- 7.0
-
getDefaultSqlPrecision
default int getDefaultSqlPrecision()The default column precision, for use in DDL generation.- Since:
- 7.0
-
getDefaultSqlScale
default int getDefaultSqlScale()The default column scale, for use in DDL generation.- Since:
- 7.0
-
getDefaultSqlLength
@Deprecated(since="7.0", forRemoval=true) default long getDefaultSqlLength(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker. UsegetDefaultSqlLength()The default column length, for use in DDL generation.- Since:
- 6.0
-
getDefaultSqlPrecision
@Deprecated(since="7.0", forRemoval=true) default int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker. UsegetDefaultSqlPrecision()The default column precision, for use in DDL generation.- Since:
- 6.0
-
getDefaultSqlScale
@Deprecated(since="7.0", forRemoval=true) default int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) Deprecated, for removal: This API element is subject to removal in a future version.This operation is a layer-breaker. UsegetDefaultSqlScale()The default column scale, for use in DDL generation.- Since:
- 6.0
-
getValueConverter
Returns the converter that this custom type uses for transforming from the domain type to the relational type, ornullif 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 by
JdbcMapping.getJdbcJavaType(). Support for multiple domain type representations works by converting objects of that type to the domain type.- Since:
- 7.0
-
isComparable
Is this a comparable type in HQL? A type is comparable if it may be used with comparison operators like>=, and withmax()andmin().- Returns:
nullby default to indicate that the assignedJdbcTypeshould decide- Since:
- 7.0
-