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 metadataBuilder = sources.getMetadataBuilder();
|
||||||
|
|
||||||
metadataBuilder.applyBasicType(BitSetUserType.INSTANCE, "bitset");
|
metadataBuilder.applyBasicType(new BitSetUserType(), "bitset");
|
||||||
//end::basic-custom-type-register-UserType-example[]
|
//end::basic-custom-type-register-UserType-example[]
|
||||||
}
|
}
|
||||||
catch (Exception ignore) {
|
catch (Exception ignore) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import java.sql.Types;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.usertype.UserType;
|
import org.hibernate.usertype.UserType;
|
||||||
|
|
||||||
|
@ -26,8 +25,6 @@ import org.jboss.logging.Logger;
|
||||||
//tag::basic-custom-type-BitSetUserType-example[]
|
//tag::basic-custom-type-BitSetUserType-example[]
|
||||||
public class BitSetUserType implements UserType<BitSet> {
|
public class BitSetUserType implements UserType<BitSet> {
|
||||||
|
|
||||||
public static final BitSetUserType INSTANCE = new BitSetUserType();
|
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(BitSetUserType.class);
|
private static final Logger log = Logger.getLogger(BitSetUserType.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,20 +38,20 @@ public class BitSetUserType implements UserType<BitSet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(BitSet x, BitSet y)
|
public boolean equals(BitSet x, BitSet y) {
|
||||||
throws HibernateException {
|
|
||||||
return Objects.equals(x, y);
|
return Objects.equals(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode(BitSet x)
|
public int hashCode(BitSet x) {
|
||||||
throws HibernateException {
|
|
||||||
return Objects.hashCode(x);
|
return Objects.hashCode(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BitSet nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
|
public BitSet nullSafeGet(ResultSet rs, int position,
|
||||||
String columnValue = (String) rs.getObject(position);
|
SharedSessionContractImplementor session, Object owner)
|
||||||
|
throws SQLException {
|
||||||
|
String columnValue = rs.getString(position);
|
||||||
if (rs.wasNull()) {
|
if (rs.wasNull()) {
|
||||||
columnValue = null;
|
columnValue = null;
|
||||||
}
|
}
|
||||||
|
@ -64,25 +61,23 @@ public class BitSetUserType implements UserType<BitSet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nullSafeSet(
|
public void nullSafeSet(PreparedStatement st, BitSet value, int index,
|
||||||
PreparedStatement st, BitSet value, int index, SharedSessionContractImplementor session)
|
SharedSessionContractImplementor session)
|
||||||
throws HibernateException, SQLException {
|
throws SQLException {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
log.debugv("Binding null to parameter {0} ",index);
|
log.debugv("Binding null to parameter {0} ",index);
|
||||||
st.setNull(index, Types.VARCHAR);
|
st.setNull(index, Types.VARCHAR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String stringValue = BitSetHelper.bitSetToString((BitSet) value);
|
String stringValue = BitSetHelper.bitSetToString(value);
|
||||||
log.debugv("Binding {0} to parameter {1} ", stringValue, index);
|
log.debugv("Binding {0} to parameter {1} ", stringValue, index);
|
||||||
st.setString(index, stringValue);
|
st.setString(index, stringValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BitSet deepCopy(BitSet value)
|
public BitSet deepCopy(BitSet bitSet) {
|
||||||
throws HibernateException {
|
return bitSet == null ? null : (BitSet) bitSet.clone();
|
||||||
return value == null ? null :
|
|
||||||
BitSet.valueOf(BitSet.class.cast(value).toLongArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,15 +86,13 @@ public class BitSetUserType implements UserType<BitSet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Serializable disassemble(BitSet value)
|
public Serializable disassemble(BitSet value) {
|
||||||
throws HibernateException {
|
|
||||||
return deepCopy(value);
|
return deepCopy(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BitSet assemble(Serializable cached, Object owner)
|
public BitSet assemble(Serializable cached, Object owner) {
|
||||||
throws HibernateException {
|
return deepCopy((BitSet) cached);
|
||||||
return deepCopy( (BitSet) cached );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,16 +21,212 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
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,
|
* A custom type is <em>not</em> an actual persistent attribute type,
|
||||||
* rather it is a class responsible for serializing instances of some
|
* rather it is a class responsible for serializing instances of some
|
||||||
* other class to and from JDBC. This other class should have "value"
|
* other class to and from JDBC. This other class should have "value"
|
||||||
* semantics, since its identity is lost as part of this serialization
|
* semantics, since its identity is lost as part of this serialization
|
||||||
* process.
|
* process.
|
||||||
* <p>
|
* <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
|
* Every implementor of {@code UserType} must be immutable and must
|
||||||
* declare a public default constructor.
|
* declare a public default constructor.
|
||||||
* <p>
|
* <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:
|
* This interface:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>abstracts user code away from changes to the internal interface
|
* <li>abstracts user code away from changes to the internal interface
|
||||||
|
@ -41,26 +237,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
* <p>
|
* <p>
|
||||||
* The class {@link org.hibernate.type.CustomType} automatically adapts
|
* The class {@link org.hibernate.type.CustomType} automatically adapts
|
||||||
* between {@code UserType} and {@link org.hibernate.type.Type}.
|
* between {@code UserType} and {@link org.hibernate.type.Type}.
|
||||||
* <p>
|
|
||||||
* In principle, a custom type could implement {@code Type} directly,
|
* In principle, a custom type could implement {@code Type} directly,
|
||||||
* or extend one of the abstract classes in {@link org.hibernate.type}.
|
* or extend one of the abstract classes in {@link org.hibernate.type}.
|
||||||
* But this approach risks breakage resulting from future incompatible
|
* But this approach risks breakage resulting from future incompatible
|
||||||
* changes to classes or interfaces in that package, and is therefore
|
* changes to classes or interfaces in that package, and is therefore
|
||||||
* discouraged.
|
* 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.Type
|
||||||
* @see org.hibernate.type.CustomType
|
* @see org.hibernate.type.CustomType
|
||||||
|
|
Loading…
Reference in New Issue