HHH-8844 - Add support for Java 8 date and time types (JSR-310)

This commit is contained in:
Steve Ebersole 2015-03-25 15:36:17 -05:00
parent 9901f85f77
commit 507726e929
14 changed files with 230 additions and 59 deletions

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type;
import java.time.Duration;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.java.DurationJavaDescriptor;
import org.hibernate.type.descriptor.sql.BigIntTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class DurationType
extends AbstractSingleColumnStandardBasicType<Duration>
implements LiteralType<Duration> {
/**
* Singleton access
*/
public static final DurationType INSTANCE = new DurationType();
public DurationType() {
super( BigIntTypeDescriptor.INSTANCE, DurationJavaDescriptor.INSTANCE );
}
@Override
public String objectToSQLString(Duration value, Dialect dialect) throws Exception {
return String.valueOf( value.toNanos() );
}
@Override
public String getName() {
return Duration.class.getSimpleName();
}
@Override
protected boolean registerUnderJavaType() {
return true;
}
}

View File

@ -78,7 +78,7 @@ public class InstantType
@Override
public String getName() {
return "Instant";
return Instant.class.getSimpleName();
}
@Override

View File

@ -44,5 +44,7 @@ public class Java8DateTimeTypeContributor implements TypeContributor {
typeContributions.contributeType( ZonedDateTimeType.INSTANCE );
typeContributions.contributeType( OffsetDateTimeType.INSTANCE );
typeContributions.contributeType( OffsetTimeType.INSTANCE );
typeContributions.contributeType( DurationType.INSTANCE );
}
}

View File

@ -55,7 +55,7 @@ public class LocalDateTimeType
@Override
public String getName() {
return "LocalDateTime";
return LocalDateTime.class.getSimpleName();
}
@Override

View File

@ -32,7 +32,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.type.descriptor.java.LocalDateJavaDescriptor;
import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor;
import org.hibernate.type.descriptor.sql.DateTypeDescriptor;
/**
* @author Steve Ebersole
@ -49,12 +49,12 @@ public class LocalDateType
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "yyyy-MM-dd", Locale.ENGLISH );
public LocalDateType() {
super( TimestampTypeDescriptor.INSTANCE, LocalDateJavaDescriptor.INSTANCE );
super( DateTypeDescriptor.INSTANCE, LocalDateJavaDescriptor.INSTANCE );
}
@Override
public String getName() {
return "LocalDate";
return LocalDate.class.getSimpleName();
}
@Override

View File

@ -32,7 +32,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.type.descriptor.java.LocalTimeJavaDescriptor;
import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor;
import org.hibernate.type.descriptor.sql.TimeTypeDescriptor;
/**
* A type that maps between {@link java.sql.Types#TIMESTAMP TIMESTAMP} and {@link java.time.LocalDateTime}.
@ -50,12 +50,12 @@ public class LocalTimeType
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "HH:mm:ss", Locale.ENGLISH );
public LocalTimeType() {
super( TimestampTypeDescriptor.INSTANCE, LocalTimeJavaDescriptor.INSTANCE );
super( TimeTypeDescriptor.INSTANCE, LocalTimeJavaDescriptor.INSTANCE );
}
@Override
public String getName() {
return "LocalTime";
return LocalTime.class.getSimpleName();
}
@Override

View File

@ -32,7 +32,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.type.descriptor.java.OffsetTimeJavaDescriptor;
import org.hibernate.type.descriptor.sql.TimestampTypeDescriptor;
import org.hibernate.type.descriptor.sql.TimeTypeDescriptor;
/**
* @author Steve Ebersole
@ -49,12 +49,12 @@ public class OffsetTimeType
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "HH:mm:ss.S xxxxx", Locale.ENGLISH );
public OffsetTimeType() {
super( TimestampTypeDescriptor.INSTANCE, OffsetTimeJavaDescriptor.INSTANCE );
super( TimeTypeDescriptor.INSTANCE, OffsetTimeJavaDescriptor.INSTANCE );
}
@Override
public String objectToSQLString(OffsetTime value, Dialect dialect) throws Exception {
return "{ts '" + FORMATTER.format( value ) + "'}";
return "{t '" + FORMATTER.format( value ) + "'}";
}
@Override

View File

@ -0,0 +1,102 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.type.descriptor.java;
import java.time.Duration;
import org.hibernate.type.descriptor.WrapperOptions;
/**
* @author Steve Ebersole
*/
public class DurationJavaDescriptor extends AbstractTypeDescriptor<Duration> {
/**
* Singleton access
*/
public static final DurationJavaDescriptor INSTANCE = new DurationJavaDescriptor();
@SuppressWarnings("unchecked")
public DurationJavaDescriptor() {
super( Duration.class, ImmutableMutabilityPlan.INSTANCE );
}
@Override
public String toString(Duration value) {
if ( value == null ) {
return null;
}
return String.valueOf( value.toNanos() );
}
@Override
public Duration fromString(String string) {
if ( string == null ) {
return null;
}
return Duration.ofNanos( Long.valueOf( string ) );
}
@Override
@SuppressWarnings("unchecked")
public <X> X unwrap(Duration duration, Class<X> type, WrapperOptions options) {
if ( duration == null ) {
return null;
}
if ( Duration.class.isAssignableFrom( type ) ) {
return (X) duration;
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) duration.toString();
}
if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( duration.toNanos() );
}
throw unknownUnwrap( type );
}
@Override
public <X> Duration wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( Duration.class.isInstance( value ) ) {
return (Duration) value;
}
if ( Long.class.isInstance( value ) ) {
return Duration.ofNanos( (Long) value );
}
if ( String.class.isInstance( value ) ) {
return Duration.parse( (String) value );
}
throw unknownWrap( value.getClass() );
}
}

