HHH-14919 - Improve handling for java.sql.Date, Time and Timestamp

This commit is contained in:
Steve Ebersole 2021-11-08 16:37:31 -06:00
parent 9c9a326ae6
commit 599b0ba39f
12 changed files with 425 additions and 308 deletions

View File

@ -6,6 +6,8 @@
*/ */
package org.hibernate.userguide.mapping.basic; package org.hibernate.userguide.mapping.basic;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.time.Instant; import java.time.Instant;
import java.util.Date; import java.util.Date;
@ -45,7 +47,7 @@ public class DatePrecisionTests {
final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor(); final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor();
assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) ); assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) );
assertThat( jtd.getJavaTypeClass(), equalTo( Date.class ) ); assertThat( jtd.getJavaTypeClass(), equalTo( Timestamp.class ) );
assertThat( jtd.getPrecision(), equalTo( TemporalType.TIMESTAMP ) ); assertThat( jtd.getPrecision(), equalTo( TemporalType.TIMESTAMP ) );
assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.TIMESTAMP ) ); assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.TIMESTAMP ) );
} }
@ -55,7 +57,7 @@ public class DatePrecisionTests {
final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor(); final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor();
assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) ); assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) );
assertThat( jtd.getJavaTypeClass(), equalTo( Date.class ) ); assertThat( jtd.getJavaTypeClass(), equalTo( java.sql.Date.class ) );
assertThat( jtd.getPrecision(), equalTo( TemporalType.DATE ) ); assertThat( jtd.getPrecision(), equalTo( TemporalType.DATE ) );
assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.DATE ) ); assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.DATE ) );
} }
@ -65,7 +67,7 @@ public class DatePrecisionTests {
final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor(); final TemporalJavaTypeDescriptor jtd = (TemporalJavaTypeDescriptor) jdbcMapping.getJavaTypeDescriptor();
assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) ); assertThat( jtd, is( attribute.getJavaTypeDescriptor() ) );
assertThat( jtd.getJavaTypeClass(), equalTo( Date.class ) ); assertThat( jtd.getJavaTypeClass(), equalTo( Time.class ) );
assertThat( jtd.getPrecision(), equalTo( TemporalType.TIME ) ); assertThat( jtd.getPrecision(), equalTo( TemporalType.TIME ) );
assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.TIME ) ); assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.TIME ) );
} }

View File

