HHH-14919 - Improve handling for java.sql.Date, Time and Timestamp
This commit is contained in:
parent
9c9a326ae6
commit
599b0ba39f
|
@ -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 ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue