From 3e1249bb971c335c950294365a479b71620ee40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Sun, 11 Aug 2024 14:12:57 +0200 Subject: [PATCH 1/4] HHH-18532 Test cases checking that value is unwrapped to requested type Co-authored-by: Marco Belladelli --- .../type/java/DateSubclassesUnwrapTest.java | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java new file mode 100644 index 000000000000..08f937ee7527 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java @@ -0,0 +1,219 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.type.java; + +import java.sql.Time; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; + +import org.hibernate.HibernateException; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.CalendarDateJavaType; +import org.hibernate.type.descriptor.java.CalendarJavaType; +import org.hibernate.type.descriptor.java.CalendarTimeJavaType; +import org.hibernate.type.descriptor.java.DateJavaType; +import org.hibernate.type.descriptor.java.InstantJavaType; +import org.hibernate.type.descriptor.java.JdbcDateJavaType; +import org.hibernate.type.descriptor.java.JdbcTimeJavaType; +import org.hibernate.type.descriptor.java.JdbcTimestampJavaType; +import org.hibernate.type.descriptor.java.LocalDateJavaType; +import org.hibernate.type.descriptor.java.LocalDateTimeJavaType; +import org.hibernate.type.descriptor.java.LocalTimeJavaType; +import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType; +import org.hibernate.type.descriptor.java.OffsetTimeJavaType; +import org.hibernate.type.descriptor.java.ZonedDateTimeJavaType; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DomainModel +@SessionFactory +public class DateSubclassesUnwrapTest { + @Test + void testJdbcTimestampJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final JdbcTimestampJavaType javaType = JdbcTimestampJavaType.INSTANCE; + final Date date = new Date(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( date, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( date, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( date, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( date, Date.class, wrapperOptions ) ); + } + + @Test + void testJdbcDateJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final JdbcDateJavaType javaType = JdbcDateJavaType.INSTANCE; + final Date date = new Date(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( date, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( date, java.sql.Date.class, wrapperOptions ) ); + assertThrows( IllegalArgumentException.class, () -> javaType.unwrap( date, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( date, Date.class, wrapperOptions ) ); + } + + @Test + void testJdbcTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final JdbcTimeJavaType javaType = JdbcTimeJavaType.INSTANCE; + final Date date = new Date(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( date, Timestamp.class, wrapperOptions ) ); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> javaType.unwrap( date, java.sql.Date.class, wrapperOptions ) + ); + assertInstanceOf( Time.class, javaType.unwrap( date, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( date, Date.class, wrapperOptions ) ); + } + + @Test + void testDateJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final DateJavaType javaType = DateJavaType.INSTANCE; + final Date date = new Date(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( date, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( date, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( date, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( date, Date.class, wrapperOptions ) ); + } + + @Test + void testInstantJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final InstantJavaType javaType = InstantJavaType.INSTANCE; + final Instant instant = Instant.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( instant, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( instant, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( instant, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( instant, Date.class, wrapperOptions ) ); + } + + @Test + void testLocalDateJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final LocalDateJavaType javaType = LocalDateJavaType.INSTANCE; + final LocalDate date = LocalDate.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( date, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( date, java.sql.Date.class, wrapperOptions ) ); + assertThrows( HibernateException.class, () -> javaType.unwrap( date, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( date, Date.class, wrapperOptions ) ); + } + + @Test + void testLocalDateTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final LocalDateTimeJavaType javaType = LocalDateTimeJavaType.INSTANCE; + final LocalDateTime dateTime = LocalDateTime.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( dateTime, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( dateTime, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( dateTime, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( dateTime, Date.class, wrapperOptions ) ); + } + + @Test + void testLocalTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final LocalTimeJavaType javaType = LocalTimeJavaType.INSTANCE; + final LocalTime time = LocalTime.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( time, Timestamp.class, wrapperOptions ) ); + assertThrows( HibernateException.class, () -> javaType.unwrap( time, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( time, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( time, Date.class, wrapperOptions ) ); + } + + @Test + void testOffsetDateTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final OffsetDateTimeJavaType javaType = OffsetDateTimeJavaType.INSTANCE; + final OffsetDateTime dateTime = OffsetDateTime.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( dateTime, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( dateTime, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( dateTime, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( dateTime, Date.class, wrapperOptions ) ); + } + + @Test + void testOffsetTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final OffsetTimeJavaType javaType = OffsetTimeJavaType.INSTANCE; + final OffsetTime time = OffsetTime.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( time, Timestamp.class, wrapperOptions ) ); + assertThrows( IllegalArgumentException.class, + () -> javaType.unwrap( time, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( time, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( time, Date.class, wrapperOptions ) ); + } + + @Test + void testZonedDateTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final ZonedDateTimeJavaType javaType = ZonedDateTimeJavaType.INSTANCE; + final ZonedDateTime dateTime = ZonedDateTime.now(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( dateTime, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( dateTime, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( dateTime, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( dateTime, Date.class, wrapperOptions ) ); + } + + @Test + void testCalendarDateJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final CalendarDateJavaType javaType = CalendarDateJavaType.INSTANCE; + final Calendar calendar = Calendar.getInstance(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( calendar, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( calendar, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( calendar, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( calendar, Date.class, wrapperOptions ) ); + } + + @Test + void testCalendarJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final CalendarJavaType javaType = CalendarJavaType.INSTANCE; + final Calendar calendar = Calendar.getInstance(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( calendar, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( calendar, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( calendar, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( calendar, Date.class, wrapperOptions ) ); + } + + @Test + void testCalendarTimeJavaType(SessionFactoryScope scope) { + final WrapperOptions wrapperOptions = scope.getSessionFactory().getWrapperOptions(); + final CalendarTimeJavaType javaType = CalendarTimeJavaType.INSTANCE; + final Calendar calendar = Calendar.getInstance(); + + assertInstanceOf( Timestamp.class, javaType.unwrap( calendar, Timestamp.class, wrapperOptions ) ); + assertInstanceOf( java.sql.Date.class, javaType.unwrap( calendar, java.sql.Date.class, wrapperOptions ) ); + assertInstanceOf( Time.class, javaType.unwrap( calendar, Time.class, wrapperOptions ) ); + assertInstanceOf( Date.class, javaType.unwrap( calendar, Date.class, wrapperOptions ) ); + } +} From d6d7cf3c1ffae017c4b6f3f70c7a2f1ef0468e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Wed, 28 Aug 2024 12:55:48 +0200 Subject: [PATCH 2/4] HHH-18532 Fixed two things: - reordered matching in unwrap methods to push java.util.Date after java.sql.{Date,Time,Timestamp} - use new java.sql.{Date,Time}(instant.toEpochMilli()) instead of java.sql.{Date,Time}.from(instant); later method is not subclasses so it is always returning instance of java.util.Date --- .../descriptor/java/JdbcDateJavaType.java | 16 ++++++------- .../descriptor/java/JdbcTimeJavaType.java | 16 ++++++------- .../java/JdbcTimestampJavaType.java | 24 +++++++++---------- .../java/LocalDateTimeJavaType.java | 4 ++-- .../java/OffsetDateTimeJavaType.java | 4 ++-- .../descriptor/java/OffsetTimeJavaType.java | 4 ++++ .../java/ZonedDateTimeJavaType.java | 4 ++-- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index 58a990fec108..a86e12af8274 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -123,6 +123,14 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { return unwrapSqlDate( value ); } + if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { + return new java.sql.Timestamp( unwrapDateEpoch( value ) ); + } + + if ( java.sql.Time.class.isAssignableFrom( type ) ) { + throw new IllegalArgumentException( "Illegal attempt to treat `java.sql.Date` as `java.sql.Time`" ); + } + if ( java.util.Date.class.isAssignableFrom( type ) ) { return value; } @@ -141,14 +149,6 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { return cal; } - if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return new java.sql.Timestamp( unwrapDateEpoch( value ) ); - } - - if ( java.sql.Time.class.isAssignableFrom( type ) ) { - throw new IllegalArgumentException( "Illegal attempt to treat `java.sql.Date` as `java.sql.Time`" ); - } - throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 66dd05612700..0518e05d251c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -140,6 +140,14 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { : new Time( value.getTime() % 86_400_000 ); } + 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`" ); + } + if ( Date.class.isAssignableFrom( type ) ) { return value; } @@ -158,14 +166,6 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { 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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index ade64a05d7fd..83473d651602 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -127,6 +127,18 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { : new Timestamp( 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() ); + } + if ( Date.class.isAssignableFrom( type ) ) { return value; } @@ -146,18 +158,6 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { 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() % 86_400_000 ); - } - throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index 810ce18c9cad..3b3a5e9973ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -97,12 +97,12 @@ public X unwrap(LocalDateTime value, Class type, WrapperOptions options) if ( java.sql.Date.class.isAssignableFrom( type ) ) { Instant instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) java.sql.Date.from( instant ); + return (X) new java.sql.Date( instant.toEpochMilli() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { Instant instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) java.sql.Time.from( instant ); + return (X) new java.sql.Time( instant.toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index cec90437010d..9869496bac76 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -158,11 +158,11 @@ public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) java.sql.Date.from( offsetDateTime.toInstant() ); + return (X) new java.sql.Date( offsetDateTime.toInstant().toEpochMilli() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) java.sql.Time.from( offsetDateTime.toInstant() ); + return (X) new java.sql.Time( offsetDateTime.toInstant().toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java index 1fc1d7939316..da9f2f70e813 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java @@ -138,6 +138,10 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options return (X) Long.valueOf( instant.toEpochMilli() ); } + if ( java.sql.Date.class.isAssignableFrom( type ) ) { + throw new IllegalArgumentException( "Illegal attempt to treat `java.time.OffsetTime` as `java.sql.Date`" ); + } + if ( Date.class.isAssignableFrom( type ) ) { return (X) Date.from( instant ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index d17661d6132c..ae344c5988fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -120,11 +120,11 @@ public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions o } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) java.sql.Date.from( zonedDateTime.toInstant() ); + return (X) new java.sql.Date( zonedDateTime.toInstant().toEpochMilli() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) java.sql.Time.from( zonedDateTime.toInstant() ); + return (X) new java.sql.Time( zonedDateTime.toInstant().toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { From 9a87812c6b5e947a4333e31ca507f84c02d20d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Wed, 28 Aug 2024 16:24:27 +0200 Subject: [PATCH 3/4] HHH-18532 Suggested fix to conversion of epoch time in milliseconds to java.sql.Time --- .../descriptor/java/AbstractTemporalJavaType.java | 11 +++++++++++ .../type/descriptor/java/CalendarDateJavaType.java | 2 +- .../type/descriptor/java/CalendarJavaType.java | 2 +- .../type/descriptor/java/CalendarTimeJavaType.java | 2 +- .../type/descriptor/java/DateJavaType.java | 2 +- .../type/descriptor/java/InstantJavaType.java | 2 +- .../type/descriptor/java/JdbcTimeJavaType.java | 14 ++++---------- .../descriptor/java/JdbcTimestampJavaType.java | 2 +- .../descriptor/java/LocalDateTimeJavaType.java | 2 +- .../descriptor/java/OffsetDateTimeJavaType.java | 2 +- .../descriptor/java/ZonedDateTimeJavaType.java | 2 +- 11 files changed, 24 insertions(+), 19 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java index 1c1a12f79ebf..28f2168951ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java @@ -4,6 +4,10 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Time; +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.Comparator; import jakarta.persistence.TemporalType; @@ -60,6 +64,13 @@ private TemporalJavaType forMissingPrecision(TypeConfiguration typeConfig return (TemporalJavaType) this; } + public static Time millisToSqlTime(long millis) { + final LocalTime localTime = Instant.ofEpochMilli( millis ).atZone( ZoneId.systemDefault() ).toLocalTime(); + final Time time = Time.valueOf( localTime ); + time.setTime( time.getTime() + localTime.getNano() / 1_000_000 ); + return time; + } + protected TemporalJavaType forTimestampPrecision(TypeConfiguration typeConfiguration) { throw new UnsupportedOperationException( this + " as `jakarta.persistence.TemporalType.TIMESTAMP` not supported" diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index db5446e701ca..e81a5f7871f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -100,7 +100,7 @@ public X unwrap(Calendar value, Class type, WrapperOptions options) { return (X) new java.sql.Date( value.getTimeInMillis() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return (X) millisToSqlTime( value.getTimeInMillis() ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { return (X) new java.sql.Timestamp( value.getTimeInMillis() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java index 3b23b21b162f..5a9b2e3eb604 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java @@ -117,7 +117,7 @@ public X unwrap(Calendar value, Class type, WrapperOptions options) { return (X) new java.sql.Date( value.getTimeInMillis() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return (X) millisToSqlTime( value.getTimeInMillis() ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { return (X) new java.sql.Timestamp( value.getTimeInMillis() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java index 077c1fdb50c9..6c8d947a5fa8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java @@ -102,7 +102,7 @@ public X unwrap(Calendar value, Class type, WrapperOptions options) { return (X) new java.sql.Date( value.getTimeInMillis() ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return (X) millisToSqlTime( value.getTimeInMillis() ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { return (X) new java.sql.Timestamp( value.getTimeInMillis() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java index 1157578d10ce..241625152e57 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java @@ -124,7 +124,7 @@ public X unwrap(Date value, Class type, WrapperOptions options) { 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() % 86_400_000 ); + : millisToSqlTime( value.getTime() ); return (X) rtn; } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java index 2bf123afbd54..6033fd5da694 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java @@ -126,7 +126,7 @@ public X unwrap(Instant instant, Class type, WrapperOptions options) { } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( instant.toEpochMilli() % 86_400_000 ); + return (X) millisToSqlTime( instant.toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 0518e05d251c..23424875488b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -120,7 +120,7 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { if ( LocalTime.class.isAssignableFrom( type ) ) { final Time time = value instanceof java.sql.Time ? ( (java.sql.Time) value ) - : new java.sql.Time( value.getTime() % 86_400_000 ); + : millisToSqlTime( value.getTime() ); final LocalTime localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { @@ -135,9 +135,7 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( Time.class.isAssignableFrom( type ) ) { - return value instanceof Time - ? value - : new Time( value.getTime() % 86_400_000 ); + return millisToSqlTime( value.getTime() ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { @@ -175,12 +173,8 @@ public Date wrap(Object value, WrapperOptions options) { return null; } - if ( value instanceof Time time ) { - return time; - } - if ( value instanceof Date date ) { - return new Time( date.getTime() % 86_400_000 ); + return millisToSqlTime( date.getTime() ); } if ( value instanceof LocalTime localTime ) { @@ -197,7 +191,7 @@ public Date wrap(Object value, WrapperOptions options) { } if ( value instanceof Calendar calendar ) { - return new Time( calendar.getTimeInMillis() % 86_400_000 ); + return millisToSqlTime( calendar.getTimeInMillis() ); } throw unknownWrap( value.getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 83473d651602..ec1aaa24a51c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -136,7 +136,7 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { if ( java.sql.Time.class.isAssignableFrom( type ) ) { return value instanceof java.sql.Time ? ( java.sql.Time ) value - : new java.sql.Time( value.getTime() ); + : millisToSqlTime( value.getTime() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index 3b3a5e9973ff..1cacf5701556 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -102,7 +102,7 @@ public X unwrap(LocalDateTime value, Class type, WrapperOptions options) if ( java.sql.Time.class.isAssignableFrom( type ) ) { Instant instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) new java.sql.Time( instant.toEpochMilli() ); + return (X) millisToSqlTime( instant.toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index 9869496bac76..90525cba5f3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -162,7 +162,7 @@ public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( offsetDateTime.toInstant().toEpochMilli() ); + return (X) millisToSqlTime( offsetDateTime.toInstant().toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index ae344c5988fe..fd9e1fb3b7b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -124,7 +124,7 @@ public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions o } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( zonedDateTime.toInstant().toEpochMilli() ); + return (X) millisToSqlTime( zonedDateTime.toInstant().toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { From 670de9200419a64403a537212d927742f03eeb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Tue, 29 Apr 2025 17:40:07 +0200 Subject: [PATCH 4/4] HHH-18532 Using java.util.Calendar instead of java.time.* clases to convert milliseconds into java.sql.Time --- .../descriptor/java/AbstractTemporalJavaType.java | 15 +++++++++------ .../type/java/DateSubclassesUnwrapTest.java | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java index 28f2168951ad..bda7ae012931 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java @@ -5,9 +5,7 @@ package org.hibernate.type.descriptor.java; import java.sql.Time; -import java.time.Instant; -import java.time.LocalTime; -import java.time.ZoneId; +import java.util.Calendar; import java.util.Comparator; import jakarta.persistence.TemporalType; @@ -65,9 +63,14 @@ private TemporalJavaType forMissingPrecision(TypeConfiguration typeConfig } public static Time millisToSqlTime(long millis) { - final LocalTime localTime = Instant.ofEpochMilli( millis ).atZone( ZoneId.systemDefault() ).toLocalTime(); - final Time time = Time.valueOf( localTime ); - time.setTime( time.getTime() + localTime.getNano() / 1_000_000 ); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis( millis ); + calendar.set(Calendar.YEAR, 1970); + calendar.set(Calendar.MONTH, 0); + calendar.set(Calendar.DAY_OF_MONTH, 1); + + final Time time = new Time(millis); + time.setTime( calendar.getTimeInMillis() ); return time; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java index 08f937ee7527..dc17ccc4bf1f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/DateSubclassesUnwrapTest.java @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifier: LGPL-2.1-or-later + * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ package org.hibernate.orm.test.mapping.type.java;