HHH-15590 Test that JdbcLiteralFormatter works for all supported java types

This commit is contained in:
Christian Beikov 2022-10-12 14:31:02 +02:00
parent b56d25c2b2
commit f13230803d
15 changed files with 550 additions and 48 deletions

View File

@ -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:

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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)" );
}
/**

View File

@ -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;

View File

@ -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:

View File

@ -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,

View File

@ -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 <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
return new JdbcLiteralFormatterUUIDData<>( javaType );
}
@Override
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
return new BasicBinder<>( javaType, this ) {

View File

@ -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<? extends T> c = (Class<? extends T>) 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<? extends T>) type;
}
c = c.getSuperclass();
} while ( c != Object.class );
if ( !clazz.isEnum() && Serializable.class.isAssignableFrom( clazz ) ) {
//noinspection unchecked
return (BindableType<? extends T>) resolveParameterBindType( Serializable.class );
}
return null;
}
@Override

View File

@ -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<SimpleDateFormat> LOCAL_DATE_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_DATE, Locale.ENGLISH ) );
private static final ThreadLocal<SimpleDateFormat> LOCAL_TIME_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIME, Locale.ENGLISH ) );
private static final ThreadLocal<SimpleDateFormat> TIME_WITH_OFFSET_FORMAT = ThreadLocal.withInitial( () -> new SimpleDateFormat( FORMAT_STRING_TIME_WITH_OFFSET, Locale.ENGLISH ) );
private static final ThreadLocal<SimpleDateFormat> 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() ) );
}
}

View File

@ -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 <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
return new JdbcLiteralFormatterUUIDData<>( javaType );
}
@Override
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
return new BasicBinder<>( javaType, this ) {

View File

@ -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<T> extends BasicJdbcLiteralFormatter<T> {
public JdbcLiteralFormatterUUIDData(JavaType<T> 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 );
}
}

View File

@ -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<Object> literalValues() throws Exception {
return List.<Object>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<Object> query = cb.createQuery();
query.from( BasicEntity.class );
query.select( cb.literal( 1 ) );
query.where( cb.isNotNull( cb.literal( literalValue ) ) );
session.createQuery( query ).list();
}
);
}
}

View File

@ -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;
}
}