@ -16,6 +16,7 @@ import org.hibernate.Filter;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
@ -74,12 +75,11 @@ public class FilterImpl implements Filter, Serializable {
*/ */
public Filter setParameter(String name, Object value) throws IllegalArgumentException { public Filter setParameter(String name, Object value) throws IllegalArgumentException {
// Make sure this is a defined parameter and check the incoming value type // Make sure this is a defined parameter and check the incoming value type
// TODO: what should be the actual exception type here? BasicType<?> type = (BasicType<?>) definition.getParameterType( name );
Type type = definition.getParameterType( name );
if ( type == null ) { if ( type == null ) {
throw new IllegalArgumentException( "Undefined filter parameter [" + name + "]" ); throw new IllegalArgumentException( "Undefined filter parameter [" + name + "]" );
} }
if ( value != null && !type.getReturnedClass().isAssignableFrom( value.getClass() ) ) { if ( value != null && !type.getJavaTypeDescriptor().isInstance( value ) ) {
throw new IllegalArgumentException( "Incorrect type for parameter [" + name + "]" ); throw new IllegalArgumentException( "Incorrect type for parameter [" + name + "]" );
} }
parameters.put( name, value ); parameters.put( name, value );

View File

@ -180,13 +180,11 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
this.bindType = clarifiedType; this.bindType = clarifiedType;
} }
if ( ! getTypeConfiguration().getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled() ) { if ( bindType != null ) {
if ( bindType != null ) { value = bindType.getExpressableJavaTypeDescriptor().coerce( value, this );
value = bindType.getExpressableJavaTypeDescriptor().coerce( value, this ); }
} else if ( queryParameter.getHibernateType() != null ) {
else if ( queryParameter.getHibernateType() != null ) { value = queryParameter.getHibernateType().getExpressableJavaTypeDescriptor().coerce( value, this );
value = queryParameter.getHibernateType().getExpressableJavaTypeDescriptor().coerce( value, this );
}
} }
if ( isBindingValidationRequired ) { if ( isBindingValidationRequired ) {

View File

@ -17,7 +17,7 @@ public class JdbcParameterBindingImpl implements JdbcParameterBinding {
private final Object bindValue; private final Object bindValue;
public JdbcParameterBindingImpl(JdbcMapping jdbcMapping, Object bindValue) { public JdbcParameterBindingImpl(JdbcMapping jdbcMapping, Object bindValue) {
assert bindValue == null || jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isInstance( bindValue ); assert bindValue == null || jdbcMapping.getJavaTypeDescriptor().isInstance( bindValue );
this.jdbcMapping = jdbcMapping; this.jdbcMapping = jdbcMapping;
this.bindValue = bindValue; this.bindValue = bindValue;
} }

View File

@ -36,7 +36,7 @@ public abstract class AbstractClassJavaTypeDescriptor<T> implements BasicJavaTyp
* @see #AbstractClassJavaTypeDescriptor(Class, MutabilityPlan) * @see #AbstractClassJavaTypeDescriptor(Class, MutabilityPlan)
*/ */
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
protected AbstractClassJavaTypeDescriptor(Class<T> type) { protected AbstractClassJavaTypeDescriptor(Class<? extends T> type) {
this( type, (MutabilityPlan<T>) ImmutableMutabilityPlan.INSTANCE ); this( type, (MutabilityPlan<T>) ImmutableMutabilityPlan.INSTANCE );
} }
@ -47,7 +47,7 @@ public abstract class AbstractClassJavaTypeDescriptor<T> implements BasicJavaTyp
* @param mutabilityPlan The plan for handling mutability aspects of the java type. * @param mutabilityPlan The plan for handling mutability aspects of the java type.
*/ */
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
protected AbstractClassJavaTypeDescriptor(Class<T> type, MutabilityPlan<T> mutabilityPlan) { protected AbstractClassJavaTypeDescriptor(Class<? extends T> type, MutabilityPlan<? extends T> mutabilityPlan) {
this( this(
type, type,
mutabilityPlan, mutabilityPlan,
@ -64,13 +64,14 @@ public abstract class AbstractClassJavaTypeDescriptor<T> implements BasicJavaTyp
* @param mutabilityPlan The plan for handling mutability aspects of the java type. * @param mutabilityPlan The plan for handling mutability aspects of the java type.
* @param comparator The comparator for handling comparison of values * @param comparator The comparator for handling comparison of values
*/ */
@SuppressWarnings("unchecked")
protected AbstractClassJavaTypeDescriptor( protected AbstractClassJavaTypeDescriptor(
Class<T> type, Class<? extends T> type,
MutabilityPlan<T> mutabilityPlan, MutabilityPlan<? extends T> mutabilityPlan,
Comparator<T> comparator) { Comparator<? extends T> comparator) {
this.type = type; this.type = (Class<T>) type;
this.mutabilityPlan = mutabilityPlan; this.mutabilityPlan = (MutabilityPlan<T>) mutabilityPlan;
this.comparator = comparator; this.comparator = (Comparator<T>) comparator;
} }
@Override @Override
@ -107,11 +108,11 @@ public abstract class AbstractClassJavaTypeDescriptor<T> implements BasicJavaTyp
return (value == null) ? "null" : value.toString(); return (value == null) ? "null" : value.toString();
} }
protected HibernateException unknownUnwrap(Class conversionType) { protected HibernateException unknownUnwrap(Class<?> conversionType) {
return JavaTypeDescriptorHelper.unknownUnwrap( type, conversionType, this ); return JavaTypeDescriptorHelper.unknownUnwrap( type, conversionType, this );
} }
protected HibernateException unknownWrap(Class conversionType) { protected HibernateException unknownWrap(Class<?> conversionType) {
return JavaTypeDescriptorHelper.unknownWrap( conversionType, type, this ); return JavaTypeDescriptorHelper.unknownWrap( conversionType, type, this );
} }
} }

View File

@ -15,20 +15,22 @@ import org.hibernate.type.spi.TypeConfiguration;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractTemporalJavaTypeDescriptor<T> extends AbstractClassJavaTypeDescriptor<T> implements TemporalJavaTypeDescriptor<T> { public abstract class AbstractTemporalJavaTypeDescriptor<T>
extends AbstractClassJavaTypeDescriptor<T>
implements TemporalJavaTypeDescriptor<T> {
protected AbstractTemporalJavaTypeDescriptor(Class<T> type) { protected AbstractTemporalJavaTypeDescriptor(Class<? extends T> type) {
super( type ); super( type );
} }
protected AbstractTemporalJavaTypeDescriptor(Class<T> type, MutabilityPlan<T> mutabilityPlan) { protected AbstractTemporalJavaTypeDescriptor(Class<? extends T> type, MutabilityPlan<? extends T> mutabilityPlan) {
super( type, mutabilityPlan ); super( type, mutabilityPlan );
} }
public AbstractTemporalJavaTypeDescriptor( public AbstractTemporalJavaTypeDescriptor(
Class<T> type, Class<? extends T> type,
MutabilityPlan<T> mutabilityPlan, MutabilityPlan<? extends T> mutabilityPlan,
Comparator<T> comparator) { Comparator<? extends T> comparator) {
super( type, mutabilityPlan, comparator ); super( type, mutabilityPlan, comparator );
} }
@ -62,19 +64,19 @@ public abstract class AbstractTemporalJavaTypeDescriptor<T> extends AbstractClas
protected <X> TemporalJavaTypeDescriptor<X> forTimestampPrecision(TypeConfiguration typeConfiguration) { protected <X> TemporalJavaTypeDescriptor<X> forTimestampPrecision(TypeConfiguration typeConfiguration) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
toString() + " as `jakarta.persistence.TemporalType.TIMESTAMP` not supported" this + " as `jakarta.persistence.TemporalType.TIMESTAMP` not supported"
); );
} }
protected <X> TemporalJavaTypeDescriptor<X> forDatePrecision(TypeConfiguration typeConfiguration) { protected <X> TemporalJavaTypeDescriptor<X> forDatePrecision(TypeConfiguration typeConfiguration) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
toString() + " as `jakarta.persistence.TemporalType.DATE` not supported" this + " as `jakarta.persistence.TemporalType.DATE` not supported"
); );
} }
protected <X> TemporalJavaTypeDescriptor<X> forTimePrecision(TypeConfiguration typeConfiguration) { protected <X> TemporalJavaTypeDescriptor<X> forTimePrecision(TypeConfiguration typeConfiguration) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
toString() + " as `jakarta.persistence.TemporalType.TIME` not supported" this + " as `jakarta.persistence.TemporalType.TIME` not supported"
); );
} }

