HHH-16287 Consider hibernate.timezone.default_storage for OffsetTime typing and storage
This commit is contained in:
parent
c54f71473e
commit
e8a098ef1d
|
@ -8,7 +8,9 @@ package org.hibernate.userguide.mapping.basic;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
@ -18,6 +20,7 @@ import org.hibernate.annotations.TimeZoneColumn;
|
||||||
import org.hibernate.annotations.TimeZoneStorage;
|
import org.hibernate.annotations.TimeZoneStorage;
|
||||||
import org.hibernate.annotations.TimeZoneStorageType;
|
import org.hibernate.annotations.TimeZoneStorageType;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.dialect.H2Dialect;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
@ -47,6 +50,15 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
@ServiceRegistry(settings = @Setting( name = AvailableSettings.TIMEZONE_DEFAULT_STORAGE, value = "AUTO"))
|
@ServiceRegistry(settings = @Setting( name = AvailableSettings.TIMEZONE_DEFAULT_STORAGE, value = "AUTO"))
|
||||||
public class TimeZoneStorageMappingTests {
|
public class TimeZoneStorageMappingTests {
|
||||||
|
|
||||||
|
private static final ZoneOffset JVM_TIMEZONE_OFFSET = OffsetDateTime.now().getOffset();
|
||||||
|
private static final OffsetTime OFFSET_TIME = OffsetTime.of(
|
||||||
|
LocalTime.of(
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
),
|
||||||
|
ZoneOffset.ofHoursMinutes( 5, 45 )
|
||||||
|
);
|
||||||
private static final OffsetDateTime OFFSET_DATE_TIME = OffsetDateTime.of(
|
private static final OffsetDateTime OFFSET_DATE_TIME = OffsetDateTime.of(
|
||||||
LocalDateTime.of(
|
LocalDateTime.of(
|
||||||
2022,
|
2022,
|
||||||
|
@ -69,11 +81,12 @@ public class TimeZoneStorageMappingTests {
|
||||||
),
|
),
|
||||||
ZoneOffset.ofHoursMinutes( 5, 45 )
|
ZoneOffset.ofHoursMinutes( 5, 45 )
|
||||||
);
|
);
|
||||||
|
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern( "HH:mm:ssxxx" );
|
||||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "dd/MM/yyyy 'at' HH:mm:ssxxx" );
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "dd/MM/yyyy 'at' HH:mm:ssxxx" );
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup(SessionFactoryScope scope) {
|
public void setup(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( s -> s.persist( new TimeZoneStorageEntity( 1, OFFSET_DATE_TIME, ZONED_DATE_TIME ) ) );
|
scope.inTransaction( s -> s.persist( new TimeZoneStorageEntity( 1, OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
@ -109,26 +122,40 @@ public class TimeZoneStorageMappingTests {
|
||||||
session -> {
|
session -> {
|
||||||
List<Tuple> resultList = session.createQuery(
|
List<Tuple> resultList = session.createQuery(
|
||||||
"select " +
|
"select " +
|
||||||
|
"e.offsetTime" + suffix + ", " +
|
||||||
"e.offsetDateTime" + suffix + ", " +
|
"e.offsetDateTime" + suffix + ", " +
|
||||||
"e.zonedDateTime" + suffix + ", " +
|
"e.zonedDateTime" + suffix + ", " +
|
||||||
|
"extract(offset from e.offsetTime" + suffix + "), " +
|
||||||
"extract(offset from e.offsetDateTime" + suffix + "), " +
|
"extract(offset from e.offsetDateTime" + suffix + "), " +
|
||||||
"extract(offset from e.zonedDateTime" + suffix + "), " +
|
"extract(offset from e.zonedDateTime" + suffix + "), " +
|
||||||
|
"e.offsetTime" + suffix + " + 1 hour, " +
|
||||||
"e.offsetDateTime" + suffix + " + 1 hour, " +
|
"e.offsetDateTime" + suffix + " + 1 hour, " +
|
||||||
"e.zonedDateTime" + suffix + " + 1 hour, " +
|
"e.zonedDateTime" + suffix + " + 1 hour, " +
|
||||||
|
"e.offsetTime" + suffix + " + 1 hour - e.offsetTime" + suffix + ", " +
|
||||||
"e.offsetDateTime" + suffix + " + 1 hour - e.offsetDateTime" + suffix + ", " +
|
"e.offsetDateTime" + suffix + " + 1 hour - e.offsetDateTime" + suffix + ", " +
|
||||||
"e.zonedDateTime" + suffix + " + 1 hour - e.zonedDateTime" + suffix + ", " +
|
"e.zonedDateTime" + suffix + " + 1 hour - e.zonedDateTime" + suffix + ", " +
|
||||||
"1 from TimeZoneStorageEntity e " +
|
"1 from TimeZoneStorageEntity e " +
|
||||||
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
|
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
|
||||||
Tuple.class
|
Tuple.class
|
||||||
).getResultList();
|
).getResultList();
|
||||||
assertThat( resultList.get( 0 ).get( 0, OffsetDateTime.class ), Matchers.is( OFFSET_DATE_TIME ) );
|
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ), Matchers.is( OFFSET_TIME ) );
|
||||||
assertThat( resultList.get( 0 ).get( 1, ZonedDateTime.class ), Matchers.is( ZONED_DATE_TIME ) );
|
assertThat( resultList.get( 0 ).get( 1, OffsetDateTime.class ), Matchers.is( OFFSET_DATE_TIME ) );
|
||||||
assertThat( resultList.get( 0 ).get( 2, ZoneOffset.class ), Matchers.is( OFFSET_DATE_TIME.getOffset() ) );
|
assertThat( resultList.get( 0 ).get( 2, ZonedDateTime.class ), Matchers.is( ZONED_DATE_TIME ) );
|
||||||
assertThat( resultList.get( 0 ).get( 3, ZoneOffset.class ), Matchers.is( ZONED_DATE_TIME.getOffset() ) );
|
if ( !( scope.getSessionFactory().getJdbcServices().getDialect() instanceof H2Dialect) ) {
|
||||||
assertThat( resultList.get( 0 ).get( 4, OffsetDateTime.class ), Matchers.is( OFFSET_DATE_TIME.plusHours( 1L ) ) );
|
// H2 bug: https://github.com/h2database/h2database/issues/3757
|
||||||
assertThat( resultList.get( 0 ).get( 5, ZonedDateTime.class ), Matchers.is( ZONED_DATE_TIME.plusHours( 1L ) ) );
|
assertThat(
|
||||||
assertThat( resultList.get( 0 ).get( 6, Duration.class ), Matchers.is( Duration.ofHours( 1L ) ) );
|
resultList.get( 0 ).get( 3, ZoneOffset.class ),
|
||||||
assertThat( resultList.get( 0 ).get( 7, Duration.class ), Matchers.is( Duration.ofHours( 1L ) ) );
|
Matchers.is( OFFSET_TIME.getOffset() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertThat( resultList.get( 0 ).get( 4, ZoneOffset.class ), Matchers.is( OFFSET_DATE_TIME.getOffset() ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 5, ZoneOffset.class ), Matchers.is( ZONED_DATE_TIME.getOffset() ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 6, OffsetTime.class ), Matchers.is( OFFSET_TIME.plusHours( 1L ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 7, OffsetDateTime.class ), Matchers.is( OFFSET_DATE_TIME.plusHours( 1L ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 8, ZonedDateTime.class ), Matchers.is( ZONED_DATE_TIME.plusHours( 1L ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 9, Duration.class ), Matchers.is( Duration.ofHours( 1L ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 10, Duration.class ), Matchers.is( Duration.ofHours( 1L ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 11, Duration.class ), Matchers.is( Duration.ofHours( 1L ) ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -138,14 +165,22 @@ public class TimeZoneStorageMappingTests {
|
||||||
session -> {
|
session -> {
|
||||||
List<Tuple> resultList = session.createQuery(
|
List<Tuple> resultList = session.createQuery(
|
||||||
"select " +
|
"select " +
|
||||||
|
"format(e.offsetTime" + suffix + " as 'HH:mm:ssxxx'), " +
|
||||||
"format(e.offsetDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
|
"format(e.offsetDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
|
||||||
"format(e.zonedDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
|
"format(e.zonedDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
|
||||||
"1 from TimeZoneStorageEntity e " +
|
"1 from TimeZoneStorageEntity e " +
|
||||||
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
|
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
|
||||||
Tuple.class
|
Tuple.class
|
||||||
).getResultList();
|
).getResultList();
|
||||||
assertThat( resultList.get( 0 ).get( 0, String.class ), Matchers.is( FORMATTER.format( OFFSET_DATE_TIME ) ) );
|
if ( !( scope.getSessionFactory().getJdbcServices().getDialect() instanceof H2Dialect) ) {
|
||||||
assertThat( resultList.get( 0 ).get( 1, String.class ), Matchers.is( FORMATTER.format( ZONED_DATE_TIME ) ) );
|
// H2 bug: https://github.com/h2database/h2database/issues/3757
|
||||||
|
assertThat(
|
||||||
|
resultList.get( 0 ).get( 0, String.class ),
|
||||||
|
Matchers.is( TIME_FORMATTER.format( OFFSET_TIME ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertThat( resultList.get( 0 ).get( 1, String.class ), Matchers.is( FORMATTER.format( OFFSET_DATE_TIME ) ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 2, String.class ), Matchers.is( FORMATTER.format( ZONED_DATE_TIME ) ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -155,29 +190,48 @@ public class TimeZoneStorageMappingTests {
|
||||||
scope.inSession(
|
scope.inSession(
|
||||||
session -> {
|
session -> {
|
||||||
List<Tuple> resultList = session.createQuery(
|
List<Tuple> resultList = session.createQuery(
|
||||||
"select e.offsetDateTimeNormalized, e.zonedDateTimeNormalized, e.offsetDateTimeNormalizedUtc, e.zonedDateTimeNormalizedUtc from TimeZoneStorageEntity e",
|
"select " +
|
||||||
|
"e.offsetTimeNormalized, " +
|
||||||
|
"e.offsetDateTimeNormalized, " +
|
||||||
|
"e.zonedDateTimeNormalized, " +
|
||||||
|
"e.offsetTimeNormalizedUtc, " +
|
||||||
|
"e.offsetDateTimeNormalizedUtc, " +
|
||||||
|
"e.zonedDateTimeNormalizedUtc " +
|
||||||
|
"from TimeZoneStorageEntity e",
|
||||||
Tuple.class
|
Tuple.class
|
||||||
).getResultList();
|
).getResultList();
|
||||||
assertThat( resultList.get( 0 ).get( 0, OffsetDateTime.class ).toInstant(), Matchers.is( OFFSET_DATE_TIME.toInstant() ) );
|
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ).toLocalTime(), Matchers.is( OFFSET_TIME.withOffsetSameInstant( JVM_TIMEZONE_OFFSET ).toLocalTime() ) );
|
||||||
assertThat( resultList.get( 0 ).get( 1, ZonedDateTime.class ).toInstant(), Matchers.is( ZONED_DATE_TIME.toInstant() ) );
|
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ).getOffset(), Matchers.is( JVM_TIMEZONE_OFFSET ) );
|
||||||
assertThat( resultList.get( 0 ).get( 2, OffsetDateTime.class ).toInstant(), Matchers.is( OFFSET_DATE_TIME.toInstant() ) );
|
assertThat( resultList.get( 0 ).get( 1, OffsetDateTime.class ).toInstant(), Matchers.is( OFFSET_DATE_TIME.toInstant() ) );
|
||||||
assertThat( resultList.get( 0 ).get( 3, ZonedDateTime.class ).toInstant(), Matchers.is( ZONED_DATE_TIME.toInstant() ) );
|
assertThat( resultList.get( 0 ).get( 2, ZonedDateTime.class ).toInstant(), Matchers.is( ZONED_DATE_TIME.toInstant() ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 3, OffsetTime.class ).toLocalTime(), Matchers.is( OFFSET_TIME.withOffsetSameInstant( ZoneOffset.UTC ).toLocalTime() ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 3, OffsetTime.class ).getOffset(), Matchers.is( ZoneOffset.UTC ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 4, OffsetDateTime.class ).toInstant(), Matchers.is( OFFSET_DATE_TIME.toInstant() ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 5, ZonedDateTime.class ).toInstant(), Matchers.is( ZONED_DATE_TIME.toInstant() ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class)
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTimezoneTypes.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTimezoneTypes.class, comment = "Extracting the offset usually only makes sense if the temporal retains the offset. On DBs that have native TZ support we test this anyway to make sure it's not broken'")
|
||||||
public void testNormalizeOffset(SessionFactoryScope scope) {
|
public void testNormalizeOffset(SessionFactoryScope scope) {
|
||||||
scope.inSession(
|
scope.inSession(
|
||||||
session -> {
|
session -> {
|
||||||
List<Tuple> resultList = session.createQuery(
|
List<Tuple> resultList = session.createQuery(
|
||||||
"select extract(offset from e.offsetDateTimeNormalizedUtc), extract(offset from e.zonedDateTimeNormalizedUtc) from TimeZoneStorageEntity e",
|
"select " +
|
||||||
|
"extract(offset from e.offsetTimeNormalizedUtc), " +
|
||||||
|
"extract(offset from e.offsetDateTimeNormalizedUtc), " +
|
||||||
|
"extract(offset from e.zonedDateTimeNormalizedUtc) " +
|
||||||
|
"from TimeZoneStorageEntity e",
|
||||||
Tuple.class
|
Tuple.class
|
||||||
).getResultList();
|
).getResultList();
|
||||||
assertThat( resultList.get( 0 ).get( 0, ZoneOffset.class ), Matchers.is( ZoneOffset.systemDefault().getRules().getOffset( OFFSET_DATE_TIME.toInstant() ) ) );
|
if ( !( scope.getSessionFactory().getJdbcServices().getDialect() instanceof H2Dialect) ) {
|
||||||
|
// H2 bug: https://github.com/h2database/h2database/issues/3757
|
||||||
|
assertThat( resultList.get( 0 ).get( 0, ZoneOffset.class ), Matchers.is( ZoneOffset.UTC ) );
|
||||||
|
}
|
||||||
assertThat( resultList.get( 0 ).get( 1, ZoneOffset.class ), Matchers.is( ZoneOffset.UTC ) );
|
assertThat( resultList.get( 0 ).get( 1, ZoneOffset.class ), Matchers.is( ZoneOffset.UTC ) );
|
||||||
|
assertThat( resultList.get( 0 ).get( 2, ZoneOffset.class ), Matchers.is( ZoneOffset.UTC ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -189,6 +243,11 @@ public class TimeZoneStorageMappingTests {
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
//tag::time-zone-column-examples-mapping-example[]
|
//tag::time-zone-column-examples-mapping-example[]
|
||||||
|
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
|
||||||
|
@TimeZoneColumn(name = "birthtime_offset_offset")
|
||||||
|
@Column(name = "birthtime_offset")
|
||||||
|
private OffsetTime offsetTimeColumn;
|
||||||
|
|
||||||
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
|
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
|
||||||
@TimeZoneColumn(name = "birthday_offset_offset")
|
@TimeZoneColumn(name = "birthday_offset_offset")
|
||||||
@Column(name = "birthday_offset")
|
@Column(name = "birthday_offset")
|
||||||
|
@ -200,6 +259,10 @@ public class TimeZoneStorageMappingTests {
|
||||||
private ZonedDateTime zonedDateTimeColumn;
|
private ZonedDateTime zonedDateTimeColumn;
|
||||||
//end::time-zone-column-examples-mapping-example[]
|
//end::time-zone-column-examples-mapping-example[]
|
||||||
|
|
||||||
|
@TimeZoneStorage
|
||||||
|
@Column(name = "birthtime_offset_auto")
|
||||||
|
private OffsetTime offsetTimeAuto;
|
||||||
|
|
||||||
@TimeZoneStorage
|
@TimeZoneStorage
|
||||||
@Column(name = "birthday_offset_auto")
|
@Column(name = "birthday_offset_auto")
|
||||||
private OffsetDateTime offsetDateTimeAuto;
|
private OffsetDateTime offsetDateTimeAuto;
|
||||||
|
@ -208,6 +271,10 @@ public class TimeZoneStorageMappingTests {
|
||||||
@Column(name = "birthday_zoned_auto")
|
@Column(name = "birthday_zoned_auto")
|
||||||
private ZonedDateTime zonedDateTimeAuto;
|
private ZonedDateTime zonedDateTimeAuto;
|
||||||
|
|
||||||
|
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
|
||||||
|
@Column(name = "birthtime_offset_normalized")
|
||||||
|
private OffsetTime offsetTimeNormalized;
|
||||||
|
|
||||||
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
|
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
|
||||||
@Column(name = "birthday_offset_normalized")
|
@Column(name = "birthday_offset_normalized")
|
||||||
private OffsetDateTime offsetDateTimeNormalized;
|
private OffsetDateTime offsetDateTimeNormalized;
|
||||||
|
@ -216,6 +283,10 @@ public class TimeZoneStorageMappingTests {
|
||||||
@Column(name = "birthday_zoned_normalized")
|
@Column(name = "birthday_zoned_normalized")
|
||||||
private ZonedDateTime zonedDateTimeNormalized;
|
private ZonedDateTime zonedDateTimeNormalized;
|
||||||
|
|
||||||
|
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
|
||||||
|
@Column(name = "birthtime_offset_normalized_utc")
|
||||||
|
private OffsetTime offsetTimeNormalizedUtc;
|
||||||
|
|
||||||
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
|
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
|
||||||
@Column(name = "birthday_offset_normalized_utc")
|
@Column(name = "birthday_offset_normalized_utc")
|
||||||
private OffsetDateTime offsetDateTimeNormalizedUtc;
|
private OffsetDateTime offsetDateTimeNormalizedUtc;
|
||||||
|
@ -227,14 +298,18 @@ public class TimeZoneStorageMappingTests {
|
||||||
public TimeZoneStorageEntity() {
|
public TimeZoneStorageEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeZoneStorageEntity(Integer id, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime) {
|
public TimeZoneStorageEntity(Integer id, OffsetTime offsetTime, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.offsetTimeColumn = offsetTime;
|
||||||
this.offsetDateTimeColumn = offsetDateTime;
|
this.offsetDateTimeColumn = offsetDateTime;
|
||||||
this.zonedDateTimeColumn = zonedDateTime;
|
this.zonedDateTimeColumn = zonedDateTime;
|
||||||
|
this.offsetTimeAuto = offsetTime;
|
||||||
this.offsetDateTimeAuto = offsetDateTime;
|
this.offsetDateTimeAuto = offsetDateTime;
|
||||||
this.zonedDateTimeAuto = zonedDateTime;
|
this.zonedDateTimeAuto = zonedDateTime;
|
||||||
|
this.offsetTimeNormalized = offsetTime;
|
||||||
this.offsetDateTimeNormalized = offsetDateTime;
|
this.offsetDateTimeNormalized = offsetDateTime;
|
||||||
this.zonedDateTimeNormalized = zonedDateTime;
|
this.zonedDateTimeNormalized = zonedDateTime;
|
||||||
|
this.offsetTimeNormalizedUtc = offsetTime;
|
||||||
this.offsetDateTimeNormalizedUtc = offsetDateTime;
|
this.offsetDateTimeNormalizedUtc = offsetDateTime;
|
||||||
this.zonedDateTimeNormalizedUtc = zonedDateTime;
|
this.zonedDateTimeNormalizedUtc = zonedDateTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
|
||||||
|
@ -83,6 +84,7 @@ public class CUBRIDDialect extends Dialect {
|
||||||
//(always 3, millisecond precision)
|
//(always 3, millisecond precision)
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return "datetime";
|
return "datetime";
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "datetimetz";
|
return "datetimetz";
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -72,7 +72,7 @@ import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
@ -195,6 +195,10 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
case BLOB:
|
case BLOB:
|
||||||
return "bytes";
|
return "bytes";
|
||||||
|
|
||||||
|
// We do not use the time with timezone type because PG deprecated it and it lacks certain operations like subtraction
|
||||||
|
// case TIME_UTC:
|
||||||
|
// return columnType( TIME_WITH_TIMEZONE );
|
||||||
|
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
|
||||||
|
@ -269,6 +273,12 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TIME:
|
||||||
|
// The PostgreSQL JDBC driver reports TIME for timetz, but we use it only for mapping OffsetTime to UTC
|
||||||
|
if ( "timetz".equals( columnTypeName ) ) {
|
||||||
|
jdbcTypeCode = TIME_UTC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
||||||
if ( "timestamptz".equals( columnTypeName ) ) {
|
if ( "timestamptz".equals( columnTypeName ) ) {
|
||||||
|
@ -324,7 +334,7 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
.getJdbcTypeRegistry();
|
.getJdbcTypeRegistry();
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
|
||||||
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
||||||
|
@ -423,7 +433,13 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
|
|
||||||
functionContributions.getFunctionRegistry().register(
|
functionContributions.getFunctionRegistry().register(
|
||||||
"format",
|
"format",
|
||||||
new FormatFunction( "experimental_strftime", functionContributions.getTypeConfiguration() )
|
new FormatFunction(
|
||||||
|
"experimental_strftime",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
functionContributions.getTypeConfiguration()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
functionFactory.windowFunctions();
|
functionFactory.windowFunctions();
|
||||||
functionFactory.listagg_stringAgg( "string" );
|
functionFactory.listagg_stringAgg( "string" );
|
||||||
|
@ -757,22 +773,30 @@ public class CockroachLegacyDialect extends Dialect {
|
||||||
if ( unit == null ) {
|
if ( unit == null ) {
|
||||||
return "(?3-?2)";
|
return "(?3-?2)";
|
||||||
}
|
}
|
||||||
switch (unit) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
case YEAR:
|
|
||||||
return "(extract(year from ?3)-extract(year from ?2))";
|
|
||||||
case QUARTER:
|
|
||||||
return "(extract(year from ?3)*4-extract(year from ?2)*4+extract(month from ?3)//3-extract(month from ?2)//3)";
|
|
||||||
case MONTH:
|
|
||||||
return "(extract(year from ?3)*12-extract(year from ?2)*12+extract(month from ?3)-extract(month from ?2))";
|
|
||||||
}
|
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP ) {
|
|
||||||
// special case: subtraction of two dates
|
// special case: subtraction of two dates
|
||||||
// results in an integer number of days
|
// results in an integer number of days
|
||||||
// instead of an INTERVAL
|
// instead of an INTERVAL
|
||||||
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
switch ( unit ) {
|
||||||
|
case YEAR:
|
||||||
|
case MONTH:
|
||||||
|
case QUARTER:
|
||||||
|
// age only supports timestamptz, so we have to cast the date expressions
|
||||||
|
return "extract(" + translateDurationField( unit ) + " from age(cast(?3 as timestamptz),cast(?2 as timestamptz)))";
|
||||||
|
default:
|
||||||
|
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
|
case YEAR:
|
||||||
|
return "extract(year from ?3-?2)";
|
||||||
|
case QUARTER:
|
||||||
|
return "(extract(year from ?3-?2)*4+extract(month from ?3-?2)//3)";
|
||||||
|
case MONTH:
|
||||||
|
return "(extract(year from ?3-?2)*12+extract(month from ?3-?2))";
|
||||||
|
// Prior to v20, Cockroach didn't support extracting from an interval/duration,
|
||||||
|
// so we use the extract_duration function
|
||||||
case WEEK:
|
case WEEK:
|
||||||
return "extract_duration(hour from ?3-?2)/168";
|
return "extract_duration(hour from ?3-?2)/168";
|
||||||
case DAY:
|
case DAY:
|
||||||
|
|
|
@ -11,7 +11,11 @@ import java.sql.DatabaseMetaData;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
import java.time.temporal.TemporalAccessor;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.boot.model.FunctionContributions;
|
import org.hibernate.boot.model.FunctionContributions;
|
||||||
|
@ -96,11 +100,16 @@ import static org.hibernate.type.SqlTypes.CLOB;
|
||||||
import static org.hibernate.type.SqlTypes.DECIMAL;
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.SQLXML;
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
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.appendAsTimestampWithMillis;
|
||||||
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@linkplain Dialect SQL dialect} for DB2.
|
* A {@linkplain Dialect SQL dialect} for DB2.
|
||||||
|
@ -187,6 +196,7 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return "clob";
|
return "clob";
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp($p)";
|
return "timestamp($p)";
|
||||||
|
case TIME:
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
return "time";
|
return "time";
|
||||||
case BINARY:
|
case BINARY:
|
||||||
|
@ -416,9 +426,37 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
if ( getDB2Version().isBefore( 11 ) ) {
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
return DB2Dialect.timestampdiffPatternV10( unit, fromTemporalType, toTemporalType );
|
return DB2Dialect.timestampdiffPatternV10( unit, fromTemporalType, toTemporalType );
|
||||||
}
|
}
|
||||||
StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String fromExpression;
|
||||||
boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String toExpression;
|
||||||
|
if ( unit.isDateUnit() ) {
|
||||||
|
fromExpression = "?2";
|
||||||
|
toExpression = "?3";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch ( fromTemporalType ) {
|
||||||
|
case DATE:
|
||||||
|
fromExpression = "cast(?2 as timestamp)";
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
fromExpression = "timestamp('1970-01-01',?2)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fromExpression = "?2";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ( toTemporalType ) {
|
||||||
|
case DATE:
|
||||||
|
toExpression = "cast(?3 as timestamp)";
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
toExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
toExpression = "?3";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
|
@ -434,26 +472,24 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
default:
|
default:
|
||||||
pattern.append( "?1s_between(" );
|
pattern.append( "?1s_between(" );
|
||||||
}
|
}
|
||||||
if ( castTo ) {
|
pattern.append( toExpression );
|
||||||
pattern.append( "cast(?3 as timestamp)" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pattern.append( "?3" );
|
|
||||||
}
|
|
||||||
pattern.append( ',' );
|
pattern.append( ',' );
|
||||||
if ( castFrom ) {
|
pattern.append( fromExpression );
|
||||||
pattern.append( "cast(?2 as timestamp)" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pattern.append( "?2" );
|
|
||||||
}
|
|
||||||
pattern.append( ')' );
|
pattern.append( ')' );
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
pattern.append( "+(microsecond(?3)-microsecond(?2))/1e6)" );
|
pattern.append( "+(microsecond(");
|
||||||
|
pattern.append( toExpression );
|
||||||
|
pattern.append(")-microsecond(");
|
||||||
|
pattern.append( fromExpression );
|
||||||
|
pattern.append("))/1e6)" );
|
||||||
break;
|
break;
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
pattern.append( "*1e9+(microsecond(?3)-microsecond(?2))*1e3)" );
|
pattern.append( "*1e9+(microsecond(");
|
||||||
|
pattern.append( toExpression );
|
||||||
|
pattern.append(")-microsecond(");
|
||||||
|
pattern.append( fromExpression );
|
||||||
|
pattern.append("))*1e3)" );
|
||||||
break;
|
break;
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append( ')' );
|
pattern.append( ')' );
|
||||||
|
@ -468,19 +504,24 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
final StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
final boolean castTo;
|
final String timestampExpression;
|
||||||
if ( unit.isDateUnit() ) {
|
if ( unit.isDateUnit() ) {
|
||||||
castTo = temporalType == TemporalType.TIME;
|
if ( temporalType == TemporalType.TIME ) {
|
||||||
|
timestampExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
castTo = temporalType == TemporalType.DATE;
|
if ( temporalType == TemporalType.DATE ) {
|
||||||
}
|
timestampExpression = "cast(?3 as timestamp)";
|
||||||
if (castTo) {
|
}
|
||||||
pattern.append("cast(?3 as timestamp)");
|
else {
|
||||||
}
|
timestampExpression = "?3";
|
||||||
else {
|
}
|
||||||
pattern.append("?3");
|
|
||||||
}
|
}
|
||||||
|
pattern.append(timestampExpression);
|
||||||
pattern.append("+(");
|
pattern.append("+(");
|
||||||
// DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
|
// DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
|
@ -503,6 +544,83 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return pattern.toString();
|
return pattern.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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:
|
||||||
|
appender.appendSql( "timestamp '" );
|
||||||
|
appendAsTimestampWithNanos( appender, temporalAccessor, false, 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:
|
||||||
|
appender.appendSql( "time '" );
|
||||||
|
appendAsLocalTime( appender, date );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIMESTAMP:
|
||||||
|
appender.appendSql( "timestamp '" );
|
||||||
|
appendAsTimestampWithNanos( 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
|
@Override
|
||||||
public String getLowercaseFunction() {
|
public String getLowercaseFunction() {
|
||||||
return getDB2Version().isBefore( 9, 7 ) ? "lcase" : super.getLowercaseFunction();
|
return getDB2Version().isBefore( 9, 7 ) ? "lcase" : super.getLowercaseFunction();
|
||||||
|
@ -894,6 +1012,12 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
return "dayofweek(?2)";
|
return "dayofweek(?2)";
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
return "quarter(?2)";
|
return "quarter(?2)";
|
||||||
|
case EPOCH:
|
||||||
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
|
return timestampdiffPattern( TemporalUnit.SECOND, TemporalType.TIMESTAMP, TemporalType.TIMESTAMP )
|
||||||
|
.replace( "?2", "'1970-01-01 00:00:00'" )
|
||||||
|
.replace( "?3", "?2" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.extractPattern( unit );
|
return super.extractPattern( unit );
|
||||||
}
|
}
|
||||||
|
@ -939,4 +1063,20 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
public String getCreateUserDefinedTypeExtensionsString() {
|
public String getCreateUserDefinedTypeExtensionsString() {
|
||||||
return " instantiable mode db2sql";
|
return " instantiable mode db2sql";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The more "standard" syntax is {@code rid_bit(alias)} but here we use {@code alias.rowid}.
|
||||||
|
* <p>
|
||||||
|
* There is also an alternative {@code rid()} of type {@code bigint}, but it cannot be used
|
||||||
|
* with partitioning.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String rowId(String rowId) {
|
||||||
|
return "rowid";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int rowIdSqlType() {
|
||||||
|
return VARBINARY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,9 @@ import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hibernate.type.SqlTypes.ROWID;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SQL dialect for DB2 for z/OS, previously known as known as Db2 UDB for z/OS and Db2 UDB for z/OS and OS/390.
|
* An SQL dialect for DB2 for z/OS, previously known as known as Db2 UDB for z/OS and Db2 UDB for z/OS and OS/390.
|
||||||
|
@ -74,9 +76,13 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String columnType(int sqlTypeCode) {
|
protected String columnType(int sqlTypeCode) {
|
||||||
if ( sqlTypeCode == TIMESTAMP_WITH_TIMEZONE && getVersion().isAfter( 10 ) ) {
|
if ( getVersion().isAfter( 10 ) ) {
|
||||||
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html
|
switch ( sqlTypeCode ) {
|
||||||
return "timestamp with time zone";
|
case TIME_WITH_TIMEZONE:
|
||||||
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
|
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html
|
||||||
|
return "timestamp with time zone";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.columnType( sqlTypeCode );
|
return super.columnType( sqlTypeCode );
|
||||||
}
|
}
|
||||||
|
@ -160,14 +166,7 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
final boolean castTo;
|
|
||||||
if ( unit.isDateUnit() ) {
|
|
||||||
castTo = temporalType == TemporalType.TIME;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
castTo = temporalType == TemporalType.DATE;
|
|
||||||
}
|
|
||||||
pattern.append("add_");
|
pattern.append("add_");
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
|
@ -185,12 +184,24 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
pattern.append("?1");
|
pattern.append("?1");
|
||||||
}
|
}
|
||||||
pattern.append("s(");
|
pattern.append("s(");
|
||||||
if (castTo) {
|
final String timestampExpression;
|
||||||
pattern.append("cast(?3 as timestamp)");
|
if ( unit.isDateUnit() ) {
|
||||||
|
if ( temporalType == TemporalType.TIME ) {
|
||||||
|
timestampExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pattern.append("?3");
|
if ( temporalType == TemporalType.DATE ) {
|
||||||
|
timestampExpression = "cast(?3 as timestamp)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pattern.append(timestampExpression);
|
||||||
pattern.append(",");
|
pattern.append(",");
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
|
@ -219,4 +230,23 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I speculate that this is a correct implementation of rowids for DB2 for z/OS,
|
||||||
|
// just on the basis of the DB2 docs, but I currently have no way to test it
|
||||||
|
// Note that the implementation inherited from DB2Dialect for LUW will not work!
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rowId(String rowId) {
|
||||||
|
return rowId.isEmpty() ? "rowid_" : rowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int rowIdSqlType() {
|
||||||
|
return ROWID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRowIdColumnString(String rowId) {
|
||||||
|
return rowId( rowId ) + " rowid not null generated always";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,8 +92,10 @@ import static org.hibernate.type.SqlTypes.NCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.NCLOB;
|
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
@ -162,6 +164,10 @@ public class DerbyLegacyDialect extends Dialect {
|
||||||
case NCLOB:
|
case NCLOB:
|
||||||
return "clob";
|
return "clob";
|
||||||
|
|
||||||
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return "time";
|
||||||
|
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
|
@ -383,6 +389,8 @@ public class DerbyLegacyDialect extends Dialect {
|
||||||
return "(({fn timestampdiff(sql_tsi_day,date(char(year(?2),4)||'-01-01'),{fn timestampadd(sql_tsi_day,{fn timestampdiff(sql_tsi_day,{d '1753-01-01'},?2)}/7*7,{d '1753-01-04'})})}+7)/7)";
|
return "(({fn timestampdiff(sql_tsi_day,date(char(year(?2),4)||'-01-01'),{fn timestampadd(sql_tsi_day,{fn timestampdiff(sql_tsi_day,{d '1753-01-01'},?2)}/7*7,{d '1753-01-04'})})}+7)/7)";
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
return "((month(?2)+2)/3)";
|
return "((month(?2)+2)/3)";
|
||||||
|
case EPOCH:
|
||||||
|
return "{fn timestampdiff(sql_tsi_second,{ts '1970-01-01 00:00:00'},?2)}";
|
||||||
default:
|
default:
|
||||||
return "?1(?2)";
|
return "?1(?2)";
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class FirebirdDialect extends Dialect {
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
return getVersion().isBefore( 4, 0 ) ? "time" : super.columnType( sqlTypeCode );
|
return getVersion().isBefore( 4, 0 ) ? "time" : "time with time zone";
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return getVersion().isBefore( 4, 0 ) ? "timestamp" : "timestamp with time zone";
|
return getVersion().isBefore( 4, 0 ) ? "timestamp" : "timestamp with time zone";
|
||||||
case BINARY:
|
case BINARY:
|
||||||
|
|
|
@ -76,8 +76,11 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLe
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -88,6 +91,7 @@ import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.SECOND;
|
import static org.hibernate.query.sqm.TemporalUnit.SECOND;
|
||||||
import static org.hibernate.type.SqlTypes.ARRAY;
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BIGINT;
|
||||||
import static org.hibernate.type.SqlTypes.BINARY;
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
import static org.hibernate.type.SqlTypes.CHAR;
|
import static org.hibernate.type.SqlTypes.CHAR;
|
||||||
import static org.hibernate.type.SqlTypes.DECIMAL;
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
|
@ -104,6 +108,8 @@ import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.OTHER;
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
@ -112,7 +118,6 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A legacy {@linkplain Dialect SQL dialect} for H2.
|
* A legacy {@linkplain Dialect SQL dialect} for H2.
|
||||||
|
@ -208,6 +213,9 @@ public class H2LegacyDialect extends Dialect {
|
||||||
// which caused problems for schema update tool
|
// which caused problems for schema update tool
|
||||||
case NUMERIC:
|
case NUMERIC:
|
||||||
return getVersion().isBefore( 2 ) ? columnType( DECIMAL ) : super.columnType( sqlTypeCode );
|
return getVersion().isBefore( 2 ) ? columnType( DECIMAL ) : super.columnType( sqlTypeCode );
|
||||||
|
// Support was only added in 2.0
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return getVersion().isBefore( 2 ) ? columnType( TIMESTAMP_WITH_TIMEZONE ) : super.columnType( sqlTypeCode );
|
||||||
case NCHAR:
|
case NCHAR:
|
||||||
return columnType( CHAR );
|
return columnType( CHAR );
|
||||||
case NVARCHAR:
|
case NVARCHAR:
|
||||||
|
@ -263,7 +271,15 @@ public class H2LegacyDialect extends Dialect {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
.getJdbcTypeRegistry();
|
.getJdbcTypeRegistry();
|
||||||
|
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantJdbcType.INSTANCE );
|
if ( getVersion().isBefore( 2 ) ) {
|
||||||
|
// Support for TIME_WITH_TIMEZONE was only added in 2.0
|
||||||
|
jdbcTypeRegistry.addDescriptor( TIME_WITH_TIMEZONE, TimestampWithTimeZoneJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsJdbcTimeJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsOffsetTimeJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsInstantJdbcType.INSTANCE );
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
|
@ -878,4 +894,14 @@ public class H2LegacyDialect extends Dialect {
|
||||||
public UniqueDelegate getUniqueDelegate() {
|
public UniqueDelegate getUniqueDelegate() {
|
||||||
return uniqueDelegate;
|
return uniqueDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rowId(String rowId) {
|
||||||
|
return "_rowid_";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int rowIdSqlType() {
|
||||||
|
return BIGINT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,10 @@ import java.sql.DatabaseMetaData;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -135,6 +138,7 @@ import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@linkplain Dialect SQL dialect} for Oracle 8i and above.
|
* A {@linkplain Dialect SQL dialect} for Oracle 8i and above.
|
||||||
|
@ -153,6 +157,13 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
|
|
||||||
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
||||||
|
|
||||||
|
private static final String yqmSelect =
|
||||||
|
"( TRUNC(%2$s, 'MONTH') + NUMTOYMINTERVAL(%1$s, 'MONTH') + ( LEAST( EXTRACT( DAY FROM %2$s ), EXTRACT( DAY FROM LAST_DAY( TRUNC(%2$s, 'MONTH') + NUMTOYMINTERVAL(%1$s, 'MONTH') ) ) ) - 1 ) )";
|
||||||
|
|
||||||
|
private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
|
||||||
|
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
|
||||||
|
private static final String ADD_MONTH_EXPRESSION = String.format( yqmSelect, "?2", "?3" );
|
||||||
|
|
||||||
private final LimitHandler limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY )
|
private final LimitHandler limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY )
|
||||||
? Oracle12LimitHandler.INSTANCE
|
? Oracle12LimitHandler.INSTANCE
|
||||||
: new LegacyOracleLimitHandler( getVersion() );
|
: new LegacyOracleLimitHandler( getVersion() );
|
||||||
|
@ -316,6 +327,11 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return getVersion().isBefore( 9 ) ? currentTimestamp() : "current_timestamp";
|
return getVersion().isBefore( 9 ) ? currentTimestamp() : "current_timestamp";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInsertReturningGeneratedKeys() {
|
||||||
|
return getVersion().isSameOrAfter( 12 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Oracle doesn't have any sort of {@link Types#BOOLEAN}
|
* Oracle doesn't have any sort of {@link Types#BOOLEAN}
|
||||||
|
@ -448,6 +464,8 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return "to_number(to_char(?2,'MI'))";
|
return "to_number(to_char(?2,'MI'))";
|
||||||
case SECOND:
|
case SECOND:
|
||||||
return "to_number(to_char(?2,'SS'))";
|
return "to_number(to_char(?2,'SS'))";
|
||||||
|
case EPOCH:
|
||||||
|
return "trunc((cast(?2 at time zone 'UTC' as date) - date '1970-1-1')*86400)";
|
||||||
default:
|
default:
|
||||||
return super.extractPattern(unit);
|
return super.extractPattern(unit);
|
||||||
}
|
}
|
||||||
|
@ -455,150 +473,119 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
|
|
||||||
StringBuilder pattern = new StringBuilder();
|
StringBuilder pattern = new StringBuilder();
|
||||||
pattern.append("(?3+");
|
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case YEAR:
|
case YEAR:
|
||||||
|
pattern.append( ADD_YEAR_EXPRESSION );
|
||||||
|
break;
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
|
pattern.append( ADD_QUARTER_EXPRESSION );
|
||||||
|
break;
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append("numtoyminterval");
|
pattern.append( ADD_MONTH_EXPRESSION );
|
||||||
break;
|
break;
|
||||||
case WEEK:
|
case WEEK:
|
||||||
|
pattern.append("(?3+numtodsinterval((?2)*7,'day'))");
|
||||||
|
break;
|
||||||
case DAY:
|
case DAY:
|
||||||
case HOUR:
|
case HOUR:
|
||||||
case MINUTE:
|
case MINUTE:
|
||||||
case SECOND:
|
case SECOND:
|
||||||
|
pattern.append("(?3+numtodsinterval(?2,'?1'))");
|
||||||
|
break;
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
|
pattern.append("(?3+numtodsinterval((?2)/1e9,'second'))");
|
||||||
|
break;
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
pattern.append("numtodsinterval");
|
pattern.append("(?3+numtodsinterval(?2,'second'))");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SemanticException(unit + " is not a legal field");
|
throw new SemanticException(unit + " is not a legal field");
|
||||||
}
|
}
|
||||||
pattern.append("(");
|
|
||||||
switch ( unit ) {
|
|
||||||
case NANOSECOND:
|
|
||||||
case QUARTER:
|
|
||||||
case WEEK:
|
|
||||||
pattern.append("(");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pattern.append("?2");
|
|
||||||
switch ( unit ) {
|
|
||||||
case QUARTER:
|
|
||||||
pattern.append(")*3");
|
|
||||||
break;
|
|
||||||
case WEEK:
|
|
||||||
pattern.append(")*7");
|
|
||||||
break;
|
|
||||||
case NANOSECOND:
|
|
||||||
pattern.append(")/1e9");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pattern.append(",'");
|
|
||||||
switch ( unit ) {
|
|
||||||
case QUARTER:
|
|
||||||
pattern.append("month");
|
|
||||||
break;
|
|
||||||
case WEEK:
|
|
||||||
pattern.append("day");
|
|
||||||
break;
|
|
||||||
case NANOSECOND:
|
|
||||||
case NATIVE:
|
|
||||||
pattern.append("second");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pattern.append("?1");
|
|
||||||
}
|
|
||||||
pattern.append("')");
|
|
||||||
pattern.append(")");
|
|
||||||
return pattern.toString();
|
return pattern.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampdiffPattern(
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
TemporalUnit unit,
|
final StringBuilder pattern = new StringBuilder();
|
||||||
TemporalType fromTemporalType, TemporalType toTemporalType) {
|
final boolean hasTimePart = toTemporalType != TemporalType.DATE || fromTemporalType != TemporalType.DATE;
|
||||||
StringBuilder pattern = new StringBuilder();
|
switch ( unit ) {
|
||||||
boolean timestamp = toTemporalType == TemporalType.TIMESTAMP || fromTemporalType == TemporalType.TIMESTAMP;
|
|
||||||
switch (unit) {
|
|
||||||
case YEAR:
|
case YEAR:
|
||||||
extractField(pattern, YEAR, unit);
|
extractField( pattern, YEAR, unit );
|
||||||
break;
|
break;
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, YEAR, unit);
|
extractField( pattern, YEAR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MONTH, unit);
|
extractField( pattern, MONTH, unit );
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case WEEK:
|
case WEEK:
|
||||||
case DAY:
|
case DAY:
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
break;
|
break;
|
||||||
case HOUR:
|
case HOUR:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case MINUTE:
|
case MINUTE:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MINUTE, unit);
|
extractField( pattern, MINUTE, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
case SECOND:
|
case SECOND:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MINUTE, unit);
|
extractField( pattern, MINUTE, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, SECOND, unit);
|
extractField( pattern, SECOND, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SemanticException("unrecognized field: " + unit);
|
throw new SemanticException( "unrecognized field: " + unit );
|
||||||
}
|
}
|
||||||
return pattern.toString();
|
return pattern.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractField(
|
private void extractField(StringBuilder pattern, TemporalUnit unit, TemporalUnit toUnit) {
|
||||||
StringBuilder pattern,
|
pattern.append( "extract(" );
|
||||||
TemporalUnit unit, TemporalUnit toUnit) {
|
pattern.append( translateExtractField( unit ) );
|
||||||
pattern.append("extract(");
|
pattern.append( " from (?3-?2) " );
|
||||||
pattern.append( translateExtractField(unit) );
|
switch ( unit ) {
|
||||||
pattern.append(" from (?3-?2) ");
|
|
||||||
switch (unit) {
|
|
||||||
case YEAR:
|
case YEAR:
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append("year to month");
|
pattern.append( "year to month" );
|
||||||
break;
|
break;
|
||||||
case DAY:
|
case DAY:
|
||||||
case HOUR:
|
case HOUR:
|
||||||
case MINUTE:
|
case MINUTE:
|
||||||
case SECOND:
|
case SECOND:
|
||||||
pattern.append("day to second");
|
pattern.append( "day to second" );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SemanticException(unit + " is not a legal field");
|
throw new SemanticException( unit + " is not a legal field" );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
pattern.append( unit.conversionFactor( toUnit, this ) );
|
pattern.append( unit.conversionFactor( toUnit, this ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,8 +614,9 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return "number($p,$s)";
|
return "number($p,$s)";
|
||||||
|
|
||||||
case DATE:
|
case DATE:
|
||||||
case TIME:
|
|
||||||
return "date";
|
return "date";
|
||||||
|
case TIME:
|
||||||
|
return getVersion().isBefore( 9 ) ? "date" : super.columnType( sqlTypeCode );
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
// the only difference between date and timestamp
|
// the only difference between date and timestamp
|
||||||
// on Oracle is that date has no fractional seconds
|
// on Oracle is that date has no fractional seconds
|
||||||
|
@ -811,7 +799,7 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
typeContributions.contributeJdbcType( OracleJdbcHelper.getArrayJdbcType( serviceRegistry ) );
|
typeContributions.contributeJdbcType( OracleJdbcHelper.getArrayJdbcType( serviceRegistry ) );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
typeContributions.contributeJdbcType( ArrayJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleReflectionStructJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
// Oracle requires a custom binder for binding untyped nulls with the NULL type
|
// Oracle requires a custom binder for binding untyped nulls with the NULL type
|
||||||
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
|
||||||
|
@ -1107,24 +1095,15 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQueryHintString(String sql, String hints) {
|
public String getQueryHintString(String sql, String hints) {
|
||||||
String statementType = statementType(sql);
|
final String statementType = statementType( sql );
|
||||||
|
final int start = sql.indexOf( statementType );
|
||||||
final int pos = sql.indexOf( statementType );
|
if ( start < 0 ) {
|
||||||
if ( pos > -1 ) {
|
return sql;
|
||||||
final StringBuilder buffer = new StringBuilder( sql.length() + hints.length() + 8 );
|
}
|
||||||
if ( pos > 0 ) {
|
else {
|
||||||
buffer.append( sql, 0, pos );
|
int end = start + statementType.length();
|
||||||
}
|
return sql.substring( 0, end ) + " /*+ " + hints + " */" + sql.substring( end );
|
||||||
buffer
|
|
||||||
.append( statementType )
|
|
||||||
.append( " /*+ " )
|
|
||||||
.append( hints )
|
|
||||||
.append( " */" )
|
|
||||||
.append( sql.substring( pos + statementType.length() ) );
|
|
||||||
sql = buffer.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sql;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1161,14 +1140,15 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String statementType(String sql) {
|
|
||||||
Matcher matcher = SQL_STATEMENT_TYPE_PATTERN.matcher( sql );
|
|
||||||
|
|
||||||
|
private String statementType(String sql) {
|
||||||
|
final Matcher matcher = SQL_STATEMENT_TYPE_PATTERN.matcher( sql );
|
||||||
if ( matcher.matches() && matcher.groupCount() == 1 ) {
|
if ( matcher.matches() && matcher.groupCount() == 1 ) {
|
||||||
return matcher.group(1);
|
return matcher.group(1);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
throw new IllegalArgumentException( "Can't determine SQL statement type for statement: " + sql );
|
throw new IllegalArgumentException( "Can't determine SQL statement type for statement: " + sql );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1276,6 +1256,32 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return getWriteLockString( aliases, timeout );
|
return getWriteLockString( aliases, timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTemporalLiteralOffset() {
|
||||||
|
// Oracle *does* support offsets, but only
|
||||||
|
// in the ANSI syntax, not in the JDBC
|
||||||
|
// escape-based syntax, which we use in
|
||||||
|
// almost all circumstances (see below)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDateTimeLiteral(SqlAppender appender, TemporalAccessor temporalAccessor, TemporalType precision, TimeZone jdbcTimeZone) {
|
||||||
|
// we usually use the JDBC escape-based syntax
|
||||||
|
// because we want to let the JDBC driver handle
|
||||||
|
// TIME (a concept which does not exist in Oracle)
|
||||||
|
// but for the special case of timestamps with an
|
||||||
|
// offset we need to use the ANSI syntax
|
||||||
|
if ( precision == TemporalType.TIMESTAMP && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
|
||||||
|
appender.appendSql( "timestamp '" );
|
||||||
|
appendAsTimestampWithNanos( appender, temporalAccessor, true, jdbcTimeZone, false );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.appendDateTimeLiteral( appender, temporalAccessor, precision, jdbcTimeZone );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||||
// Unlike other databases, Oracle requires an explicit reset for the fm modifier,
|
// Unlike other databases, Oracle requires an explicit reset for the fm modifier,
|
||||||
|
@ -1432,4 +1438,9 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
public String getCreateUserDefinedTypeKindString() {
|
public String getCreateUserDefinedTypeKindString() {
|
||||||
return "object";
|
return "object";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String rowId(String rowId) {
|
||||||
|
return "rowid";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
@ -130,9 +130,11 @@ import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.OTHER;
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
import static org.hibernate.type.SqlTypes.SQLXML;
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
import static org.hibernate.type.SqlTypes.STRUCT;
|
import static org.hibernate.type.SqlTypes.STRUCT;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
@ -209,6 +211,10 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
case LONG32VARBINARY:
|
case LONG32VARBINARY:
|
||||||
return "bytea";
|
return "bytea";
|
||||||
|
|
||||||
|
// We do not use the time with timezone type because PG deprecated it and it lacks certain operations like subtraction
|
||||||
|
// case TIME_UTC:
|
||||||
|
// return columnType( TIME_WITH_TIMEZONE );
|
||||||
|
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
|
||||||
|
@ -323,6 +329,12 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TIME:
|
||||||
|
// The PostgreSQL JDBC driver reports TIME for timetz, but we use it only for mapping OffsetTime to UTC
|
||||||
|
if ( "timetz".equals( columnTypeName ) ) {
|
||||||
|
jdbcTypeCode = TIME_UTC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
||||||
if ( "timestamptz".equals( columnTypeName ) ) {
|
if ( "timestamptz".equals( columnTypeName ) ) {
|
||||||
|
@ -443,36 +455,31 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
if ( unit == null ) {
|
if ( unit == null ) {
|
||||||
return "(?3-?2)";
|
return "(?3-?2)";
|
||||||
}
|
}
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP && unit == DAY ) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
// special case: subtraction of two dates
|
// special case: subtraction of two dates
|
||||||
// results in an integer number of days
|
// results in an integer number of days
|
||||||
// instead of an INTERVAL
|
// instead of an INTERVAL
|
||||||
return "(?3-?2)";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
StringBuilder pattern = new StringBuilder();
|
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case YEAR:
|
case YEAR:
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
|
||||||
break;
|
|
||||||
case QUARTER:
|
|
||||||
pattern.append( "(" );
|
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
|
||||||
pattern.append( "+" );
|
|
||||||
extractField( pattern, QUARTER, fromTemporalType, toTemporalType, unit );
|
|
||||||
pattern.append( ")" );
|
|
||||||
break;
|
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append( "(" );
|
case QUARTER:
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
return "extract(" + translateDurationField( unit ) + " from age(?3,?2))";
|
||||||
pattern.append( "+" );
|
default:
|
||||||
extractField( pattern, MONTH, fromTemporalType, toTemporalType, unit );
|
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
||||||
pattern.append( ")" );
|
}
|
||||||
break;
|
}
|
||||||
|
else {
|
||||||
|
switch ( unit ) {
|
||||||
|
case YEAR:
|
||||||
|
return "extract(year from ?3-?2)";
|
||||||
|
case QUARTER:
|
||||||
|
return "(extract(year from ?3-?2)*4+extract(month from ?3-?2)/3)";
|
||||||
|
case MONTH:
|
||||||
|
return "(extract(year from ?3-?2)*12+extract(month from ?3-?2))";
|
||||||
case WEEK: //week is not supported by extract() when the argument is a duration
|
case WEEK: //week is not supported by extract() when the argument is a duration
|
||||||
|
return "(extract(day from ?3-?2)/7)";
|
||||||
case DAY:
|
case DAY:
|
||||||
extractField( pattern, DAY, fromTemporalType, toTemporalType, unit );
|
return "extract(day from ?3-?2)";
|
||||||
break;
|
|
||||||
//in order to avoid multiple calls to extract(),
|
//in order to avoid multiple calls to extract(),
|
||||||
//we use extract(epoch from x - y) * factor for
|
//we use extract(epoch from x - y) * factor for
|
||||||
//all the following units:
|
//all the following units:
|
||||||
|
@ -481,15 +488,14 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
case SECOND:
|
case SECOND:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
extractField( pattern, EPOCH, fromTemporalType, toTemporalType, unit );
|
return "extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this );
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new SemanticException( "unrecognized field: " + unit );
|
throw new SemanticException( "unrecognized field: " + unit );
|
||||||
}
|
}
|
||||||
return pattern.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
protected void extractField(
|
protected void extractField(
|
||||||
StringBuilder pattern,
|
StringBuilder pattern,
|
||||||
TemporalUnit unit,
|
TemporalUnit unit,
|
||||||
|
@ -499,7 +505,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
pattern.append( "extract(" );
|
pattern.append( "extract(" );
|
||||||
pattern.append( translateDurationField( unit ) );
|
pattern.append( translateDurationField( unit ) );
|
||||||
pattern.append( " from " );
|
pattern.append( " from " );
|
||||||
if ( toTimestamp != TemporalType.TIMESTAMP && fromTimestamp != TemporalType.TIMESTAMP ) {
|
if ( toTimestamp == TemporalType.DATE && fromTimestamp == TemporalType.DATE ) {
|
||||||
// special case subtraction of two
|
// special case subtraction of two
|
||||||
// dates results in an integer not
|
// dates results in an integer not
|
||||||
// an Interval
|
// an Interval
|
||||||
|
@ -1325,7 +1331,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
// dialect uses oid for Blobs, byte arrays cannot be used.
|
// dialect uses oid for Blobs, byte arrays cannot be used.
|
||||||
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
|
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
|
||||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
||||||
|
|
||||||
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||||
|
@ -1411,23 +1417,13 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
// disabled foreign key constraints still prevent 'truncate table'
|
// disabled foreign key constraints still prevent 'truncate table'
|
||||||
// (these would help if we used 'delete' instead of 'truncate')
|
// (these would help if we used 'delete' instead of 'truncate')
|
||||||
|
|
||||||
// @Override
|
@Override
|
||||||
// public String getDisableConstraintsStatement() {
|
public String rowId(String rowId) {
|
||||||
// return "set constraints all deferred";
|
return "ctid";
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// @Override
|
@Override
|
||||||
// public String getEnableConstraintsStatement() {
|
public int rowIdSqlType() {
|
||||||
// return "set constraints all immediate";
|
return OTHER;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public String getDisableConstraintStatement(String tableName, String name) {
|
|
||||||
// return "alter table " + tableName + " alter constraint " + name + " deferrable";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public String getEnableConstraintStatement(String tableName, String name) {
|
|
||||||
// return "alter table " + tableName + " alter constraint " + name + " deferrable";
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,6 @@ import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SQL dialect for Postgres Plus
|
* An SQL dialect for Postgres Plus
|
||||||
*
|
*
|
||||||
|
@ -84,12 +82,10 @@ public class PostgresPlusLegacyDialect extends PostgreSQLLegacyDialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP && unit == DAY ) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
// special case: subtraction of two dates results in an INTERVAL on Postgres Plus
|
// special case: subtraction of two dates results in an INTERVAL on Postgres Plus
|
||||||
// because there is no date type i.e. without time for Oracle compatibility
|
// because there is no date type i.e. without time for Oracle compatibility
|
||||||
final StringBuilder pattern = new StringBuilder();
|
return super.timestampdiffPattern( unit, TemporalType.TIMESTAMP, TemporalType.TIMESTAMP );
|
||||||
extractField( pattern, DAY, fromTemporalType, toTemporalType, unit );
|
|
||||||
return pattern.toString();
|
|
||||||
}
|
}
|
||||||
return super.timestampdiffPattern( unit, fromTemporalType, toTemporalType );
|
return super.timestampdiffPattern( unit, fromTemporalType, toTemporalType );
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,6 +169,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
return getVersion().isSameOrAfter( 10 ) ? "time" : super.columnType( sqlTypeCode );
|
return getVersion().isSameOrAfter( 10 ) ? "time" : super.columnType( sqlTypeCode );
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return getVersion().isSameOrAfter( 10 ) ? "datetime2($p)" : super.columnType( sqlTypeCode );
|
return getVersion().isSameOrAfter( 10 ) ? "datetime2($p)" : super.columnType( sqlTypeCode );
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return getVersion().isSameOrAfter( 10 ) ? "datetimeoffset($p)" : super.columnType( sqlTypeCode );
|
return getVersion().isSameOrAfter( 10 ) ? "datetimeoffset($p)" : super.columnType( sqlTypeCode );
|
||||||
}
|
}
|
||||||
|
@ -780,6 +781,8 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
case SECOND:
|
case SECOND:
|
||||||
//this should evaluate to a floating point type
|
//this should evaluate to a floating point type
|
||||||
return "(datepart(second,?2)+datepart(nanosecond,?2)/1e9)";
|
return "(datepart(second,?2)+datepart(nanosecond,?2)/1e9)";
|
||||||
|
case EPOCH:
|
||||||
|
return "datediff_big(second, '1970-01-01', ?2)";
|
||||||
case WEEK:
|
case WEEK:
|
||||||
// Thanks https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
|
// Thanks https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
|
||||||
if ( getVersion().isBefore( 10 ) ) {
|
if ( getVersion().isBefore( 10 ) ) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
import static org.hibernate.type.SqlTypes.TIME;
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL Dialect for Sybase/SQL Anywhere
|
* SQL Dialect for Sybase/SQL Anywhere
|
||||||
|
@ -65,6 +66,7 @@ public class SybaseAnywhereDialect extends SybaseDialect {
|
||||||
return "time";
|
return "time";
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp with time zone";
|
return "timestamp with time zone";
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
|
import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
|
||||||
|
import org.hibernate.usertype.internal.OffsetTimeCompositeUserType;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.JoinTable;
|
import jakarta.persistence.JoinTable;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
|
||||||
|
import static org.hibernate.boot.model.internal.TimeZoneStorageHelper.isOffsetTimeClass;
|
||||||
import static org.hibernate.boot.model.internal.TimeZoneStorageHelper.useColumnForTimeZoneStorage;
|
import static org.hibernate.boot.model.internal.TimeZoneStorageHelper.useColumnForTimeZoneStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -484,11 +486,19 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( useColumnForTimeZoneStorage( element, context ) ) {
|
else if ( useColumnForTimeZoneStorage( element, context ) ) {
|
||||||
final Column column = createTimestampColumn( element, path, context );
|
final Column column = createTemporalColumn( element, path, context );
|
||||||
columnOverride.put(
|
if ( isOffsetTimeClass( element ) ) {
|
||||||
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
|
columnOverride.put(
|
||||||
new Column[]{ column }
|
path + "." + OffsetTimeCompositeUserType.LOCAL_TIME_NAME,
|
||||||
);
|
new Column[] { column }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
columnOverride.put(
|
||||||
|
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
|
||||||
|
new Column[] { column }
|
||||||
|
);
|
||||||
|
}
|
||||||
final Column offsetColumn = createTimeZoneColumn( element, column );
|
final Column offsetColumn = createTimeZoneColumn( element, column );
|
||||||
columnOverride.put(
|
columnOverride.put(
|
||||||
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
|
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
|
||||||
|
@ -527,7 +537,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Column createTimestampColumn(XAnnotatedElement element, String path, MetadataBuildingContext context) {
|
private static Column createTemporalColumn(XAnnotatedElement element, String path, MetadataBuildingContext context) {
|
||||||
int precision;
|
int precision;
|
||||||
final Column annotatedColumn = element.getAnnotation( Column.class );
|
final Column annotatedColumn = element.getAnnotation( Column.class );
|
||||||
if ( annotatedColumn != null ) {
|
if ( annotatedColumn != null ) {
|
||||||
|
|
|
@ -13,9 +13,11 @@ import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.usertype.CompositeUserType;
|
import org.hibernate.usertype.CompositeUserType;
|
||||||
import org.hibernate.usertype.internal.OffsetDateTimeCompositeUserType;
|
import org.hibernate.usertype.internal.OffsetDateTimeCompositeUserType;
|
||||||
|
import org.hibernate.usertype.internal.OffsetTimeCompositeUserType;
|
||||||
import org.hibernate.usertype.internal.ZonedDateTimeCompositeUserType;
|
import org.hibernate.usertype.internal.ZonedDateTimeCompositeUserType;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
import static org.hibernate.TimeZoneStorageStrategy.COLUMN;
|
import static org.hibernate.TimeZoneStorageStrategy.COLUMN;
|
||||||
|
@ -23,6 +25,7 @@ import static org.hibernate.dialect.TimeZoneSupport.NATIVE;
|
||||||
|
|
||||||
public class TimeZoneStorageHelper {
|
public class TimeZoneStorageHelper {
|
||||||
|
|
||||||
|
private static final String OFFSET_TIME_CLASS = OffsetTime.class.getName();
|
||||||
private static final String OFFSET_DATETIME_CLASS = OffsetDateTime.class.getName();
|
private static final String OFFSET_DATETIME_CLASS = OffsetDateTime.class.getName();
|
||||||
private static final String ZONED_DATETIME_CLASS = ZonedDateTime.class.getName();
|
private static final String ZONED_DATETIME_CLASS = ZonedDateTime.class.getName();
|
||||||
|
|
||||||
|
@ -38,13 +41,29 @@ public class TimeZoneStorageHelper {
|
||||||
else if ( ZONED_DATETIME_CLASS.equals( returnedClassName ) ) {
|
else if ( ZONED_DATETIME_CLASS.equals( returnedClassName ) ) {
|
||||||
return ZonedDateTimeCompositeUserType.class;
|
return ZonedDateTimeCompositeUserType.class;
|
||||||
}
|
}
|
||||||
|
else if ( OFFSET_TIME_CLASS.equals( returnedClassName ) ) {
|
||||||
|
return OffsetTimeCompositeUserType.class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isZonedDateTimeClass(String returnedClassName) {
|
private static boolean isTemporalWithTimeZoneClass(String returnedClassName) {
|
||||||
return OFFSET_DATETIME_CLASS.equals( returnedClassName )
|
return OFFSET_DATETIME_CLASS.equals( returnedClassName )
|
||||||
|| ZONED_DATETIME_CLASS.equals( returnedClassName );
|
|| ZONED_DATETIME_CLASS.equals( returnedClassName )
|
||||||
|
|| isOffsetTimeClass( returnedClassName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOffsetTimeClass(XAnnotatedElement element) {
|
||||||
|
if ( element instanceof XProperty ) {
|
||||||
|
XProperty property = (XProperty) element;
|
||||||
|
return isOffsetTimeClass( property.getType().getName() );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isOffsetTimeClass(String returnedClassName) {
|
||||||
|
return OFFSET_TIME_CLASS.equals( returnedClassName );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean useColumnForTimeZoneStorage(XAnnotatedElement element, MetadataBuildingContext context) {
|
static boolean useColumnForTimeZoneStorage(XAnnotatedElement element, MetadataBuildingContext context) {
|
||||||
|
@ -52,7 +71,7 @@ public class TimeZoneStorageHelper {
|
||||||
if ( timeZoneStorage == null ) {
|
if ( timeZoneStorage == null ) {
|
||||||
if ( element instanceof XProperty ) {
|
if ( element instanceof XProperty ) {
|
||||||
XProperty property = (XProperty) element;
|
XProperty property = (XProperty) element;
|
||||||
return isZonedDateTimeClass( property.getType().getName() )
|
return isTemporalWithTimeZoneClass( property.getType().getName() )
|
||||||
//no @TimeZoneStorage annotation, so we need to use the default storage strategy
|
//no @TimeZoneStorage annotation, so we need to use the default storage strategy
|
||||||
&& context.getBuildingOptions().getDefaultTimeZoneStorage() == COLUMN;
|
&& context.getBuildingOptions().getDefaultTimeZoneStorage() == COLUMN;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.io.InputStream;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -65,7 +66,6 @@ import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
|
import org.hibernate.type.descriptor.jdbc.JsonAsStringJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
|
import org.hibernate.type.descriptor.jdbc.XmlAsStringJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.DdlType;
|
import org.hibernate.type.descriptor.sql.DdlType;
|
||||||
|
@ -687,7 +687,11 @@ public class MetadataBuildingProcess {
|
||||||
|
|
||||||
final JdbcType timestampWithTimeZoneOverride = getTimestampWithTimeZoneOverride( options, jdbcTypeRegistry );
|
final JdbcType timestampWithTimeZoneOverride = getTimestampWithTimeZoneOverride( options, jdbcTypeRegistry );
|
||||||
if ( timestampWithTimeZoneOverride != null ) {
|
if ( timestampWithTimeZoneOverride != null ) {
|
||||||
adaptToDefaultTimeZoneStorage( typeConfiguration, timestampWithTimeZoneOverride );
|
adaptTimestampTypesToDefaultTimeZoneStorage( typeConfiguration, timestampWithTimeZoneOverride );
|
||||||
|
}
|
||||||
|
final JdbcType timeWithTimeZoneOverride = getTimeWithTimeZoneOverride( options, jdbcTypeRegistry );
|
||||||
|
if ( timeWithTimeZoneOverride != null ) {
|
||||||
|
adaptTimeTypesToDefaultTimeZoneStorage( typeConfiguration, timeWithTimeZoneOverride );
|
||||||
}
|
}
|
||||||
final int preferredSqlTypeCodeForInstant = getPreferredSqlTypeCodeForInstant( serviceRegistry );
|
final int preferredSqlTypeCodeForInstant = getPreferredSqlTypeCodeForInstant( serviceRegistry );
|
||||||
if ( preferredSqlTypeCodeForInstant != SqlTypes.TIMESTAMP_UTC ) {
|
if ( preferredSqlTypeCodeForInstant != SqlTypes.TIMESTAMP_UTC ) {
|
||||||
|
@ -728,7 +732,25 @@ public class MetadataBuildingProcess {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adaptToDefaultTimeZoneStorage(
|
private static void adaptTimeTypesToDefaultTimeZoneStorage(
|
||||||
|
TypeConfiguration typeConfiguration,
|
||||||
|
JdbcType timestampWithTimeZoneOverride) {
|
||||||
|
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
|
||||||
|
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
||||||
|
final BasicType<?> offsetDateTimeType = new NamedBasicTypeImpl<>(
|
||||||
|
javaTypeRegistry.getDescriptor( OffsetTime.class ),
|
||||||
|
timestampWithTimeZoneOverride,
|
||||||
|
"OffsetTime"
|
||||||
|
);
|
||||||
|
basicTypeRegistry.register(
|
||||||
|
offsetDateTimeType,
|
||||||
|
"org.hibernate.type.OffsetTimeType",
|
||||||
|
OffsetTime.class.getSimpleName(),
|
||||||
|
OffsetTime.class.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void adaptTimestampTypesToDefaultTimeZoneStorage(
|
||||||
TypeConfiguration typeConfiguration,
|
TypeConfiguration typeConfiguration,
|
||||||
JdbcType timestampWithTimeZoneOverride) {
|
JdbcType timestampWithTimeZoneOverride) {
|
||||||
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
|
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
|
||||||
|
@ -757,6 +779,19 @@ public class MetadataBuildingProcess {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static JdbcType getTimeWithTimeZoneOverride(MetadataBuildingOptions options, JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
|
switch ( options.getDefaultTimeZoneStorage() ) {
|
||||||
|
case NORMALIZE:
|
||||||
|
// For NORMALIZE, we replace the standard types that use TIME_WITH_TIMEZONE to use TIME
|
||||||
|
return jdbcTypeRegistry.getDescriptor( Types.TIME );
|
||||||
|
case NORMALIZE_UTC:
|
||||||
|
// For NORMALIZE_UTC, we replace the standard types that use TIME_WITH_TIMEZONE to use TIME_UTC
|
||||||
|
return jdbcTypeRegistry.getDescriptor( SqlTypes.TIME_UTC );
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static JdbcType getTimestampWithTimeZoneOverride(MetadataBuildingOptions options, JdbcTypeRegistry jdbcTypeRegistry) {
|
private static JdbcType getTimestampWithTimeZoneOverride(MetadataBuildingOptions options, JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
switch ( options.getDefaultTimeZoneStorage() ) {
|
switch ( options.getDefaultTimeZoneStorage() ) {
|
||||||
case NORMALIZE:
|
case NORMALIZE:
|
||||||
|
|
|
@ -195,6 +195,7 @@ public class ColumnOrderingStrategyStandard implements ColumnOrderingStrategy {
|
||||||
return (int) length;
|
return (int) length;
|
||||||
case DATE:
|
case DATE:
|
||||||
case TIME:
|
case TIME:
|
||||||
|
case TIME_UTC:
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
return 4;
|
return 4;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
|
|
|
@ -137,8 +137,10 @@ import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.POINT;
|
import static org.hibernate.type.SqlTypes.POINT;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
||||||
|
@ -254,6 +256,9 @@ public abstract class AbstractHANADialect extends Dialect {
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
return "double";
|
return "double";
|
||||||
//no explicit precision
|
//no explicit precision
|
||||||
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return "time";
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
|
@ -1196,19 +1201,19 @@ public abstract class AbstractHANADialect extends Dialect {
|
||||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
// if ( temporalType == TemporalType.TIME ) {
|
if ( fromTemporalType == TemporalType.TIME && toTemporalType == TemporalType.TIME ) {
|
||||||
// return "nano100_between(cast(?3 as timestamp), cast(?2 as timestamp))*100";
|
return "seconds_between(?2,?3)*1000000000";
|
||||||
// }
|
}
|
||||||
// else {
|
else {
|
||||||
return "nano100_between(?2,?3)*100";
|
return "nano100_between(?2,?3)*100";
|
||||||
// }
|
}
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
// if ( temporalType == TemporalType.TIME ) {
|
if ( fromTemporalType == TemporalType.TIME && toTemporalType == TemporalType.TIME ) {
|
||||||
// return "nano100_between(cast(?3 as timestamp), cast(?2 as timestamp))";
|
return "seconds_between(?2,?3)*10000000";
|
||||||
// }
|
}
|
||||||
// else {
|
else {
|
||||||
return "nano100_between(?2,?3)";
|
return "nano100_between(?2,?3)";
|
||||||
// }
|
}
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
return "months_between(?2,?3)/3";
|
return "months_between(?2,?3)/3";
|
||||||
case WEEK:
|
case WEEK:
|
||||||
|
|
|
@ -322,6 +322,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements AggregateJdbcT
|
||||||
break;
|
break;
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
values[column] = fromRawObject(
|
values[column] = fromRawObject(
|
||||||
jdbcMapping,
|
jdbcMapping,
|
||||||
parseTime(
|
parseTime(
|
||||||
|
@ -804,6 +805,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements AggregateJdbcT
|
||||||
case SqlTypes.DATE:
|
case SqlTypes.DATE:
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
case SqlTypes.TIMESTAMP:
|
case SqlTypes.TIMESTAMP:
|
||||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIMESTAMP_UTC:
|
case SqlTypes.TIMESTAMP_UTC:
|
||||||
|
@ -940,6 +942,8 @@ public abstract class AbstractPostgreSQLStructJdbcType implements AggregateJdbcT
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
if ( value instanceof java.util.Date ) {
|
if ( value instanceof java.util.Date ) {
|
||||||
appendAsTime( appender, (java.util.Date) value, jdbcTimeZone );
|
appendAsTime( appender, (java.util.Date) value, jdbcTimeZone );
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.sqm.IntervalType;
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
import org.hibernate.query.sqm.NullOrdering;
|
import org.hibernate.query.sqm.NullOrdering;
|
||||||
import org.hibernate.query.sqm.TemporalUnit;
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
@ -58,7 +59,7 @@ import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
@ -76,7 +77,9 @@ import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||||
|
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
|
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
|
||||||
|
import static org.hibernate.query.sqm.TemporalUnit.YEAR;
|
||||||
import static org.hibernate.type.SqlTypes.ARRAY;
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
import static org.hibernate.type.SqlTypes.BINARY;
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
import static org.hibernate.type.SqlTypes.BLOB;
|
import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
|
@ -94,9 +97,11 @@ import static org.hibernate.type.SqlTypes.NCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.NCLOB;
|
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.OTHER;
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
@ -212,6 +217,10 @@ public class CockroachDialect extends Dialect {
|
||||||
case BLOB:
|
case BLOB:
|
||||||
return "bytes";
|
return "bytes";
|
||||||
|
|
||||||
|
// We do not use the time with timezone type because PG deprecated it and it lacks certain operations like subtraction
|
||||||
|
// case TIME_UTC:
|
||||||
|
// return columnType( TIME_WITH_TIMEZONE );
|
||||||
|
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
|
||||||
|
@ -284,6 +293,12 @@ public class CockroachDialect extends Dialect {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TIME:
|
||||||
|
// The PostgreSQL JDBC driver reports TIME for timetz, but we use it only for mapping OffsetTime to UTC
|
||||||
|
if ( "timetz".equals( columnTypeName ) ) {
|
||||||
|
jdbcTypeCode = TIME_UTC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
||||||
if ( "timestamptz".equals( columnTypeName ) ) {
|
if ( "timestamptz".equals( columnTypeName ) ) {
|
||||||
|
@ -339,7 +354,7 @@ public class CockroachDialect extends Dialect {
|
||||||
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
.getJdbcTypeRegistry();
|
.getJdbcTypeRegistry();
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
|
||||||
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {
|
||||||
|
@ -422,7 +437,13 @@ public class CockroachDialect extends Dialect {
|
||||||
|
|
||||||
functionContributions.getFunctionRegistry().register(
|
functionContributions.getFunctionRegistry().register(
|
||||||
"format",
|
"format",
|
||||||
new FormatFunction( "experimental_strftime", functionContributions.getTypeConfiguration() )
|
new FormatFunction(
|
||||||
|
"experimental_strftime",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
functionContributions.getTypeConfiguration()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
functionFactory.windowFunctions();
|
functionFactory.windowFunctions();
|
||||||
functionFactory.listagg_stringAgg( "string" );
|
functionFactory.listagg_stringAgg( "string" );
|
||||||
|
@ -756,30 +777,46 @@ public class CockroachDialect extends Dialect {
|
||||||
if ( unit == null ) {
|
if ( unit == null ) {
|
||||||
return "(?3-?2)";
|
return "(?3-?2)";
|
||||||
}
|
}
|
||||||
switch (unit) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
case YEAR:
|
|
||||||
return "(extract(year from ?3)-extract(year from ?2))";
|
|
||||||
case QUARTER:
|
|
||||||
return "(extract(year from ?3)*4-extract(year from ?2)*4+extract(month from ?3)//3-extract(month from ?2)//3)";
|
|
||||||
case MONTH:
|
|
||||||
return "(extract(year from ?3)*12-extract(year from ?2)*12+extract(month from ?3)-extract(month from ?2))";
|
|
||||||
}
|
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP ) {
|
|
||||||
// special case: subtraction of two dates
|
// special case: subtraction of two dates
|
||||||
// results in an integer number of days
|
// results in an integer number of days
|
||||||
// instead of an INTERVAL
|
// instead of an INTERVAL
|
||||||
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
switch ( unit ) {
|
||||||
|
case YEAR:
|
||||||
|
case MONTH:
|
||||||
|
case QUARTER:
|
||||||
|
// age only supports timestamptz, so we have to cast the date expressions
|
||||||
|
return "extract(" + translateDurationField( unit ) + " from age(cast(?3 as timestamptz),cast(?2 as timestamptz)))";
|
||||||
|
default:
|
||||||
|
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case WEEK:
|
case YEAR:
|
||||||
return "extract_duration(hour from ?3-?2)/168";
|
return "extract(year from ?3-?2)";
|
||||||
|
case QUARTER:
|
||||||
|
return "(extract(year from ?3-?2)*4+extract(month from ?3-?2)//3)";
|
||||||
|
case MONTH:
|
||||||
|
return "(extract(year from ?3-?2)*12+extract(month from ?3-?2))";
|
||||||
|
case WEEK: //week is not supported by extract() when the argument is a duration
|
||||||
|
return "(extract(day from ?3-?2)/7)";
|
||||||
case DAY:
|
case DAY:
|
||||||
return "extract_duration(hour from ?3-?2)/24";
|
return "extract(day from ?3-?2)";
|
||||||
|
//in order to avoid multiple calls to extract(),
|
||||||
|
//we use extract(epoch from x - y) * factor for
|
||||||
|
//all the following units:
|
||||||
|
|
||||||
|
// Note that CockroachDB also has an extract_duration function which returns an int,
|
||||||
|
// but we don't use that here because it is deprecated since v20
|
||||||
|
case HOUR:
|
||||||
|
case MINUTE:
|
||||||
|
case SECOND:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
return "extract_duration(microsecond from ?3-?2)*1e3";
|
case NATIVE:
|
||||||
|
return "cast(extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this ) + " as int)";
|
||||||
default:
|
default:
|
||||||
return "extract_duration(?1 from ?3-?2)";
|
throw new SemanticException( "unrecognized field: " + unit );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ import static org.hibernate.type.SqlTypes.CLOB;
|
||||||
import static org.hibernate.type.SqlTypes.DECIMAL;
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.SQLXML;
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
|
@ -189,6 +190,7 @@ public class DB2Dialect extends Dialect {
|
||||||
return "clob";
|
return "clob";
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp($p)";
|
return "timestamp($p)";
|
||||||
|
case TIME:
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
return "time";
|
return "time";
|
||||||
case BINARY:
|
case BINARY:
|
||||||
|
@ -408,9 +410,37 @@ public class DB2Dialect extends Dialect {
|
||||||
if ( getDB2Version().isBefore( 11 ) ) {
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
return timestampdiffPatternV10( unit, fromTemporalType, toTemporalType );
|
return timestampdiffPatternV10( unit, fromTemporalType, toTemporalType );
|
||||||
}
|
}
|
||||||
StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String fromExpression;
|
||||||
boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String toExpression;
|
||||||
|
if ( unit.isDateUnit() ) {
|
||||||
|
fromExpression = "?2";
|
||||||
|
toExpression = "?3";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch ( fromTemporalType ) {
|
||||||
|
case DATE:
|
||||||
|
fromExpression = "cast(?2 as timestamp)";
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
fromExpression = "timestamp('1970-01-01',?2)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fromExpression = "?2";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ( toTemporalType ) {
|
||||||
|
case DATE:
|
||||||
|
toExpression = "cast(?3 as timestamp)";
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
toExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
toExpression = "?3";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
|
@ -426,26 +456,24 @@ public class DB2Dialect extends Dialect {
|
||||||
default:
|
default:
|
||||||
pattern.append( "?1s_between(" );
|
pattern.append( "?1s_between(" );
|
||||||
}
|
}
|
||||||
if ( castTo ) {
|
pattern.append( toExpression );
|
||||||
pattern.append( "cast(?3 as timestamp)" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pattern.append( "?3" );
|
|
||||||
}
|
|
||||||
pattern.append( ',' );
|
pattern.append( ',' );
|
||||||
if ( castFrom ) {
|
pattern.append( fromExpression );
|
||||||
pattern.append( "cast(?2 as timestamp)" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pattern.append( "?2" );
|
|
||||||
}
|
|
||||||
pattern.append( ')' );
|
pattern.append( ')' );
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
pattern.append( "+(microsecond(?3)-microsecond(?2))/1e6)" );
|
pattern.append( "+(microsecond(");
|
||||||
|
pattern.append( toExpression );
|
||||||
|
pattern.append(")-microsecond(");
|
||||||
|
pattern.append( fromExpression );
|
||||||
|
pattern.append("))/1e6)" );
|
||||||
break;
|
break;
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
pattern.append( "*1e9+(microsecond(?3)-microsecond(?2))*1e3)" );
|
pattern.append( "*1e9+(microsecond(");
|
||||||
|
pattern.append( toExpression );
|
||||||
|
pattern.append(")-microsecond(");
|
||||||
|
pattern.append( fromExpression );
|
||||||
|
pattern.append("))*1e3)" );
|
||||||
break;
|
break;
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append( ')' );
|
pattern.append( ')' );
|
||||||
|
@ -458,10 +486,36 @@ public class DB2Dialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String timestampdiffPatternV10(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
public static String timestampdiffPatternV10(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
final boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String fromExpression;
|
||||||
final boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
|
final String toExpression;
|
||||||
final String fromExpression = castFrom ? "cast(?2 as timestamp)" : "?2";
|
if ( unit.isDateUnit() ) {
|
||||||
final String toExpression = castTo ? "cast(?3 as timestamp)" : "?3";
|
if ( fromTemporalType == TemporalType.TIME ) {
|
||||||
|
fromExpression = "timestamp('1970-01-01',?2)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fromExpression = "?2";
|
||||||
|
}
|
||||||
|
if ( toTemporalType == TemporalType.TIME ) {
|
||||||
|
toExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toExpression = "?3";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( fromTemporalType == TemporalType.DATE ) {
|
||||||
|
fromExpression = "cast(?2 as timestamp)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fromExpression = "?2";
|
||||||
|
}
|
||||||
|
if ( toTemporalType == TemporalType.DATE ) {
|
||||||
|
toExpression = "cast(?3 as timestamp)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toExpression = "?3";
|
||||||
|
}
|
||||||
|
}
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1))+(microsecond(t2)-microsecond(t1))/1e6 " +
|
return "(select (days(t2)-days(t1))*86400+(midnight_seconds(t2)-midnight_seconds(t1))+(microsecond(t2)-microsecond(t1))/1e6 " +
|
||||||
|
@ -498,19 +552,24 @@ public class DB2Dialect extends Dialect {
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
final StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
final boolean castTo;
|
final String timestampExpression;
|
||||||
if ( unit.isDateUnit() ) {
|
if ( unit.isDateUnit() ) {
|
||||||
castTo = temporalType == TemporalType.TIME;
|
if ( temporalType == TemporalType.TIME ) {
|
||||||
|
timestampExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
castTo = temporalType == TemporalType.DATE;
|
if ( temporalType == TemporalType.DATE ) {
|
||||||
}
|
timestampExpression = "cast(?3 as timestamp)";
|
||||||
if (castTo) {
|
}
|
||||||
pattern.append("cast(?3 as timestamp)");
|
else {
|
||||||
}
|
timestampExpression = "?3";
|
||||||
else {
|
}
|
||||||
pattern.append("?3");
|
|
||||||
}
|
}
|
||||||
|
pattern.append(timestampExpression);
|
||||||
pattern.append("+(");
|
pattern.append("+(");
|
||||||
// DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
|
// DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.List;
|
||||||
|
|
||||||
import static org.hibernate.type.SqlTypes.ROWID;
|
import static org.hibernate.type.SqlTypes.ROWID;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQL dialect for DB2 for z/OS version 12.1 and above, previously known as:
|
* A SQL dialect for DB2 for z/OS version 12.1 and above, previously known as:
|
||||||
|
@ -78,9 +79,13 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String columnType(int sqlTypeCode) {
|
protected String columnType(int sqlTypeCode) {
|
||||||
if ( sqlTypeCode == TIMESTAMP_WITH_TIMEZONE && getVersion().isAfter( 10 ) ) {
|
if ( getVersion().isAfter( 10 ) ) {
|
||||||
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html
|
switch ( sqlTypeCode ) {
|
||||||
return "timestamp with time zone";
|
case TIME_WITH_TIMEZONE:
|
||||||
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
|
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html
|
||||||
|
return "timestamp with time zone";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.columnType( sqlTypeCode );
|
return super.columnType( sqlTypeCode );
|
||||||
}
|
}
|
||||||
|
@ -150,14 +155,7 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
final boolean castTo;
|
|
||||||
if ( unit.isDateUnit() ) {
|
|
||||||
castTo = temporalType == TemporalType.TIME;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
castTo = temporalType == TemporalType.DATE;
|
|
||||||
}
|
|
||||||
pattern.append("add_");
|
pattern.append("add_");
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
|
@ -175,12 +173,24 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
pattern.append("?1");
|
pattern.append("?1");
|
||||||
}
|
}
|
||||||
pattern.append("s(");
|
pattern.append("s(");
|
||||||
if (castTo) {
|
final String timestampExpression;
|
||||||
pattern.append("cast(?3 as timestamp)");
|
if ( unit.isDateUnit() ) {
|
||||||
|
if ( temporalType == TemporalType.TIME ) {
|
||||||
|
timestampExpression = "timestamp('1970-01-01',?3)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pattern.append("?3");
|
if ( temporalType == TemporalType.DATE ) {
|
||||||
|
timestampExpression = "cast(?3 as timestamp)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timestampExpression = "?3";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pattern.append(timestampExpression);
|
||||||
pattern.append(",");
|
pattern.append(",");
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
|
|
|
@ -85,8 +85,10 @@ import static org.hibernate.type.SqlTypes.NCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.NCLOB;
|
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
@ -154,6 +156,10 @@ public class DerbyDialect extends Dialect {
|
||||||
case NCLOB:
|
case NCLOB:
|
||||||
return "clob";
|
return "clob";
|
||||||
|
|
||||||
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return "time";
|
||||||
|
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
|
|
|
@ -178,14 +178,16 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.LongNVarcharJdbcType;
|
import org.hibernate.type.descriptor.jdbc.LongNVarcharJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NCharJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NCharJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NVarcharJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NVarcharJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -229,6 +231,7 @@ import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
@ -404,6 +407,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( DATE ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( DATE ) );
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME ) );
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME_WITH_TIMEZONE ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME_WITH_TIMEZONE ) );
|
||||||
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME_UTC ) );
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP ) );
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_WITH_TIMEZONE ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_WITH_TIMEZONE ) );
|
||||||
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_UTC ) );
|
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_UTC ) );
|
||||||
|
@ -534,17 +538,21 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
case DATE:
|
case DATE:
|
||||||
return "date";
|
return "date";
|
||||||
case TIME:
|
case TIME:
|
||||||
return "time";
|
return "time($p)";
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
// type included here for completeness but note that
|
// type included here for completeness but note that
|
||||||
// very few databases support it, and the general
|
// very few databases support it, and the general
|
||||||
// advice is to caution against its use (for reasons,
|
// advice is to caution against its use (for reasons,
|
||||||
// check the comments in the Postgres documentation).
|
// check the comments in the Postgres documentation).
|
||||||
return "time with time zone";
|
return "time($p) with time zone";
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return "timestamp($p)";
|
return "timestamp($p)";
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "timestamp($p) with time zone";
|
return "timestamp($p) with time zone";
|
||||||
|
case TIME_UTC:
|
||||||
|
return getTimeZoneSupport() == TimeZoneSupport.NATIVE
|
||||||
|
? columnType( TIME_WITH_TIMEZONE )
|
||||||
|
: columnType( TIME );
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
return getTimeZoneSupport() == TimeZoneSupport.NATIVE
|
return getTimeZoneSupport() == TimeZoneSupport.NATIVE
|
||||||
? columnType( TIMESTAMP_WITH_TIMEZONE )
|
? columnType( TIMESTAMP_WITH_TIMEZONE )
|
||||||
|
@ -1555,10 +1563,12 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
|
if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
|
||||||
jdbcTypeRegistry.addDescriptor( InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsOffsetTimeJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jdbcTypeRegistry.addDescriptor( InstantAsTimestampJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TimestampUtcAsJdbcTimestampJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsJdbcTimeJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( supportsStandardArrays() ) {
|
if ( supportsStandardArrays() ) {
|
||||||
|
@ -4899,6 +4909,9 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
precision = (int) ceil( precision * LOG_BASE2OF10 );
|
precision = (int) ceil( precision * LOG_BASE2OF10 );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SqlTypes.TIME:
|
||||||
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
case SqlTypes.TIMESTAMP:
|
case SqlTypes.TIMESTAMP:
|
||||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIMESTAMP_UTC:
|
case SqlTypes.TIMESTAMP_UTC:
|
||||||
|
|
|
@ -69,8 +69,11 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2
|
||||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
@ -98,6 +101,9 @@ import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.OTHER;
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
@ -190,6 +196,9 @@ public class H2Dialect extends Dialect {
|
||||||
// which caused problems for schema update tool
|
// which caused problems for schema update tool
|
||||||
case NUMERIC:
|
case NUMERIC:
|
||||||
return getVersion().isBefore( 2 ) ? columnType( DECIMAL ) : super.columnType( sqlTypeCode );
|
return getVersion().isBefore( 2 ) ? columnType( DECIMAL ) : super.columnType( sqlTypeCode );
|
||||||
|
// Support was only added in 2.0
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return getVersion().isBefore( 2 ) ? columnType( TIMESTAMP_WITH_TIMEZONE ) : super.columnType( sqlTypeCode );
|
||||||
case NCHAR:
|
case NCHAR:
|
||||||
return columnType( CHAR );
|
return columnType( CHAR );
|
||||||
case NVARCHAR:
|
case NVARCHAR:
|
||||||
|
@ -243,7 +252,15 @@ public class H2Dialect extends Dialect {
|
||||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
.getJdbcTypeRegistry();
|
.getJdbcTypeRegistry();
|
||||||
|
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantJdbcType.INSTANCE );
|
if ( getVersion().isBefore( 2 ) ) {
|
||||||
|
// Support for TIME_WITH_TIMEZONE was only added in 2.0
|
||||||
|
jdbcTypeRegistry.addDescriptor( TIME_WITH_TIMEZONE, TimestampWithTimeZoneJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsJdbcTimeJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimeUtcAsOffsetTimeJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
jdbcTypeRegistry.addDescriptor( TimestampUtcAsInstantJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||||
|
|
|
@ -164,6 +164,7 @@ public class JsonHelper {
|
||||||
break;
|
break;
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
appender.append( '"' );
|
appender.append( '"' );
|
||||||
JdbcTimeJavaType.INSTANCE.appendEncodedString(
|
JdbcTimeJavaType.INSTANCE.appendEncodedString(
|
||||||
appender,
|
appender,
|
||||||
|
@ -798,6 +799,7 @@ public class JsonHelper {
|
||||||
);
|
);
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
return jdbcMapping.getJdbcJavaType().wrap(
|
return jdbcMapping.getJdbcJavaType().wrap(
|
||||||
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
||||||
string,
|
string,
|
||||||
|
|
|
@ -529,86 +529,84 @@ public class OracleDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
StringBuilder pattern = new StringBuilder();
|
final StringBuilder pattern = new StringBuilder();
|
||||||
boolean timestamp = toTemporalType == TemporalType.TIMESTAMP || fromTemporalType == TemporalType.TIMESTAMP;
|
final boolean hasTimePart = toTemporalType != TemporalType.DATE || fromTemporalType != TemporalType.DATE;
|
||||||
switch (unit) {
|
switch ( unit ) {
|
||||||
case YEAR:
|
case YEAR:
|
||||||
extractField(pattern, YEAR, unit);
|
extractField( pattern, YEAR, unit );
|
||||||
break;
|
break;
|
||||||
case QUARTER:
|
case QUARTER:
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, YEAR, unit);
|
extractField( pattern, YEAR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MONTH, unit);
|
extractField( pattern, MONTH, unit );
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case WEEK:
|
case WEEK:
|
||||||
case DAY:
|
case DAY:
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
break;
|
break;
|
||||||
case HOUR:
|
case HOUR:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case MINUTE:
|
case MINUTE:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MINUTE, unit);
|
extractField( pattern, MINUTE, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
case SECOND:
|
case SECOND:
|
||||||
pattern.append("(");
|
pattern.append( "(" );
|
||||||
extractField(pattern, DAY, unit);
|
extractField( pattern, DAY, unit );
|
||||||
if (timestamp) {
|
if ( hasTimePart ) {
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, HOUR, unit);
|
extractField( pattern, HOUR, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, MINUTE, unit);
|
extractField( pattern, MINUTE, unit );
|
||||||
pattern.append("+");
|
pattern.append( "+" );
|
||||||
extractField(pattern, SECOND, unit);
|
extractField( pattern, SECOND, unit );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SemanticException("unrecognized field: " + unit);
|
throw new SemanticException( "unrecognized field: " + unit );
|
||||||
}
|
}
|
||||||
return pattern.toString();
|
return pattern.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractField(
|
private void extractField(StringBuilder pattern, TemporalUnit unit, TemporalUnit toUnit) {
|
||||||
StringBuilder pattern,
|
pattern.append( "extract(" );
|
||||||
TemporalUnit unit, TemporalUnit toUnit) {
|
pattern.append( translateExtractField( unit ) );
|
||||||
pattern.append("extract(");
|
pattern.append( " from (?3-?2) " );
|
||||||
pattern.append( translateExtractField(unit) );
|
switch ( unit ) {
|
||||||
pattern.append(" from (?3-?2) ");
|
|
||||||
switch (unit) {
|
|
||||||
case YEAR:
|
case YEAR:
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append("year to month");
|
pattern.append( "year to month" );
|
||||||
break;
|
break;
|
||||||
case DAY:
|
case DAY:
|
||||||
case HOUR:
|
case HOUR:
|
||||||
case MINUTE:
|
case MINUTE:
|
||||||
case SECOND:
|
case SECOND:
|
||||||
pattern.append("day to second");
|
pattern.append( "day to second" );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SemanticException(unit + " is not a legal field");
|
throw new SemanticException( unit + " is not a legal field" );
|
||||||
}
|
}
|
||||||
pattern.append(")");
|
pattern.append( ")" );
|
||||||
pattern.append( unit.conversionFactor( toUnit, this ) );
|
pattern.append( unit.conversionFactor( toUnit, this ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,8 +635,9 @@ public class OracleDialect extends Dialect {
|
||||||
return "number($p,$s)";
|
return "number($p,$s)";
|
||||||
|
|
||||||
case DATE:
|
case DATE:
|
||||||
case TIME:
|
|
||||||
return "date";
|
return "date";
|
||||||
|
case TIME:
|
||||||
|
return "timestamp($p)";
|
||||||
// the only difference between date and timestamp
|
// the only difference between date and timestamp
|
||||||
// on Oracle is that date has no fractional seconds
|
// on Oracle is that date has no fractional seconds
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
|
|
|
@ -80,7 +80,7 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
@ -119,9 +119,11 @@ import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.OTHER;
|
import static org.hibernate.type.SqlTypes.OTHER;
|
||||||
import static org.hibernate.type.SqlTypes.SQLXML;
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
import static org.hibernate.type.SqlTypes.STRUCT;
|
import static org.hibernate.type.SqlTypes.STRUCT;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
@ -220,6 +222,10 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
case LONG32VARBINARY:
|
case LONG32VARBINARY:
|
||||||
return "bytea";
|
return "bytea";
|
||||||
|
|
||||||
|
// We do not use the time with timezone type because PG deprecated it and it lacks certain operations like subtraction
|
||||||
|
// case TIME_UTC:
|
||||||
|
// return columnType( TIME_WITH_TIMEZONE );
|
||||||
|
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
|
||||||
|
@ -325,6 +331,12 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TIME:
|
||||||
|
// The PostgreSQL JDBC driver reports TIME for timetz, but we use it only for mapping OffsetTime to UTC
|
||||||
|
if ( "timetz".equals( columnTypeName ) ) {
|
||||||
|
jdbcTypeCode = TIME_UTC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
// The PostgreSQL JDBC driver reports TIMESTAMP for timestamptz, but we use it only for mapping Instant
|
||||||
if ( "timestamptz".equals( columnTypeName ) ) {
|
if ( "timestamptz".equals( columnTypeName ) ) {
|
||||||
|
@ -445,36 +457,31 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
if ( unit == null ) {
|
if ( unit == null ) {
|
||||||
return "(?3-?2)";
|
return "(?3-?2)";
|
||||||
}
|
}
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP && unit == DAY ) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
// special case: subtraction of two dates
|
// special case: subtraction of two dates
|
||||||
// results in an integer number of days
|
// results in an integer number of days
|
||||||
// instead of an INTERVAL
|
// instead of an INTERVAL
|
||||||
return "(?3-?2)";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
StringBuilder pattern = new StringBuilder();
|
|
||||||
switch ( unit ) {
|
switch ( unit ) {
|
||||||
case YEAR:
|
case YEAR:
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
|
||||||
break;
|
|
||||||
case QUARTER:
|
|
||||||
pattern.append( "(" );
|
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
|
||||||
pattern.append( "+" );
|
|
||||||
extractField( pattern, QUARTER, fromTemporalType, toTemporalType, unit );
|
|
||||||
pattern.append( ")" );
|
|
||||||
break;
|
|
||||||
case MONTH:
|
case MONTH:
|
||||||
pattern.append( "(" );
|
case QUARTER:
|
||||||
extractField( pattern, YEAR, fromTemporalType, toTemporalType, unit );
|
return "extract(" + translateDurationField( unit ) + " from age(?3,?2))";
|
||||||
pattern.append( "+" );
|
default:
|
||||||
extractField( pattern, MONTH, fromTemporalType, toTemporalType, unit );
|
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
||||||
pattern.append( ")" );
|
}
|
||||||
break;
|
}
|
||||||
|
else {
|
||||||
|
switch ( unit ) {
|
||||||
|
case YEAR:
|
||||||
|
return "extract(year from ?3-?2)";
|
||||||
|
case QUARTER:
|
||||||
|
return "(extract(year from ?3-?2)*4+extract(month from ?3-?2)/3)";
|
||||||
|
case MONTH:
|
||||||
|
return "(extract(year from ?3-?2)*12+extract(month from ?3-?2))";
|
||||||
case WEEK: //week is not supported by extract() when the argument is a duration
|
case WEEK: //week is not supported by extract() when the argument is a duration
|
||||||
|
return "(extract(day from ?3-?2)/7)";
|
||||||
case DAY:
|
case DAY:
|
||||||
extractField( pattern, DAY, fromTemporalType, toTemporalType, unit );
|
return "extract(day from ?3-?2)";
|
||||||
break;
|
|
||||||
//in order to avoid multiple calls to extract(),
|
//in order to avoid multiple calls to extract(),
|
||||||
//we use extract(epoch from x - y) * factor for
|
//we use extract(epoch from x - y) * factor for
|
||||||
//all the following units:
|
//all the following units:
|
||||||
|
@ -483,15 +490,14 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
case SECOND:
|
case SECOND:
|
||||||
case NANOSECOND:
|
case NANOSECOND:
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
extractField( pattern, EPOCH, fromTemporalType, toTemporalType, unit );
|
return "extract(epoch from ?3-?2)" + EPOCH.conversionFactor( unit, this );
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new SemanticException( "unrecognized field: " + unit );
|
throw new SemanticException( "unrecognized field: " + unit );
|
||||||
}
|
}
|
||||||
return pattern.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
protected void extractField(
|
protected void extractField(
|
||||||
StringBuilder pattern,
|
StringBuilder pattern,
|
||||||
TemporalUnit unit,
|
TemporalUnit unit,
|
||||||
|
@ -501,7 +507,7 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
pattern.append( "extract(" );
|
pattern.append( "extract(" );
|
||||||
pattern.append( translateDurationField( unit ) );
|
pattern.append( translateDurationField( unit ) );
|
||||||
pattern.append( " from " );
|
pattern.append( " from " );
|
||||||
if ( toTimestamp != TemporalType.TIMESTAMP && fromTimestamp != TemporalType.TIMESTAMP ) {
|
if ( toTimestamp == TemporalType.DATE && fromTimestamp == TemporalType.DATE ) {
|
||||||
// special case subtraction of two
|
// special case subtraction of two
|
||||||
// dates results in an integer not
|
// dates results in an integer not
|
||||||
// an Interval
|
// an Interval
|
||||||
|
@ -1333,7 +1339,7 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
// dialect uses oid for Blobs, byte arrays cannot be used.
|
// dialect uses oid for Blobs, byte arrays cannot be used.
|
||||||
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
|
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
|
||||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
||||||
|
|
||||||
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||||
|
|
|
@ -24,8 +24,6 @@ import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An SQL dialect for Postgres Plus
|
* An SQL dialect for Postgres Plus
|
||||||
*
|
*
|
||||||
|
@ -88,12 +86,10 @@ public class PostgresPlusDialect extends PostgreSQLDialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP && unit == DAY ) {
|
if ( toTemporalType == TemporalType.DATE && fromTemporalType == TemporalType.DATE ) {
|
||||||
// special case: subtraction of two dates results in an INTERVAL on Postgres Plus
|
// special case: subtraction of two dates results in an INTERVAL on Postgres Plus
|
||||||
// because there is no date type i.e. without time for Oracle compatibility
|
// because there is no date type i.e. without time for Oracle compatibility
|
||||||
final StringBuilder pattern = new StringBuilder();
|
return super.timestampdiffPattern( unit, TemporalType.TIMESTAMP, TemporalType.TIMESTAMP );
|
||||||
extractField( pattern, DAY, fromTemporalType, toTemporalType, unit );
|
|
||||||
return pattern.toString();
|
|
||||||
}
|
}
|
||||||
return super.timestampdiffPattern( unit, fromTemporalType, toTemporalType );
|
return super.timestampdiffPattern( unit, fromTemporalType, toTemporalType );
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
import static org.hibernate.type.SqlTypes.TIME;
|
import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.UUID;
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
@ -179,6 +180,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
return "time";
|
return "time";
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return "datetime2($p)";
|
return "datetime2($p)";
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return "datetimeoffset($p)";
|
return "datetimeoffset($p)";
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -374,6 +374,8 @@ public class StructJdbcType implements AggregateJdbcType {
|
||||||
if ( rawJdbcValue != null ) {
|
if ( rawJdbcValue != null ) {
|
||||||
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
|
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
|
||||||
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
|
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIMESTAMP_UTC:
|
case SqlTypes.TIMESTAMP_UTC:
|
||||||
// Only transform the raw jdbc value if it could be a TIMESTAMPTZ
|
// Only transform the raw jdbc value if it could be a TIMESTAMPTZ
|
||||||
|
|
|
@ -160,6 +160,7 @@ public class XmlHelper {
|
||||||
);
|
);
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
return fromRawObject(
|
return fromRawObject(
|
||||||
jdbcMapping,
|
jdbcMapping,
|
||||||
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
JdbcTimeJavaType.INSTANCE.fromEncodedString(
|
||||||
|
@ -598,6 +599,7 @@ public class XmlHelper {
|
||||||
break;
|
break;
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
JdbcTimeJavaType.INSTANCE.appendEncodedString(
|
JdbcTimeJavaType.INSTANCE.appendEncodedString(
|
||||||
appender,
|
appender,
|
||||||
jdbcJavaType.unwrap( value, java.sql.Time.class, options )
|
jdbcJavaType.unwrap( value, java.sql.Time.class, options )
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@ -38,6 +39,8 @@ import static org.hibernate.type.SqlTypes.TIME;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
|
||||||
|
@ -117,6 +120,8 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
|
||||||
aggregateParentReadExpression + "." + column + ".date()"
|
aggregateParentReadExpression + "." + column + ".date()"
|
||||||
);
|
);
|
||||||
case TIME:
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
case TIME_UTC:
|
||||||
return template.replace(
|
return template.replace(
|
||||||
placeholder,
|
placeholder,
|
||||||
"to_timestamp(" + aggregateParentReadExpression + "." + column + ".string(),'hh24:mi:ss')"
|
"to_timestamp(" + aggregateParentReadExpression + "." + column + ".string(),'hh24:mi:ss')"
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.query.ReturnableType;
|
import org.hibernate.query.ReturnableType;
|
||||||
import org.hibernate.query.spi.QueryEngine;
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
@ -48,6 +49,7 @@ import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||||
import org.hibernate.sql.ast.tree.predicate.BetweenPredicate;
|
import org.hibernate.sql.ast.tree.predicate.BetweenPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ public class FormatFunction extends AbstractSqmFunctionDescriptor implements Fun
|
||||||
private final String nativeFunctionName;
|
private final String nativeFunctionName;
|
||||||
private final boolean reversedArguments;
|
private final boolean reversedArguments;
|
||||||
private final boolean concatPattern;
|
private final boolean concatPattern;
|
||||||
|
private final boolean supportsTime;
|
||||||
|
|
||||||
public FormatFunction(String nativeFunctionName, TypeConfiguration typeConfiguration) {
|
public FormatFunction(String nativeFunctionName, TypeConfiguration typeConfiguration) {
|
||||||
this( nativeFunctionName, false, true, typeConfiguration );
|
this( nativeFunctionName, false, true, typeConfiguration );
|
||||||
|
@ -74,6 +77,15 @@ public class FormatFunction extends AbstractSqmFunctionDescriptor implements Fun
|
||||||
boolean reversedArguments,
|
boolean reversedArguments,
|
||||||
boolean concatPattern,
|
boolean concatPattern,
|
||||||
TypeConfiguration typeConfiguration) {
|
TypeConfiguration typeConfiguration) {
|
||||||
|
this( nativeFunctionName, reversedArguments, concatPattern, true, typeConfiguration );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormatFunction(
|
||||||
|
String nativeFunctionName,
|
||||||
|
boolean reversedArguments,
|
||||||
|
boolean concatPattern,
|
||||||
|
boolean supportsTime,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
super(
|
super(
|
||||||
"format",
|
"format",
|
||||||
new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL, STRING ),
|
new ArgumentTypesValidator( StandardArgumentsValidators.exactly( 2 ), TEMPORAL, STRING ),
|
||||||
|
@ -84,6 +96,7 @@ public class FormatFunction extends AbstractSqmFunctionDescriptor implements Fun
|
||||||
this.nativeFunctionName = nativeFunctionName;
|
this.nativeFunctionName = nativeFunctionName;
|
||||||
this.reversedArguments = reversedArguments;
|
this.reversedArguments = reversedArguments;
|
||||||
this.concatPattern = concatPattern;
|
this.concatPattern = concatPattern;
|
||||||
|
this.supportsTime = supportsTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,9 +111,15 @@ public class FormatFunction extends AbstractSqmFunctionDescriptor implements Fun
|
||||||
if ( reversedArguments ) {
|
if ( reversedArguments ) {
|
||||||
format.accept( walker );
|
format.accept( walker );
|
||||||
sqlAppender.append( ',' );
|
sqlAppender.append( ',' );
|
||||||
|
if ( !supportsTime && isTimeTemporal( expression ) ) {
|
||||||
|
sqlAppender.append( "date'1970-01-01'+" );
|
||||||
|
}
|
||||||
expression.accept( walker );
|
expression.accept( walker );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if ( !supportsTime && isTimeTemporal( expression ) ) {
|
||||||
|
sqlAppender.append( "date'1970-01-01'+" );
|
||||||
|
}
|
||||||
expression.accept( walker );
|
expression.accept( walker );
|
||||||
sqlAppender.append( ',' );
|
sqlAppender.append( ',' );
|
||||||
format.accept( walker );
|
format.accept( walker );
|
||||||
|
@ -108,6 +127,23 @@ public class FormatFunction extends AbstractSqmFunctionDescriptor implements Fun
|
||||||
sqlAppender.append( ')' );
|
sqlAppender.append( ')' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isTimeTemporal(SqlAstNode expression) {
|
||||||
|
if ( expression instanceof Expression ) {
|
||||||
|
final JdbcMappingContainer expressionType = ( (Expression) expression ).getExpressionType();
|
||||||
|
if ( expressionType.getJdbcTypeCount() == 1 ) {
|
||||||
|
switch ( expressionType.getSingleJdbcMapping().getJdbcType().getDefaultSqlTypeCode() ) {
|
||||||
|
case SqlTypes.TIME:
|
||||||
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
|
protected <T> SelfRenderingSqmFunction<T> generateSqmFunctionExpression(
|
||||||
List<? extends SqmTypedNode<?>> arguments,
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,10 +33,9 @@ public class SQLServerFormatEmulation extends FormatFunction {
|
||||||
List<? extends SqlAstNode> arguments,
|
List<? extends SqlAstNode> arguments,
|
||||||
SqlAstTranslator<?> walker) {
|
SqlAstTranslator<?> walker) {
|
||||||
final Expression datetime = (Expression) arguments.get(0);
|
final Expression datetime = (Expression) arguments.get(0);
|
||||||
final boolean isTime = TypeConfiguration.getSqlTemporalType( datetime.getExpressionType() ) == TemporalType.TIME;
|
|
||||||
|
|
||||||
sqlAppender.appendSql("format(");
|
sqlAppender.appendSql("format(");
|
||||||
if ( isTime ) {
|
if ( needsDateTimeCast( datetime ) ) {
|
||||||
sqlAppender.appendSql("cast(");
|
sqlAppender.appendSql("cast(");
|
||||||
datetime.accept( walker );
|
datetime.accept( walker );
|
||||||
sqlAppender.appendSql(" as datetime)");
|
sqlAppender.appendSql(" as datetime)");
|
||||||
|
@ -47,4 +47,16 @@ public class SQLServerFormatEmulation extends FormatFunction {
|
||||||
arguments.get( 1 ).accept( walker );
|
arguments.get( 1 ).accept( walker );
|
||||||
sqlAppender.appendSql(')');
|
sqlAppender.appendSql(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean needsDateTimeCast(Expression datetime) {
|
||||||
|
final boolean isTime = TypeConfiguration.getSqlTemporalType( datetime.getExpressionType() ) == TemporalType.TIME;
|
||||||
|
if ( isTime ) {
|
||||||
|
// Since SQL Server has no dedicated type for time with time zone, we use the offsetdatetime which has a date part
|
||||||
|
return datetime.getExpressionType()
|
||||||
|
.getSingleJdbcMapping()
|
||||||
|
.getJdbcType()
|
||||||
|
.getDefaultSqlTypeCode() != SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
|
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
|
||||||
|
@ -31,6 +32,7 @@ import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
|
||||||
import org.hibernate.type.descriptor.java.TemporalJavaType;
|
import org.hibernate.type.descriptor.java.TemporalJavaType;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
|
import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
|
||||||
|
import org.hibernate.usertype.internal.OffsetTimeCompositeUserType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -117,7 +119,12 @@ public class SqmExpressionHelper {
|
||||||
|
|
||||||
public static SqmExpression<?> getActualExpression(SqmExpression<?> expression) {
|
public static SqmExpression<?> getActualExpression(SqmExpression<?> expression) {
|
||||||
if ( isCompositeTemporal( expression ) ) {
|
if ( isCompositeTemporal( expression ) ) {
|
||||||
return ( (SqmPath<?>) expression ).get( AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME );
|
if ( expression.getJavaTypeDescriptor().getJavaTypeClass() == OffsetTime.class ) {
|
||||||
|
return ( (SqmPath<?>) expression ).get( OffsetTimeCompositeUserType.LOCAL_TIME_NAME );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ( (SqmPath<?>) expression ).get( AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return expression;
|
return expression;
|
||||||
|
@ -127,18 +134,24 @@ public class SqmExpressionHelper {
|
||||||
public static SqmExpression<?> getOffsetAdjustedExpression(SqmExpression<?> expression) {
|
public static SqmExpression<?> getOffsetAdjustedExpression(SqmExpression<?> expression) {
|
||||||
if ( isCompositeTemporal( expression ) ) {
|
if ( isCompositeTemporal( expression ) ) {
|
||||||
final SqmPath<?> compositePath = (SqmPath<?>) expression;
|
final SqmPath<?> compositePath = (SqmPath<?>) expression;
|
||||||
final SqmPath<Object> instantPath = compositePath.get( AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME );
|
final SqmPath<Object> temporalPath;
|
||||||
final NodeBuilder nodeBuilder = instantPath.nodeBuilder();
|
if ( expression.getJavaTypeDescriptor().getJavaTypeClass() == OffsetTime.class ) {
|
||||||
|
temporalPath = compositePath.get( OffsetTimeCompositeUserType.LOCAL_TIME_NAME );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
temporalPath = compositePath.get( AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME );
|
||||||
|
}
|
||||||
|
final NodeBuilder nodeBuilder = temporalPath.nodeBuilder();
|
||||||
return new SqmBinaryArithmetic<>(
|
return new SqmBinaryArithmetic<>(
|
||||||
BinaryArithmeticOperator.ADD,
|
BinaryArithmeticOperator.ADD,
|
||||||
instantPath,
|
temporalPath,
|
||||||
new SqmToDuration<>(
|
new SqmToDuration<>(
|
||||||
compositePath.get( AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME ),
|
compositePath.get( AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME ),
|
||||||
new SqmDurationUnit<>( TemporalUnit.SECOND, nodeBuilder.getIntegerType(), nodeBuilder ),
|
new SqmDurationUnit<>( TemporalUnit.SECOND, nodeBuilder.getIntegerType(), nodeBuilder ),
|
||||||
nodeBuilder.getTypeConfiguration().getBasicTypeForJavaType( Duration.class ),
|
nodeBuilder.getTypeConfiguration().getBasicTypeForJavaType( Duration.class ),
|
||||||
nodeBuilder
|
nodeBuilder
|
||||||
),
|
),
|
||||||
instantPath.getNodeType(),
|
temporalPath.getNodeType(),
|
||||||
nodeBuilder
|
nodeBuilder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -440,9 +440,9 @@ public class SqlTypes {
|
||||||
* JDBC} timezone.
|
* JDBC} timezone.
|
||||||
*
|
*
|
||||||
* @see org.hibernate.cfg.AvailableSettings#PREFERRED_INSTANT_JDBC_TYPE
|
* @see org.hibernate.cfg.AvailableSettings#PREFERRED_INSTANT_JDBC_TYPE
|
||||||
* @see org.hibernate.type.descriptor.jdbc.InstantJdbcType
|
* @see org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType
|
||||||
* @see org.hibernate.type.descriptor.jdbc.InstantAsTimestampJdbcType
|
* @see org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType
|
||||||
* @see org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType
|
* @see org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType
|
||||||
*/
|
*/
|
||||||
public static final int TIMESTAMP_UTC = 3003;
|
public static final int TIMESTAMP_UTC = 3003;
|
||||||
|
|
||||||
|
@ -479,6 +479,18 @@ public class SqlTypes {
|
||||||
@Internal
|
@Internal
|
||||||
public static final int MATERIALIZED_NCLOB = 3006;
|
public static final int MATERIALIZED_NCLOB = 3006;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type code representing the generic SQL type {@code TIME},
|
||||||
|
* where the value is given in UTC, instead of in the system or
|
||||||
|
* {@linkplain org.hibernate.cfg.AvailableSettings#JDBC_TIME_ZONE
|
||||||
|
* JDBC} timezone.
|
||||||
|
*
|
||||||
|
* @see org.hibernate.annotations.TimeZoneStorageType#NORMALIZE_UTC
|
||||||
|
* @see org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType
|
||||||
|
* @see org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType
|
||||||
|
*/
|
||||||
|
public static final int TIME_UTC = 3007;
|
||||||
|
|
||||||
// Interval types
|
// Interval types
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -689,6 +701,8 @@ public class SqlTypes {
|
||||||
switch ( typeCode ) {
|
switch ( typeCode ) {
|
||||||
case DATE:
|
case DATE:
|
||||||
case TIME:
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
case TIME_UTC:
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
|
@ -728,6 +742,8 @@ public class SqlTypes {
|
||||||
public static boolean hasTimePart(int typeCode) {
|
public static boolean hasTimePart(int typeCode) {
|
||||||
switch ( typeCode ) {
|
switch ( typeCode ) {
|
||||||
case TIME:
|
case TIME:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
case TIME_UTC:
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
case TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
case TIMESTAMP_UTC:
|
case TIMESTAMP_UTC:
|
||||||
|
|
|
@ -413,12 +413,40 @@ public final class StandardBasicTypes {
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link org.hibernate.type.SqlTypes#TIME TIME}.
|
* The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link org.hibernate.type.SqlTypes#TIME_WITH_TIMEZONE TIME_WITH_TIMEZONE}.
|
||||||
*/
|
*/
|
||||||
public static final BasicTypeReference<OffsetTime> OFFSET_TIME = new BasicTypeReference<>(
|
public static final BasicTypeReference<OffsetTime> OFFSET_TIME = new BasicTypeReference<>(
|
||||||
"OffsetTime",
|
"OffsetTime",
|
||||||
OffsetTime.class,
|
OffsetTime.class,
|
||||||
// todo (6.0): why not TIME_WITH_TIMEZONE ?
|
SqlTypes.TIME_WITH_TIMEZONE
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link org.hibernate.type.SqlTypes#TIME_UTC TIME_UTC}.
|
||||||
|
* This maps to {@link org.hibernate.TimeZoneStorageStrategy#NORMALIZE_UTC}.
|
||||||
|
*/
|
||||||
|
public static final BasicTypeReference<OffsetTime> OFFSET_TIME_UTC = new BasicTypeReference<>(
|
||||||
|
"OffsetTimeUtc",
|
||||||
|
OffsetTime.class,
|
||||||
|
SqlTypes.TIME_UTC
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link org.hibernate.type.SqlTypes#TIME_WITH_TIMEZONE TIME_WITH_TIMEZONE}.
|
||||||
|
* This maps to {@link org.hibernate.TimeZoneStorageStrategy#NATIVE}.
|
||||||
|
*/
|
||||||
|
public static final BasicTypeReference<OffsetTime> OFFSET_TIME_WITH_TIMEZONE = new BasicTypeReference<>(
|
||||||
|
"OffsetTimeWithTimezone",
|
||||||
|
OffsetTime.class,
|
||||||
|
SqlTypes.TIME_WITH_TIMEZONE
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link org.hibernate.type.SqlTypes#TIME TIME}.
|
||||||
|
*/
|
||||||
|
public static final BasicTypeReference<OffsetTime> OFFSET_TIME_WITHOUT_TIMEZONE = new BasicTypeReference<>(
|
||||||
|
"OffsetTimeWithoutTimezone",
|
||||||
|
OffsetTime.class,
|
||||||
SqlTypes.TIME
|
SqlTypes.TIME
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1038,6 +1066,27 @@ public final class StandardBasicTypes {
|
||||||
OffsetTime.class.getSimpleName(), OffsetTime.class.getName()
|
OffsetTime.class.getSimpleName(), OffsetTime.class.getName()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handle(
|
||||||
|
OFFSET_TIME_UTC,
|
||||||
|
null,
|
||||||
|
basicTypeRegistry,
|
||||||
|
OFFSET_TIME_UTC.getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
handle(
|
||||||
|
OFFSET_TIME_WITH_TIMEZONE,
|
||||||
|
null,
|
||||||
|
basicTypeRegistry,
|
||||||
|
OFFSET_TIME_WITH_TIMEZONE.getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
handle(
|
||||||
|
OFFSET_TIME_WITHOUT_TIMEZONE,
|
||||||
|
null,
|
||||||
|
basicTypeRegistry,
|
||||||
|
OFFSET_TIME_WITHOUT_TIMEZONE.getName()
|
||||||
|
);
|
||||||
|
|
||||||
handle(
|
handle(
|
||||||
ZONED_DATE_TIME,
|
ZONED_DATE_TIME,
|
||||||
"org.hibernate.type.ZonedDateTimeType",
|
"org.hibernate.type.ZonedDateTimeType",
|
||||||
|
|
|
@ -456,12 +456,17 @@ public final class DateTimeUtils {
|
||||||
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
||||||
return temporal;
|
return temporal;
|
||||||
}
|
}
|
||||||
final int precisionMask = pow10( 9 - defaultTimestampPrecision );
|
|
||||||
final int nano = temporal.get( ChronoField.NANO_OF_SECOND );
|
|
||||||
final int nanosToRound = nano % precisionMask;
|
|
||||||
final int finalNano = nano - nanosToRound + ( nanosToRound >= ( precisionMask >> 1 ) ? precisionMask : 0 );
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (T) temporal.with( ChronoField.NANO_OF_SECOND, finalNano );
|
return (T) temporal.with(
|
||||||
|
ChronoField.NANO_OF_SECOND,
|
||||||
|
roundToPrecision( temporal.get( ChronoField.NANO_OF_SECOND ), defaultTimestampPrecision )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long roundToPrecision(int nano, int precision) {
|
||||||
|
final int precisionMask = pow10( 9 - precision );
|
||||||
|
final int nanosToRound = nano % precisionMask;
|
||||||
|
return nano - nanosToRound + ( nanosToRound >= ( precisionMask >> 1 ) ? precisionMask : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int pow10(int exponent) {
|
private static int pow10(int exponent) {
|
||||||
|
|
|
@ -147,6 +147,6 @@ public class CalendarTimeJavaType extends AbstractTemporalJavaType<Calendar> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
||||||
return 0; //seconds (currently ignored since Dialects don't parameterize time type by precision)
|
return dialect.getDefaultTimestampPrecision();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -22,6 +23,7 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.internal.util.CharSequenceHelper;
|
import org.hibernate.internal.util.CharSequenceHelper;
|
||||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
@ -127,9 +129,15 @@ public class JdbcTimeJavaType extends AbstractTemporalJavaType<Date> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LocalTime.class.isAssignableFrom( type ) ) {
|
if ( LocalTime.class.isAssignableFrom( type ) ) {
|
||||||
return value instanceof java.sql.Time
|
final Time time = value instanceof java.sql.Time
|
||||||
? ( (java.sql.Time) value ).toLocalTime()
|
? ( (java.sql.Time) value )
|
||||||
: new java.sql.Time( value.getTime() ).toLocalTime();
|
: new java.sql.Time( value.getTime() );
|
||||||
|
final LocalTime localTime = time.toLocalTime();
|
||||||
|
final long millis = time.getTime() % 1000;
|
||||||
|
if ( millis == 0 ) {
|
||||||
|
return localTime;
|
||||||
|
}
|
||||||
|
return localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Time.class.isAssignableFrom( type ) ) {
|
if ( Time.class.isAssignableFrom( type ) ) {
|
||||||
|
@ -174,7 +182,13 @@ public class JdbcTimeJavaType extends AbstractTemporalJavaType<Date> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( value instanceof LocalTime ) {
|
if ( value instanceof LocalTime ) {
|
||||||
return Time.valueOf( (LocalTime) value );
|
final LocalTime localTime = (LocalTime) value;
|
||||||
|
final Time time = Time.valueOf( localTime );
|
||||||
|
if ( localTime.getNano() == 0 ) {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
// Preserve milliseconds, which java.sql.Time supports
|
||||||
|
return new Time( time.getTime() + DateTimeUtils.roundToPrecision( localTime.getNano(), 3 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( value instanceof Date ) {
|
if ( value instanceof Date ) {
|
||||||
|
@ -250,8 +264,7 @@ public class JdbcTimeJavaType extends AbstractTemporalJavaType<Date> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
||||||
//seconds (currently ignored since Dialects don't parameterize time type by precision)
|
return dialect.getDefaultTimestampPrecision();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.time.LocalTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
@ -23,6 +24,7 @@ import java.util.GregorianCalendar;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
@ -81,7 +83,12 @@ public class LocalTimeJavaType extends AbstractTemporalJavaType<LocalTime> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Time.class.isAssignableFrom( type ) ) {
|
if ( Time.class.isAssignableFrom( type ) ) {
|
||||||
return (X) Time.valueOf( value );
|
final Time time = Time.valueOf( value );
|
||||||
|
if ( value.getNano() == 0 ) {
|
||||||
|
return (X) time;
|
||||||
|
}
|
||||||
|
// Preserve milliseconds, which java.sql.Time supports
|
||||||
|
return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( value.getNano(), 3 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle documentation says to set the Date to January 1, 1970 when convert from
|
// Oracle documentation says to set the Date to January 1, 1970 when convert from
|
||||||
|
@ -122,6 +129,16 @@ public class LocalTimeJavaType extends AbstractTemporalJavaType<LocalTime> {
|
||||||
return (LocalTime) value;
|
return (LocalTime) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value instanceof Time) {
|
||||||
|
final Time time = (Time) value;
|
||||||
|
final LocalTime localTime = time.toLocalTime();
|
||||||
|
final long millis = time.getTime() % 1000;
|
||||||
|
if ( millis == 0 ) {
|
||||||
|
return localTime;
|
||||||
|
}
|
||||||
|
return localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
|
||||||
|
}
|
||||||
|
|
||||||
if (value instanceof Timestamp) {
|
if (value instanceof Timestamp) {
|
||||||
final Timestamp ts = (Timestamp) value;
|
final Timestamp ts = (Timestamp) value;
|
||||||
return LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalTime();
|
return LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalTime();
|
||||||
|
@ -158,7 +175,7 @@ public class LocalTimeJavaType extends AbstractTemporalJavaType<LocalTime> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
||||||
return 0;
|
return dialect.getDefaultTimestampPrecision();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.time.OffsetDateTime;
|
||||||
import java.time.OffsetTime;
|
import java.time.OffsetTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
@ -23,6 +24,7 @@ import java.util.GregorianCalendar;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
@ -49,8 +51,9 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) {
|
||||||
return context.getJdbcType( Types.TIME );
|
return stdIndicators.getTypeConfiguration().getJdbcTypeRegistry()
|
||||||
|
.getDescriptor( stdIndicators.getDefaultZonedTimeSqlType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,13 +90,22 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
|
||||||
return (X) offsetTime.withOffsetSameInstant( getCurrentSystemOffset() ).toLocalTime();
|
return (X) offsetTime.withOffsetSameInstant( getCurrentSystemOffset() ).toLocalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( OffsetDateTime.class.isAssignableFrom( type ) ) {
|
||||||
|
return (X) offsetTime.atDate( LocalDate.EPOCH );
|
||||||
|
}
|
||||||
|
|
||||||
// for legacy types, we assume that the JDBC timezone is passed to JDBC
|
// for legacy types, we assume that the JDBC timezone is passed to JDBC
|
||||||
// (since PS.setTime() and friends do accept a timezone passed as a Calendar)
|
// (since PS.setTime() and friends do accept a timezone passed as a Calendar)
|
||||||
|
|
||||||
final OffsetTime jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) );
|
final OffsetTime jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) );
|
||||||
|
|
||||||
if ( Time.class.isAssignableFrom( type ) ) {
|
if ( Time.class.isAssignableFrom( type ) ) {
|
||||||
return (X) Time.valueOf( jdbcOffsetTime.toLocalTime() );
|
final Time time = Time.valueOf( jdbcOffsetTime.toLocalTime() );
|
||||||
|
if ( jdbcOffsetTime.getNano() == 0 ) {
|
||||||
|
return (X) time;
|
||||||
|
}
|
||||||
|
// Preserve milliseconds, which java.sql.Time supports
|
||||||
|
return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( jdbcOffsetTime.getNano(), 3 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
final OffsetDateTime jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH );
|
final OffsetDateTime jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH );
|
||||||
|
@ -145,6 +157,10 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
|
||||||
return ((LocalTime) value).atOffset( getCurrentSystemOffset() );
|
return ((LocalTime) value).atOffset( getCurrentSystemOffset() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( value instanceof OffsetDateTime ) {
|
||||||
|
return ( (OffsetDateTime) value ).toOffsetTime();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above),
|
* Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above),
|
||||||
* we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()).
|
* we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()).
|
||||||
|
@ -165,8 +181,14 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
|
||||||
|
|
||||||
if (value instanceof Time) {
|
if (value instanceof Time) {
|
||||||
final Time time = (Time) value;
|
final Time time = (Time) value;
|
||||||
return time.toLocalTime().atOffset( getCurrentJdbcOffset(options) )
|
final OffsetTime offsetTime = time.toLocalTime()
|
||||||
|
.atOffset( getCurrentJdbcOffset( options) )
|
||||||
.withOffsetSameInstant( getCurrentSystemOffset() );
|
.withOffsetSameInstant( getCurrentSystemOffset() );
|
||||||
|
final long millis = time.getTime() % 1000;
|
||||||
|
if ( millis == 0 ) {
|
||||||
|
return offsetTime;
|
||||||
|
}
|
||||||
|
return offsetTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof Timestamp) {
|
if (value instanceof Timestamp) {
|
||||||
|
@ -217,7 +239,7 @@ public class OffsetTimeJavaType extends AbstractTemporalJavaType<OffsetTime> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
|
||||||
return 0;
|
return dialect.getDefaultTimestampPrecision();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,116 +6,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.type.descriptor.jdbc;
|
package org.hibernate.type.descriptor.jdbc;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.ValueBinder;
|
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
|
||||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterTemporal;
|
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link TimestampUtcAsJdbcTimestampJdbcType}
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class InstantAsTimestampJdbcType implements JdbcType {
|
@Deprecated(forRemoval = true)
|
||||||
|
public class InstantAsTimestampJdbcType extends TimestampUtcAsJdbcTimestampJdbcType {
|
||||||
public static final InstantAsTimestampJdbcType INSTANCE = new InstantAsTimestampJdbcType();
|
public static final InstantAsTimestampJdbcType INSTANCE = new InstantAsTimestampJdbcType();
|
||||||
private static final Calendar UTC_CALENDAR = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
|
||||||
|
|
||||||
public InstantAsTimestampJdbcType() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getJdbcTypeCode() {
|
|
||||||
return Types.TIMESTAMP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDefaultSqlTypeCode() {
|
|
||||||
return SqlTypes.TIMESTAMP_UTC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFriendlyName() {
|
|
||||||
return "TIMESTAMP_UTC";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TimestampUtcDescriptor";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
|
||||||
Integer length,
|
|
||||||
Integer scale,
|
|
||||||
TypeConfiguration typeConfiguration) {
|
|
||||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
|
||||||
return Instant.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
|
||||||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
|
||||||
return new BasicBinder<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
|
||||||
final Instant instant = javaType.unwrap( value, Instant.class, options );
|
|
||||||
st.setTimestamp( index, Timestamp.from( instant ), UTC_CALENDAR );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
|
||||||
throws SQLException {
|
|
||||||
final Instant instant = javaType.unwrap( value, Instant.class, options );
|
|
||||||
st.setTimestamp( name, Timestamp.from( instant ), UTC_CALENDAR );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
|
||||||
return new BasicExtractor<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
|
||||||
final Timestamp timestamp = rs.getTimestamp( paramIndex, UTC_CALENDAR );
|
|
||||||
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
|
||||||
final Timestamp timestamp = statement.getTimestamp( index, UTC_CALENDAR );
|
|
||||||
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
|
||||||
final Timestamp timestamp = statement.getTimestamp( name, UTC_CALENDAR );
|
|
||||||
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,121 +6,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.type.descriptor.jdbc;
|
package org.hibernate.type.descriptor.jdbc;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
|
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.ValueBinder;
|
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
|
||||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterTemporal;
|
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link TimestampUtcAsOffsetDateTimeJdbcType}
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class InstantAsTimestampWithTimeZoneJdbcType implements JdbcType {
|
@Deprecated(forRemoval = true)
|
||||||
|
public class InstantAsTimestampWithTimeZoneJdbcType extends TimestampUtcAsOffsetDateTimeJdbcType {
|
||||||
public static final InstantAsTimestampWithTimeZoneJdbcType INSTANCE = new InstantAsTimestampWithTimeZoneJdbcType();
|
public static final InstantAsTimestampWithTimeZoneJdbcType INSTANCE = new InstantAsTimestampWithTimeZoneJdbcType();
|
||||||
|
|
||||||
public InstantAsTimestampWithTimeZoneJdbcType() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getJdbcTypeCode() {
|
|
||||||
return Types.TIMESTAMP_WITH_TIMEZONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDefaultSqlTypeCode() {
|
|
||||||
return SqlTypes.TIMESTAMP_UTC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFriendlyName() {
|
|
||||||
return "TIMESTAMP_UTC";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TimestampUtcDescriptor";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
|
||||||
Integer length,
|
|
||||||
Integer scale,
|
|
||||||
TypeConfiguration typeConfiguration) {
|
|
||||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
|
||||||
return OffsetDateTime.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
|
||||||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
|
||||||
return new BasicBinder<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected void doBind(
|
|
||||||
PreparedStatement st,
|
|
||||||
X value,
|
|
||||||
int index,
|
|
||||||
WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
st.setObject( index, dateTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIMESTAMP_WITH_TIMEZONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(
|
|
||||||
CallableStatement st,
|
|
||||||
X value,
|
|
||||||
String name,
|
|
||||||
WrapperOptions wrapperOptions)
|
|
||||||
throws SQLException {
|
|
||||||
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
st.setObject( name, dateTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIMESTAMP_WITH_TIMEZONE );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
|
||||||
return new BasicExtractor<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
return javaType.wrap( rs.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
return javaType.wrap( statement.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
return javaType.wrap( statement.getObject( name, OffsetDateTime.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,154 +6,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.type.descriptor.jdbc;
|
package org.hibernate.type.descriptor.jdbc;
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.descriptor.ValueBinder;
|
|
||||||
import org.hibernate.type.descriptor.ValueExtractor;
|
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
|
||||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterTemporal;
|
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link TimestampUtcAsInstantJdbcType}
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class InstantJdbcType implements JdbcType {
|
@Deprecated(forRemoval = true)
|
||||||
|
public class InstantJdbcType extends TimestampUtcAsInstantJdbcType {
|
||||||
public static final InstantJdbcType INSTANCE = new InstantJdbcType();
|
public static final InstantJdbcType INSTANCE = new InstantJdbcType();
|
||||||
|
|
||||||
public InstantJdbcType() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getJdbcTypeCode() {
|
|
||||||
return Types.TIMESTAMP_WITH_TIMEZONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDefaultSqlTypeCode() {
|
|
||||||
return SqlTypes.TIMESTAMP_UTC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFriendlyName() {
|
|
||||||
return "TIMESTAMP_UTC";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TimestampUtcDescriptor";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
|
||||||
Integer length,
|
|
||||||
Integer scale,
|
|
||||||
TypeConfiguration typeConfiguration) {
|
|
||||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
|
||||||
return Instant.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
|
||||||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
|
||||||
return new BasicBinder<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected void doBind(
|
|
||||||
PreparedStatement st,
|
|
||||||
X value,
|
|
||||||
int index,
|
|
||||||
WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
try {
|
|
||||||
final Instant dateTime = javaType.unwrap( value, Instant.class, wrapperOptions );
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
st.setObject( index, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
|
||||||
}
|
|
||||||
catch (SQLException|AbstractMethodError e) {
|
|
||||||
// fall back to treating it as a JDBC Timestamp
|
|
||||||
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, wrapperOptions );
|
|
||||||
st.setTimestamp( index, timestamp );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBind(
|
|
||||||
CallableStatement st,
|
|
||||||
X value,
|
|
||||||
String name,
|
|
||||||
WrapperOptions wrapperOptions)
|
|
||||||
throws SQLException {
|
|
||||||
try {
|
|
||||||
final Instant dateTime = javaType.unwrap( value, Instant.class, wrapperOptions );
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
st.setObject( name, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
|
||||||
}
|
|
||||||
catch (SQLException|AbstractMethodError e) {
|
|
||||||
// fall back to treating it as a JDBC Timestamp
|
|
||||||
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, wrapperOptions );
|
|
||||||
st.setTimestamp( name, timestamp );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
|
||||||
return new BasicExtractor<>( javaType, this ) {
|
|
||||||
@Override
|
|
||||||
protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
try {
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
return javaType.wrap( rs.getObject( position, Instant.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
catch (SQLException|AbstractMethodError e) {
|
|
||||||
// fall back to treating it as a JDBC Timestamp
|
|
||||||
return javaType.wrap( rs.getTimestamp( position ), wrapperOptions );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
try {
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
return javaType.wrap( statement.getObject( position, Instant.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
catch (SQLException|AbstractMethodError e) {
|
|
||||||
// fall back to treating it as a JDBC Timestamp
|
|
||||||
return javaType.wrap( statement.getTimestamp( position ), wrapperOptions );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException {
|
|
||||||
try {
|
|
||||||
// supposed to be supported in JDBC 4.2
|
|
||||||
return javaType.wrap( statement.getObject( name, Instant.class ), wrapperOptions );
|
|
||||||
}
|
|
||||||
catch (SQLException|AbstractMethodError e) {
|
|
||||||
// fall back to treating it as a JDBC Timestamp
|
|
||||||
return javaType.wrap( statement.getTimestamp( name ), wrapperOptions );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import static org.hibernate.type.SqlTypes.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descriptor for the SQL/JDBC side of a value mapping. A {@code JdbcType} is
|
* Descriptor for the SQL/JDBC side of a value mapping. A {@code JdbcType} is
|
||||||
* always coupled with a {@link JavaType} to describe the typing aspects of an
|
* always coupled with a {@link JavaType} to describe the typing aspects of an
|
||||||
|
@ -193,32 +195,32 @@ public interface JdbcType extends Serializable {
|
||||||
|
|
||||||
default boolean isInteger() {
|
default boolean isInteger() {
|
||||||
int typeCode = getDdlTypeCode();
|
int typeCode = getDdlTypeCode();
|
||||||
return SqlTypes.isIntegral(typeCode)
|
return isIntegral(typeCode)
|
||||||
|| typeCode == Types.BIT; //HIGHLY DUBIOUS!
|
|| typeCode == BIT; //HIGHLY DUBIOUS!
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isFloat() {
|
default boolean isFloat() {
|
||||||
return SqlTypes.isFloatOrRealOrDouble( getDdlTypeCode() );
|
return isFloatOrRealOrDouble( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isDecimal() {
|
default boolean isDecimal() {
|
||||||
return SqlTypes.isNumericOrDecimal( getDdlTypeCode() );
|
return isNumericOrDecimal( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNumber() {
|
default boolean isNumber() {
|
||||||
return SqlTypes.isNumericType( getDdlTypeCode() );
|
return isNumericType( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isBinary() {
|
default boolean isBinary() {
|
||||||
return SqlTypes.isBinaryType( getDdlTypeCode() );
|
return isBinaryType( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isString() {
|
default boolean isString() {
|
||||||
return SqlTypes.isCharacterOrClobType( getDdlTypeCode() );
|
return isCharacterOrClobType( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isTemporal() {
|
default boolean isTemporal() {
|
||||||
return SqlTypes.isTemporalType( getDdlTypeCode() );
|
return isTemporalType( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isLob() {
|
default boolean isLob() {
|
||||||
|
@ -227,9 +229,9 @@ public interface JdbcType extends Serializable {
|
||||||
|
|
||||||
static boolean isLob(int jdbcTypeCode) {
|
static boolean isLob(int jdbcTypeCode) {
|
||||||
switch ( jdbcTypeCode ) {
|
switch ( jdbcTypeCode ) {
|
||||||
case SqlTypes.BLOB:
|
case BLOB:
|
||||||
case SqlTypes.CLOB:
|
case CLOB:
|
||||||
case SqlTypes.NCLOB: {
|
case NCLOB: {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,11 +244,11 @@ public interface JdbcType extends Serializable {
|
||||||
|
|
||||||
static boolean isNationalized(int jdbcTypeCode) {
|
static boolean isNationalized(int jdbcTypeCode) {
|
||||||
switch ( jdbcTypeCode ) {
|
switch ( jdbcTypeCode ) {
|
||||||
case SqlTypes.NCHAR:
|
case NCHAR:
|
||||||
case SqlTypes.NVARCHAR:
|
case NVARCHAR:
|
||||||
case SqlTypes.LONGNVARCHAR:
|
case LONGNVARCHAR:
|
||||||
case SqlTypes.LONG32NVARCHAR:
|
case LONG32NVARCHAR:
|
||||||
case SqlTypes.NCLOB: {
|
case NCLOB: {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +256,7 @@ public interface JdbcType extends Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isInterval() {
|
default boolean isInterval() {
|
||||||
return SqlTypes.isIntervalType( getDdlTypeCode() );
|
return isIntervalType( getDdlTypeCode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
default CastType getCastType() {
|
default CastType getCastType() {
|
||||||
|
@ -263,40 +265,42 @@ public interface JdbcType extends Serializable {
|
||||||
|
|
||||||
static CastType getCastType(int typeCode) {
|
static CastType getCastType(int typeCode) {
|
||||||
switch ( typeCode ) {
|
switch ( typeCode ) {
|
||||||
case Types.INTEGER:
|
case INTEGER:
|
||||||
case Types.TINYINT:
|
case TINYINT:
|
||||||
case Types.SMALLINT:
|
case SMALLINT:
|
||||||
return CastType.INTEGER;
|
return CastType.INTEGER;
|
||||||
case Types.BIGINT:
|
case BIGINT:
|
||||||
return CastType.LONG;
|
return CastType.LONG;
|
||||||
case Types.FLOAT:
|
case FLOAT:
|
||||||
case Types.REAL:
|
case REAL:
|
||||||
return CastType.FLOAT;
|
return CastType.FLOAT;
|
||||||
case Types.DOUBLE:
|
case DOUBLE:
|
||||||
return CastType.DOUBLE;
|
return CastType.DOUBLE;
|
||||||
case Types.CHAR:
|
case CHAR:
|
||||||
case Types.NCHAR:
|
case NCHAR:
|
||||||
case Types.VARCHAR:
|
case VARCHAR:
|
||||||
case Types.NVARCHAR:
|
case NVARCHAR:
|
||||||
case Types.LONGVARCHAR:
|
case LONGVARCHAR:
|
||||||
case Types.LONGNVARCHAR:
|
case LONGNVARCHAR:
|
||||||
return CastType.STRING;
|
return CastType.STRING;
|
||||||
case Types.CLOB:
|
case CLOB:
|
||||||
return CastType.CLOB;
|
return CastType.CLOB;
|
||||||
case Types.BOOLEAN:
|
case BOOLEAN:
|
||||||
return CastType.BOOLEAN;
|
return CastType.BOOLEAN;
|
||||||
case Types.DECIMAL:
|
case DECIMAL:
|
||||||
case Types.NUMERIC:
|
case NUMERIC:
|
||||||
return CastType.FIXED;
|
return CastType.FIXED;
|
||||||
case Types.DATE:
|
case DATE:
|
||||||
return CastType.DATE;
|
return CastType.DATE;
|
||||||
case Types.TIME:
|
case TIME:
|
||||||
|
case TIME_UTC:
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
return CastType.TIME;
|
return CastType.TIME;
|
||||||
case Types.TIMESTAMP:
|
case TIMESTAMP:
|
||||||
return CastType.TIMESTAMP;
|
return CastType.TIMESTAMP;
|
||||||
case Types.TIMESTAMP_WITH_TIMEZONE:
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
return CastType.OFFSET_TIMESTAMP;
|
return CastType.OFFSET_TIMESTAMP;
|
||||||
case Types.NULL:
|
case NULL:
|
||||||
return CastType.NULL;
|
return CastType.NULL;
|
||||||
default:
|
default:
|
||||||
return CastType.OTHER;
|
return CastType.OTHER;
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class JdbcTypeFamilyInformation {
|
||||||
BINARY( Types.BINARY, Types.VARBINARY, Types.LONGVARBINARY/*, SqlTypes.LONG32VARBINARY*/ ),
|
BINARY( Types.BINARY, Types.VARBINARY, Types.LONGVARBINARY/*, SqlTypes.LONG32VARBINARY*/ ),
|
||||||
NUMERIC( Types.BIGINT, Types.DECIMAL, Types.DOUBLE, Types.FLOAT, Types.INTEGER, Types.NUMERIC, Types.REAL, Types.SMALLINT, Types.TINYINT ),
|
NUMERIC( Types.BIGINT, Types.DECIMAL, Types.DOUBLE, Types.FLOAT, Types.INTEGER, Types.NUMERIC, Types.REAL, Types.SMALLINT, Types.TINYINT ),
|
||||||
CHARACTER( Types.CHAR, Types.LONGNVARCHAR, Types.LONGVARCHAR, /*SqlTypes.LONG32NVARCHAR ,SqlTypes.LONG32VARCHAR,*/ Types.NCHAR, Types.NVARCHAR, Types.VARCHAR ),
|
CHARACTER( Types.CHAR, Types.LONGNVARCHAR, Types.LONGVARCHAR, /*SqlTypes.LONG32NVARCHAR ,SqlTypes.LONG32VARCHAR,*/ Types.NCHAR, Types.NVARCHAR, Types.VARCHAR ),
|
||||||
DATETIME( Types.DATE, Types.TIME, Types.TIMESTAMP ),
|
DATETIME( Types.DATE, Types.TIME, Types.TIME_WITH_TIMEZONE, Types.TIMESTAMP, Types.TIMESTAMP_WITH_TIMEZONE ),
|
||||||
CLOB( Types.CLOB, Types.NCLOB );
|
CLOB( Types.CLOB, Types.NCLOB );
|
||||||
|
|
||||||
private final int[] typeCodes;
|
private final int[] typeCodes;
|
||||||
|
|
|
@ -193,6 +193,28 @@ public interface JdbcTypeIndicators {
|
||||||
return getTypeConfiguration().getCurrentBaseSqlTypeIndicators();
|
return getTypeConfiguration().getCurrentBaseSqlTypeIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the SQL column type used for storing times under the
|
||||||
|
* given {@linkplain TimeZoneStorageStrategy storage strategy}
|
||||||
|
*
|
||||||
|
* @see SqlTypes#TIME_WITH_TIMEZONE
|
||||||
|
* @see SqlTypes#TIME
|
||||||
|
* @see SqlTypes#TIME_UTC
|
||||||
|
*/
|
||||||
|
static int getZonedTimeSqlType(TimeZoneStorageStrategy storageStrategy) {
|
||||||
|
switch ( storageStrategy ) {
|
||||||
|
case NATIVE:
|
||||||
|
return SqlTypes.TIME_WITH_TIMEZONE;
|
||||||
|
case COLUMN:
|
||||||
|
case NORMALIZE:
|
||||||
|
return SqlTypes.TIME;
|
||||||
|
case NORMALIZE_UTC:
|
||||||
|
return SqlTypes.TIME_UTC;
|
||||||
|
default:
|
||||||
|
throw new AssertionFailure( "unknown time zone storage strategy" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SQL column type used for storing datetimes under the
|
* @return the SQL column type used for storing datetimes under the
|
||||||
* given {@linkplain TimeZoneStorageStrategy storage strategy}
|
* given {@linkplain TimeZoneStorageStrategy storage strategy}
|
||||||
|
@ -216,6 +238,18 @@ public interface JdbcTypeIndicators {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the SQL column type used for storing datetimes under the
|
||||||
|
* default {@linkplain TimeZoneStorageStrategy storage strategy}
|
||||||
|
*
|
||||||
|
* @see SqlTypes#TIME_WITH_TIMEZONE
|
||||||
|
* @see SqlTypes#TIME
|
||||||
|
* @see SqlTypes#TIME_UTC
|
||||||
|
*/
|
||||||
|
default int getDefaultZonedTimeSqlType() {
|
||||||
|
return getZonedTimeSqlType( getDefaultTimeZoneStorageStrategy() );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the SQL column type used for storing datetimes under the
|
* @return the SQL column type used for storing datetimes under the
|
||||||
* default {@linkplain TimeZoneStorageStrategy storage strategy}
|
* default {@linkplain TimeZoneStorageStrategy storage strategy}
|
||||||
|
@ -228,7 +262,7 @@ public interface JdbcTypeIndicators {
|
||||||
final TemporalType temporalPrecision = getTemporalPrecision();
|
final TemporalType temporalPrecision = getTemporalPrecision();
|
||||||
switch ( temporalPrecision == null ? TemporalType.TIMESTAMP : temporalPrecision ) {
|
switch ( temporalPrecision == null ? TemporalType.TIMESTAMP : temporalPrecision ) {
|
||||||
case TIME:
|
case TIME:
|
||||||
return Types.TIME;
|
return getZonedTimeSqlType( getDefaultTimeZoneStorageStrategy() );
|
||||||
case DATE:
|
case DATE:
|
||||||
return Types.DATE;
|
return Types.DATE;
|
||||||
case TIMESTAMP:
|
case TIMESTAMP:
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -124,6 +125,7 @@ public class JdbcTypeJavaClassMappings {
|
||||||
workMap.put( Time.class, SqlTypes.TIME );
|
workMap.put( Time.class, SqlTypes.TIME );
|
||||||
workMap.put( Timestamp.class, SqlTypes.TIMESTAMP );
|
workMap.put( Timestamp.class, SqlTypes.TIMESTAMP );
|
||||||
workMap.put( LocalTime.class, SqlTypes.TIME );
|
workMap.put( LocalTime.class, SqlTypes.TIME );
|
||||||
|
workMap.put( OffsetTime.class, SqlTypes.TIME_WITH_TIMEZONE );
|
||||||
workMap.put( LocalDate.class, SqlTypes.DATE );
|
workMap.put( LocalDate.class, SqlTypes.DATE );
|
||||||
workMap.put( LocalDateTime.class, SqlTypes.TIMESTAMP );
|
workMap.put( LocalDateTime.class, SqlTypes.TIMESTAMP );
|
||||||
workMap.put( OffsetDateTime.class, SqlTypes.TIMESTAMP_WITH_TIMEZONE );
|
workMap.put( OffsetDateTime.class, SqlTypes.TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
@ -181,6 +183,8 @@ public class JdbcTypeJavaClassMappings {
|
||||||
workMap.put( SqlTypes.DATE, java.sql.Date.class );
|
workMap.put( SqlTypes.DATE, java.sql.Date.class );
|
||||||
workMap.put( SqlTypes.TIME, Time.class );
|
workMap.put( SqlTypes.TIME, Time.class );
|
||||||
workMap.put( SqlTypes.TIMESTAMP, Timestamp.class );
|
workMap.put( SqlTypes.TIMESTAMP, Timestamp.class );
|
||||||
|
workMap.put( SqlTypes.TIME_WITH_TIMEZONE, OffsetTime.class );
|
||||||
|
workMap.put( SqlTypes.TIMESTAMP_WITH_TIMEZONE, OffsetDateTime.class );
|
||||||
workMap.put( SqlTypes.BLOB, Blob.class );
|
workMap.put( SqlTypes.BLOB, Blob.class );
|
||||||
workMap.put( SqlTypes.CLOB, Clob.class );
|
workMap.put( SqlTypes.CLOB, Clob.class );
|
||||||
workMap.put( SqlTypes.NCLOB, NClob.class );
|
workMap.put( SqlTypes.NCLOB, NClob.class );
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Time;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIME_UTC TIME_UTC} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimeUtcAsJdbcTimeJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final TimeUtcAsJdbcTimeJdbcType INSTANCE = new TimeUtcAsJdbcTimeJdbcType();
|
||||||
|
private static final Calendar UTC_CALENDAR = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||||
|
|
||||||
|
public TimeUtcAsJdbcTimeJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.TIME_UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIME_UTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimeUtcDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return OffsetTime.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setTime( index, Time.valueOf( offsetTime.withOffsetSameInstant( ZoneOffset.UTC ).toLocalTime() ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setTime( name, Time.valueOf( offsetTime.withOffsetSameInstant( ZoneOffset.UTC ).toLocalTime() ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||||
|
final Time time = rs.getTime( paramIndex, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( time == null ? null : time.toLocalTime().atOffset( ZoneOffset.UTC ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final Time time = statement.getTime( index, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( time == null ? null : time.toLocalTime().atOffset( ZoneOffset.UTC ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
final Time time = statement.getTime( name, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( time == null ? null : time.toLocalTime().atOffset( ZoneOffset.UTC ), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIME_UTC TIME_UTC} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimeUtcAsOffsetTimeJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final TimeUtcAsOffsetTimeJdbcType INSTANCE = new TimeUtcAsOffsetTimeJdbcType();
|
||||||
|
|
||||||
|
public TimeUtcAsOffsetTimeJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIME_WITH_TIMEZONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.TIME_UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIME_UTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimeUtcDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return OffsetTime.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setObject( index, offsetTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIME_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setObject( name, offsetTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIME_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( rs.getObject( paramIndex, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( index, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( name, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIME_WITH_TIMEZONE TIME_WITH_TIMEZONE} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimeWithTimeZoneJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final TimeWithTimeZoneJdbcType INSTANCE = new TimeWithTimeZoneJdbcType();
|
||||||
|
|
||||||
|
public TimeWithTimeZoneJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIME_WITH_TIMEZONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIME_WITH_TIMEZONE";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimeWithTimezoneDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return OffsetTime.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIME );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setObject( index, offsetTime, Types.TIME_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final OffsetTime offsetTime = javaType.unwrap( value, OffsetTime.class, options );
|
||||||
|
st.setObject( name, offsetTime, Types.TIME_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( rs.getObject( paramIndex, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( index, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( name, OffsetTime.class ), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimestampUtcAsInstantJdbcType implements JdbcType {
|
||||||
|
public static final TimestampUtcAsInstantJdbcType INSTANCE = new TimestampUtcAsInstantJdbcType();
|
||||||
|
private static final Calendar UTC_CALENDAR = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||||
|
|
||||||
|
public TimestampUtcAsInstantJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.TIMESTAMP_UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIMESTAMP_UTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimestampUtcDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return Instant.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(
|
||||||
|
PreparedStatement st,
|
||||||
|
X value,
|
||||||
|
int index,
|
||||||
|
WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
final Instant instant = javaType.unwrap( value, Instant.class, wrapperOptions );
|
||||||
|
try {
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
st.setObject( index, instant, Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
catch (SQLException|AbstractMethodError e) {
|
||||||
|
// fall back to treating it as a JDBC Timestamp
|
||||||
|
st.setTimestamp( index, Timestamp.from( instant ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(
|
||||||
|
CallableStatement st,
|
||||||
|
X value,
|
||||||
|
String name,
|
||||||
|
WrapperOptions wrapperOptions)
|
||||||
|
throws SQLException {
|
||||||
|
final Instant instant = javaType.unwrap( value, Instant.class, wrapperOptions );
|
||||||
|
try {
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
st.setObject( name, instant, Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
catch (SQLException|AbstractMethodError e) {
|
||||||
|
// fall back to treating it as a JDBC Timestamp
|
||||||
|
st.setTimestamp( name, Timestamp.from( instant ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
try {
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
return javaType.wrap( rs.getObject( position, Instant.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
catch (SQLException|AbstractMethodError e) {
|
||||||
|
// fall back to treating it as a JDBC Timestamp
|
||||||
|
return javaType.wrap( rs.getTimestamp( position, UTC_CALENDAR ), wrapperOptions );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
try {
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
return javaType.wrap( statement.getObject( position, Instant.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
catch (SQLException|AbstractMethodError e) {
|
||||||
|
// fall back to treating it as a JDBC Timestamp
|
||||||
|
return javaType.wrap( statement.getTimestamp( position, UTC_CALENDAR ), wrapperOptions );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
try {
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
return javaType.wrap( statement.getObject( name, Instant.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
catch (SQLException|AbstractMethodError e) {
|
||||||
|
// fall back to treating it as a JDBC Timestamp
|
||||||
|
return javaType.wrap( statement.getTimestamp( name, UTC_CALENDAR ), wrapperOptions );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimestampUtcAsJdbcTimestampJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final TimestampUtcAsJdbcTimestampJdbcType INSTANCE = new TimestampUtcAsJdbcTimestampJdbcType();
|
||||||
|
private static final Calendar UTC_CALENDAR = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
|
||||||
|
|
||||||
|
public TimestampUtcAsJdbcTimestampJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIMESTAMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.TIMESTAMP_UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIMESTAMP_UTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimestampUtcDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return Instant.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final Instant instant = javaType.unwrap( value, Instant.class, options );
|
||||||
|
st.setTimestamp( index, Timestamp.from( instant ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final Instant instant = javaType.unwrap( value, Instant.class, options );
|
||||||
|
st.setTimestamp( name, Timestamp.from( instant ), UTC_CALENDAR );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||||
|
final Timestamp timestamp = rs.getTimestamp( paramIndex, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
final Timestamp timestamp = statement.getTimestamp( index, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
final Timestamp timestamp = statement.getTimestamp( name, UTC_CALENDAR );
|
||||||
|
return javaType.wrap( timestamp == null ? null : timestamp.toInstant(), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
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.JdbcLiteralFormatterTemporal;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descriptor for {@link SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC} handling.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class TimestampUtcAsOffsetDateTimeJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final TimestampUtcAsOffsetDateTimeJdbcType INSTANCE = new TimestampUtcAsOffsetDateTimeJdbcType();
|
||||||
|
|
||||||
|
public TimestampUtcAsOffsetDateTimeJdbcType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.TIMESTAMP_UTC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "TIMESTAMP_UTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TimestampUtcDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||||
|
Integer length,
|
||||||
|
Integer scale,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||||
|
return OffsetDateTime.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(
|
||||||
|
PreparedStatement st,
|
||||||
|
X value,
|
||||||
|
int index,
|
||||||
|
WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
st.setObject( index, dateTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(
|
||||||
|
CallableStatement st,
|
||||||
|
X value,
|
||||||
|
String name,
|
||||||
|
WrapperOptions wrapperOptions)
|
||||||
|
throws SQLException {
|
||||||
|
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
||||||
|
// supposed to be supported in JDBC 4.2
|
||||||
|
st.setObject( name, dateTime.withOffsetSameInstant( ZoneOffset.UTC ), Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
return javaType.wrap( rs.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException {
|
||||||
|
return javaType.wrap( statement.getObject( name, OffsetDateTime.class ), wrapperOptions );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import java.sql.SQLException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Descriptor for {@link Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE} handling.
|
* Descriptor for {@link Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE} handling.
|
||||||
|
@ -75,16 +76,24 @@ public class TimestampWithTimeZoneJdbcType implements JdbcType {
|
||||||
PreparedStatement st,
|
PreparedStatement st,
|
||||||
X value,
|
X value,
|
||||||
int index,
|
int index,
|
||||||
WrapperOptions wrapperOptions) throws SQLException {
|
WrapperOptions options) throws SQLException {
|
||||||
try {
|
try {
|
||||||
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
|
||||||
// supposed to be supported in JDBC 4.2
|
// supposed to be supported in JDBC 4.2
|
||||||
st.setObject( index, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
st.setObject( index, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
}
|
}
|
||||||
catch (SQLException|AbstractMethodError e) {
|
catch (SQLException|AbstractMethodError e) {
|
||||||
// fall back to treating it as a JDBC Timestamp
|
// fall back to treating it as a JDBC Timestamp
|
||||||
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, wrapperOptions );
|
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, options );
|
||||||
st.setTimestamp( index, timestamp );
|
if ( value instanceof Calendar ) {
|
||||||
|
st.setTimestamp( index, timestamp, (Calendar) value );
|
||||||
|
}
|
||||||
|
else if ( options.getJdbcTimeZone() != null ) {
|
||||||
|
st.setTimestamp( index, timestamp, Calendar.getInstance( options.getJdbcTimeZone() ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
st.setTimestamp( index, timestamp );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,17 +102,25 @@ public class TimestampWithTimeZoneJdbcType implements JdbcType {
|
||||||
CallableStatement st,
|
CallableStatement st,
|
||||||
X value,
|
X value,
|
||||||
String name,
|
String name,
|
||||||
WrapperOptions wrapperOptions)
|
WrapperOptions options)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
try {
|
try {
|
||||||
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, wrapperOptions );
|
final OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
|
||||||
// supposed to be supported in JDBC 4.2
|
// supposed to be supported in JDBC 4.2
|
||||||
st.setObject( name, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
st.setObject( name, dateTime, Types.TIMESTAMP_WITH_TIMEZONE );
|
||||||
}
|
}
|
||||||
catch (SQLException|AbstractMethodError e) {
|
catch (SQLException|AbstractMethodError e) {
|
||||||
// fall back to treating it as a JDBC Timestamp
|
// fall back to treating it as a JDBC Timestamp
|
||||||
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, wrapperOptions );
|
final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, options );
|
||||||
st.setTimestamp( name, timestamp );
|
if ( value instanceof Calendar ) {
|
||||||
|
st.setTimestamp( name, timestamp, (Calendar) value );
|
||||||
|
}
|
||||||
|
else if ( options.getJdbcTimeZone() != null ) {
|
||||||
|
st.setTimestamp( name, timestamp, Calendar.getInstance( options.getJdbcTimeZone() ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
st.setTimestamp( name, timestamp );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -113,38 +130,44 @@ public class TimestampWithTimeZoneJdbcType implements JdbcType {
|
||||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
|
||||||
return new BasicExtractor<>( javaType, this ) {
|
return new BasicExtractor<>( javaType, this ) {
|
||||||
@Override
|
@Override
|
||||||
protected X doExtract(ResultSet rs, int position, WrapperOptions wrapperOptions) throws SQLException {
|
protected X doExtract(ResultSet rs, int position, WrapperOptions options) throws SQLException {
|
||||||
try {
|
try {
|
||||||
// supposed to be supported in JDBC 4.2
|
// supposed to be supported in JDBC 4.2
|
||||||
return javaType.wrap( rs.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
return javaType.wrap( rs.getObject( position, OffsetDateTime.class ), options );
|
||||||
}
|
}
|
||||||
catch (SQLException|AbstractMethodError e) {
|
catch (SQLException|AbstractMethodError e) {
|
||||||
// fall back to treating it as a JDBC Timestamp
|
// fall back to treating it as a JDBC Timestamp
|
||||||
return javaType.wrap( rs.getTimestamp( position ), wrapperOptions );
|
return options.getJdbcTimeZone() != null ?
|
||||||
|
javaType.wrap( rs.getTimestamp( position, Calendar.getInstance( options.getJdbcTimeZone() ) ), options ) :
|
||||||
|
javaType.wrap( rs.getTimestamp( position ), options );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected X doExtract(CallableStatement statement, int position, WrapperOptions wrapperOptions) throws SQLException {
|
protected X doExtract(CallableStatement statement, int position, WrapperOptions options) throws SQLException {
|
||||||
try {
|
try {
|
||||||
// supposed to be supported in JDBC 4.2
|
// supposed to be supported in JDBC 4.2
|
||||||
return javaType.wrap( statement.getObject( position, OffsetDateTime.class ), wrapperOptions );
|
return javaType.wrap( statement.getObject( position, OffsetDateTime.class ), options );
|
||||||
}
|
}
|
||||||
catch (SQLException|AbstractMethodError e) {
|
catch (SQLException|AbstractMethodError e) {
|
||||||
// fall back to treating it as a JDBC Timestamp
|
// fall back to treating it as a JDBC Timestamp
|
||||||
return javaType.wrap( statement.getTimestamp( position ), wrapperOptions );
|
return options.getJdbcTimeZone() != null ?
|
||||||
|
javaType.wrap( statement.getTimestamp( position, Calendar.getInstance( options.getJdbcTimeZone() ) ), options ) :
|
||||||
|
javaType.wrap( statement.getTimestamp( position ), options );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions wrapperOptions) throws SQLException {
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
try {
|
try {
|
||||||
// supposed to be supported in JDBC 4.2
|
// supposed to be supported in JDBC 4.2
|
||||||
return javaType.wrap( statement.getObject( name, OffsetDateTime.class ), wrapperOptions );
|
return javaType.wrap( statement.getObject( name, OffsetDateTime.class ), options );
|
||||||
}
|
}
|
||||||
catch (SQLException|AbstractMethodError e) {
|
catch (SQLException|AbstractMethodError e) {
|
||||||
// fall back to treating it as a JDBC Timestamp
|
// fall back to treating it as a JDBC Timestamp
|
||||||
return javaType.wrap( statement.getTimestamp( name ), wrapperOptions );
|
return options.getJdbcTimeZone() != null ?
|
||||||
|
javaType.wrap( statement.getTimestamp( name, Calendar.getInstance( options.getJdbcTimeZone() ) ), options ) :
|
||||||
|
javaType.wrap( statement.getTimestamp( name ), options );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.type.descriptor.jdbc.RealJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.RowIdJdbcType;
|
import org.hibernate.type.descriptor.jdbc.RowIdJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.TimeWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.TinyIntJdbcType;
|
import org.hibernate.type.descriptor.jdbc.TinyIntJdbcType;
|
||||||
|
@ -64,6 +65,7 @@ public class JdbcTypeBaseline {
|
||||||
target.addDescriptor( TimestampJdbcType.INSTANCE );
|
target.addDescriptor( TimestampJdbcType.INSTANCE );
|
||||||
target.addDescriptor( TimestampWithTimeZoneJdbcType.INSTANCE );
|
target.addDescriptor( TimestampWithTimeZoneJdbcType.INSTANCE );
|
||||||
target.addDescriptor( TimeJdbcType.INSTANCE );
|
target.addDescriptor( TimeJdbcType.INSTANCE );
|
||||||
|
target.addDescriptor( TimeWithTimeZoneJdbcType.INSTANCE );
|
||||||
|
|
||||||
target.addDescriptor( BinaryJdbcType.INSTANCE );
|
target.addDescriptor( BinaryJdbcType.INSTANCE );
|
||||||
target.addDescriptor( VarbinaryJdbcType.INSTANCE );
|
target.addDescriptor( VarbinaryJdbcType.INSTANCE );
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class DdlTypeImpl implements DdlType {
|
||||||
final int paren = typeNamePattern.indexOf( '(' );
|
final int paren = typeNamePattern.indexOf( '(' );
|
||||||
if ( paren > 0 ) {
|
if ( paren > 0 ) {
|
||||||
final int parenEnd = typeNamePattern.lastIndexOf( ')' );
|
final int parenEnd = typeNamePattern.lastIndexOf( ')' );
|
||||||
return parenEnd == typeNamePattern.length()
|
return parenEnd + 1 == typeNamePattern.length()
|
||||||
? typeNamePattern.substring( 0, paren )
|
? typeNamePattern.substring( 0, paren )
|
||||||
: ( typeNamePattern.substring( 0, paren ) + typeNamePattern.substring( parenEnd + 1 ) );
|
: ( typeNamePattern.substring( 0, paren ) + typeNamePattern.substring( parenEnd + 1 ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,9 @@ public class DdlTypeRegistry implements Serializable {
|
||||||
return getTypeName( typeCode, Size.precision( dialect.getFloatPrecision() ) );
|
return getTypeName( typeCode, Size.precision( dialect.getFloatPrecision() ) );
|
||||||
case SqlTypes.DOUBLE:
|
case SqlTypes.DOUBLE:
|
||||||
return getTypeName( typeCode, Size.precision( dialect.getDoublePrecision() ) );
|
return getTypeName( typeCode, Size.precision( dialect.getDoublePrecision() ) );
|
||||||
|
case SqlTypes.TIME:
|
||||||
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
case SqlTypes.TIMESTAMP:
|
case SqlTypes.TIMESTAMP:
|
||||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||||
case SqlTypes.TIMESTAMP_UTC:
|
case SqlTypes.TIMESTAMP_UTC:
|
||||||
|
|
|
@ -776,6 +776,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
||||||
return TemporalType.TIMESTAMP;
|
return TemporalType.TIMESTAMP;
|
||||||
case SqlTypes.TIME:
|
case SqlTypes.TIME:
|
||||||
case SqlTypes.TIME_WITH_TIMEZONE:
|
case SqlTypes.TIME_WITH_TIMEZONE:
|
||||||
|
case SqlTypes.TIME_UTC:
|
||||||
return TemporalType.TIME;
|
return TemporalType.TIME;
|
||||||
case SqlTypes.DATE:
|
case SqlTypes.DATE:
|
||||||
return TemporalType.DATE;
|
return TemporalType.DATE;
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* 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.usertype.internal;
|
||||||
|
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.annotations.JdbcTypeCode;
|
||||||
|
import org.hibernate.annotations.TimeZoneStorage;
|
||||||
|
import org.hibernate.annotations.TimeZoneStorageType;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.spi.ValueAccess;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class OffsetTimeCompositeUserType extends AbstractTimeZoneStorageCompositeUserType<OffsetTime> {
|
||||||
|
|
||||||
|
public static final String LOCAL_TIME_NAME = "utcTime";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPropertyValue(OffsetTime component, int property) throws HibernateException {
|
||||||
|
switch ( property ) {
|
||||||
|
case 0:
|
||||||
|
return component.withOffsetSameInstant( ZoneOffset.UTC );
|
||||||
|
case 1:
|
||||||
|
return component.getOffset();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetTime instantiate(ValueAccess values, SessionFactoryImplementor sessionFactory) {
|
||||||
|
final OffsetTime utcTime = values.getValue( 0, OffsetTime.class );
|
||||||
|
final ZoneOffset zoneOffset = values.getValue( 1, ZoneOffset.class );
|
||||||
|
return utcTime == null || zoneOffset == null ? null : utcTime.withOffsetSameInstant( zoneOffset );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> embeddable() {
|
||||||
|
return OffsetTimeEmbeddable.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<OffsetTime> returnedClass() {
|
||||||
|
return OffsetTime.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OffsetTimeEmbeddable {
|
||||||
|
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
|
||||||
|
private OffsetTime utcTime;
|
||||||
|
@JdbcTypeCode( SqlTypes.INTEGER )
|
||||||
|
private ZoneOffset zoneOffset;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,13 @@ import java.time.OffsetTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DialectContext;
|
||||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||||
import org.hibernate.testing.orm.junit.Jpa;
|
import org.hibernate.testing.orm.junit.Jpa;
|
||||||
|
import org.hibernate.testing.orm.junit.Setting;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -25,11 +30,15 @@ import jakarta.persistence.TypedQuery;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
@Jpa(
|
@Jpa(
|
||||||
annotatedClasses = LocalTimeTest.TestEntity.class
|
annotatedClasses = LocalTimeTest.TestEntity.class,
|
||||||
|
properties = @Setting(name = AvailableSettings.TIMEZONE_DEFAULT_STORAGE, value = "NORMALIZE")
|
||||||
)
|
)
|
||||||
public class LocalTimeTest {
|
public class LocalTimeTest {
|
||||||
|
|
||||||
private static final LocalTime LOCAL_TIME = LocalTime.now();
|
private static final LocalTime LOCAL_TIME = DateTimeUtils.roundToDefaultPrecision(
|
||||||
|
LocalTime.now(),
|
||||||
|
DialectContext.getDialect()
|
||||||
|
);
|
||||||
|
|
||||||
private static final OffsetTime OFFSET_TIME = OffsetTime.of( LOCAL_TIME, ZoneOffset.ofHours( 2 ) );
|
private static final OffsetTime OFFSET_TIME = OffsetTime.of( LOCAL_TIME, ZoneOffset.ofHours( 2 ) );
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.TimeZoneStorageType;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
import org.hibernate.dialect.AbstractHANADialect;
|
import org.hibernate.dialect.AbstractHANADialect;
|
||||||
import org.hibernate.dialect.H2Dialect;
|
import org.hibernate.dialect.H2Dialect;
|
||||||
import org.hibernate.dialect.HSQLDialect;
|
import org.hibernate.dialect.HSQLDialect;
|
||||||
|
@ -41,6 +44,12 @@ import org.junit.runners.Parameterized;
|
||||||
*/
|
*/
|
||||||
public class OffsetTimeTest extends AbstractJavaTimeTypeTest<OffsetTime, OffsetTimeTest.EntityWithOffsetTime> {
|
public class OffsetTimeTest extends AbstractJavaTimeTypeTest<OffsetTime, OffsetTimeTest.EntityWithOffsetTime> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
super.configure(configuration);
|
||||||
|
configuration.setProperty( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, TimeZoneStorageType.NORMALIZE.toString() );
|
||||||
|
}
|
||||||
|
|
||||||
private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> {
|
private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> {
|
||||||
public ParametersBuilder add(int hour, int minute, int second, int nanosecond, String offset, ZoneId defaultTimeZone) {
|
public ParametersBuilder add(int hour, int minute, int second, int nanosecond, String offset, ZoneId defaultTimeZone) {
|
||||||
if ( !isNanosecondPrecisionSupported() ) {
|
if ( !isNanosecondPrecisionSupported() ) {
|
||||||
|
|
|
@ -14,6 +14,27 @@ earlier versions, see any other pertinent migration guides as well.
|
||||||
* link:{docsBase}/6.1/migration-guide/migration-guide.html[6.1 Migration guide]
|
* link:{docsBase}/6.1/migration-guide/migration-guide.html[6.1 Migration guide]
|
||||||
* link:{docsBase}/6.0/migration-guide/migration-guide.html[6.0 Migration guide]
|
* link:{docsBase}/6.0/migration-guide/migration-guide.html[6.0 Migration guide]
|
||||||
|
|
||||||
|
[[ddl-changes]]
|
||||||
|
== DDL type changes
|
||||||
|
|
||||||
|
[[ddl-offset-time]]
|
||||||
|
=== OffsetTime mapping changes
|
||||||
|
|
||||||
|
`OffsetTime` now depends on `@TimeZoneStorage` and the `hibernate.timezone.default_storage` setting.
|
||||||
|
Since the default for this setting is now `TimeZoneStorageType.DEFAULT`, this means that the DDL expectations for such columns changed.
|
||||||
|
|
||||||
|
If the target database supports time zone types natively like H2, Oracle, SQL Server and DB2 z/OS,
|
||||||
|
the type code `SqlTypes.TIME_WITH_TIMEZONE` is now used, which maps to the DDL type `time with time zone`.
|
||||||
|
|
||||||
|
Due to this change, schema validation errors could occur on existing databases.
|
||||||
|
|
||||||
|
The migration to `time with time zone` requires a migration expression like `cast(old as time with time zone)`
|
||||||
|
which will interpret the previous time as local time and compute the offset for the `time with time zone` based on the current date
|
||||||
|
and time zone settings of your database session.
|
||||||
|
|
||||||
|
If the target database does not support time zone types natively, Hibernate behaves just like before.
|
||||||
|
|
||||||
|
To retain backwards compatibility, configure the setting `hibernate.timezone.default_storage` to `NORMALIZE`.
|
||||||
|
|
||||||
[[ddl-changes]]
|
[[ddl-changes]]
|
||||||
== DDL type changes
|
== DDL type changes
|
||||||
|
|
Loading…
Reference in New Issue