From c88751bc3ecbdd27dbbc7029554b8f3556a10d82 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Sun, 13 Jul 2025 00:17:23 +0000 Subject: [PATCH 01/18] Post-steps for release : `7.0.6.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index ec84e92c974b..68b61b167d92 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=7.0.6.Final \ No newline at end of file +hibernateVersion=7.0.7-SNAPSHOT \ No newline at end of file From d7a0fca51256124dc7e1b425cbef97d2d7a397fe Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 11 Jul 2025 16:44:48 +0200 Subject: [PATCH 02/18] Make sure that quarkus-version script returns the stdout / update withMaven to work with the Quarkus' wrapper Co-authored-by: Christian Beikov --- ci/quarkus.Jenkinsfile | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ci/quarkus.Jenkinsfile b/ci/quarkus.Jenkinsfile index b78f824cc04f..8a19a82d69ed 100644 --- a/ci/quarkus.Jenkinsfile +++ b/ci/quarkus.Jenkinsfile @@ -17,6 +17,7 @@ pipeline { agent none tools { jdk 'OpenJDK 17 Latest' + maven 'Apache Maven 3.9' } options { buildDiscarder(logRotator(numToKeepStr: '3', artifactNumToKeepStr: '3')) @@ -46,7 +47,7 @@ pipeline { } } dir('quarkus') { - def latestQuarkusVersion = sh (script: "git ls-remote --heads https://github.com/quarkusio/quarkus.git | grep -oP 'refs/heads/\\K[0-9]+\\.[0-9]+?\$' | sort -V -r | head -n 1").trim() + def latestQuarkusVersion = sh (script: "git ls-remote --heads https://github.com/quarkusio/quarkus.git | grep -oP 'refs/heads/\\K[0-9]+\\.[0-9]+?\$' | sort -V -r | head -n 1", returnStdout: true).trim() sh "git clone -b ${latestQuarkusVersion} --single-branch https://github.com/quarkusio/quarkus.git . || git reset --hard && git clean -fx && git pull" script { def sedStatus = sh (script: "sed -i 's@.*@${env.HIBERNATE_VERSION}@' pom.xml", returnStatus: true) @@ -57,15 +58,18 @@ pipeline { // Need to override the default maven configuration this way, because there is no other way to do it sh "sed -i 's/-Xmx5g/-Xmx2048m/' ./.mvn/jvm.config" sh "echo -e '\\n-XX:MaxMetaspaceSize=1024m'>>./.mvn/jvm.config" - withMaven(mavenLocalRepo: env.WORKSPACE + '/.m2repository', publisherStrategy:'EXPLICIT') { - sh "./mvnw -pl !docs -Dquickly install" - // Need to kill the gradle daemons started during the Maven install run - sh "sudo pkill -f '.*GradleDaemon.*' || true" - // Need to override the default maven configuration this way, because there is no other way to do it - sh "sed -i 's/-Xmx2048m/-Xmx1340m/' ./.mvn/jvm.config" - sh "sed -i 's/MaxMetaspaceSize=1024m/MaxMetaspaceSize=512m/' ./.mvn/jvm.config" - def excludes = "'!integration-tests/kafka-oauth-keycloak,!integration-tests/kafka-sasl-elytron,!integration-tests/hibernate-search-orm-opensearch,!integration-tests/maven,!integration-tests/quartz,!integration-tests/reactive-messaging-kafka,!integration-tests/resteasy-reactive-kotlin/standard,!integration-tests/opentelemetry-reactive-messaging,!integration-tests/virtual-threads/kafka-virtual-threads,!integration-tests/smallrye-jwt-oidc-webapp,!extensions/oidc-db-token-state-manager/deployment,!docs'" - sh "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED=true ./mvnw -Dinsecure.repositories=WARN -pl :quarkus-hibernate-orm -amd -pl ${excludes} verify -Dstart-containers -Dtest-containers -Dskip.gradle.build" + withMaven(mavenLocalRepo: env.WORKSPACE + '/.m2repository', publisherStrategy: 'EXPLICIT') { + // to account for script-only maven wrapper use in Quarkus: + withEnv(["MAVEN_ARGS=${env.MAVEN_ARGS?:""} ${env.MAVEN_CONFIG}"]) { + sh "./mvnw -pl !docs -Dquickly install" + // Need to kill the gradle daemons started during the Maven install run + sh "sudo pkill -f '.*GradleDaemon.*' || true" + // Need to override the default maven configuration this way, because there is no other way to do it + sh "sed -i 's/-Xmx2048m/-Xmx1340m/' ./.mvn/jvm.config" + sh "sed -i 's/MaxMetaspaceSize=1024m/MaxMetaspaceSize=512m/' ./.mvn/jvm.config" + def excludes = "'!integration-tests/kafka-oauth-keycloak,!integration-tests/kafka-sasl-elytron,!integration-tests/hibernate-search-orm-opensearch,!integration-tests/maven,!integration-tests/quartz,!integration-tests/reactive-messaging-kafka,!integration-tests/resteasy-reactive-kotlin/standard,!integration-tests/opentelemetry-reactive-messaging,!integration-tests/virtual-threads/kafka-virtual-threads,!integration-tests/smallrye-jwt-oidc-webapp,!extensions/oidc-db-token-state-manager/deployment,!docs'" + sh "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED=true ./mvnw -Dinsecure.repositories=WARN -pl :quarkus-hibernate-orm -amd -pl ${excludes} verify -Dstart-containers -Dtest-containers -Dskip.gradle.build" + } } } } From cf645b0560284d68fcd7e94983c591609e644442 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 16 Jul 2025 13:20:48 +0200 Subject: [PATCH 03/18] Make FormulaWithPartitionByTest deterministic and enable partition by tests on H2 --- .../org/hibernate/community/dialect/H2LegacyDialect.java | 5 +++++ .../src/main/java/org/hibernate/dialect/H2Dialect.java | 5 +++++ .../orm/test/mapping/formula/FormulaWithPartitionByTest.java | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index eb7df8592267..165d262600da 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -1046,6 +1046,11 @@ public String getCaseInsensitiveLike() { } } + @Override + public boolean supportsPartitionBy() { + return getVersion().isSameOrAfter( 1, 4, 200 ); + } + @Override public boolean supportsCaseInsensitiveLike() { return getVersion().isSameOrAfter( 1, 4, 194 ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 78df4e46f702..2bd49a18ba9a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -1075,6 +1075,11 @@ public boolean supportsCaseInsensitiveLike(){ return true; } + @Override + public boolean supportsPartitionBy() { + return true; + } + @Override public boolean supportsBindingNullSqlTypeForSetNull() { return true; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/FormulaWithPartitionByTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/FormulaWithPartitionByTest.java index 38a8c03d3d8e..85bc8713aba8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/FormulaWithPartitionByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/formula/FormulaWithPartitionByTest.java @@ -82,6 +82,7 @@ void tearDown(SessionFactoryScope scope) { public static class DisplayItem implements Serializable { @Id + @Column(name = "DISPLAY_ITEM_ID") private Integer id; @Column(name = "DISCOUNT_CODE") @@ -90,7 +91,7 @@ public static class DisplayItem implements Serializable { @Column(name = "DISCOUNT_VALUE") private Double discountValue; - @Formula("ROW_NUMBER() OVER( PARTITION BY DISCOUNT_CODE ORDER BY SIGN(DISCOUNT_VALUE) DESC )") + @Formula("ROW_NUMBER() OVER(PARTITION BY DISCOUNT_CODE ORDER BY DISPLAY_ITEM_ID)") private Integer itemsByCode; public Integer getId() { From 967fed96b9f7e963ef968d928f2b022dcfc4d5ba Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 16 Jul 2025 12:22:54 +0200 Subject: [PATCH 04/18] HHH-19550 Attribute join on correlated from node receives wrong root --- .../query/sqm/tree/domain/SqmCorrelation.java | 4 + ...ributeJoinOnCorrelatedOrderedJoinTest.java | 100 ++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/AttributeJoinOnCorrelatedOrderedJoinTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelation.java index 213505bad2e4..f58bf4ffd47c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelation.java @@ -20,4 +20,8 @@ public interface SqmCorrelation extends SqmFrom, SqmPathWrapper { SqmRoot getCorrelatedRoot(); + @Override + default SqmRoot findRoot() { + return getCorrelatedRoot(); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/AttributeJoinOnCorrelatedOrderedJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/AttributeJoinOnCorrelatedOrderedJoinTest.java new file mode 100644 index 000000000000..be368d1dea03 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/subquery/AttributeJoinOnCorrelatedOrderedJoinTest.java @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.criteria.subquery; + +import jakarta.persistence.*; +import org.hibernate.Session; +import org.hibernate.query.criteria.*; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import java.io.Serializable; + +@Jpa( annotatedClasses = { + AttributeJoinOnCorrelatedOrderedJoinTest.Primary.class, + AttributeJoinOnCorrelatedOrderedJoinTest.Secondary.class, + AttributeJoinOnCorrelatedOrderedJoinTest.Tertiary.class, +} ) +@Jira( "https://hibernate.atlassian.net/browse/HHH-19550" ) +public class AttributeJoinOnCorrelatedOrderedJoinTest { + + @Test + public void test(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + final HibernateCriteriaBuilder cb = em.unwrap( Session.class ).getCriteriaBuilder(); + final JpaCriteriaQuery query = cb.createTupleQuery(); + final JpaRoot primary = query.from( Primary.class ); + // setup ordered joins + final JpaEntityJoin entityJoinedPrimary = primary.join( Primary.class ); + entityJoinedPrimary.on( primary.get( "id" ).equalTo( entityJoinedPrimary.get( "id" ) ) ); + // Need an attribute join to correlate + final JpaJoin secondaryJoin = primary.join( "secondary" ); + + final JpaSubQuery subquery = query.subquery( String.class ); + final JpaJoin correlatedSecondaryJoin = subquery.correlate( secondaryJoin ); + // The association join is being added to the result of getLhs().findRoot() + // so if the correlated join returns a wrong node, this is messed up + // and will produce an exception when copying the criteria tree + final JpaJoin tertiary = correlatedSecondaryJoin.join( "tertiary" ); + subquery.select( tertiary.get( "name" ) ).where( cb.equal( + tertiary.get( "secondaryFk" ), + correlatedSecondaryJoin.get( "id" ) + ) ); + query.multiselect( primary.get( "id" ), secondaryJoin.get( "name" ), subquery ); + em.createQuery( query ).getResultList(); + } ); + } + + @Entity( name = "PrimaryEntity" ) + public static class Primary implements Serializable { + @Id + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY) + private Secondary secondary; + + public Primary() { + } + } + + @Entity( name = "SecondaryEntity" ) + public static class Secondary implements Serializable { + @Id + private Integer id; + + private String name; + @ManyToOne(fetch = FetchType.LAZY) + private Tertiary tertiary; + + public Secondary() { + } + + public Secondary(Integer id, String name) { + this.id = id; + this.name = name; + } + } + + @Entity( name = "TertiaryEntity" ) + public static class Tertiary implements Serializable { + @Id + private Integer id; + + private Integer secondaryFk; + + private String name; + + public Tertiary() { + } + + public Tertiary(Integer id, Integer secondaryFk, String name) { + this.id = id; + this.secondaryFk = secondaryFk; + this.name = name; + } + } +} From 19b4eaffbbf889e0a14853f8d0caa7f0253b5fda Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 15 Jul 2025 10:03:47 +0200 Subject: [PATCH 05/18] HHH-19368 Add test for issue --- ...nheritanceToOneSubtypeJoinGroupByTest.java | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/InheritanceToOneSubtypeJoinGroupByTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/InheritanceToOneSubtypeJoinGroupByTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/InheritanceToOneSubtypeJoinGroupByTest.java new file mode 100644 index 000000000000..c9c915d9f7b9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/InheritanceToOneSubtypeJoinGroupByTest.java @@ -0,0 +1,164 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.inheritance; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Tuple; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( annotatedClasses = { + InheritanceToOneSubtypeJoinGroupByTest.Base.class, + InheritanceToOneSubtypeJoinGroupByTest.EntityA.class, + InheritanceToOneSubtypeJoinGroupByTest.EntityB.class, + InheritanceToOneSubtypeJoinGroupByTest.WhitelistEntry.class, +} ) +@SessionFactory +public class InheritanceToOneSubtypeJoinGroupByTest { + @Test + void testGroupByA(SessionFactoryScope scope) { + scope.inSession( session -> { + final EntityA result = session.createQuery( + "SELECT a FROM WhitelistEntry we JOIN we.primaryKey.a a group by a", + EntityA.class + ).getSingleResult(); + assertThat( result.getAName() ).isEqualTo( "a" ); + } ); + } + + @Test + void testGroupByB(SessionFactoryScope scope) { + scope.inSession( session -> { + final Tuple result = session.createQuery( + "SELECT b.id, b.bName FROM WhitelistEntry we JOIN we.primaryKey.b b group by b", + Tuple.class + ).getSingleResult(); + assertThat( result.get( 1, String.class ) ).isEqualTo( "b" ); + } ); + } + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final EntityA a = new EntityA(); + a.setAName( "a" ); + final EntityB b = new EntityB(); + b.setBName( "b" ); + final WhitelistEntry whitelistEntry = new WhitelistEntry(); + whitelistEntry.setName( "whitelistEntry" ); + final WhitelistEntryPK primaryKey = new WhitelistEntryPK(); + primaryKey.setA( a ); + primaryKey.setB( b ); + whitelistEntry.setPrimaryKey( primaryKey ); + session.persist( a ); + session.persist( b ); + session.persist( whitelistEntry ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } + + @Entity(name = "Base") + @Inheritance + public static class Base { + @Id + @GeneratedValue + private Long id; + } + + + @Entity(name = "EntityA") + public static class EntityA extends Base { + private String aName; + + public String getAName() { + return aName; + } + + public void setAName(String name) { + this.aName = name; + } + } + + @Entity(name = "EntityB") + public static class EntityB extends Base { + private String bName; + + public String getBName() { + return bName; + } + + public void setBName(String name) { + this.bName = name; + } + } + + @Embeddable + public static class WhitelistEntryPK { + @ManyToOne + private EntityB b; + + @ManyToOne + private EntityA a; + + public WhitelistEntryPK() { + } + + public EntityB getB() { + return b; + } + + public void setB(EntityB b) { + this.b = b; + } + + public EntityA getA() { + return a; + } + + public void setA(EntityA a) { + this.a = a; + } + } + + @Entity(name = "WhitelistEntry") + public static class WhitelistEntry { + @EmbeddedId + private WhitelistEntryPK primaryKey; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public WhitelistEntryPK getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(WhitelistEntryPK primaryKey) { + this.primaryKey = primaryKey; + } + } +} From 8ffb02364dd9685cab78d92ab31ea5fb5cf3fb6b Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Tue, 15 Jul 2025 09:41:07 +0200 Subject: [PATCH 06/18] HHH-19368 Account for single-table inheritance subqueries in EVPs --- .../sqm/sql/internal/EntityValuedPathInterpretation.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index 98d063c2f002..746dcdd366e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -402,7 +402,9 @@ private static boolean supportsFunctionalDependency(Dialect dialect, EntityMappi final FunctionalDependencyAnalysisSupport analysisSupport = dialect.getFunctionalDependencyAnalysisSupport(); if ( analysisSupport.supportsAnalysis() ) { if ( entityMappingType.getSqmMultiTableMutationStrategy() == null ) { - return true; + // A subquery may be used to render a single-table inheritance subtype, in which case + // we cannot use functional dependency analysis unless the dialect supports table groups + return analysisSupport.supportsTableGroups() || entityMappingType.getSuperMappingType() == null; } else { return analysisSupport.supportsTableGroups() && ( analysisSupport.supportsConstants() || From 2356f67df5fe04021978cd5384422294c969da75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20=C5=A0albaba?= Date: Thu, 17 Jul 2025 16:00:29 +0200 Subject: [PATCH 07/18] Add support for testing with custom EDB image --- docker_db.sh | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/docker_db.sh b/docker_db.sh index e280694fb29f..032ee55cd228 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -217,37 +217,52 @@ edb() { edb_13() { $CONTAINER_CLI rm -f edb || true - # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:13 -f edb13.Dockerfile .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:13 + if [[ -z "${DB_IMAGE_EDB}" ]]; then + DB_IMAGE_EDB="edb-test:13" + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:13 -f edb13.Dockerfile .) + fi + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d $DB_IMAGE_EDB } edb_14() { $CONTAINER_CLI rm -f edb || true - # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:14 -f edb14.Dockerfile .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:14 + if [[ -z "${DB_IMAGE_EDB}" ]]; then + DB_IMAGE_EDB="edb-test:14" + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:14 -f edb14.Dockerfile .) + fi + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d $DB_IMAGE_EDB } edb_15() { $CONTAINER_CLI rm -f edb || true - # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:15 -f edb15.Dockerfile .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:15 + if [[ -z "${DB_IMAGE_EDB}" ]]; then + DB_IMAGE_EDB="edb-test:15" + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:15 -f edb15.Dockerfile .) + fi + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d $DB_IMAGE_EDB } edb_16() { $CONTAINER_CLI rm -f edb || true - # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:16 -f edb16.Dockerfile .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:16 + if [[ -z "${DB_IMAGE_EDB}" ]]; then + DB_IMAGE_EDB="edb-test:16" + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:16 -f edb16.Dockerfile .) + fi + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d $DB_IMAGE_EDB } edb_17() { $CONTAINER_CLI rm -f edb || true - # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator - (cd edb; $CONTAINER_CLI build -t edb-test:17 -f edb17.Dockerfile .) - $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d edb-test:17 + if [[ -z "${DB_IMAGE_EDB}" ]]; then + DB_IMAGE_EDB="edb-test:17" + # We need to build a derived image because the existing image is mainly made for use by a kubernetes operator + (cd edb; $CONTAINER_CLI build -t edb-test:17 -f edb17.Dockerfile .) + fi + $CONTAINER_CLI run --name edb -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p 5444:5444 -d $DB_IMAGE_EDB } db2() { From f359b4ad9f5e0aff71926437efa0d65d8effa1a9 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 17 Jul 2025 13:48:44 +0200 Subject: [PATCH 08/18] HHH-19624 Test EDB with EDB drivers --- documentation/documentation.gradle | 1 + hibernate-core/hibernate-core.gradle | 1 + .../hibernate/dialect/PostgreSQLDialect.java | 4 ++ ...stgreSQLCastingIntervalSecondJdbcType.java | 2 +- ...eSQLMultipleTypesOtherContributorTest.java | 7 ++- ...stgreSQLInetTypesOtherContributorTest.java | 6 +-- .../inet/PostgreSQLInetTypesOtherTest.java | 6 +-- .../PostgreSQLFunctionProcedureTest.java | 29 ------------- .../PostgreSQLStoredProcedureTest.java | 29 ------------- .../test/type/PostgresIntervalSecondTest.java | 3 +- ...edDriverManagerConnectionProviderImpl.java | 43 +++++++++++++++++++ .../testing/junit4/CustomRunner.java | 35 +++++++++++++++ .../orm/junit/DialectFeatureChecks.java | 7 +++ .../src/main/groovy/local.databases.gradle | 8 ++-- .../src/main/groovy/local.java-module.gradle | 1 + settings.gradle | 2 + 16 files changed, 110 insertions(+), 74 deletions(-) diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index 19b3daf882f5..c1ab82d0df3c 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -165,6 +165,7 @@ dependencies { javadocClasspath jakartaLibs.jsonbApi javadocClasspath libs.ant javadocClasspath jdbcLibs.postgresql + javadocClasspath jdbcLibs.edb javadocClasspath libs.jackson javadocClasspath gradleApi() javadocClasspath libs.jacksonXml diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 3fa4a5d740e2..a417f71604bd 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -42,6 +42,7 @@ dependencies { compileOnly libs.jackson compileOnly libs.jacksonXml compileOnly jdbcLibs.postgresql + compileOnly jdbcLibs.edb testImplementation project(':hibernate-testing') testImplementation project(':hibernate-ant') diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 874d4d52f513..cd394e84b834 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -212,6 +212,10 @@ private static OptionalTableUpdateStrategy determineOptionalTableUpdateStrategy( : PostgreSQLDialect::withoutMerge; } + public PostgreSQLDriverKind getDriverKind() { + return driverKind; + } + @Override protected DatabaseVersion getMinimumSupportedVersion() { return MINIMUM_VERSION; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/PostgreSQLCastingIntervalSecondJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/PostgreSQLCastingIntervalSecondJdbcType.java index 921f3fae13a0..fbb7c0e12ec1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/PostgreSQLCastingIntervalSecondJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/PostgreSQLCastingIntervalSecondJdbcType.java @@ -78,7 +78,7 @@ public void appendWriteExpression( Dialect dialect) { appender.append( '(' ); appender.append( writeExpression ); - appender.append( "*interval'1 second)" ); + appender.append( "*interval'1 second')" ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/PostgreSQLMultipleTypesOtherContributorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/PostgreSQLMultipleTypesOtherContributorTest.java index 01444afe786d..ef1fb59790e8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/PostgreSQLMultipleTypesOtherContributorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/PostgreSQLMultipleTypesOtherContributorTest.java @@ -16,15 +16,14 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.boot.spi.MetadataBuilderContributor; import org.hibernate.boot.spi.MetadataBuilderImplementor; -import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.query.NativeQuery; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.type.SqlTypes; import org.hibernate.type.spi.TypeConfiguration; -import org.hibernate.testing.RequiresDialect; - import org.hibernate.orm.test.id.usertype.inet.Inet; import org.hibernate.orm.test.id.usertype.inet.InetJavaType; import org.hibernate.orm.test.id.usertype.inet.InetJdbcType; @@ -40,7 +39,7 @@ /** * @author Vlad Mihalcea */ -@RequiresDialect(PostgreSQLDialect.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.IsPgJdbc.class) public class PostgreSQLMultipleTypesOtherContributorTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherContributorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherContributorTest.java index b4a2af695af0..6d348d0e6ea2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherContributorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherContributorTest.java @@ -10,11 +10,11 @@ import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.spi.MetadataBuilderContributor; import org.hibernate.boot.spi.MetadataBuilderImplementor; -import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.type.spi.TypeConfiguration; -import org.hibernate.testing.RequiresDialect; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; @@ -23,7 +23,7 @@ /** * @author Vlad Mihalcea */ -@RequiresDialect(PostgreSQLDialect.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.IsPgJdbc.class) public class PostgreSQLInetTypesOtherContributorTest extends PostgreSQLInetTypesOtherTest { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherTest.java index c341da980dfc..e02103aefff9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/PostgreSQLInetTypesOtherTest.java @@ -9,13 +9,13 @@ import org.hibernate.boot.spi.MetadataBuilderContributor; import org.hibernate.boot.spi.MetadataBuilderImplementor; -import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.query.NativeQuery; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.type.spi.TypeConfiguration; -import org.hibernate.testing.RequiresDialect; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; @@ -24,7 +24,7 @@ /** * @author Vlad Mihalcea */ -@RequiresDialect(PostgreSQLDialect.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.IsPgJdbc.class) public class PostgreSQLInetTypesOtherTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java index c2300fec9aaa..e3c796cadf46 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java @@ -7,7 +7,6 @@ import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.Timestamp; import java.sql.Types; import java.time.LocalDateTime; @@ -234,34 +233,6 @@ public void testFunctionWithJDBC() { } ); } - @Test - public void testFunctionWithJDBCByName() { - doInJPA( this::entityManagerFactory, entityManager -> { - try { - Session session = entityManager.unwrap( Session.class ); - Long phoneCount = session.doReturningWork( connection -> { - CallableStatement function = null; - try { - function = connection.prepareCall( "{ ? = call fn_count_phones(?) }" ); - function.registerOutParameter( "phoneCount", Types.BIGINT ); - function.setLong( "personId", 1L ); - function.execute(); - return function.getLong( 1 ); - } - finally { - if ( function != null ) { - function.close(); - } - } - } ); - assertEquals( Long.valueOf( 2 ), phoneCount ); - } - catch (Exception e) { - assertEquals( SQLFeatureNotSupportedException.class, e.getCause().getClass() ); - } - } ); - } - @Test @JiraKey(value = "HHH-11863") public void testSysRefCursorAsOutParameter() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java index dd3dd31b1384..60befa8191cf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java @@ -7,7 +7,6 @@ import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; @@ -118,34 +117,6 @@ public void testStoredProcedureWithJDBC(EntityManagerFactoryScope scope) { } ); } - @Test - public void testProcedureWithJDBCByName(EntityManagerFactoryScope scope) { - scope.inTransaction( entityManager -> { - try { - Session session = entityManager.unwrap( Session.class ); - Long phoneCount = session.doReturningWork( connection -> { - CallableStatement procedure = null; - try { - procedure = connection.prepareCall( "{ call sp_count_phones(?,?) }" ); - procedure.registerOutParameter( "phoneCount", Types.BIGINT ); - procedure.setLong( "personId", 1L ); - procedure.execute(); - return procedure.getLong( 1 ); - } - finally { - if ( procedure != null ) { - procedure.close(); - } - } - } ); - assertEquals( Long.valueOf( 2 ), phoneCount ); - } - catch (Exception e) { - assertEquals( SQLFeatureNotSupportedException.class, e.getCause().getClass() ); - } - } ); - } - @Test @JiraKey("HHH-11863") @RequiresDialect(value = PostgreSQLDialect.class, majorVersion = 14, comment = "Stored procedure OUT parameters are only supported since version 14") diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PostgresIntervalSecondTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PostgresIntervalSecondTest.java index 693b5ff215a3..9e5a1b08e2cf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PostgresIntervalSecondTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PostgresIntervalSecondTest.java @@ -11,6 +11,7 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.type.PostgreSQLCastingIntervalSecondJdbcType; import org.hibernate.dialect.type.PostgreSQLIntervalSecondJdbcType; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; @@ -59,7 +60,7 @@ public void verifyMappings(SessionFactoryScope scope) { assertThat( durationJdbcType ).isEqualTo( NumericJdbcType.INSTANCE ); final JdbcType intervalType = jdbcTypeRegistry.getDescriptor( SqlTypes.INTERVAL_SECOND ); - assertThat( intervalType ).isOfAnyClassIn( PostgreSQLIntervalSecondJdbcType.class ); + assertThat( intervalType ).isOfAnyClassIn( PostgreSQLIntervalSecondJdbcType.class, PostgreSQLCastingIntervalSecondJdbcType.class ); // a simple duration field with no overrides - so should be using a default JdbcType assertThat( entityDescriptor.findAttributeMapping( "duration" ) diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java index 7a919b812158..3014a4419a60 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java @@ -173,6 +173,49 @@ else if ( "org.postgresql.Driver".equals( config.driverClassName ) ) { } } ); } + else if ( "com.edb.Driver".equals( config.driverClassName ) ) { + validateConnections( c -> { + // Until pgjdbc provides a method for this out of the box, we have to do this manually + // See https://github.com/pgjdbc/pgjdbc/issues/3049 + try { + final Class pgConnection = Class.forName( "com.edb.jdbc.PgConnection" ); + final Object connection = c.unwrap( pgConnection ); + final Object typeInfo = pgConnection.getMethod( "getTypeInfo" ).invoke( connection ); + final Class typeInfoCacheClass = Class.forName( "com.edb.jdbc.TypeInfoCache" ); + final Field oidToPgNameField = typeInfoCacheClass.getDeclaredField( "oidToPgName" ); + final Field pgNameToOidField = typeInfoCacheClass.getDeclaredField( "pgNameToOid" ); + final Field pgNameToSQLTypeField = typeInfoCacheClass.getDeclaredField( "pgNameToSQLType" ); + final Field oidToSQLTypeField = typeInfoCacheClass.getDeclaredField( "oidToSQLType" ); + oidToPgNameField.setAccessible( true ); + pgNameToOidField.setAccessible( true ); + pgNameToSQLTypeField.setAccessible( true ); + oidToSQLTypeField.setAccessible( true ); + //noinspection unchecked + final Map oidToPgName = (Map) oidToPgNameField.get( typeInfo ); + //noinspection unchecked + final Map pgNameToOid = (Map) pgNameToOidField.get( typeInfo ); + //noinspection unchecked + final Map pgNameToSQLType = (Map) pgNameToSQLTypeField.get( typeInfo ); + //noinspection unchecked + final Map oidToSQLType = (Map) oidToSQLTypeField.get( typeInfo ); + for ( Iterator> iter = pgNameToOid.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = iter.next(); + final String typeName = entry.getKey(); + if ( !PGJDBC_STANDARD_TYPE_NAMES.contains( typeName ) ) { + final Integer oid = entry.getValue(); + oidToPgName.remove( oid ); + oidToSQLType.remove( oid ); + pgNameToSQLType.remove( typeName ); + iter.remove(); + } + } + return true; + } + catch (Exception e) { + throw new RuntimeException( e ); + } + } ); + } } public void onDefaultTimeZoneChange() { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java index 866a92cc02ed..c37b18dae8be 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/CustomRunner.java @@ -11,6 +11,7 @@ import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import org.hibernate.dialect.Dialect; import org.hibernate.internal.util.StringHelper; @@ -25,7 +26,9 @@ import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialects; import org.hibernate.testing.orm.junit.DialectContext; +import org.hibernate.testing.orm.junit.DialectFeatureCheck; import org.hibernate.testing.orm.junit.DialectFilterExtension; +import org.hibernate.testing.orm.junit.RequiresDialectFeatureGroup; import org.hibernate.testing.orm.junit.SkipForDialectGroup; import org.junit.BeforeClass; import org.junit.Ignore; @@ -442,6 +445,30 @@ protected Ignore convertSkipToIgnore(FrameworkMethod frameworkMethod) { } } + Collection effectiveRequiresDialectFeatures = Helper.collectAnnotations( + org.hibernate.testing.orm.junit.RequiresDialectFeature.class, + RequiresDialectFeatureGroup.class, + frameworkMethod, + getTestClass() + ); + + for ( org.hibernate.testing.orm.junit.RequiresDialectFeature effectiveRequiresDialectFeature : effectiveRequiresDialectFeatures ) { + try { + final Class featureClass = effectiveRequiresDialectFeature.feature(); + final DialectFeatureCheck featureCheck = featureClass.getConstructor().newInstance(); + boolean testResult = featureCheck.apply( dialect ); + if ( effectiveRequiresDialectFeature.reverse() ) { + testResult = !testResult; + } + if ( !testResult ) { + return buildIgnore( effectiveRequiresDialectFeature ); + } + } + catch (ReflectiveOperationException e) { + throw new RuntimeException( "Unable to instantiate DialectFeatureCheck class", e ); + } + } + return null; } @@ -567,6 +594,14 @@ private Ignore buildIgnore(RequiresDialectFeature requiresDialectFeature) { ); } + private Ignore buildIgnore(org.hibernate.testing.orm.junit.RequiresDialectFeature requiresDialectFeature) { + return buildIgnore( + String.format( Locale.ROOT, "Failed @RequiresDialectFeature [%s]", requiresDialectFeature.feature() ), + requiresDialectFeature.comment(), + requiresDialectFeature.jiraKey() + ); + } + private boolean isMatch(Class condition) { try { Skip.Matcher matcher = condition.newInstance(); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index ea7a8b5a0f7a..38d4c60c9329 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -55,6 +55,7 @@ import org.hibernate.dialect.NationalizationSupport; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.PostgreSQLDriverKind; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SpannerDialect; import org.hibernate.dialect.SybaseASEDialect; @@ -970,6 +971,12 @@ public boolean apply(Dialect dialect) { } } + public static class IsPgJdbc implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect instanceof PostgreSQLDialect && ( (PostgreSQLDialect) dialect ).getDriverKind() == PostgreSQLDriverKind.PG_JDBC; + } + } + public static class SupportsCommentOn implements DialectFeatureCheck { public boolean apply(Dialect dialect) { return dialect.supportsCommentOn(); diff --git a/local-build-plugins/src/main/groovy/local.databases.gradle b/local-build-plugins/src/main/groovy/local.databases.gradle index 150ec7f19236..4156a32b27d6 100644 --- a/local-build-plugins/src/main/groovy/local.databases.gradle +++ b/local-build-plugins/src/main/groovy/local.databases.gradle @@ -77,13 +77,13 @@ ext { ], edb_ci : [ 'db.dialect' : 'org.hibernate.dialect.PostgresPlusDialect', - 'jdbc.driver': 'org.postgresql.Driver', + 'jdbc.driver': 'com.edb.Driver', 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com - 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', - 'jdbc.datasource' : 'org.postgresql.Driver', -// 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', + 'jdbc.url' : 'jdbc:edb://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', + 'jdbc.datasource' : 'com.edb.Driver', +// 'jdbc.datasource' : 'com.edb.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], sybase_ci : [ diff --git a/local-build-plugins/src/main/groovy/local.java-module.gradle b/local-build-plugins/src/main/groovy/local.java-module.gradle index 40499736c718..f6a909bc4178 100644 --- a/local-build-plugins/src/main/groovy/local.java-module.gradle +++ b/local-build-plugins/src/main/groovy/local.java-module.gradle @@ -70,6 +70,7 @@ dependencies { testRuntimeOnly jdbcLibs.derbyTools testRuntimeOnly jdbcLibs.hsqldb testRuntimeOnly jdbcLibs.postgresql + testRuntimeOnly jdbcLibs.edb testRuntimeOnly jdbcLibs.mssql testRuntimeOnly jdbcLibs.informix testRuntimeOnly jdbcLibs.cockroachdb diff --git a/settings.gradle b/settings.gradle index 90b3c1818675..0c7993dca334 100644 --- a/settings.gradle +++ b/settings.gradle @@ -227,6 +227,7 @@ dependencyResolutionManagement { def mysqlVersion = version "mysql", "9.2.0" def oracleVersion = version "oracle", "23.7.0.25.01" def pgsqlVersion = version "pgsql", "42.7.4" + def edbVersion = version "edb", "42.7.3.3" def sybaseVersion = version "sybase", "1.3.1" def tidbVersion = version "tidb", mysqlVersion def altibaseVersion = version "altibase", "7.3.0.0.3" @@ -238,6 +239,7 @@ dependencyResolutionManagement { library( "derbyTools", "org.apache.derby", "derbytools" ).versionRef( derbyVersion ) library( "postgresql", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) library( "cockroachdb", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) + library( "edb", "com.enterprisedb", "edb-jdbc" ).versionRef( edbVersion ) library( "mysql", "com.mysql", "mysql-connector-j" ).versionRef( mysqlVersion ) library( "tidb", "com.mysql", "mysql-connector-j" ).versionRef( tidbVersion ) library( "mariadb", "org.mariadb.jdbc", "mariadb-java-client" ).versionRef( mariadbVersion ) From 3728d11ac965a7831bffad632ea3ddc982257f92 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 18 Jul 2025 09:24:25 +0200 Subject: [PATCH 09/18] HHH-19621 Don't render code units for DB2 for i --- .../hibernate/community/dialect/DB2iLegacyDialect.java | 10 ++++++++-- .../main/java/org/hibernate/dialect/DB2iDialect.java | 10 ++++++++-- .../dialect/function/DB2SubstringFunction.java | 9 ++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java index 94d2c3aed20a..c452a3b95fd4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2iLegacyDialect.java @@ -7,6 +7,7 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.function.DB2SubstringFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.DB2zIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; @@ -57,9 +58,14 @@ public DB2iLegacyDialect(DatabaseVersion version) { @Override public void initializeFunctionRegistry(FunctionContributions functionContributions) { - super.initializeFunctionRegistry(functionContributions); + super.initializeFunctionRegistry( functionContributions ); + // DB2 for i doesn't allow code units: https://www.ibm.com/docs/en/i/7.1.0?topic=functions-substring + functionContributions.getFunctionRegistry().register( + "substring", + new DB2SubstringFunction( false, functionContributions.getTypeConfiguration() ) + ); if ( getVersion().isSameOrAfter( 7, 2 ) ) { - CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions); + CommonFunctionFactory functionFactory = new CommonFunctionFactory( functionContributions ); functionFactory.listagg( null ); functionFactory.inverseDistributionOrderedSetAggregates(); functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index 64a4dcdf82cd..a629384f9927 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -8,6 +8,7 @@ import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.function.CommonFunctionFactory; +import org.hibernate.dialect.function.DB2SubstringFunction; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.DB2zIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; @@ -65,9 +66,14 @@ protected DatabaseVersion getMinimumSupportedVersion() { @Override public void initializeFunctionRegistry(FunctionContributions functionContributions) { - super.initializeFunctionRegistry(functionContributions); + super.initializeFunctionRegistry( functionContributions ); + // DB2 for i doesn't allow code units: https://www.ibm.com/docs/en/i/7.1.0?topic=functions-substring + functionContributions.getFunctionRegistry().register( + "substring", + new DB2SubstringFunction( false, functionContributions.getTypeConfiguration() ) + ); if ( getVersion().isSameOrAfter( 7, 2 ) ) { - CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions); + CommonFunctionFactory functionFactory = new CommonFunctionFactory( functionContributions ); functionFactory.listagg( null ); functionFactory.inverseDistributionOrderedSetAggregates(); functionFactory.hypotheticalOrderedSetAggregates_windowEmulation(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java index 8653831f64f0..1d8ca27519de 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/DB2SubstringFunction.java @@ -28,7 +28,13 @@ */ public class DB2SubstringFunction extends AbstractSqmSelfRenderingFunctionDescriptor { + private final boolean needsCodeUnit; + public DB2SubstringFunction(TypeConfiguration typeConfiguration) { + this( true, typeConfiguration ); + } + + public DB2SubstringFunction(boolean needsCodeUnit, TypeConfiguration typeConfiguration) { super( "substring", new ArgumentTypesValidator( StandardArgumentsValidators.between( 2, 4 ), STRING, INTEGER, INTEGER, FunctionParameterType.ANY ), @@ -36,6 +42,7 @@ public DB2SubstringFunction(TypeConfiguration typeConfiguration) { StandardBasicTypes.STRING ) ), StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING, INTEGER, INTEGER ) ); + this.needsCodeUnit = needsCodeUnit; } @Override @@ -51,7 +58,7 @@ public void render( sqlAppender.appendSql( ',' ); arguments.get( i ).accept( walker ); } - if ( argumentCount != 4 ) { + if ( argumentCount != 4 && needsCodeUnit ) { sqlAppender.appendSql( ",codeunits32" ); } sqlAppender.appendSql( ')' ); From 5d3a8854d67005649a9c86b41fa91d01adad86b6 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 4 Jun 2025 09:28:08 +0200 Subject: [PATCH 10/18] HHH-19516 Upgrade to JUnit 5.13.0 --- .../extension/engine/BytecodeEnhancedTestEngine.java | 9 ++++++++- settings.gradle | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java index 86914a9d2999..4ae99dfbb63c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java @@ -39,6 +39,7 @@ import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; import org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor; +import org.junit.jupiter.engine.descriptor.LauncherStoreFacade; import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor; import org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor; import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext; @@ -215,7 +216,8 @@ private Method findMethodReplacement(ClassTestDescriptor updated, Method testMet protected JupiterEngineExecutionContext createExecutionContext(ExecutionRequest request) { return new JupiterEngineExecutionContext( request.getEngineExecutionListener(), - this.getJupiterConfiguration( request ) + this.getJupiterConfiguration( request ), + new LauncherStoreFacade( request.getStore() ) ); } @@ -272,6 +274,11 @@ public boolean isParallelExecutionEnabled() { return configuration.isParallelExecutionEnabled(); } + @Override + public boolean isClosingStoredAutoCloseablesEnabled() { + return configuration.isClosingStoredAutoCloseablesEnabled(); + } + @Override public boolean isExtensionAutoDetectionEnabled() { return configuration.isExtensionAutoDetectionEnabled(); diff --git a/settings.gradle b/settings.gradle index 0c7993dca334..c9ec73615a1f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -161,7 +161,7 @@ dependencyResolutionManagement { library( "xjc", "org.glassfish.jaxb", "jaxb-xjc" ).versionRef( xjcVersion ) } testLibs { - def junit5Version = version "junit5", "5.12.0" + def junit5Version = version "junit5", "5.13.0" def junit4Version = version "junit4", "4.13.2" def junit5LauncherVersion = version "junit5Launcher", "1.12.0" From 06ae4aa9a66bb47b71e84d41d5c4d73e2322b4d1 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 18 Jul 2025 08:42:12 +0200 Subject: [PATCH 11/18] Ensure compatibility with JUnit < 5.13 --- .../extension/engine/BytecodeEnhancedTestEngine.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java index 4ae99dfbb63c..ca634910482f 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java @@ -9,6 +9,8 @@ import static org.hibernate.testing.bytecode.enhancement.extension.engine.BytecodeEnhancedClassUtils.enhanceTestClass; import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; @@ -44,6 +46,7 @@ import org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor; import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext; import org.junit.platform.engine.EngineDiscoveryRequest; +import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.ExecutionRequest; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; @@ -214,6 +217,15 @@ private Method findMethodReplacement(ClassTestDescriptor updated, Method testMet @Override protected JupiterEngineExecutionContext createExecutionContext(ExecutionRequest request) { + try { + // Try constructing the JupiterEngineExecutionContext the way it was done in 5.12 and before + final Constructor constructorV5_12 = JupiterEngineExecutionContext.class + .getConstructor( EngineExecutionListener.class, JupiterConfiguration.class ); + return constructorV5_12.newInstance( request.getEngineExecutionListener(), this.getJupiterConfiguration( request ) ); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e ) { + // Ignore errors as they are probably due to version mismatches and try the 5.13 way + } + return new JupiterEngineExecutionContext( request.getEngineExecutionListener(), this.getJupiterConfiguration( request ), From b34e5bcb79f771c7a321b6992e3b236793c6ac4e Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 18 Jul 2025 09:04:06 +0200 Subject: [PATCH 12/18] Fix missing newline --- .../extension/engine/BytecodeEnhancedTestEngine.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java index ca634910482f..3bccc2927867 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/bytecode/enhancement/extension/engine/BytecodeEnhancedTestEngine.java @@ -222,7 +222,8 @@ protected JupiterEngineExecutionContext createExecutionContext(ExecutionRequest final Constructor constructorV5_12 = JupiterEngineExecutionContext.class .getConstructor( EngineExecutionListener.class, JupiterConfiguration.class ); return constructorV5_12.newInstance( request.getEngineExecutionListener(), this.getJupiterConfiguration( request ) ); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e ) { + } + catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { // Ignore errors as they are probably due to version mismatches and try the 5.13 way } From 2160b5f866fe57610d66b96719d81af176a9924a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 11 Jun 2025 10:55:51 +0200 Subject: [PATCH 13/18] HHH-19457 Add test for issue --- ...JoinedDiscriminatorSameChildTableTest.java | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/JoinedDiscriminatorSameChildTableTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/JoinedDiscriminatorSameChildTableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/JoinedDiscriminatorSameChildTableTest.java new file mode 100644 index 000000000000..9679f00be5bb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/joined/JoinedDiscriminatorSameChildTableTest.java @@ -0,0 +1,163 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.inheritance.joined; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = { + JoinedDiscriminatorSameChildTableTest.EntityParent.class, + JoinedDiscriminatorSameChildTableTest.EntityChildOne.class, + JoinedDiscriminatorSameChildTableTest.EntityChildTwo.class, + JoinedDiscriminatorSameChildTableTest.EntityRelation.class, +}) +@SessionFactory(useCollectingStatementInspector = true) +@Jira("https://hibernate.atlassian.net/browse/HHH-19457") +public class JoinedDiscriminatorSameChildTableTest { + @Test + public void testParents(SessionFactoryScope scope) { + final SQLStatementInspector inspector = scope.getCollectingStatementInspector(); + + scope.inSession( session -> { + final EntityRelation relation = session.find( EntityRelation.class, "relation_1" ); + inspector.clear(); + assertThat( relation.getParents() ).hasSize( 2 ); + } ); + // no need to filter by discriminator column, as we're selecting all subtypes of EntityParent + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); + + scope.inSession( session -> { + final EntityRelation relation = session.createQuery( + "from EntityRelation where id = 'relation_1'", + EntityRelation.class + ).getSingleResult(); + inspector.clear(); + assertThat( relation.getParents() ).hasSize( 2 ); + } ); + inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 ); + } + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final EntityRelation relation = new EntityRelation(); + relation.setId( "relation_1" ); + session.persist( relation ); + + final EntityChildOne c1 = new EntityChildOne(); + c1.setId( "child_1" ); + c1.setIdRelation( "relation_1" ); + session.persist( c1 ); + + final EntityChildTwo c2 = new EntityChildTwo(); + c2.setId( "child_2" ); + c2.setIdRelation( "relation_1" ); + session.persist( c2 ); + } ); + } + + @AfterAll + public void tearDown(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } + + @Entity(name = "EntityParent") + @Table(name = "parent_table") + @Inheritance(strategy = InheritanceType.JOINED) + @DiscriminatorColumn(name = "disc_col") + static abstract class EntityParent { + @Id + @Column(name = "id") + private String id; + + @Column(name = "id_relation") + private String idRelation; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "id_relation", referencedColumnName = "id", insertable = false, updatable = false) + private EntityRelation relation; + + public EntityRelation getRelation() { + return relation; + } + + public void setRelation(EntityRelation requisition) { + this.relation = requisition; + } + + public String getIdRelation() { + return idRelation; + } + + public void setIdRelation(String idRelation) { + this.idRelation = idRelation; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + @Entity(name = "EntityRelation") + static class EntityRelation { + @Id + private String id; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "relation") + private List parents; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public List getParents() { + return parents; + } + } + + @Entity(name = "EntityChildOne") + @Table(name = "child_table") + @DiscriminatorValue("child-one") + static class EntityChildOne extends EntityParent { + private String name; + } + + @Entity(name = "EntityChildTwo") + @Table(name = "child_table") + @DiscriminatorValue("child-two") + static class EntityChildTwo extends EntityParent { + private Integer age; + } +} From 96e180e02af14d64bdb0bd3e76d2040aa107051f Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 11 Jun 2025 10:56:53 +0200 Subject: [PATCH 14/18] HHH-19457 Ignore joined discriminator when selecting all subtypes --- .../persister/entity/JoinedSubclassEntityPersister.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 7e73a464e72a..603a9d237cc6 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -1293,15 +1293,16 @@ private boolean applyDiscriminatorPredicate( Map entityNameUses, MappingMetamodelImplementor metamodel) { if ( tableReference.getTableExpression().equals( getRootTableName() ) ) { - assert join.getJoinType() == SqlAstJoinType.INNER : "Found table reference join with root table of non-INNER type: " + join.getJoinType(); final String discriminatorPredicate = getPrunedDiscriminatorPredicate( entityNameUses, metamodel, "t" -// tableReference.getIdentificationVariable() ); - tableReference.setPrunedTableExpression( "(select * from " + getRootTableName() + " t where " + discriminatorPredicate + ")" ); -// join.applyPredicate( new SqlFragmentPredicate( discriminatorPredicate ) ); + // null means we're filtering for all subtypes, so we don't need to apply a predicate + if ( discriminatorPredicate != null ) { + assert join.getJoinType() == SqlAstJoinType.INNER : "Found table reference join with root table of non-INNER type: " + join.getJoinType(); + tableReference.setPrunedTableExpression( "(select * from " + getRootTableName() + " t where " + discriminatorPredicate + ")" ); + } return true; } return false; From 9a14fda45aaf79d4c6be9de280b617b9a9eddf61 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Thu, 17 Jul 2025 16:45:01 +0200 Subject: [PATCH 15/18] HHH-19261 - query hints in Oracle are not concatenated with comma's, but with spaces Signed-off-by: Jan Schatteman --- .../community/dialect/OracleLegacyDialect.java | 14 ++++++++++++++ .../java/org/hibernate/dialect/OracleDialect.java | 13 +++++++++++++ .../orm/test/queryhint/OracleQueryHintTest.java | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 1b545ae97ff6..9f30dd1341cf 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -11,6 +11,7 @@ import java.sql.Types; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Matcher; @@ -119,8 +120,10 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; +import static java.lang.String.join; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.query.common.TemporalUnit.DAY; import static org.hibernate.query.common.TemporalUnit.HOUR; import static org.hibernate.query.common.TemporalUnit.MINUTE; @@ -1275,6 +1278,17 @@ public boolean useFollowOnLocking(String sql, QueryOptions queryOptions) { ); } + @Override + public String getQueryHintString(String query, List hintList) { + if ( hintList.isEmpty() ) { + return query; + } + else { + final String hints = join( " ", hintList ); + return isEmpty( hints ) ? query : getQueryHintString( query, hints ); + } + } + @Override public String getQueryHintString(String sql, String hints) { final String statementType = statementType( sql ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index c3abaade3337..1dfafcc97685 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -113,10 +113,12 @@ import java.sql.Types; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.List; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.lang.String.join; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS; import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor; @@ -1337,6 +1339,17 @@ public boolean useFollowOnLocking(String sql, QueryOptions queryOptions) { || queryOptions.hasLimit() && queryOptions.getLimit().getFirstRow() != null; } + @Override + public String getQueryHintString(String query, List hintList) { + if ( hintList.isEmpty() ) { + return query; + } + else { + final String hints = join( " ", hintList ); + return isEmpty( hints ) ? query : getQueryHintString( query, hints ); + } + } + @Override public String getQueryHintString(String sql, String hints) { final String statementType = statementType( sql ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/queryhint/OracleQueryHintTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/queryhint/OracleQueryHintTest.java index 96d3034da77f..84d0f30f4973 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/queryhint/OracleQueryHintTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/queryhint/OracleQueryHintTest.java @@ -94,7 +94,7 @@ public void testQueryHint(SessionFactoryScope scope) { } ); statementInspector.assertExecutedCount( 1 ); - assertTrue( statementInspector.getSqlQueries().get( 0 ).contains( "select /*+ ALL_ROWS, USE_CONCAT */" ) ); + assertTrue( statementInspector.getSqlQueries().get( 0 ).contains( "select /*+ ALL_ROWS USE_CONCAT */" ) ); statementInspector.clear(); // ensure the insertion logic can handle a comment appended to the front From 4ccdf24ca2f7027c3d6c850fc1a10ac262464789 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 22 Jul 2025 11:18:50 +0200 Subject: [PATCH 16/18] Update MariaDB testing to latest GA release 11.8.2 --- docker_db.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docker_db.sh b/docker_db.sh index 032ee55cd228..b18f54807f61 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -116,7 +116,7 @@ mysql_9_2() { } mariadb() { - mariadb_11_7 + mariadb_11_8 } mariadb_wait_until_start() @@ -164,7 +164,13 @@ mariadb_11_4() { mariadb_11_7() { $CONTAINER_CLI rm -f mariadb || true - $CONTAINER_CLI run --name mariadb -e MARIADB_USER=hibernate_orm_test -e MARIADB_PASSWORD=hibernate_orm_test -e MARIADB_DATABASE=hibernate_orm_test -e MARIADB_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d ${DB_IMAGE_MARIADB_11_7:-docker.io/mariadb:11.7-rc} --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --lower_case_table_names=2 + $CONTAINER_CLI run --name mariadb -e MARIADB_USER=hibernate_orm_test -e MARIADB_PASSWORD=hibernate_orm_test -e MARIADB_DATABASE=hibernate_orm_test -e MARIADB_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d ${DB_IMAGE_MARIADB_11_7:-docker.io/mariadb:11.7.2} --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --lower_case_table_names=2 + mariadb_wait_until_start +} + +mariadb_11_8() { + $CONTAINER_CLI rm -f mariadb || true + $CONTAINER_CLI run --name mariadb -e MARIADB_USER=hibernate_orm_test -e MARIADB_PASSWORD=hibernate_orm_test -e MARIADB_DATABASE=hibernate_orm_test -e MARIADB_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d ${DB_IMAGE_MARIADB_11_8:-docker.io/mariadb:11.8.2} --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --lower_case_table_names=2 mariadb_wait_until_start } From d70a1f69a72368f0dd18aaa83e743176171019e2 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 16 Apr 2025 16:02:06 +0200 Subject: [PATCH 17/18] HHH-19633 Don't override parent provided service with default service --- .../SessionFactoryServiceRegistryBuilderImpl.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryBuilderImpl.java index 0bbc3b2392d9..acc0716fcb6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/service/internal/SessionFactoryServiceRegistryBuilderImpl.java @@ -5,6 +5,7 @@ package org.hibernate.service.internal; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.hibernate.boot.spi.SessionFactoryOptions; @@ -26,6 +27,16 @@ public class SessionFactoryServiceRegistryBuilderImpl implements SessionFactoryS public SessionFactoryServiceRegistryBuilderImpl(ServiceRegistryImplementor parent) { this.parent = parent; + if ( parent != null ) { + for ( Iterator> iterator = initiators.iterator(); iterator.hasNext(); ) { + final SessionFactoryServiceInitiator initiator = iterator.next(); + if ( parent.locateServiceBinding( initiator.getServiceInitiated() ) != null ) { + // Parent takes precedence over the standard service initiators + iterator.remove(); + } + } + + } } /** From 78b6fc4baa6344f756b96ed1f6a04ea0b771296c Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 23 Jul 2025 06:58:22 +0000 Subject: [PATCH 18/18] Pre-steps for release : `7.0.7.Final` --- changelog.txt | 21 +++++++++++++++++++++ gradle/version.properties | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 32702f229382..c020b54e029a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,27 @@ Hibernate 7 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 7.0.7.Final (July 23, 2025) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/34298 + +** Bug + * [HHH-19633] - SessionFactoryServiceRegistryBuilderImpl doesn't allow service override through parent + * [HHH-19621] - SUBSTRING function for DB2i Series is broken + * [HHH-19579] - Criteria update join - Column 'code' in SET is ambiguous + * [HHH-19550] - Attribute join on correlated from node receives wrong root + * [HHH-19524] - @OneToOne relationship unnecessary joins in nativeQuery + * [HHH-19457] - Inheritance with type JOINED not working in a related entity + * [HHH-19368] - Group by and single-table inheritance sub-select query error + * [HHH-19031] - Loading an Entity a second time when it contains an embedded object causes IllegalArgumentException + * [HHH-18774] - two bad bugs in cascade refresh + +** Task + * [HHH-19624] - Test EDB with the EDB drivers + * [HHH-19519] - Document breaking changes in new Hibernate Maven Plugin + + Changes in 7.0.6.Final (July 13, 2025) ------------------------------------------------------------------------------------------------------------------------ diff --git a/gradle/version.properties b/gradle/version.properties index 68b61b167d92..e9d156fc191a 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=7.0.7-SNAPSHOT \ No newline at end of file +hibernateVersion=7.0.7.Final \ No newline at end of file