View File

@ -28,13 +28,34 @@ import org.hibernate.type.spi.TypeConfiguration;
*/ */
public interface JavaType<T> extends Serializable { public interface JavaType<T> extends Serializable {
/** /**
* Get the Java type described * Get the Java type (Type) described
*
* @see #getJavaTypeClass
*/ */
default Type getJavaType() { default Type getJavaType() {
// default on this side since #getJavaTypeClass is the currently implemented method // default on this side since #getJavaTypeClass is the currently implemented method
return getJavaTypeClass(); return getJavaTypeClass();
} }
/**
* Get the Java type (Class) described
*
* @see #getJavaType
*/
default Class<T> getJavaTypeClass() {
return ReflectHelper.getClass( getJavaType() );
}
/**
* Is the given value an instance of the described type?
*
* Generally this comes down to {@link #getJavaTypeClass() getJavaTypeClass().}{@link Class#isInstance isInstance()},
* though some descriptors (mainly the java.sql.Date, Time and Timestamp descriptors) might need different semantics
*/
default boolean isInstance(Object value) {
return getJavaTypeClass().isInstance( value );
}
/** /**
* Retrieve the mutability plan for this Java type. * Retrieve the mutability plan for this Java type.
*/ */
@ -199,15 +220,6 @@ public interface JavaType<T> extends Serializable {
return (T) value; return (T) value;
} }
/**
* Retrieve the Java type handled here.
*
* @return The Java type.
*/
default Class<T> getJavaTypeClass() {
return ReflectHelper.getClass( getJavaType() );
}
/** /**
* The check constraint that should be added to the column * The check constraint that should be added to the column
* definition in generated DDL. * definition in generated DDL.

View File

@ -15,18 +15,21 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import jakarta.persistence.TemporalType;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType;
/** /**
* Descriptor for {@link java.sql.Date} handling. * Descriptor for {@link java.sql.Date} handling.
* *
* @author Steve Ebersole * @implSpec Unlike most {@link JavaType} implementations, can handle 2 different "domain
* representations" (most map just a single type): general {@link Date} values in addition
* to {@link java.sql.Date} values. This capability is shared with
* {@link JdbcTimeJavaTypeDescriptor} and {@link JdbcTimestampJavaTypeDescriptor}.
*/ */
public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> { public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> {
public static final JdbcDateJavaTypeDescriptor INSTANCE = new JdbcDateJavaTypeDescriptor(); public static final JdbcDateJavaTypeDescriptor INSTANCE = new JdbcDateJavaTypeDescriptor();
@ -42,19 +45,9 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static final DateTimeFormatter LITERAL_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; public static final DateTimeFormatter LITERAL_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
public static class DateMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final DateMutabilityPlan INSTANCE = new DateMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
return value instanceof java.sql.Date
? new java.sql.Date( value.getTime() )
: new Date( value.getTime() );
}
}
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public JdbcDateJavaTypeDescriptor() { public JdbcDateJavaTypeDescriptor() {
super( Date.class, DateMutabilityPlan.INSTANCE ); super( java.sql.Date.class, DateMutabilityPlan.INSTANCE );
} }
@Override @Override
@ -63,29 +56,10 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
} }
@Override @Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { public boolean isInstance(Object value) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.DATE ); // this check holds true for java.sql.Date as well
} return value instanceof Date
&& !( value instanceof java.sql.Time );
@Override
protected <X> TemporalJavaTypeDescriptor<X> forDatePrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaTypeDescriptor<X>) this;
}
@Override
public String toString(Date value) {
return new SimpleDateFormat( DATE_FORMAT ).format( value );
}
@Override
public Date fromString(CharSequence string) {
try {
return new Date( new SimpleDateFormat(DATE_FORMAT).parse( string.toString() ).getTime() );
}
catch ( ParseException pe) {
throw new HibernateException( "could not parse date string" + string, pe );
}
} }
@Override @Override
@ -93,6 +67,7 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
if ( one == another ) { if ( one == another ) {
return true; return true;
} }
if ( one == null || another == null ) { if ( one == null || another == null ) {
return false; return false;
} }
@ -101,8 +76,8 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
return true; return true;
} }
Calendar calendar1 = Calendar.getInstance(); final Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance(); final Calendar calendar2 = Calendar.getInstance();
calendar1.setTime( one ); calendar1.setTime( one );
calendar2.setTime( another ); calendar2.setTime( another );
@ -113,7 +88,7 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
@Override @Override
public int extractHashCode(Date value) { public int extractHashCode(Date value) {
Calendar calendar = Calendar.getInstance(); final Calendar calendar = Calendar.getInstance();
calendar.setTime( value ); calendar.setTime( value );
int hashCode = 1; int hashCode = 1;
hashCode = 31 * hashCode + calendar.get( Calendar.MONTH ); hashCode = 31 * hashCode + calendar.get( Calendar.MONTH );
@ -122,56 +97,80 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
return hashCode; return hashCode;
} }
@SuppressWarnings({ "unchecked" })
@Override @Override
public <X> X unwrap(Date value, Class<X> type, WrapperOptions options) { public Date coerce(Object value, CoercionContext coercionContext) {
return wrap( value, null );
}
@SuppressWarnings("unchecked")
@Override
public Object unwrap(Date value, Class type, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( LocalDate.class.isAssignableFrom( type ) ) {
return unwrapLocalDate( value );
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) { if ( java.sql.Date.class.isAssignableFrom( type ) ) {
final java.sql.Date rtn = value instanceof java.sql.Date return unwrapSqlDate( value );
? ( java.sql.Date ) value
: new java.sql.Date( value.getTime() );
return (X) rtn;
} }
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
final java.sql.Time rtn = value instanceof java.sql.Time if ( java.util.Date.class.isAssignableFrom( type ) ) {
? ( java.sql.Time ) value return value;
: new java.sql.Time( value.getTime() );
return (X) rtn;
} }
if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
final java.sql.Timestamp rtn = value instanceof java.sql.Timestamp if ( Long.class.isAssignableFrom( type ) ) {
? ( java.sql.Timestamp ) value return unwrapDateEpoch( value );
: new java.sql.Timestamp( value.getTime() );
return (X) rtn;
} }
if ( Date.class.isAssignableFrom( type ) ) {
return (X) value; if ( String.class.isAssignableFrom( type ) ) {
return toString( value );
} }
if ( Calendar.class.isAssignableFrom( type ) ) { if ( Calendar.class.isAssignableFrom( type ) ) {
final GregorianCalendar cal = new GregorianCalendar(); final GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis( value.getTime() ); cal.setTimeInMillis( unwrapDateEpoch( value ) );
return (X) cal; return cal;
} }
if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( value.getTime() ); if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
return new java.sql.Timestamp( value.getTime() );
} }
if ( LocalDate.class.isAssignableFrom( type ) ) {
if ( value instanceof java.sql.Date ) { if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return (X) ( (java.sql.Date) value ).toLocalDate(); throw new IllegalArgumentException( "Illegal attempt to treat `java.sql.Date` as `java.sql.Time`" );
}
} }
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
private LocalDate unwrapLocalDate(Date value) {
return value instanceof java.sql.Date
? ( (java.sql.Date) value ).toLocalDate()
: new java.sql.Date( value.getTime() ).toLocalDate();
}
private java.sql.Date unwrapSqlDate(Date value) {
return value instanceof java.sql.Date
? (java.sql.Date) value
: new java.sql.Date( value.getTime() );
}
private static long unwrapDateEpoch(Date value) {
return value.getTime();
}
@Override @Override
public <X> Date wrap(X value, WrapperOptions options) { public Date wrap(Object value, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( value instanceof java.sql.Date ) { if ( value instanceof java.sql.Date ) {
return (Date) value; return (java.sql.Date) value;
} }
if ( value instanceof Long ) { if ( value instanceof Long ) {
@ -192,4 +191,42 @@ public class JdbcDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
throw unknownWrap( value.getClass() ); throw unknownWrap( value.getClass() );
} }
@Override
public String toString(Date value) {
return new SimpleDateFormat( DATE_FORMAT ).format( value );
}
@Override
public Date fromString(CharSequence string) {
try {
return new java.sql.Date( new SimpleDateFormat(DATE_FORMAT).parse( string.toString() ).getTime() );
}
catch ( ParseException pe) {
throw new HibernateException( "could not parse date string" + string, pe );
}
}
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.DATE );
}
@Override
protected <X> TemporalJavaTypeDescriptor<X> forDatePrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaTypeDescriptor<X>) this;
}
public static class DateMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final DateMutabilityPlan INSTANCE = new DateMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
if ( value instanceof java.sql.Date ) {
return value;
}
return new java.sql.Date( value.getTime() );
}
}
} }

View File

@ -16,8 +16,6 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import jakarta.persistence.TemporalType;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
@ -25,10 +23,15 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType;
/** /**
* Descriptor for {@link Time} handling. * Descriptor for {@link Time} handling.
* *
* @author Steve Ebersole * @implSpec Unlike most {@link JavaType} implementations, can handle 2 different "domain
* representations" (most map just a single type): general {@link Date} values in addition
* to {@link Time} values. This capability is shared with
* {@link JdbcDateJavaTypeDescriptor} and {@link JdbcTimestampJavaTypeDescriptor}.
*/ */
public class JdbcTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> { public class JdbcTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> {
public static final JdbcTimeJavaTypeDescriptor INSTANCE = new JdbcTimeJavaTypeDescriptor(); public static final JdbcTimeJavaTypeDescriptor INSTANCE = new JdbcTimeJavaTypeDescriptor();
@ -48,19 +51,9 @@ public class JdbcTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static final DateTimeFormatter LOGGABLE_FORMATTER = DateTimeFormatter.ISO_LOCAL_TIME; public static final DateTimeFormatter LOGGABLE_FORMATTER = DateTimeFormatter.ISO_LOCAL_TIME;
public static class TimeMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final TimeMutabilityPlan INSTANCE = new TimeMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
return value instanceof Time
? new Time( value.getTime() )
: new Date( value.getTime() );
}
}
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public JdbcTimeJavaTypeDescriptor() { public JdbcTimeJavaTypeDescriptor() {
super( Date.class, TimeMutabilityPlan.INSTANCE ); super( Time.class, TimeMutabilityPlan.INSTANCE );
} }
@Override @Override
@ -69,14 +62,125 @@ public class JdbcTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
} }
@Override @Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { public boolean isInstance(Object value) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIME ); // this check holds true for java.sql.Time as well
return value instanceof Date
&& !( value instanceof java.sql.Date );
} }
@Override @Override
protected <X> TemporalJavaTypeDescriptor<X> forTimePrecision(TypeConfiguration typeConfiguration) { public int extractHashCode(Date value) {
//noinspection unchecked final Calendar calendar = Calendar.getInstance();
return (TemporalJavaTypeDescriptor<X>) this; calendar.setTime( value );
int hashCode = 1;
hashCode = 31 * hashCode + calendar.get( Calendar.HOUR_OF_DAY );
hashCode = 31 * hashCode + calendar.get( Calendar.MINUTE );
hashCode = 31 * hashCode + calendar.get( Calendar.SECOND );
hashCode = 31 * hashCode + calendar.get( Calendar.MILLISECOND );
return hashCode;
}
@Override
public boolean areEqual(Date one, Date another) {
if ( one == another ) {
return true;
}
if ( one == null || another == null ) {
return false;
}
if ( one.getTime() == another.getTime() ) {
return true;
}
final Calendar calendar1 = Calendar.getInstance();
final Calendar calendar2 = Calendar.getInstance();
calendar1.setTime( one );
calendar2.setTime( another );
return calendar1.get( Calendar.HOUR_OF_DAY ) == calendar2.get( Calendar.HOUR_OF_DAY )
&& calendar1.get( Calendar.MINUTE ) == calendar2.get( Calendar.MINUTE )
&& calendar1.get( Calendar.SECOND ) == calendar2.get( Calendar.SECOND )
&& calendar1.get( Calendar.MILLISECOND ) == calendar2.get( Calendar.MILLISECOND );
}
@Override
public Date coerce(Object value, CoercionContext coercionContext) {
return wrap( value, null );
}
@SuppressWarnings("unchecked")
@Override
public Object unwrap(Date value, Class type, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( LocalTime.class.isAssignableFrom( type ) ) {
return value instanceof java.sql.Time
? ( (java.sql.Time) value ).toLocalTime()
: new java.sql.Time( value.getTime() ).toLocalTime();
}
if ( Time.class.isAssignableFrom( type ) ) {
return value instanceof Time
? value
: new Time( value.getTime() );
}
if ( Date.class.isAssignableFrom( type ) ) {
return value;
}
if ( Long.class.isAssignableFrom( type ) ) {
return value.getTime();
}
if ( String.class.isAssignableFrom( type ) ) {
return toString( value );
}
if ( Calendar.class.isAssignableFrom( type ) ) {
final GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis( value.getTime() );
return cal;
}
if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
return new java.sql.Timestamp( value.getTime() );
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
throw new IllegalArgumentException( "Illegal attempt to treat `java.sql.Time` as `java.sql.Date`" );
}
throw unknownUnwrap( type );
}
@Override
public Date wrap(Object value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof LocalTime ) {
return Time.valueOf( (LocalTime) value );
}
if ( value instanceof Date ) {
return (Date) value;
}
if ( value instanceof Long ) {
return new Time( (Long) value );
}
if ( value instanceof Calendar ) {
return new Time( ( (Calendar) value ).getTimeInMillis() );
}
throw unknownWrap( value.getClass() );
} }
@Override @Override
@ -95,113 +199,28 @@ public class JdbcTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript
} }
@Override @Override
public int extractHashCode(Date value) { public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) {
Calendar calendar = Calendar.getInstance(); return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIME );
calendar.setTime( value );
int hashCode = 1;
hashCode = 31 * hashCode + calendar.get( Calendar.HOUR_OF_DAY );
hashCode = 31 * hashCode + calendar.get( Calendar.MINUTE );
hashCode = 31 * hashCode + calendar.get( Calendar.SECOND );
hashCode = 31 * hashCode + calendar.get( Calendar.MILLISECOND );
return hashCode;
}
@Override
public boolean areEqual(Date one, Date another) {
if ( one == another ) {
return true;
}
if ( one == null || another == null ) {
return false;
}
if ( one.getTime() == another.getTime() ) {
return true;
}
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar1.setTime( one );
calendar2.setTime( another );
return calendar1.get( Calendar.HOUR_OF_DAY ) == calendar2.get( Calendar.HOUR_OF_DAY )
&& calendar1.get( Calendar.MINUTE ) == calendar2.get( Calendar.MINUTE )
&& calendar1.get( Calendar.SECOND ) == calendar2.get( Calendar.SECOND )
&& calendar1.get( Calendar.MILLISECOND ) == calendar2.get( Calendar.MILLISECOND );
}
@SuppressWarnings({ "unchecked" })
@Override
public <X> X unwrap(Date value, Class<X> type, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( Time.class.isAssignableFrom( type ) ) {
final Time rtn = value instanceof Time
? ( Time ) value
: new Time( value.getTime() );
return (X) rtn;
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
final java.sql.Date rtn = value instanceof java.sql.Date
? ( java.sql.Date ) value
: new java.sql.Date( value.getTime() );
return (X) rtn;
}
if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
final java.sql.Timestamp rtn = value instanceof java.sql.Timestamp
? ( java.sql.Timestamp ) value
: new java.sql.Timestamp( value.getTime() );
return (X) rtn;
}
if ( Date.class.isAssignableFrom( type ) ) {
return (X) value;
}
if ( Calendar.class.isAssignableFrom( type ) ) {
final GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis( value.getTime() );
return (X) cal;
}
if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( value.getTime() );
}
if ( LocalTime.class.isAssignableFrom( type ) ) {
if ( value instanceof Time ) {
return (X) ( (Time) value ).toLocalTime();
}
}
throw unknownUnwrap( type );
}
@Override
public <X> Date wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof Time ) {
return (Time) value;
}
if ( value instanceof Long ) {
return new Time( (Long) value );
}
if ( value instanceof Calendar ) {
return new Time( ( (Calendar) value ).getTimeInMillis() );
}
if ( value instanceof Date ) {
return new Time( ( (Date) value ).getTime() );
}
if ( value instanceof LocalTime ) {
return Time.valueOf( (LocalTime) value );
}
throw unknownWrap( value.getClass() );
} }
@Override @Override
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
return 0; //seconds (currently ignored since Dialects don't parameterize time type by precision) //seconds (currently ignored since Dialects don't parameterize time type by precision)
return 0;
}
@Override
protected <X> TemporalJavaTypeDescriptor<X> forTimePrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaTypeDescriptor<X>) this;
}
public static class TimeMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final TimeMutabilityPlan INSTANCE = new TimeMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
return new Time( value.getTime() );
}
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.type.descriptor.java;
import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.time.ZoneId; import java.time.ZoneId;
@ -31,7 +32,10 @@ import org.hibernate.type.spi.TypeConfiguration;
/** /**
* Descriptor for {@link Timestamp} handling. * Descriptor for {@link Timestamp} handling.
* *
* @author Steve Ebersole * @implSpec Unlike most {@link JavaType} implementations, can handle 2 different "domain
* representations" (most map just a single type): general {@link Date} values in addition
* to {@link Timestamp} values. This capability is shared with
* {@link JdbcDateJavaTypeDescriptor} and {@link JdbcTimeJavaTypeDescriptor}.
*/ */
public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> implements VersionJavaType<Date> { public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor<Date> implements VersionJavaType<Date> {
public static final JdbcTimestampJavaTypeDescriptor INSTANCE = new JdbcTimestampJavaTypeDescriptor(); public static final JdbcTimestampJavaTypeDescriptor INSTANCE = new JdbcTimestampJavaTypeDescriptor();
@ -48,25 +52,9 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
public static final DateTimeFormatter LITERAL_FORMATTER = DateTimeFormatter.ofPattern( TIMESTAMP_FORMAT ) public static final DateTimeFormatter LITERAL_FORMATTER = DateTimeFormatter.ofPattern( TIMESTAMP_FORMAT )
.withZone( ZoneId.from( ZoneOffset.UTC ) ); .withZone( ZoneId.from( ZoneOffset.UTC ) );
public static class TimestampMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final TimestampMutabilityPlan INSTANCE = new TimestampMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
if ( value instanceof Timestamp ) {
Timestamp orig = (Timestamp) value;
Timestamp ts = new Timestamp( orig.getTime() );
ts.setNanos( orig.getNanos() );
return ts;
}
else {
return new Date( value.getTime() );
}
}
}
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public JdbcTimestampJavaTypeDescriptor() { public JdbcTimestampJavaTypeDescriptor() {
super( Date.class, TimestampMutabilityPlan.INSTANCE ); super( Timestamp.class, TimestampMutabilityPlan.INSTANCE );
} }
@Override @Override
@ -75,33 +63,9 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
} }
@Override @Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { public boolean isInstance(Object value) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIMESTAMP ); // this check holds true for java.sql.Timestamp as well
} return value instanceof Date;
@Override
protected <X> TemporalJavaTypeDescriptor<X> forTimestampPrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaTypeDescriptor<X>) this;
}
@Override
public String toString(Date value) {
return LITERAL_FORMATTER.format( value.toInstant() );
}
@Override
public Date fromString(CharSequence string) {
try {
final TemporalAccessor accessor = LITERAL_FORMATTER.parse( string );
return new Timestamp(
accessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L
+ accessor.get( ChronoField.NANO_OF_SECOND ) / 1_000_000
);
}
catch ( DateTimeParseException pe) {
throw new HibernateException( "could not parse timestamp string" + string, pe );
}
} }
@Override @Override
@ -143,41 +107,50 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
return Long.valueOf( value.getTime() / 1000 ).hashCode(); return Long.valueOf( value.getTime() / 1000 ).hashCode();
} }
@SuppressWarnings({ "unchecked" })
@Override @Override
public <X> X unwrap(Date value, Class<X> type, WrapperOptions options) { public Date coerce(Object value, CoercionContext coercionContext) {
return wrap( value, null );
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Object unwrap(Date value, Class type, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Timestamp.class.isAssignableFrom( type ) ) { if ( Timestamp.class.isAssignableFrom( type ) ) {
final Timestamp rtn = value instanceof Timestamp return value instanceof Timestamp
? ( Timestamp ) value ? (Timestamp) value
: new Timestamp( value.getTime() ); : new Timestamp( value.getTime() );
return (X) rtn;
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
final java.sql.Date rtn = value instanceof java.sql.Date
? ( java.sql.Date ) value
: new java.sql.Date( value.getTime() );
return (X) rtn;
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
final java.sql.Time rtn = value instanceof java.sql.Time
? ( java.sql.Time ) value
: new java.sql.Time( value.getTime() );
return (X) rtn;
} }
if ( Date.class.isAssignableFrom( type ) ) { if ( Date.class.isAssignableFrom( type ) ) {
return (X) value; return value;
} }
if ( Calendar.class.isAssignableFrom( type ) ) { if ( Calendar.class.isAssignableFrom( type ) ) {
final GregorianCalendar cal = new GregorianCalendar(); final GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis( value.getTime() ); cal.setTimeInMillis( value.getTime() );
return (X) cal; return cal;
} }
if ( Long.class.isAssignableFrom( type ) ) { if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( value.getTime() ); return value.getTime();
} }
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
return value instanceof java.sql.Date
? ( java.sql.Date ) value
: new java.sql.Date( value.getTime() );
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return value instanceof java.sql.Time
? ( java.sql.Time ) value
: new java.sql.Time( value.getTime() );
}
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
@ -190,6 +163,10 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
return (Timestamp) value; return (Timestamp) value;
} }
if ( value instanceof Date ) {
return new Timestamp( ( (Date) value ).getTime() );
}
if ( value instanceof Long ) { if ( value instanceof Long ) {
return new Timestamp( (Long) value ); return new Timestamp( (Long) value );
} }
@ -198,13 +175,39 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
return new Timestamp( ( (Calendar) value ).getTimeInMillis() ); return new Timestamp( ( (Calendar) value ).getTimeInMillis() );
} }
if ( value instanceof Date ) {
return new Timestamp( ( (Date) value ).getTime() );
}
throw unknownWrap( value.getClass() ); throw unknownWrap( value.getClass() );
} }
@Override
public String toString(Date value) {
return LITERAL_FORMATTER.format( value.toInstant() );
}
@Override
public Date fromString(CharSequence string) {
try {
final TemporalAccessor accessor = LITERAL_FORMATTER.parse( string );
return new Timestamp(
accessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L
+ accessor.get( ChronoField.NANO_OF_SECOND ) / 1_000_000
);
}
catch ( DateTimeParseException pe) {
throw new HibernateException( "could not parse timestamp string" + string, pe );
}
}
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) {
return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIMESTAMP );
}
@Override
protected <X> TemporalJavaTypeDescriptor<X> forTimestampPrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaTypeDescriptor<X>) this;
}
@Override @Override
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
return dialect.getDefaultTimestampPrecision(); return dialect.getDefaultTimestampPrecision();
@ -220,4 +223,21 @@ public class JdbcTimestampJavaTypeDescriptor extends AbstractTemporalJavaTypeDes
return new Timestamp( System.currentTimeMillis() ); return new Timestamp( System.currentTimeMillis() );
} }
public static class TimestampMutabilityPlan extends MutableMutabilityPlan<Date> {
public static final TimestampMutabilityPlan INSTANCE = new TimestampMutabilityPlan();
@Override
public Date deepCopyNotNull(Date value) {
if ( value instanceof Timestamp ) {
// make sure to get the nanos
final Timestamp orig = (Timestamp) value;
final Timestamp copy = new Timestamp( orig.getTime() );
copy.setNanos( orig.getNanos() );
return copy;
}
else {
return new Date( value.getTime() );
}
}
}
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.orm.test.bootstrap.binding.annotations.basics; package org.hibernate.orm.test.bootstrap.binding.annotations.basics;
import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@ -61,7 +62,7 @@ public class SimpleEntityTypeResolutionsTests {
case "someDate": { case "someDate": {
assertThat( assertThat(
propertyResolution.getDomainJavaDescriptor().getJavaTypeClass(), propertyResolution.getDomainJavaDescriptor().getJavaTypeClass(),
sameInstance( Date.class ) sameInstance( Timestamp.class )
); );
assertThat( propertyValue.getTemporalPrecision(), is( TemporalType.TIMESTAMP ) ); assertThat( propertyValue.getTemporalPrecision(), is( TemporalType.TIMESTAMP ) );
break; break;

View File

@ -1,13 +1,13 @@
package org.hibernate.orm.test.type.descriptor.java; package org.hibernate.orm.test.type.descriptor.java;
import java.util.Date; import java.sql.Date;
import org.hibernate.type.descriptor.java.JdbcDateJavaTypeDescriptor; import org.hibernate.type.descriptor.java.JdbcDateJavaTypeDescriptor;
import org.hibernate.testing.orm.junit.BaseUnitTest; import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@BaseUnitTest @BaseUnitTest
public class JdbcDateJavaTypeDescriptorTest { public class JdbcDateJavaTypeDescriptorTest {
@ -15,7 +15,32 @@ public class JdbcDateJavaTypeDescriptorTest {
@Test @Test
public void testToString() { public void testToString() {
final JdbcDateJavaTypeDescriptor javaTypeDescriptor = JdbcDateJavaTypeDescriptor.INSTANCE; final JdbcDateJavaTypeDescriptor javaTypeDescriptor = JdbcDateJavaTypeDescriptor.INSTANCE;
final String actual = javaTypeDescriptor.toString( new Date( 0 ) );
assertEquals( "01 January 1970", actual ); final String utilDate = javaTypeDescriptor.toString( new java.util.Date( 0 ) );
assertThat( utilDate ).isEqualTo( "01 January 1970" );
final String sqlDate = javaTypeDescriptor.toString( new java.sql.Date( 0 ) );
assertThat( sqlDate ).isEqualTo( "01 January 1970" );
}
@Test
public void testIsInstance() {
final JdbcDateJavaTypeDescriptor javaTypeDescriptor = JdbcDateJavaTypeDescriptor.INSTANCE;
javaTypeDescriptor.isInstance( new java.sql.Date( 0 ) );
javaTypeDescriptor.isInstance( new java.util.Date( 0 ) );
}
@Test
public void testWrap() {
final JdbcDateJavaTypeDescriptor javaTypeDescriptor = JdbcDateJavaTypeDescriptor.INSTANCE;
final Date sqlDate = new Date( 0 );
final java.util.Date wrappedSqlDate = javaTypeDescriptor.wrap( sqlDate, null );
assertThat( wrappedSqlDate ).isSameAs( sqlDate );
final java.util.Date utilDate = new java.util.Date( 0 );
final java.util.Date wrappedUtilDate = javaTypeDescriptor.wrap( utilDate, null );
assertThat( wrappedUtilDate ).isInstanceOf( java.sql.Date.class );
} }
} }