diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 75739b47ea..30056e24bb 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -10,6 +10,7 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -86,6 +87,7 @@ import static org.hibernate.query.sqm.TemporalUnit.DAY; import static org.hibernate.query.sqm.TemporalUnit.NATIVE; import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -505,8 +507,14 @@ public class CockroachLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -528,8 +536,8 @@ public class CockroachLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, date ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -555,8 +563,8 @@ public class CockroachLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, calendar ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index 18941871de..5117c242a0 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -9,7 +9,12 @@ package org.hibernate.community.dialect; import java.sql.CallableStatement; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; import java.util.List; +import java.util.TimeZone; import org.hibernate.PessimisticLockException; import org.hibernate.boot.model.TypeContributions; @@ -96,6 +101,10 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC; import static org.hibernate.type.SqlTypes.UUID; import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARCHAR; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * A legacy {@linkplain Dialect SQL dialect} for H2. @@ -439,6 +448,105 @@ public class H2LegacyDialect extends Dialect { return "datediff(?1,?2,?3)"; } + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) && supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, date ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, date ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, calendar ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + public boolean supportsTimeWithTimeZoneLiteral() { + return getVersion().isSameOrAfter( 1, 4, 200 ); + } + @Override public boolean supportsTemporalLiteralOffset() { return true; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index ce3f617395..5190a0e221 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -11,6 +11,7 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -130,6 +131,7 @@ import static org.hibernate.type.SqlTypes.UUID; import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARCHAR; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -1057,8 +1059,14 @@ public class PostgreSQLLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -1080,8 +1088,8 @@ public class PostgreSQLLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, date ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -1107,8 +1115,8 @@ public class PostgreSQLLegacyDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, calendar ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 381a2f21d0..f4c38a7c9a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -10,6 +10,7 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -94,6 +95,7 @@ import static org.hibernate.type.SqlTypes.UUID; import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARCHAR; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -509,8 +511,14 @@ public class CockroachDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -532,8 +540,8 @@ public class CockroachDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, date ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -559,8 +567,8 @@ public class CockroachDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, calendar ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index c193780697..c61146e263 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TimeZone; +import java.util.UUID; import java.util.regex.Pattern; import org.hibernate.LockMode; @@ -4206,7 +4207,13 @@ public abstract class Dialect implements ConversionContext { appender.appendSql( literal.getSeconds() ); appender.appendSql( '.' ); appender.appendSql( literal.getNano() ); - appender.appendSql( '\'' ); + appender.appendSql( "' second" ); + } + + public void appendUUIDLiteral(SqlAppender appender, UUID literal) { + appender.appendSql( "cast('" ); + appender.appendSql( literal.toString() ); + appender.appendSql( "' as uuid)" ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 806eb1335c..6087f899a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -9,7 +9,12 @@ package org.hibernate.dialect; import java.sql.CallableStatement; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; import java.util.List; +import java.util.TimeZone; import org.hibernate.PessimisticLockException; import org.hibernate.boot.model.TypeContributions; @@ -84,6 +89,10 @@ import static org.hibernate.type.SqlTypes.UUID; import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARCHAR; import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; /** * A {@linkplain Dialect SQL dialect} for H2. @@ -406,6 +415,105 @@ public class H2Dialect extends Dialect { return "datediff(?1,?2,?3)"; } + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + TemporalAccessor temporalAccessor, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, temporalAccessor ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) && supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, date ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, date ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, date, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + @Override + public void appendDateTimeLiteral( + SqlAppender appender, + Calendar calendar, + TemporalType precision, + TimeZone jdbcTimeZone) { + switch ( precision ) { + case DATE: + appender.appendSql( "date '" ); + appendAsDate( appender, calendar ); + appender.appendSql( '\'' ); + break; + case TIME: + if ( supportsTimeWithTimeZoneLiteral() ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, calendar ); + } + appender.appendSql( '\'' ); + break; + case TIMESTAMP: + appender.appendSql( "timestamp with time zone '" ); + appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone ); + appender.appendSql( '\'' ); + break; + default: + throw new IllegalArgumentException(); + } + } + + public boolean supportsTimeWithTimeZoneLiteral() { + return getVersion().isSameOrAfter( 1, 4, 200 ); + } + @Override public boolean supportsTemporalLiteralOffset() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index fed7f64e82..117d96b85f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -11,6 +11,7 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; @@ -113,6 +114,7 @@ import static org.hibernate.type.SqlTypes.UUID; import static org.hibernate.type.SqlTypes.VARBINARY; import static org.hibernate.type.SqlTypes.VARCHAR; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; +import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -1028,8 +1030,14 @@ public class PostgreSQLDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone ); + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, temporalAccessor, true, jdbcTimeZone ); + } + else { + appender.appendSql( "time '" ); + appendAsLocalTime( appender, temporalAccessor ); + } appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -1051,8 +1059,8 @@ public class PostgreSQLDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, date ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, date, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: @@ -1078,8 +1086,8 @@ public class PostgreSQLDialect extends Dialect { appender.appendSql( '\'' ); break; case TIME: - appender.appendSql( "time '" ); - appendAsTime( appender, calendar ); + appender.appendSql( "time with time zone '" ); + appendAsTime( appender, calendar, jdbcTimeZone ); appender.appendSql( '\'' ); break; case TIMESTAMP: diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index ca60b410a1..8dafca5917 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -68,6 +68,7 @@ import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; +import java.util.UUID; import jakarta.persistence.TemporalType; @@ -785,6 +786,12 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes ); } + public void appendUUIDLiteral(SqlAppender appender, java.util.UUID literal) { + appender.appendSql( "cast('" ); + appender.appendSql( literal.toString() ); + appender.appendSql( "' as uniqueidentifier)" ); + } + @Override public void appendDateTimeLiteral( SqlAppender appender, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/VarcharUUIDJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/VarcharUUIDJdbcType.java index 879bcccb22..8cf4e2ca6e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/VarcharUUIDJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/VarcharUUIDJdbcType.java @@ -19,7 +19,9 @@ import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.BasicExtractor; +import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterUUIDData; /** * Specialized type mapping for {@link UUID} and the UUID SQL data type, @@ -52,6 +54,11 @@ public class VarcharUUIDJdbcType implements JdbcType { return UUID.class; } + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + return new JdbcLiteralFormatterUUIDData<>( javaType ); + } + @Override public ValueBinder getBinder(JavaType javaType) { return new BasicBinder<>( javaType, this ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 1c35e3b402..4fd5f236a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serializable; import java.sql.Connection; import java.util.ArrayList; import java.util.Collections; @@ -1068,9 +1069,21 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { clazz = bindValue.getClass(); } - @SuppressWarnings("unchecked") - Class c = (Class) clazz; - return resolveParameterBindType( c ); + // Resolve superclass bindable type if necessary, as we don't register types for e.g. Inet4Address + Class c = clazz; + do { + BindableType type = resolveParameterBindType( c ); + if ( type != null ) { + //noinspection unchecked + return (BindableType) type; + } + c = c.getSuperclass(); + } while ( c != Object.class ); + if ( !clazz.isEnum() && Serializable.class.isAssignableFrom( clazz ) ) { + //noinspection unchecked + return (BindableType) resolveParameterBindType( Serializable.class ); + } + return null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java index bafcc69ac4..b979c4b4de 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/DateTimeUtils.java @@ -10,6 +10,7 @@ import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; @@ -35,23 +36,33 @@ public final class DateTimeUtils { } public static final String FORMAT_STRING_DATE = "yyyy-MM-dd"; - public static final String FORMAT_STRING_TIME_WITH_OFFSET = "HH:mm:ssxxx"; + public static final String FORMAT_STRING_TIME_WITH_OFFSET = "HH:mm:ssXXX"; public static final String FORMAT_STRING_TIME = "HH:mm:ss"; public static final String FORMAT_STRING_TIMESTAMP = "yyyy-MM-dd HH:mm:ss"; public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS = FORMAT_STRING_TIMESTAMP + ".SSS"; public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS = FORMAT_STRING_TIMESTAMP + ".SSSSSS"; - public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MILLIS + "xxx"; - public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MICROS + "xxx"; + public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MILLIS + "XXX"; + public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MICROS + "XXX"; public static final DateTimeFormatter DATE_TIME_FORMATTER_DATE = DateTimeFormatter.ofPattern( FORMAT_STRING_DATE, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIME_WITH_OFFSET = DateTimeFormatter.ofPattern( FORMAT_STRING_TIME_WITH_OFFSET, Locale.ENGLISH ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIME = DateTimeFormatter.ofPattern( FORMAT_STRING_TIME, Locale.ENGLISH ); - public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS = DateTimeFormatter.ofPattern(FORMAT_STRING_TIMESTAMP_WITH_MILLIS, Locale.ENGLISH ); - public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS = DateTimeFormatter.ofPattern(FORMAT_STRING_TIMESTAMP_WITH_MICROS, Locale.ENGLISH ); + public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS = DateTimeFormatter.ofPattern( + FORMAT_STRING_TIMESTAMP_WITH_MILLIS, + Locale.ENGLISH + ); + public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS = DateTimeFormatter.ofPattern( + FORMAT_STRING_TIMESTAMP_WITH_MICROS, + Locale.ENGLISH + ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET = DateTimeFormatter.ofPattern( - FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET, Locale.ENGLISH ); + FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET, + Locale.ENGLISH + ); public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET = DateTimeFormatter.ofPattern( - FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET, Locale.ENGLISH ); + FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET, + Locale.ENGLISH + ); public static final String JDBC_ESCAPE_START_DATE = "{d '"; public static final String JDBC_ESCAPE_START_TIME = "{t '"; @@ -78,6 +89,7 @@ public final class DateTimeUtils { private static final ThreadLocal LOCAL_DATE_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_DATE, Locale.ENGLISH ) ); private static final ThreadLocal LOCAL_TIME_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ) ); + private static final ThreadLocal TIME_WITH_OFFSET_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIME_WITH_OFFSET, Locale.ENGLISH ) ); private static final ThreadLocal TIMESTAMP_WITH_MILLIS_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIMESTAMP_WITH_MILLIS, @@ -113,7 +125,7 @@ public final class DateTimeUtils { TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { - if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { if ( supportsOffset ) { DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.formatTo( temporalAccessor, appender ); } @@ -127,6 +139,23 @@ public final class DateTimeUtils { ); } } + else if ( temporalAccessor instanceof Instant ) { + if ( supportsOffset ) { + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.formatTo( + ( (Instant) temporalAccessor ).atZone( jdbcTimeZone.toZoneId() ), + appender + ); + } + else { + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo( + LocalDateTime.ofInstant( + (Instant) temporalAccessor, + jdbcTimeZone.toZoneId() + ), + appender + ); + } + } else { DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo( temporalAccessor, appender ); } @@ -137,7 +166,7 @@ public final class DateTimeUtils { TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { - if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { if ( supportsOffset ) { DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.formatTo( temporalAccessor, appender ); } @@ -151,6 +180,23 @@ public final class DateTimeUtils { ); } } + else if ( temporalAccessor instanceof Instant ) { + if ( supportsOffset ) { + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.formatTo( + ( (Instant) temporalAccessor ).atZone( jdbcTimeZone.toZoneId() ), + appender + ); + } + else { + DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo( + LocalDateTime.ofInstant( + (Instant) temporalAccessor, + jdbcTimeZone.toZoneId() + ), + appender + ); + } + } else { DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo( temporalAccessor, appender ); } @@ -165,18 +211,12 @@ public final class DateTimeUtils { TemporalAccessor temporalAccessor, boolean supportsOffset, TimeZone jdbcTimeZone) { - if ( temporalAccessor.isSupported(ChronoField.OFFSET_SECONDS) ) { + if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { if ( supportsOffset ) { DATE_TIME_FORMATTER_TIME_WITH_OFFSET.formatTo( temporalAccessor, appender ); } else { - DATE_TIME_FORMATTER_TIME.formatTo( - LocalDateTime.ofInstant( - Instant.from( temporalAccessor ), - jdbcTimeZone.toZoneId() - ), - appender - ); + DATE_TIME_FORMATTER_TIME.formatTo( LocalTime.from( temporalAccessor ), appender ); } } else { @@ -184,6 +224,10 @@ public final class DateTimeUtils { } } + public static void appendAsLocalTime(SqlAppender appender, TemporalAccessor temporalAccessor) { + DATE_TIME_FORMATTER_TIME.formatTo( temporalAccessor, appender ); + } + public static void appendAsTimestampWithMillis(SqlAppender appender, java.util.Date date, TimeZone jdbcTimeZone) { final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); @@ -220,7 +264,27 @@ public final class DateTimeUtils { appender.appendSql( LOCAL_DATE_FORMAT.get().format( date ) ); } + /** + * @deprecated Use {@link #appendAsLocalTime(SqlAppender, Date)} instead + */ + @Deprecated public static void appendAsTime(SqlAppender appender, java.util.Date date) { + appendAsLocalTime( appender, date ); + } + + public static void appendAsTime(SqlAppender appender, java.util.Date date, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIME_WITH_OFFSET_FORMAT.get(); + final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); + try { + simpleDateFormat.setTimeZone( jdbcTimeZone ); + appender.appendSql( simpleDateFormat.format( date ) ); + } + finally { + simpleDateFormat.setTimeZone( originalTimeZone ); + } + } + + public static void appendAsLocalTime(SqlAppender appender, Date date) { appender.appendSql( LOCAL_TIME_FORMAT.get().format( date ) ); } @@ -264,11 +328,19 @@ public final class DateTimeUtils { } } + /** + * @deprecated Use {@link #appendAsLocalTime(SqlAppender, Calendar)} instead + */ + @Deprecated public static void appendAsTime(SqlAppender appender, java.util.Calendar calendar) { - final SimpleDateFormat simpleDateFormat = LOCAL_TIME_FORMAT.get(); + appendAsLocalTime( appender, calendar ); + } + + public static void appendAsTime(SqlAppender appender, java.util.Calendar calendar, TimeZone jdbcTimeZone) { + final SimpleDateFormat simpleDateFormat = TIME_WITH_OFFSET_FORMAT.get(); final TimeZone originalTimeZone = simpleDateFormat.getTimeZone(); try { - simpleDateFormat.setTimeZone( calendar.getTimeZone() ); + simpleDateFormat.setTimeZone( jdbcTimeZone ); appender.appendSql( simpleDateFormat.format( calendar.getTime() ) ); } finally { @@ -276,4 +348,8 @@ public final class DateTimeUtils { } } + public static void appendAsLocalTime(SqlAppender appender, Calendar calendar) { + appender.appendSql( LOCAL_TIME_FORMAT.get().format( calendar.getTime() ) ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/UUIDJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/UUIDJdbcType.java index 1ec4872c16..aad166afb3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/UUIDJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/UUIDJdbcType.java @@ -17,6 +17,7 @@ import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterUUIDData; /** * Specialized type mapping for {@link UUID} and the UUID SQL data type. @@ -50,6 +51,11 @@ public class UUIDJdbcType implements JdbcType { return UUID.class; } + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + return new JdbcLiteralFormatterUUIDData<>( javaType ); + } + @Override public ValueBinder getBinder(JavaType javaType) { return new BasicBinder<>( javaType, this ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterUUIDData.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterUUIDData.java new file mode 100644 index 0000000000..57df01290e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/JdbcLiteralFormatterUUIDData.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.type.descriptor.jdbc.internal; + +import java.util.UUID; + +import org.hibernate.dialect.Dialect; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter; + +/** + * {@link org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter} + * implementation for handling UUID values + */ +public class JdbcLiteralFormatterUUIDData extends BasicJdbcLiteralFormatter { + + public JdbcLiteralFormatterUUIDData(JavaType javaType) { + super( javaType ); + } + + @Override + public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) { + final UUID literalValue = unwrap( value, UUID.class, wrapperOptions ); + dialect.appendUUIDLiteral( appender, literalValue ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/LiteralRenderingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/LiteralRenderingTest.java new file mode 100644 index 0000000000..182296ae27 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/contributor/LiteralRenderingTest.java @@ -0,0 +1,106 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.type.contributor; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.URL; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Year; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Currency; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; +import java.util.UUID; + +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.domain.gambit.BasicEntity; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +@ServiceRegistry +@DomainModel( standardModels = StandardDomainModel.GAMBIT ) +@SessionFactory +@TestForIssue(jiraKey = "HHH-15590") +public class LiteralRenderingTest { + + public static List literalValues() throws Exception { + return List.of( + true, + (byte) 1, + (short) 1, + (int) 1, + (long) 1, + 1f, + 1d, + 'c', + "string", + LocalDate.parse( "2000-01-01" ), + LocalDateTime.parse( "2000-01-01T01:01:01" ), + LocalTime.parse( "01:01:01" ), + LocalDateTime.parse( "2000-01-01T01:01:01" ).toInstant( ZoneOffset.UTC ), + Date.valueOf( LocalDate.parse( "2000-01-01" ) ), + Timestamp.valueOf( LocalDateTime.parse( "2000-01-01T01:01:01" ) ), + Time.valueOf( LocalTime.parse( "01:01:01" ) ), + java.util.Date.from( LocalDateTime.parse( "2000-01-01T01:01:01" ).toInstant( ZoneOffset.UTC ) ), + GregorianCalendar.from( LocalDateTime.parse( "2000-01-01T01:01:01" ).atZone( ZoneOffset.UTC ) ), + LocalDateTime.parse( "2000-01-01T01:01:01" ).atZone( ZoneOffset.UTC ), + LocalDateTime.parse( "2000-01-01T01:01:01" ).atOffset( ZoneOffset.UTC ), + LocalTime.parse( "01:01:01" ).atOffset( ZoneOffset.UTC ), + new char[]{ 'c' }, + new byte[]{ 1 }, + new Character[]{ 'c' }, + new Byte[]{ 1 }, + Locale.ENGLISH, + UUID.fromString( "53886a8a-7082-4879-b430-25cb94415be8" ), + ZoneId.of( "UTC" ), + BigDecimal.ONE, + BigInteger.ONE, + Year.of( 2000 ), + TimeZone.getTimeZone( "UTC" ), + Currency.getInstance( "EUR" ), + Duration.ofDays( 1 ), + InetAddress.getByName( "127.0.0.1" ), + new URL( "https://hibernate.org" ), + Boolean.class + ); + } + + @ParameterizedTest + @MethodSource("literalValues") + public void testIdVersionFunctions(Object literalValue, SessionFactoryScope scope) { + scope.inTransaction( + session -> { + HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + JpaCriteriaQuery query = cb.createQuery(); + query.from( BasicEntity.class ); + query.select( cb.literal( 1 ) ); + query.where( cb.isNotNull( cb.literal( literalValue ) ) ); + session.createQuery( query ).list(); + } + ); + } + +} \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/JUnitHelper.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/JUnitHelper.java index a6953ebfba..1a69859717 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/JUnitHelper.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/JUnitHelper.java @@ -37,7 +37,7 @@ public class JUnitHelper { public static boolean supportsParameterInjection(ParameterContext parameterContext, Class... supportedTypes) { for ( Class supportedType : supportedTypes ) { - if ( parameterContext.getParameter().getType().isAssignableFrom( supportedType ) ) { + if ( supportedType.isAssignableFrom( parameterContext.getParameter().getType() ) ) { return true; } }