View File

@ -25,8 +25,6 @@ package org.hibernate.type.descriptor.java;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
@ -35,7 +33,6 @@ import java.util.Date;
import java.util.GregorianCalendar;
import org.hibernate.type.InstantType;
import org.hibernate.type.LocalDateType;
import org.hibernate.type.descriptor.WrapperOptions;
/**

View File

@ -73,7 +73,17 @@ public class LocalDateJavaDescriptor extends AbstractTypeDescriptor<LocalDate> {
return (X) value;
}
final ZonedDateTime zonedDateTime = value.atStartOfDay().atZone( ZoneId.systemDefault() );
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
return (X) java.sql.Date.valueOf( value );
}
final LocalDateTime localDateTime = value.atStartOfDay();
if ( Timestamp.class.isAssignableFrom( type ) ) {
return (X) Timestamp.valueOf( localDateTime );
}
final ZonedDateTime zonedDateTime = localDateTime.atZone( ZoneId.systemDefault() );
if ( Calendar.class.isAssignableFrom( type ) ) {
return (X) GregorianCalendar.from( zonedDateTime );
@ -81,19 +91,7 @@ public class LocalDateJavaDescriptor extends AbstractTypeDescriptor<LocalDate> {
final Instant instant = zonedDateTime.toInstant();
if ( Timestamp.class.isAssignableFrom( type ) ) {
return (X) Timestamp.from( instant );
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
return (X) java.sql.Date.from( instant );
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return (X) java.sql.Time.from( instant );
}
if ( Date.class.isAssignableFrom( type ) ) {
if ( Date.class.equals( type ) ) {
return (X) Date.from( instant );
}
@ -130,7 +128,7 @@ public class LocalDateJavaDescriptor extends AbstractTypeDescriptor<LocalDate> {
}
if ( Date.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
final Date ts = (Date) value;
final Instant instant = Instant.ofEpochMilli( ts.getTime() );
return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ).toLocalDate();
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.type.descriptor.java;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
@ -74,10 +75,15 @@ public class LocalTimeJavaDescriptor extends AbstractTypeDescriptor<LocalTime> {
return (X) value;
}
if ( Time.class.isAssignableFrom( type ) ) {
return (X) Time.valueOf( value );
}
// Oracle documentation says to set the Date to January 1, 1970 when convert from
// a LocalTime to a Calendar. IMO the same should hold true for converting to all
// the legacy Date/Time types...
final ZonedDateTime zonedDateTime = value.atDate( LocalDate.of( 1970, 1, 1 ) ).atZone( ZoneId.systemDefault() );
if ( Calendar.class.isAssignableFrom( type ) ) {
@ -90,15 +96,7 @@ public class LocalTimeJavaDescriptor extends AbstractTypeDescriptor<LocalTime> {
return (X) Timestamp.from( instant );
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
return (X) java.sql.Date.from( instant );
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return (X) java.sql.Time.from( instant );
}
if ( Date.class.isAssignableFrom( type ) ) {
if ( Date.class.equals( type ) ) {
return (X) Date.from( instant );
}
@ -135,7 +133,7 @@ public class LocalTimeJavaDescriptor extends AbstractTypeDescriptor<LocalTime> {
}
if ( Date.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
final Date ts = (Date) value;
final Instant instant = Instant.ofEpochMilli( ts.getTime() );
return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ).toLocalTime();
}

View File

@ -23,9 +23,11 @@
*/
package org.hibernate.type.descriptor.java;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@ -73,34 +75,30 @@ public class OffsetTimeJavaDescriptor extends AbstractTypeDescriptor<OffsetTime>
return (X) offsetTime;
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return (X) java.sql.Time.valueOf( offsetTime.toLocalTime() );
}
final ZonedDateTime zonedDateTime = offsetTime.atDate( LocalDate.of( 1970, 1, 1 ) ).toZonedDateTime();
if ( Timestamp.class.isAssignableFrom( type ) ) {
return (X) Timestamp.valueOf( zonedDateTime.toLocalDateTime() );
}
if ( Calendar.class.isAssignableFrom( type ) ) {
return (X) GregorianCalendar.from( zonedDateTime );
}
final Instant instant = zonedDateTime.toInstant();
if ( Timestamp.class.isAssignableFrom( type ) ) {
return (X) Timestamp.from( instant );
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
return (X) java.sql.Date.from( instant );
}
if ( java.sql.Time.class.isAssignableFrom( type ) ) {
return (X) java.sql.Time.from( instant );
}
if ( Date.class.isAssignableFrom( type ) ) {
return (X) Date.from( instant );
}
if ( Long.class.isAssignableFrom( type ) ) {
return (X) Long.valueOf( instant.toEpochMilli() );
}
if ( java.util.Date.class.isAssignableFrom( type ) ) {
return (X) java.util.Date.from( instant );
}
throw unknownUnwrap( type );
}
@ -114,6 +112,10 @@ public class OffsetTimeJavaDescriptor extends AbstractTypeDescriptor<OffsetTime>
return (OffsetTime) value;
}
if ( Time.class.isInstance( value ) ) {
return ( (Time) value ).toLocalTime().atOffset( OffsetDateTime.now().getOffset() );
}
if ( Timestamp.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
return OffsetTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() );

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.test.type;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -30,6 +31,7 @@ import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
import javax.persistence.Entity;
import javax.persistence.Id;
@ -115,6 +117,7 @@ public class Java8DateTimeTests extends BaseNonConfigCoreFunctionalTestCase {
private ZonedDateTime zonedDateTime = ZonedDateTime.now();
private OffsetDateTime offsetDateTime = OffsetDateTime.now();
private OffsetTime offsetTime = OffsetTime.now();
private Duration duration = Duration.of( 20, ChronoUnit.DAYS );
public TheEntity() {
}
@ -187,5 +190,13 @@ public class Java8DateTimeTests extends BaseNonConfigCoreFunctionalTestCase {
public void setOffsetTime(OffsetTime offsetTime) {
this.offsetTime = offsetTime;
}
public Duration getDuration() {
return duration;
}
public void setDuration(Duration duration) {
this.duration = duration;
}
}
}

View File

@ -4,13 +4,13 @@ apply plugin: 'distribution'
buildDir = "target"
//project.tasks*.each {
// it.doFirst {
project.tasks*.each {
it.doFirst {
if ( !JavaVersion.current().java8Compatible ) {
throw new GradleException( "Release must use Java 8 or greater" )
}
// }
//}
}
}
idea.module {
}