From 77995dbff2317609afed515647d6715bd8507317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leo=C5=A1=20Bitto?= Date: Sun, 27 Jul 2025 10:58:07 +0200 Subject: [PATCH] HHH-19657: fix a bug in ArrayJdbcType which exposes itself in combination with setting hibernate.type.java_time_use_direct_jdbc=true --- .../type/descriptor/jdbc/ArrayJdbcType.java | 15 ++++++++----- .../javatime/GlobalJavaTimeJdbcTypeTests.java | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java index 748276f46e9a..258787576819 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java @@ -126,13 +126,18 @@ protected String getElementTypeName(JavaType javaType, SharedSessionContractI .getSizeStrategy() .resolveSize( elementJdbcType, elementJavaType, null, null, null ); final DdlTypeRegistry ddlTypeRegistry = session.getTypeConfiguration().getDdlTypeRegistry(); - final String typeName = ddlTypeRegistry.getDescriptor( elementJdbcType.getDdlTypeCode() ) - .getTypeName( size, new BasicTypeImpl<>( elementJavaType, elementJdbcType), ddlTypeRegistry ); - int cutIndex = typeName.indexOf( '(' ); - if ( cutIndex > 0 ) { + final String typeName = + ddlTypeRegistry.getDescriptor( elementJdbcType.getDdlTypeCode() ) + .getTypeName( size, new BasicTypeImpl<>( elementJavaType, elementJdbcType), ddlTypeRegistry ); + + final int cutIndexBegin = typeName.indexOf( '(' ); + if ( cutIndexBegin > 0 ) { + final int cutIndexEnd = typeName.lastIndexOf( ')' ); + assert cutIndexEnd > cutIndexBegin; // getTypeName for this case required length, etc, parameters. // Cut them out and use database defaults. - return typeName.substring( 0, cutIndex ); + // e.g. "timestamp($p) with timezone" becomes "timestamp with timezone" + return typeName.substring( 0, cutIndexBegin ) + typeName.substring( cutIndexEnd + 1 ); } else { return typeName; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/javatime/GlobalJavaTimeJdbcTypeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/javatime/GlobalJavaTimeJdbcTypeTests.java index b12aee7dbb25..739e06647016 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/javatime/GlobalJavaTimeJdbcTypeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/javatime/GlobalJavaTimeJdbcTypeTests.java @@ -8,6 +8,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import org.hibernate.cfg.MappingSettings; @@ -17,6 +18,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.PersistentClass; @@ -29,6 +31,7 @@ import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -203,6 +206,23 @@ void testLocalTime(SessionFactoryScope scope) { } ); } + @Test + @RequiresDialect(value = PostgreSQLDialect.class) + void testArray(SessionFactoryScope scope) { + final var offsetDateTime = OffsetDateTime.parse("1977-07-24T12:34:56+02:00"); + scope.inTransaction( session -> { + final var nativeQuery = session.createNativeQuery( + "WITH data AS (SELECT unnest(?) AS id, unnest(?) AS offset_date_time)" + + " INSERT INTO EntityWithJavaTimeValues (id, theOffsetDateTime) SELECT * FROM data" + ); + nativeQuery.setParameter( 1, new int[] { 1 } ); + nativeQuery.setParameter( 2, new OffsetDateTime[] { offsetDateTime } ); + assertThat( nativeQuery.executeUpdate() ).isEqualTo( 1 ); + final var found = session.find( EntityWithJavaTimeValues.class, 1 ); + assertThat( found.theOffsetDateTime.toInstant() ).isEqualTo( offsetDateTime.toInstant() ); + } ); + } + @AfterEach void dropTestData(SessionFactoryScope scope) { scope.inTransaction( (session) -> { @@ -217,6 +237,8 @@ public static class EntityWithJavaTimeValues { private Integer id; private String name; + private OffsetDateTime theOffsetDateTime; + private Instant theInstant; private LocalDateTime theLocalDateTime;