add an extended example to UserType jdoc
This commit is contained in:
parent
a4ad36e7f8
commit
fe9f909dce
|
@ -588,7 +588,7 @@ public class BootstrapTest {
|
|||
|
||||
MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
|
||||
|
||||
metadataBuilder.applyBasicType(BitSetUserType.INSTANCE, "bitset");
|
||||
metadataBuilder.applyBasicType(new BitSetUserType(), "bitset");
|
||||
//end::basic-custom-type-register-UserType-example[]
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
|
|
|
@ -14,7 +14,6 @@ import java.sql.Types;
|
|||
import java.util.BitSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
|
@ -26,8 +25,6 @@ import org.jboss.logging.Logger;
|
|||
//tag::basic-custom-type-BitSetUserType-example[]
|
||||
public class BitSetUserType implements UserType<BitSet> {
|
||||
|
||||
public static final BitSetUserType INSTANCE = new BitSetUserType();
|
||||
|
||||
private static final Logger log = Logger.getLogger(BitSetUserType.class);
|
||||
|
||||
@Override
|
||||
|
@ -41,20 +38,20 @@ public class BitSetUserType implements UserType<BitSet> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(BitSet x, BitSet y)
|
||||
throws HibernateException {
|
||||
public boolean equals(BitSet x, BitSet y) {
|
||||
return Objects.equals(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(BitSet x)
|
||||
throws HibernateException {
|
||||
public int hashCode(BitSet x) {
|
||||
return Objects.hashCode(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitSet nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
|
||||
String columnValue = (String) rs.getObject(position);
|
||||
public BitSet nullSafeGet(ResultSet rs, int position,
|
||||
SharedSessionContractImplementor session, Object owner)
|
||||
throws SQLException {
|
||||
String columnValue = rs.getString(position);
|
||||
if (rs.wasNull()) {
|
||||
columnValue = null;
|
||||
}
|
||||
|
@ -64,25 +61,23 @@ public class BitSetUserType implements UserType<BitSet> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeSet(
|
||||
PreparedStatement st, BitSet value, int index, SharedSessionContractImplementor session)
|
||||
throws HibernateException, SQLException {
|
||||
public void nullSafeSet(PreparedStatement st, BitSet value, int index,
|
||||
SharedSessionContractImplementor session)
|
||||
throws SQLException {
|
||||
if (value == null) {
|
||||
log.debugv("Binding null to parameter {0} ",index);
|
||||
st.setNull(index, Types.VARCHAR);
|
||||
}
|
||||
else {
|
||||
String stringValue = BitSetHelper.bitSetToString((BitSet) value);
|
||||
String stringValue = BitSetHelper.bitSetToString(value);
|
||||
log.debugv("Binding {0} to parameter {1} ", stringValue, index);
|
||||
st.setString(index, stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitSet deepCopy(BitSet value)
|
||||
throws HibernateException {
|
||||
return value == null ? null :
|
||||
BitSet.valueOf(BitSet.class.cast(value).toLongArray());
|
||||
public BitSet deepCopy(BitSet bitSet) {
|
||||
return bitSet == null ? null : (BitSet) bitSet.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,15 +86,13 @@ public class BitSetUserType implements UserType<BitSet> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(BitSet value)
|
||||
throws HibernateException {
|
||||
public Serializable disassemble(BitSet value) {
|
||||
return deepCopy(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitSet assemble(Serializable cached, Object owner)
|
||||
throws HibernateException {
|
||||
return deepCopy( (BitSet) cached );
|
||||
public BitSet assemble(Serializable cached, Object owner) {
|
||||
return deepCopy((BitSet) cached);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,16 +21,212 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
|||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* This interface should be implemented by user-defined custom types.
|
||||
* This interface should be implemented by user-defined custom types
|
||||
* that extend the set of {@linkplain org.hibernate.type.Type types}
|
||||
* defined in {@link org.hibernate.type}.
|
||||
* <p>
|
||||
* A custom type is <em>not</em> 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.
|
||||
* <p>
|
||||
* A custom type may be applied to an attribute of an entity either:
|
||||
* <ul>
|
||||
* <li>explicitly, using {@link org.hibernate.annotations.Type @Type},
|
||||
* or
|
||||
* <li>implicitly, using
|
||||
* {@link org.hibernate.annotations.TypeRegistration @TypeRegistration}.
|
||||
* </ul>
|
||||
* <p>
|
||||
* For example, this {@code UserType} persists {@link java.time.Period}
|
||||
* to columns of type {@code varchar}:
|
||||
* <pre>
|
||||
* 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
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public Period replace(Period detached, Period managed, Object owner) {
|
||||
* return detached;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* And it may be used like this:
|
||||
* <pre>@Type(PeriodType.class) Period period;</pre>
|
||||
* <p>
|
||||
* We could even use {@code @Type} as a meta-annotation:
|
||||
* <pre>
|
||||
* @Type(PeriodType.class)
|
||||
* @Target({METHOD, FIELD})
|
||||
* @Retention(RUNTIME)
|
||||
* public @interface TimePeriod {}
|
||||
* </pre>
|
||||
* <p>
|
||||
* And then use the {@code @TimePeriod} annotation to apply our {@code UserType}:
|
||||
* <pre>@TimePeriod Period period;</pre>
|
||||
* <p>
|
||||
* Finally, we could ask for our custom type to be used by default:
|
||||
* <pre>@TypeRegistration(basicClass = Period.class, userType = PeriodType.class)</pre>
|
||||
* <p>
|
||||
* Which completely relieves us of the need to annotate the field explicitly:
|
||||
* <pre>Period period;</pre>
|
||||
* <p>
|
||||
* But on the other hand, our {@code UserType} is overkill. Like most
|
||||
* immutable classes, {@code Period} is much easier to handle using a
|
||||
* JPA attribute converter:
|
||||
* <pre>
|
||||
* @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);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* A {@code UserType} is much more useful when the persistent attribute
|
||||
* type is mutable. For example:
|
||||
* <p>
|
||||
* <pre>
|
||||
* 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);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* Every implementor of {@code UserType} must be immutable and must
|
||||
* declare a public default constructor.
|
||||
* <p>
|
||||
* A custom type implemented as a {@code 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 {@link CompositeUserType}.
|
||||
*
|
||||
* @apiNote
|
||||
* This interface:
|
||||
* <ul>
|
||||
* <li>abstracts user code away from changes to the internal interface
|
||||
|
@ -41,26 +237,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
* <p>
|
||||
* The class {@link org.hibernate.type.CustomType} automatically adapts
|
||||
* between {@code UserType} and {@link org.hibernate.type.Type}.
|
||||
* <p>
|
||||
* In principle, a custom type could implement {@code Type} directly,
|
||||
* or extend one of the abstract classes in {@link org.hibernate.type}.
|
||||
* But this approach risks breakage resulting from future incompatible
|
||||
* changes to classes or interfaces in that package, and is therefore
|
||||
* discouraged.
|
||||
* <p>
|
||||
* A custom type implemented as a {@code 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 {@link CompositeUserType}.
|
||||
* <p>
|
||||
* A custom type may be applied to an attribute of an entity either:
|
||||
* <ul>
|
||||
* <li>explicitly, using {@link org.hibernate.annotations.Type @Type},
|
||||
* or
|
||||
* <li>implicitly, using
|
||||
* {@link org.hibernate.annotations.TypeRegistration @TypeRegistration}.
|
||||
* </ul>
|
||||
*
|
||||
* @see org.hibernate.type.Type
|
||||
* @see org.hibernate.type.CustomType
|
||||
|
|
Loading…
Reference in New Issue