use custom-rendered datetime literals on MySQL instead of JDBC escapes

This commit is contained in:
Gavin 2023-01-14 10:59:58 +01:00 committed by Gavin King
parent 2aece6fb95
commit 4a87bc4bb8
2 changed files with 126 additions and 9 deletions

View File

@ -11,6 +11,11 @@ import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
@ -104,6 +109,11 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT;
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.appendAsTimestampWithMicros;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
/**
* A {@linkplain Dialect SQL dialect} for MySQL 5.7 and above.
@ -765,6 +775,91 @@ public class MySQLDialect extends Dialect {
}
}
@Override
public boolean supportsTemporalLiteralOffset() {
return getMySQLVersion().isSameOrAfter(8,0,19);
}
@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:
appender.appendSql( "time '" );
appendAsLocalTime( appender, temporalAccessor );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
if ( temporalAccessor instanceof ZonedDateTime ) {
temporalAccessor = ((ZonedDateTime) temporalAccessor).toOffsetDateTime();
}
appender.appendSql( "timestamp '" );
appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone, false );
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:
appender.appendSql( "time '" );
appendAsLocalTime( appender, date );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
appender.appendSql( "timestamp '" );
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:
appender.appendSql( "time '" );
appendAsLocalTime( appender, calendar );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
appender.appendSql( "timestamp '" );
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
appender.appendSql( '\'' );
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public boolean supportsUnionAll() {
return true;

View File

@ -47,6 +47,7 @@ public final class DateTimeUtils {
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_NANOS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_NANOS + "XXX";
public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET_NOZ = 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 );
@ -71,6 +72,10 @@ public final class DateTimeUtils {
FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET,
Locale.ENGLISH
);
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET_NOZ = DateTimeFormatter.ofPattern(
FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET_NOZ,
Locale.ENGLISH
);
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS_AND_OFFSET = DateTimeFormatter.ofPattern(
FORMAT_STRING_TIMESTAMP_WITH_NANOS_AND_OFFSET,
Locale.ENGLISH
@ -99,15 +104,14 @@ public final class DateTimeUtils {
.optionalStart().appendZoneOrOffsetId().optionalEnd()
.toFormatter();
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,
Locale.ENGLISH
)
);
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, Locale.ENGLISH ) );
/**
* Pattern used for parsing literal offset datetimes in HQL.
@ -156,6 +160,24 @@ public final class DateTimeUtils {
);
}
public static void appendAsTimestampWithMicros(
SqlAppender appender,
TemporalAccessor temporalAccessor,
boolean supportsOffset,
TimeZone jdbcTimeZone,
boolean allowZforZeroOffset) {
appendAsTimestamp(
appender,
temporalAccessor,
supportsOffset,
jdbcTimeZone,
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS,
allowZforZeroOffset
? DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET
: DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET_NOZ
);
}
public static void appendAsTimestampWithMillis(
SqlAppender appender,
TemporalAccessor temporalAccessor,