From 17511458f90fca98e37c0f72f61a42f3e7f2543b Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 22 May 2025 09:07:32 +0000 Subject: [PATCH 01/85] Update project version to : `3.0.1-SNAPSHOT` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index e3e58d698..6b36d7e6b 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.Final \ No newline at end of file +projectVersion=3.0.1-SNAPSHOT \ No newline at end of file From bbcd35c16e5b3e7b5a464418c92eeec10f2f61f5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 22 May 2025 14:45:58 +0200 Subject: [PATCH 02/85] Update version in JBang templates --- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/Db2ReactiveTest.java.qute | 2 +- tooling/jbang/Example.java | 2 +- tooling/jbang/MariaDBReactiveTest.java.qute | 2 +- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/PostgreSQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 174bf6858..908c828e5 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:cockroachdb:1.21.0 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 0bb807c65..1b4f599e5 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:db2:1.21.0 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 3a287c0b5..4342974f8 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -9,7 +9,7 @@ //DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Final} //DEPS org.slf4j:slf4j-simple:2.0.7 //DESCRIPTION Allow authentication to PostgreSQL using SCRAM: diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index ea4e87521..bef838391 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mariadb:1.21.0 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 51e028782..5047f8a4d 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mysql:1.21.0 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index e75486fff..dfd7f4a67 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.21.0 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 9d3759fce..5b732b70f 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -10,7 +10,7 @@ //DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:${vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.21.0 From 7e21227c1ce0160fbe0c96c4ff08038223d0a2a5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 22 May 2025 14:52:56 +0200 Subject: [PATCH 03/85] [#2272] Switch from 3.0 to 4.0 * Update JBang templates * Update development version to 4.0.0-SNAPSHOT * Enable GitHub workflow on the 4 branches and tags --- .github/workflows/build.yml | 3 +++ .github/workflows/codeql.yml | 2 +- .github/workflows/scheduler.yml | 2 +- gradle/version.properties | 2 +- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/Db2ReactiveTest.java.qute | 2 +- tooling/jbang/Example.java | 2 +- tooling/jbang/MariaDBReactiveTest.java.qute | 2 +- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/PostgreSQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 11 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63f7c7764..f818b94e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,15 +7,18 @@ on: - 'wip/**' - '2.*' - '3.*' + - '4.*' tags: - '2.*' - '3.*' + - '4.*' pull_request: branches: - 'main' - 'wip/**' - '2.*' - '3.*' + - '4.*' # For building snapshots workflow_call: inputs: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5f0ffbc8..0be7d261f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,7 +2,7 @@ name: "CodeQL" on: push: - branches: [ "main", "1.0", "jakarta/main" ] + branches: [ "main", "4.0", "3.0", "2.4" ] pull_request: branches: [ "main" ] schedule: diff --git a/.github/workflows/scheduler.yml b/.github/workflows/scheduler.yml index 2a25cf60a..24299c8a7 100644 --- a/.github/workflows/scheduler.yml +++ b/.github/workflows/scheduler.yml @@ -11,7 +11,7 @@ jobs: build-snapshots: strategy: matrix: - branch: [ 'wip/2.3', 'wip/2.4', 'wip/3.0' ] + branch: [ 'wip/2.4', 'wip/3.0', 'wip/4.0' ] uses: ./.github/workflows/build.yml with: branch: ${{ matrix.branch }} diff --git a/gradle/version.properties b/gradle/version.properties index 6b36d7e6b..9b5d1147e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.1-SNAPSHOT \ No newline at end of file +projectVersion=4.0.0-SNAPSHOT diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 908c828e5..43ca44326 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:cockroachdb:1.21.0 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 1b4f599e5..3a0f544e6 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:db2:1.21.0 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 4342974f8..21dc2502a 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -9,7 +9,7 @@ //DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.slf4j:slf4j-simple:2.0.7 //DESCRIPTION Allow authentication to PostgreSQL using SCRAM: diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index bef838391..b6eb4acea 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mariadb:1.21.0 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 5047f8a4d..9747f9894 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mysql:1.21.0 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index dfd7f4a67..54ba2c45b 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -7,7 +7,7 @@ //DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.21.0 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 5b732b70f..116c60a87 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -10,7 +10,7 @@ //DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} //DEPS io.vertx:vertx-unit:${vertx.version:4.5.15} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.21.0 From 5f710f051fd2d9b30711fa92057faa6b2ac3a32b Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 17 Dec 2024 14:46:48 +0100 Subject: [PATCH 04/85] [#2036] Upgrade Vert.x SQL client to 5.0.0 * Upgrade scram-client:2.1 to 3.1 (required) --- README.md | 10 ++--- build.gradle | 2 +- .../main/asciidoc/reference/introduction.adoc | 2 +- examples/native-sql-example/build.gradle | 2 +- examples/session-example/build.gradle | 2 +- gradle.properties | 6 +-- hibernate-reactive-core/build.gradle | 2 +- .../reactive/context/impl/VertxContext.java | 15 +++++--- .../id/impl/BlockingIdentifierGenerator.java | 8 ++-- .../pool/impl/DefaultSqlClientPool.java | 21 +++++----- .../hibernate/reactive/BaseReactiveTest.java | 37 ++++++------------ .../reactive/TenantDependentPool.java | 38 ++++++------------- .../reactive/schema/SchemaValidationTest.java | 10 ++--- .../bytecode-enhancements-it/build.gradle | 2 +- .../hibernate/reactive/it/BaseReactiveIT.java | 33 +++++----------- .../build.gradle | 2 +- .../quarkus/qe/database/BaseReactiveIT.java | 33 +++++----------- .../techempower-postgres-it/build.gradle | 2 +- .../it/techempower/WorldVerticle.java | 13 ++----- .../reactive/techempower/TechEmpowerTest.java | 2 +- .../verticle-postgres-it/build.gradle | 2 +- .../reactive/it/verticle/ProductVerticle.java | 13 ++----- .../reactive/it/LocalContextTest.java | 5 +-- .../jbang/CockroachDBReactiveTest.java.qute | 4 +- tooling/jbang/Db2ReactiveTest.java.qute | 4 +- tooling/jbang/Example.java | 8 ++-- tooling/jbang/MariaDBReactiveTest.java.qute | 4 +- tooling/jbang/MySQLReactiveTest.java.qute | 4 +- .../jbang/PostgreSQLReactiveTest.java.qute | 4 +- tooling/jbang/ReactiveTest.java | 10 ++--- 30 files changed, 116 insertions(+), 184 deletions(-) diff --git a/README.md b/README.md index 6047808fb..743540d6c 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ Hibernate Reactive has been tested with: - MS SQL Server 2022 - Oracle 23 - [Hibernate ORM][] 7.0.0.Final -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.15 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.15 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.15 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.15 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.15 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0.0 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0.0 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/build.gradle b/build.gradle index 452f7ea2c..f927371ef 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ ext { // Example: // ./gradlew build -PvertxSqlClientVersion=4.0.0-SNAPSHOT if ( !project.hasProperty( 'vertxSqlClientVersion' ) ) { - vertxSqlClientVersion = '4.5.15' + vertxSqlClientVersion = '5.0.0' } testcontainersVersion = '1.21.0' diff --git a/documentation/src/main/asciidoc/reference/introduction.adoc b/documentation/src/main/asciidoc/reference/introduction.adoc index 4f2237edb..ee5090c21 100644 --- a/documentation/src/main/asciidoc/reference/introduction.adoc +++ b/documentation/src/main/asciidoc/reference/introduction.adoc @@ -89,7 +89,7 @@ Optionally, you might also add any of the following additional features: | Hibernate Validator | `org.hibernate.validator:hibernate-validator` and `org.glassfish:jakarta.el` | Compile-time checking for your HQL queries | `org.hibernate:query-validator` | Second-level cache support via JCache and EHCache | `org.hibernate.orm:hibernate-jcache` along with `org.ehcache:ehcache` -| SCRAM authentication support for PostgreSQL | `com.ongres.scram:client:2.1` +| SCRAM authentication support for PostgreSQL | `com.ongres.scram:scram-client:3.1` |=== You might also add the Hibernate {enhancer}[bytecode enhancer] to your diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index c79ec8984..3b650d34a 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -40,7 +40,7 @@ dependencies { runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0" // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:client:2.1' + runtimeOnly 'com.ongres.scram:scram-client:3.1' } // Optional: enable the bytecode enhancements diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index 4da40ba69..a001112d8 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -41,7 +41,7 @@ dependencies { runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0" // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:client:2.1' + runtimeOnly 'com.ongres.scram:scram-client:3.1' } // Optional: enable the bytecode enhancements diff --git a/gradle.properties b/gradle.properties index 5cd937337..36bc0c5ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,9 +47,9 @@ hibernateOrmVersion = 7.0.0.Final #skipOrmVersionParsing = true # Override default Vert.x Sql client version -#vertxSqlClientVersion = 4.5.15-SNAPSHOT +#vertxSqlClientVersion = 5.0.0-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 4.5.15 -#vertxWebtClientVersion = 4.5.15 +#vertxWebVersion = 5.0.0 +#vertxWebtClientVersion = 5.0.0 diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 01f593d80..26a4de751 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -39,7 +39,7 @@ dependencies { testImplementation "io.vertx:vertx-micrometer-metrics:${vertxSqlClientVersion}" // Optional dependency of vertx-pg-client, essential when connecting via SASL SCRAM - testImplementation 'com.ongres.scram:client:2.1' + testImplementation 'com.ongres.scram:scram-client:3.1' // JUnit Jupiter testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.3' diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/VertxContext.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/VertxContext.java index 4448c9b6c..02c28af5f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/VertxContext.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/VertxContext.java @@ -8,7 +8,7 @@ import java.lang.invoke.MethodHandles; import io.vertx.core.Vertx; -import io.vertx.core.impl.ContextInternal; +import io.vertx.core.internal.ContextInternal; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.logging.impl.Log; @@ -36,7 +36,7 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) { @Override public void put(Key key, T instance) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = currentContext(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); context.putLocal( key, instance ); @@ -47,9 +47,13 @@ public void put(Key key, T instance) { } } + private static ContextInternal currentContext() { + return (ContextInternal) Vertx.currentContext(); + } + @Override public T get(Key key) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = currentContext(); if ( context != null ) { T local = context.getLocal( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); @@ -63,7 +67,7 @@ public T get(Key key) { @Override public void remove(Key key) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = currentContext(); if ( context != null ) { boolean removed = context.removeLocal( key ); if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed ); @@ -75,7 +79,7 @@ public void remove(Key key) { @Override public void execute(Runnable runnable) { - final io.vertx.core.Context currentContext = Vertx.currentContext(); + final io.vertx.core.Context currentContext = currentContext(); if ( currentContext == null ) { if ( trace ) LOG.tracef( "Not in a Vert.x context, checking the VertxInstance service" ); final io.vertx.core.Context newContext = vertxInstance.getVertx().getOrCreateContext(); @@ -83,6 +87,7 @@ public void execute(Runnable runnable) { // that could lead to unintentionally share the same session with other streams. ContextInternal newContextInternal = (ContextInternal) newContext; final ContextInternal duplicate = newContextInternal.duplicate(); + if ( trace ) LOG.tracef( "Using duplicated context from VertxInstance: %s", duplicate ); duplicate.runOnContext( x -> runnable.run() ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/BlockingIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/BlockingIdentifierGenerator.java index c820f513f..329792f28 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/BlockingIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/BlockingIdentifierGenerator.java @@ -14,9 +14,9 @@ import io.vertx.core.Context; import io.vertx.core.Vertx; -import io.vertx.core.net.impl.pool.CombinerExecutor; -import io.vertx.core.net.impl.pool.Executor; -import io.vertx.core.net.impl.pool.Task; +import io.vertx.core.internal.pool.CombinerExecutor; +import io.vertx.core.internal.pool.Executor; +import io.vertx.core.internal.pool.Task; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -44,7 +44,7 @@ public abstract class BlockingIdentifierGenerator implements ReactiveIdentifierG //modification access. //This replaces the synchronization blocks one would see in a similar //service in Hibernate ORM, but using a non-blocking cooperative design. - private final CombinerExecutor executor = new CombinerExecutor( state ); + private final CombinerExecutor executor = new CombinerExecutor<>( state ); /** * Allocate a new block, by obtaining the next "hi" value from the database diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPool.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPool.java index 67476f71e..5249a79e5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPool.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPool.java @@ -13,6 +13,7 @@ import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; @@ -31,13 +32,13 @@ import io.vertx.core.Future; import io.vertx.core.Vertx; +import io.vertx.core.net.NetClientOptions; import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.PoolOptions; import io.vertx.sqlclient.SqlConnectOptions; +import io.vertx.sqlclient.impl.Utils; import io.vertx.sqlclient.spi.Driver; -import static java.util.Collections.singletonList; - /** * A pool of reactive connections backed by a Vert.x {@link Pool}. * The {@code Pool} itself is backed by an instance of {@link Vertx} @@ -189,7 +190,7 @@ protected Pool createPool(URI uri) { * * @return the new {@link Pool} */ - protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions poolOptions, Vertx vertx) { + protected Pool createPool(URI uri, T connectOptions, PoolOptions poolOptions, Vertx vertx) { try { // First try to load the Pool using the standard ServiceLoader pattern // This only works if exactly 1 Driver is on the classpath. @@ -198,8 +199,9 @@ protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions catch (ServiceConfigurationError e) { // Backup option if multiple drivers are on the classpath. // We will be able to remove this once Vertx 3.9.2 is available - final Driver driver = findDriver( uri, e ); - return driver.createPool( vertx, singletonList( connectOptions ), poolOptions ); + final Driver driver = findDriver( uri, e ); + Supplier> database = Utils.singletonSupplier( driver.downcast( connectOptions ) ); + return driver.createPool( vertx, database, poolOptions, new NetClientOptions(), null ); } } @@ -222,15 +224,14 @@ protected URI jdbcUrl(Map configurationValues) { * so we need to disambiguate according to the scheme specified * in the given {@link URI}. * - * @param uri the JDBC URL or database URI + * @param uri the JDBC URL or database URI * @param originalError the error that was thrown - * * @return the disambiguated {@link Driver} */ - private Driver findDriver(URI uri, ServiceConfigurationError originalError) { + private Driver findDriver(URI uri, ServiceConfigurationError originalError) { String scheme = scheme( uri ); - List selected = new ArrayList<>(); - for ( Driver d : ServiceLoader.load( Driver.class ) ) { + List> selected = new ArrayList<>(); + for ( Driver d : ServiceLoader.load( Driver.class ) ) { String driverName = d.getClass().getCanonicalName(); if ( matchesScheme( driverName, scheme ) ) { LOG.selectedDriver( driverName ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java index 07a79379b..508625bcb 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; @@ -29,6 +28,7 @@ import org.hibernate.reactive.provider.service.ReactiveGenerationTarget; import org.hibernate.reactive.stage.Stage; import org.hibernate.reactive.testing.SessionFactoryManager; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.tool.schema.spi.SchemaManagementTool; import org.junit.jupiter.api.AfterAll; @@ -41,7 +41,6 @@ import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.smallrye.mutiny.Uni; -import io.vertx.core.Promise; import io.vertx.core.VertxOptions; import io.vertx.junit5.RunTestOnContext; import io.vertx.junit5.Timeout; @@ -75,7 +74,7 @@ public abstract class BaseReactiveTest { * Configure Vertx JUnit5 test context */ @RegisterExtension - static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions() ); + static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions(), false ); private static VertxOptions vertxOptions() { Metrics.addRegistry( new SimpleMeterRegistry() ); @@ -217,33 +216,19 @@ protected CompletionStage setupSessionFactory(Configuration configuration) * @return a {@link CompletionStage} void that succeeds when the factory is ready. */ protected CompletionStage setupSessionFactory(Supplier confSupplier) { - CompletableFuture future = new CompletableFuture<>(); - testOnContext.vertx() + return testOnContext.vertx() .executeBlocking( // schema generation is a blocking operation and so it causes an // exception when run on the Vert.x event loop. So call it using // Vertx.executeBlocking() - promise -> startFactoryManager( promise, confSupplier ), - event -> { - if ( event.succeeded() ) { - future.complete( null ); - } - else { - future.completeExceptionally( event.cause() ); - } - } - ); - return future; - } - - private void startFactoryManager(Promise p, Supplier confSupplier) { - try { - factoryManager.start( () -> createHibernateSessionFactory( confSupplier.get() ) ); - p.complete(); - } - catch (Throwable e) { - p.fail( e ); - } + () -> startFactoryManager( confSupplier ), + false + ).toCompletionStage().thenCompose( CompletionStages::voidFuture ); + } + + private Object startFactoryManager(Supplier confSupplier) { + factoryManager.start( () -> createHibernateSessionFactory( confSupplier.get() ) ); + return null; } private SessionFactory createHibernateSessionFactory(Configuration configuration) { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TenantDependentPool.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TenantDependentPool.java index 70b3a1ba6..4dd18b30e 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TenantDependentPool.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TenantDependentPool.java @@ -7,16 +7,12 @@ import java.net.URI; import java.util.Map; -import java.util.function.Function; import java.util.stream.Collectors; import org.hibernate.reactive.MyCurrentTenantIdentifierResolver.Tenant; import org.hibernate.reactive.pool.impl.DefaultSqlClientPool; -import io.vertx.core.AsyncResult; -import io.vertx.core.Context; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.PoolOptions; @@ -58,8 +54,18 @@ protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions return pools; } - private Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions poolOptions, Vertx vertx, Tenant tenant) { - return super.createPool( changeDbName( uri, tenant ), changeDbName( connectOptions, tenant ), poolOptions, vertx ); + private Pool createPool( + URI uri, + SqlConnectOptions connectOptions, + PoolOptions poolOptions, + Vertx vertx, + Tenant tenant) { + return super.createPool( + changeDbName( uri, tenant ), + changeDbName( connectOptions, tenant ), + poolOptions, + vertx + ); } /** @@ -100,11 +106,6 @@ public Pool getTenantPool(Tenant tenantId) { return poolMap.get( tenantId ); } - @Override - public void getConnection(Handler> handler) { - poolMap.get( defaultTenantId ).getConnection( handler ); - } - @Override public Future getConnection() { return poolMap.get( defaultTenantId ).getConnection(); @@ -125,21 +126,6 @@ public PreparedQuery> preparedQuery(String sql, PrepareOptions optio return poolMap.get( defaultTenantId ).preparedQuery( sql, options ); } - @Override - public void close(Handler> handler) { - poolMap.forEach( (tenant, pool) -> pool.close( handler ) ); - } - - @Override - public Pool connectHandler(Handler handler) { - return poolMap.get( defaultTenantId ).connectHandler( handler ); - } - - @Override - public Pool connectionProvider(Function> provider) { - return poolMap.get( defaultTenantId ).connectionProvider( provider ); - } - @Override public int size() { return poolMap.get( defaultTenantId ).size(); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java index 3132b8498..56def4ef9 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java @@ -34,6 +34,7 @@ import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.MARIA; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.MYSQL; +import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.GROUPED; import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -126,13 +127,8 @@ context, setupFactory( strategy, type ) validateConf.addAnnotatedClass( BasicTypesTestEntity.class ); // The table mapping this entity shouldn't be in the db validateConf.addAnnotatedClass( Extra.class ); - return setupSessionFactory( validateConf ) - .handle( (unused, throwable) -> { - assertThat( throwable ) - .isInstanceOf( SchemaManagementException.class ) - .hasMessage( errorMessage ); - return null; - } ); + return assertThrown( SchemaManagementException.class, setupSessionFactory( validateConf ) ) + .thenAccept( throwable -> assertThat( throwable ).hasMessage( errorMessage ) ); } ) ); } diff --git a/integration-tests/bytecode-enhancements-it/build.gradle b/integration-tests/bytecode-enhancements-it/build.gradle index 17ffdf10e..071745993 100644 --- a/integration-tests/bytecode-enhancements-it/build.gradle +++ b/integration-tests/bytecode-enhancements-it/build.gradle @@ -30,7 +30,7 @@ dependencies { runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:client:2.1' + runtimeOnly 'com.ongres.scram:scram-client:3.1' // logging runtimeOnly "org.apache.logging.log4j:log4j-core:${log4jVersion}" diff --git a/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java index 2388cb421..70251e20e 100644 --- a/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java +++ b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -20,6 +19,7 @@ import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.stage.Stage; +import org.hibernate.reactive.util.impl.CompletionStages; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -29,7 +29,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.smallrye.mutiny.Uni; -import io.vertx.core.Promise; import io.vertx.core.VertxOptions; import io.vertx.junit5.RunTestOnContext; import io.vertx.junit5.VertxExtension; @@ -73,7 +72,7 @@ public abstract class BaseReactiveIT { * Configure Vertx JUnit5 test context */ @RegisterExtension - static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions() ); + static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions(), false ); private static VertxOptions vertxOptions() { return new VertxOptions() @@ -202,33 +201,19 @@ protected CompletionStage setupSessionFactory(Configuration configuration) * @return a {@link CompletionStage} void that succeeds when the factory is ready. */ protected CompletionStage setupSessionFactory(Supplier confSupplier) { - CompletableFuture future = new CompletableFuture<>(); - testOnContext.vertx() + return testOnContext.vertx() .executeBlocking( // schema generation is a blocking operation and so it causes an // exception when run on the Vert.x event loop. So call it using // Vertx.executeBlocking() - promise -> startFactoryManager( promise, confSupplier ), - event -> { - if ( event.succeeded() ) { - future.complete( null ); - } - else { - future.completeExceptionally( event.cause() ); - } - } - ); - return future; + () -> startFactoryManager( confSupplier ), + true + ).toCompletionStage().thenCompose( CompletionStages::voidFuture ); } - private void startFactoryManager(Promise p, Supplier confSupplier) { - try { - ormSessionFactory = createHibernateSessionFactory( confSupplier.get() ); - p.complete(); - } - catch (Throwable e) { - p.fail( e ); - } + private Object startFactoryManager(Supplier confSupplier) { + ormSessionFactory = createHibernateSessionFactory( confSupplier.get() ); + return null; } private SessionFactory createHibernateSessionFactory(Configuration configuration) { diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index 27689b24d..abd6e2bc0 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -32,7 +32,7 @@ dependencies { runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:client:2.1' + runtimeOnly 'com.ongres.scram:scram-client:3.1' // logging runtimeOnly "org.apache.logging.log4j:log4j-core:${log4jVersion}" diff --git a/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java index 7debdbb2c..f65358ce3 100644 --- a/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java +++ b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -20,6 +19,7 @@ import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.stage.Stage; +import org.hibernate.reactive.util.impl.CompletionStages; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -29,7 +29,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.smallrye.mutiny.Uni; -import io.vertx.core.Promise; import io.vertx.core.VertxOptions; import io.vertx.junit5.RunTestOnContext; import io.vertx.junit5.VertxExtension; @@ -73,7 +72,7 @@ public abstract class BaseReactiveIT { * Configure Vertx JUnit5 test context */ @RegisterExtension - static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions() ); + static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions(), false ); private static VertxOptions vertxOptions() { return new VertxOptions() @@ -202,33 +201,19 @@ protected CompletionStage setupSessionFactory(Configuration configuration) * @return a {@link CompletionStage} void that succeeds when the factory is ready. */ protected CompletionStage setupSessionFactory(Supplier confSupplier) { - CompletableFuture future = new CompletableFuture<>(); - testOnContext.vertx() + return testOnContext.vertx() .executeBlocking( // schema generation is a blocking operation and so it causes an // exception when run on the Vert.x event loop. So call it using // Vertx.executeBlocking() - promise -> startFactoryManager( promise, confSupplier ), - event -> { - if ( event.succeeded() ) { - future.complete( null ); - } - else { - future.completeExceptionally( event.cause() ); - } - } - ); - return future; + () -> startFactoryManager( confSupplier ), + true + ) + .toCompletionStage().thenCompose( CompletionStages::voidFuture ); } - private void startFactoryManager(Promise p, Supplier confSupplier) { - try { - ormSessionFactory = createHibernateSessionFactory( confSupplier.get() ); - p.complete(); - } - catch (Throwable e) { - p.fail( e ); - } + private Object startFactoryManager(Supplier confSupplier) { + return ormSessionFactory = createHibernateSessionFactory( confSupplier.get() ); } private SessionFactory createHibernateSessionFactory(Configuration configuration) { diff --git a/integration-tests/techempower-postgres-it/build.gradle b/integration-tests/techempower-postgres-it/build.gradle index 5c4ecda47..603a60db5 100644 --- a/integration-tests/techempower-postgres-it/build.gradle +++ b/integration-tests/techempower-postgres-it/build.gradle @@ -30,7 +30,7 @@ dependencies { runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" // The Pg client requires this dependency - runtimeOnly "com.ongres.scram:client:2.1" + runtimeOnly "com.ongres.scram:scram-client:3.1" runtimeOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}" // logging diff --git a/integration-tests/techempower-postgres-it/src/main/java/org/hibernate/reactive/it/techempower/WorldVerticle.java b/integration-tests/techempower-postgres-it/src/main/java/org/hibernate/reactive/it/techempower/WorldVerticle.java index 4a12c216f..8b89e2d9d 100644 --- a/integration-tests/techempower-postgres-it/src/main/java/org/hibernate/reactive/it/techempower/WorldVerticle.java +++ b/integration-tests/techempower-postgres-it/src/main/java/org/hibernate/reactive/it/techempower/WorldVerticle.java @@ -45,19 +45,14 @@ public WorldVerticle(Supplier emfSupplier) { this.emfSupplier = emfSupplier; } - private void startHibernate(Promise p) { - try { - this.emf = emfSupplier.get(); - p.complete(); - } - catch (Throwable t) { - p.fail( t ); - } + private Object startHibernate() { + this.emf = emfSupplier.get(); + return null; } @Override public void start(Promise startPromise) { - final Future startHibernate = vertx.executeBlocking( this::startHibernate ) + final Future startHibernate = vertx.executeBlocking( this::startHibernate, true ) .onSuccess( s -> LOG.infof( "✅ Hibernate Reactive is ready" ) ); Router router = Router.router( vertx ); diff --git a/integration-tests/techempower-postgres-it/src/test/java/org/hibernate/reactive/techempower/TechEmpowerTest.java b/integration-tests/techempower-postgres-it/src/test/java/org/hibernate/reactive/techempower/TechEmpowerTest.java index 1f28b2bb2..ac628337d 100644 --- a/integration-tests/techempower-postgres-it/src/test/java/org/hibernate/reactive/techempower/TechEmpowerTest.java +++ b/integration-tests/techempower-postgres-it/src/test/java/org/hibernate/reactive/techempower/TechEmpowerTest.java @@ -73,7 +73,7 @@ public void testWorldRepository(VertxTestContext context) { .compose( this::updates ) .onSuccess( res -> context.completeNow() ) .onFailure( context::failNow ) - .eventually( unused -> vertx.close() ); + .eventually( vertx::close ); } /** diff --git a/integration-tests/verticle-postgres-it/build.gradle b/integration-tests/verticle-postgres-it/build.gradle index 0be234620..cf98e8f56 100644 --- a/integration-tests/verticle-postgres-it/build.gradle +++ b/integration-tests/verticle-postgres-it/build.gradle @@ -30,7 +30,7 @@ dependencies { runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" // The Pg client requires this dependency - runtimeOnly "com.ongres.scram:client:2.1" + runtimeOnly "com.ongres.scram:scram-client:3.1" runtimeOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}" // logging diff --git a/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/ProductVerticle.java b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/ProductVerticle.java index 882231ad1..91d886110 100644 --- a/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/ProductVerticle.java +++ b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/ProductVerticle.java @@ -40,19 +40,14 @@ public ProductVerticle(Supplier emfSupplier) { this.emfSupplier = emfSupplier; } - private void startHibernate(Promise p) { - try { - this.emf = emfSupplier.get(); - p.complete(); - } - catch (Throwable t) { - p.fail( t ); - } + private Object startHibernate() { + this.emf = emfSupplier.get(); + return null; } @Override public void start(Promise startPromise) { - final Future startHibernate = vertx.executeBlocking( this::startHibernate ) + final Future startHibernate = vertx.executeBlocking( this::startHibernate, true ) .onSuccess( s -> LOG.infof( "✅ Hibernate Reactive is ready" ) ); Router router = Router.router( vertx ); diff --git a/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java b/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java index 0c819a393..c2300977e 100644 --- a/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java +++ b/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java @@ -31,7 +31,6 @@ import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; -import static io.vertx.core.CompositeFuture.all; import static io.vertx.core.Future.all; import static io.vertx.core.Future.failedFuture; import static io.vertx.core.Future.succeededFuture; @@ -88,7 +87,7 @@ public void testProductsGeneration(VertxTestContext context) { .compose( this::findProducts ) .onSuccess( res -> context.completeNow() ) .onFailure( context::failNow ) - .eventually( unused -> vertx.close() ); + .eventually( vertx::close ); } /** @@ -97,7 +96,7 @@ public void testProductsGeneration(VertxTestContext context) { * @see #REQUEST_NUMBER */ private Future createProducts(WebClient webClient) { - List postRequests = new ArrayList<>(); + List> postRequests = new ArrayList<>(); for ( int i = 0; i < REQUEST_NUMBER; i++ ) { final Product product = new Product( i + 1 ); diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 43ca44326..d0ebda569 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 3a0f544e6..d9396c13f 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} +//DEPS io.vertx:vertx-db2-client:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 21dc2502a..3dde6e2b1 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -5,10 +5,10 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.15} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} +//DEPS com.ongres.scram:scram-client:3.1 +//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.slf4j:slf4j-simple:2.0.7 //DESCRIPTION Allow authentication to PostgreSQL using SCRAM: diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index b6eb4acea..d3f304147 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 9747f9894..67925f99a 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 54ba2c45b..0993b9227 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.15} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 116c60a87..28656a504 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -5,11 +5,11 @@ */ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.15} -//DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.15} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.15} -//DEPS io.vertx:vertx-unit:${vertx.version:4.5.15} +//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.0} +//DEPS com.ongres.scram:scram-client:3.1 +//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-unit:${vertx.version:5.0.0} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 From 86833a5f77621541d54765af673ec8ab15b62ee3 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 22 May 2025 15:31:48 +0000 Subject: [PATCH 05/85] Update project version to : `4.0.0.Beta1` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 9b5d1147e..3062761ca 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=4.0.0-SNAPSHOT +projectVersion=4.0.0.Beta1 \ No newline at end of file From a98716ba3b861ee1194895566c963c216e7c7421 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 22 May 2025 15:32:46 +0000 Subject: [PATCH 06/85] Update project version to : `4.0.0-SNAPSHOT` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 3062761ca..618fb2d72 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=4.0.0.Beta1 \ No newline at end of file +projectVersion=4.0.0-SNAPSHOT \ No newline at end of file From b62cba8b613bdb2cfb269d19b213603c459b76db Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 28 May 2025 16:26:09 +0200 Subject: [PATCH 07/85] [#1793] Test HQL 'new' construct with many-to-one Test case showing the issue has been resolved --- .../org/hibernate/reactive/HQLQueryTest.java | 152 +++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java index e48d3ec69..00e541285 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java @@ -5,6 +5,10 @@ */ package org.hibernate.reactive; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -20,6 +24,8 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; +import static jakarta.persistence.CascadeType.PERSIST; +import static jakarta.persistence.FetchType.LAZY; import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -34,15 +40,22 @@ public class HQLQueryTest extends BaseReactiveTest { Flour rye = new Flour( 2, "Rye", "Used to bake the traditional sourdough breads of Germany.", "Wheat flour" ); Flour almond = new Flour( 3, "Almond", "made from ground almonds.", "Gluten free" ); + Author miller = new Author( "Madeline Miller"); + Author camilleri = new Author( "Andrea Camilleri"); + Book circe = new Book( "9780316556347", "Circe", miller ); + Book shapeOfWater = new Book( "0-330-49286-1 ", "The Shape of Water", camilleri ); + Book spider = new Book( "978-0-14-311203-7", "The Patience of the Spider", camilleri ); + @Override protected Collection> annotatedEntities() { - return List.of( Flour.class ); + return List.of( Flour.class, Book.class, Author.class ); } @BeforeEach public void populateDb(VertxTestContext context) { - test( context, getMutinySessionFactory() - .withTransaction( (session, transaction) -> session.persistAll( spelt, rye, almond ) ) ); + test( context, getMutinySessionFactory().withTransaction( session -> session + .persistAll( spelt, rye, almond, miller, camilleri, circe, shapeOfWater, spider ) ) + ); } @Test @@ -129,6 +142,21 @@ public void testFromQuery(VertxTestContext context) { ); } + @Test + public void testSelectNewConstructor(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( session -> session + .createQuery( "SELECT NEW Book(b.title, b.author) FROM Book b ORDER BY b.title DESC", Book.class ) + .getResultList() + ) + .invoke( books -> assertThat( books ).containsExactly( + new Book( shapeOfWater.title, camilleri ), + new Book( spider.title, camilleri ), + new Book( circe.title, miller ) + ) ) + ); + } + @Entity(name = "Flour") @Table(name = "Flour") public static class Flour { @@ -204,4 +232,122 @@ public int hashCode() { return Objects.hash( name, description, type ); } } + + @Entity(name = "Book") + @Table(name = "Book_HQL") + public static class Book { + @Id + @GeneratedValue + private Integer id; + + private String isbn; + + private String title; + + @ManyToOne(fetch = LAZY) + private Author author; + + public Book() { + } + + public Book(String title, Author author) { + this.title = title; + this.author = author; + } + + public Book(String isbn, String title, Author author) { + this.isbn = isbn; + this.title = title; + this.author = author; + author.books.add( this ); + } + + public Integer getId() { + return id; + } + + public String getIsbn() { + return isbn; + } + + public String getTitle() { + return title; + } + + public Author getAuthor() { + return author; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Book book = (Book) o; + return Objects.equals( isbn, book.isbn ) && Objects.equals( + title, + book.title + ) && Objects.equals( author, book.author ); + } + + @Override + public int hashCode() { + return Objects.hash( isbn, title, author ); + } + + @Override + public String toString() { + return id + ":" + isbn + ":" + title + ":" + author; + } + } + + @Entity(name = "Author") + @Table(name = "Author_HQL") + public static class Author { + @Id @GeneratedValue + private Integer id; + + private String name; + + @OneToMany(mappedBy = "author", cascade = PERSIST) + private List books = new ArrayList<>(); + + public Author() { + } + + public Author(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public List getBooks() { + return books; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Author author = (Author) o; + return Objects.equals( name, author.name ); + } + + @Override + public int hashCode() { + return Objects.hashCode( name ); + } + + @Override + public String toString() { + return id + ":" + name; + } + } } From 7ad676059381ff1f4f08b02ba81fccb74bcf93da Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 30 May 2025 10:36:08 +0200 Subject: [PATCH 08/85] chore: clean up HQLQueryTest class * Code formattation * Replace Jupiter assertions with AssertJ ones --- .../org/hibernate/reactive/HQLQueryTest.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java index 00e541285..77331fe2b 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/HQLQueryTest.java @@ -5,9 +5,6 @@ */ package org.hibernate.reactive; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -21,7 +18,10 @@ import io.vertx.junit5.Timeout; import io.vertx.junit5.VertxTestContext; import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import static jakarta.persistence.CascadeType.PERSIST; @@ -29,11 +29,8 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; @Timeout(value = 10, timeUnit = MINUTES) - public class HQLQueryTest extends BaseReactiveTest { Flour spelt = new Flour( 1, "Spelt", "An ancient grain, is a hexaploid species of wheat.", "Wheat flour" ); @@ -82,7 +79,7 @@ public void testAutoFlushOnResultList(VertxTestContext context) { public void testSelectScalarString(VertxTestContext context) { test( context, getSessionFactory().withSession( s -> { Stage.SelectionQuery qr = s.createSelectionQuery( "SELECT 'Prova' FROM Flour WHERE id = " + rye.getId(), String.class ); - assertNotNull( qr ); + assertThat( qr ).isNotNull(); return qr.getSingleResult(); } ).thenAccept( found -> assertEquals( "Prova", found ) ) ); } @@ -91,7 +88,7 @@ public void testSelectScalarString(VertxTestContext context) { public void testSelectScalarCount(VertxTestContext context) { test( context, getSessionFactory().withSession( s -> { Stage.SelectionQuery qr = s.createSelectionQuery( "SELECT count(*) FROM Flour", Long.class ); - assertNotNull( qr ); + assertThat( qr ).isNotNull(); return qr.getSingleResult(); } ).thenAccept( found -> assertEquals( 3L, found ) ) ); } @@ -99,14 +96,17 @@ public void testSelectScalarCount(VertxTestContext context) { @Test public void testSelectWithMultipleScalarValues(VertxTestContext context) { test( context, getSessionFactory().withSession( s -> { - Stage.SelectionQuery qr = s.createSelectionQuery( "SELECT 'Prova', f.id FROM Flour f WHERE f.id = " + rye.getId(), Object[].class ); - assertNotNull( qr ); - return qr.getSingleResult(); - } ).thenAccept( found -> { - assertTrue( found instanceof Object[] ); - assertEquals( "Prova", ( (Object[]) found )[0] ); - assertEquals( rye.getId(), ( (Object[]) found )[1] ); - } ) + Stage.SelectionQuery qr = s.createSelectionQuery( + "SELECT 'Prova', f.id FROM Flour f WHERE f.id = " + rye.getId(), + Object[].class + ); + assertThat( qr ).isNotNull(); + return qr.getSingleResult(); + } ).thenAccept( found -> { + assertThat( found ).isInstanceOf( Object[].class ); + assertEquals( "Prova", ( (Object[]) found )[0] ); + assertEquals( rye.getId(), ( (Object[]) found )[1] ); + } ) ); } @@ -114,7 +114,7 @@ public void testSelectWithMultipleScalarValues(VertxTestContext context) { public void testSingleResultQueryOnId(VertxTestContext context) { test( context, getSessionFactory().withSession( s -> { Stage.SelectionQuery qr = s.createSelectionQuery( "FROM Flour WHERE id = 1", Flour.class ); - assertNotNull( qr ); + assertThat( qr ).isNotNull(); return qr.getSingleResult(); } ).thenAccept( flour -> assertEquals( spelt, flour ) ) ); @@ -124,7 +124,7 @@ public void testSingleResultQueryOnId(VertxTestContext context) { public void testSingleResultQueryOnName(VertxTestContext context) { test( context, getSessionFactory().withSession( s -> { Stage.SelectionQuery qr = s.createSelectionQuery( "FROM Flour WHERE name = 'Almond'", Flour.class ); - assertNotNull( qr ); + assertThat( qr ).isNotNull(); return qr.getSingleResult(); } ).thenAccept( flour -> assertEquals( almond, flour ) ) ); @@ -135,7 +135,7 @@ public void testFromQuery(VertxTestContext context) { test( context, getSessionFactory() .withSession( s -> { Stage.SelectionQuery qr = s.createSelectionQuery( "FROM Flour ORDER BY name", Flour.class ); - assertNotNull( qr ); + assertThat( qr ).isNotNull(); return qr.getResultList(); } ) .thenAccept( results -> assertThat( results ).containsExactly( almond, rye, spelt ) ) From 95f2fe0a67f694cdfa3ab8d1d7fc7c54a40f903a Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 27 May 2025 12:26:30 +0200 Subject: [PATCH 09/85] [#2230] ClassCastException when with @EmebedddedId and @OneToOne relationship --- ...ReactiveEmbeddedIdentifierMappingImpl.java | 11 ++ .../internal/ReactiveEmbeddableAssembler.java | 42 ++++++ .../internal/ReactiveEmbeddableFetchImpl.java | 22 +++ .../ReactiveEmbeddableInitializerImpl.java | 135 +++++++++++++++++- .../ReactiveEntityInitializerImpl.java | 107 +++++++++----- 5 files changed, 279 insertions(+), 38 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableAssembler.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveEmbeddedIdentifierMappingImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveEmbeddedIdentifierMappingImpl.java index 3f5244fa9..204a23d3a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveEmbeddedIdentifierMappingImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveEmbeddedIdentifierMappingImpl.java @@ -13,6 +13,7 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.reactive.sql.results.graph.embeddable.internal.ReactiveEmbeddableFetchImpl; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.spi.SqlSelection; @@ -20,6 +21,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParent; +import org.hibernate.sql.results.graph.Fetchable; public class ReactiveEmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping { @@ -104,4 +106,13 @@ public String getSqlAliasStem() { public String getFetchableName() { return delegate.getFetchableName(); } + + @Override + public Fetchable getFetchable(int position) { + Fetchable fetchable = delegate.getFetchable( position ); + if ( fetchable instanceof ToOneAttributeMapping ) { + return new ReactiveToOneAttributeMapping( (ToOneAttributeMapping) fetchable ); + } + return fetchable; + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableAssembler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableAssembler.java new file mode 100644 index 000000000..db8222838 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableAssembler.java @@ -0,0 +1,42 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.sql.results.graph.embeddable.internal; + +import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; +import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler; +import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; +import org.hibernate.sql.results.graph.Initializer; +import org.hibernate.sql.results.graph.InitializerData; +import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; +import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; + +import java.util.concurrent.CompletionStage; + +import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; + +/** + * @see org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler + */ +public class ReactiveEmbeddableAssembler extends EmbeddableAssembler implements ReactiveDomainResultsAssembler { + + public ReactiveEmbeddableAssembler(EmbeddableInitializer initializer) { + super( initializer ); + } + + @Override + public CompletionStage reactiveAssemble(ReactiveRowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + final ReactiveInitializer reactiveInitializer = (ReactiveInitializer) getInitializer(); + final InitializerData data = reactiveInitializer.getData( rowProcessingState ); + final Initializer.State state = data.getState(); + if ( state == Initializer.State.KEY_RESOLVED ) { + return reactiveInitializer + .reactiveResolveInstance( data ) + .thenApply( v -> reactiveInitializer.getResolvedInstance( data ) ); + } + return completedFuture( reactiveInitializer.getResolvedInstance( data ) ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java index 50034533b..b83b49817 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java @@ -6,14 +6,20 @@ package org.hibernate.reactive.sql.results.graph.embeddable.internal; import org.hibernate.engine.FetchTiming; +import org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityFetchSelectImpl; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; +import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParent; +import org.hibernate.sql.results.graph.Fetchable; +import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl; +import org.hibernate.sql.results.graph.entity.internal.EntityFetchSelectImpl; public class ReactiveEmbeddableFetchImpl extends EmbeddableFetchImpl { @@ -37,4 +43,20 @@ public EmbeddableInitializer createInitializer( AssemblerCreationState creationState) { return new ReactiveEmbeddableInitializerImpl( this, getDiscriminatorFetch(), parent, creationState, true ); } + + @Override + public DomainResultAssembler createAssembler(InitializerParent parent, AssemblerCreationState creationState) { + Initializer initializer = creationState.resolveInitializer( this, parent, this ); + EmbeddableInitializer embeddableInitializer = initializer.asEmbeddableInitializer(); + return new ReactiveEmbeddableAssembler( embeddableInitializer ); + } + + @Override + public Fetch findFetch(Fetchable fetchable) { + Fetch fetch = super.findFetch( fetchable ); + if ( fetch instanceof EntityFetchSelectImpl entityFetchSelect ) { + return new ReactiveEntityFetchSelectImpl( entityFetchSelect ); + } + return fetch; + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java index a1d4a890e..a9feb3fee 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java @@ -5,12 +5,18 @@ */ package org.hibernate.reactive.sql.results.graph.embeddable.internal; + import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.VirtualModelPart; +import org.hibernate.metamodel.spi.EmbeddableInstantiator; +import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; +import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler; import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; import org.hibernate.sql.results.graph.AssemblerCreationState; +import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; import org.hibernate.sql.results.graph.InitializerParent; @@ -19,8 +25,13 @@ import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableInitializerImpl; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; +import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.loop; +import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +import static org.hibernate.reactive.util.impl.CompletionStages.whileLoop; +import static org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER; +import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY; public class ReactiveEmbeddableInitializerImpl extends EmbeddableInitializerImpl implements ReactiveInitializer { @@ -33,6 +44,10 @@ public ReactiveEmbeddableInitializerData( super( initializer, rowProcessingState ); } + public Object[] getRowState(){ + return rowState; + } + @Override public void setState(State state) { super.setState( state ); @@ -64,10 +79,128 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing @Override public CompletionStage reactiveResolveInstance(EmbeddableInitializerData data) { - super.resolveInstance( data ); + if ( data.getState() != State.KEY_RESOLVED ) { + return voidFuture(); + } + + data.setState( State.RESOLVED ); + return extractRowState( (ReactiveEmbeddableInitializerData) data ) + .thenAccept( unused -> prepareCompositeInstance( (ReactiveEmbeddableInitializerData) data ) ); + } + + private CompletionStage extractRowState(ReactiveEmbeddableInitializerData data) { + final DomainResultAssembler[] subAssemblers = assemblers[data.getSubclassId()]; + final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final Object[] rowState = data.getRowState(); + final boolean[] stateAllNull = {true}; + final int[] index = {0}; + final boolean[] forceExit = { false }; + return whileLoop( + () -> index[0] < subAssemblers.length && !forceExit[0], + () -> { + final int i = index[0]++; + final DomainResultAssembler assembler = subAssemblers[i]; + if ( assembler instanceof ReactiveDomainResultsAssembler reactiveAssembler ) { + return reactiveAssembler.reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState ) + .thenAccept( contributorValue -> setContributorValue( + contributorValue, + i, + rowState, + stateAllNull, + forceExit + ) ); + } + else { + setContributorValue( + assembler == null ? null : assembler.assemble( rowProcessingState ), + i, + rowState, + stateAllNull, + forceExit + ); + return voidFuture(); + } + }) + .whenComplete( + (unused, throwable) -> { + if ( stateAllNull[0] ) { + data.setState( State.MISSING ); + } + } + ); + } + + private void setContributorValue( + Object contributorValue, + int index, + Object[] rowState, + boolean[] stateAllNull, + boolean[] forceExit) { + if ( contributorValue == BATCH_PROPERTY ) { + rowState[index] = null; + } + else { + rowState[index] = contributorValue; + } + if ( contributorValue != null ) { + stateAllNull[0] = false; + } + else if ( isPartOfKey() ) { + // If this is a foreign key and there is a null part, the whole thing has to be turned into null + stateAllNull[0] = true; + forceExit[0] = true; + } + } + + private CompletionStage prepareCompositeInstance(ReactiveEmbeddableInitializerData data) { + // Virtual model parts use the owning entity as container which the fetch parent access provides. + // For an identifier or foreign key this is called during the resolveKey phase of the fetch parent, + // so we can't use the fetch parent access in that case. + final ReactiveInitializer parent = (ReactiveInitializer) getParent(); + if ( parent != null && getInitializedPart() instanceof VirtualModelPart && !isPartOfKey() && data.getState() != State.MISSING ) { + final ReactiveEmbeddableInitializerData subData = parent.getData( data.getRowProcessingState() ); + return parent + .reactiveResolveInstance( subData ) + .thenCompose( + unused -> { + data.setInstance( parent.getResolvedInstance( subData ) ); + if ( data.getState() == State.INITIALIZED ) { + return voidFuture(); + } + return doCreateCompositeInstance( data ) + .thenAccept( v -> EMBEDDED_LOAD_LOGGER.debugf( + "Created composite instance [%s]", + getNavigablePath() + ) ); + } ); + } + + return doCreateCompositeInstance( data ) + .thenAccept( v -> EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s]", getNavigablePath() ) ); + + } + + private CompletionStage doCreateCompositeInstance(ReactiveEmbeddableInitializerData data) { + if ( data.getInstance() == null ) { + return createCompositeInstance( data ) + .thenAccept( data::setInstance ); + } return voidFuture(); } + private CompletionStage createCompositeInstance(ReactiveEmbeddableInitializerData data) { + if ( data.getState() == State.MISSING ) { + return nullFuture(); + } + + final EmbeddableInstantiator instantiator = data.getConcreteEmbeddableType() == null + ? getInitializedPart().getEmbeddableTypeDescriptor().getRepresentationStrategy().getInstantiator() + : data.getConcreteEmbeddableType().getInstantiator(); + final Object instance = instantiator.instantiate( data ); + data.setState( State.RESOLVED ); + return completedFuture( instance ); + } + @Override public CompletionStage reactiveInitializeInstance(EmbeddableInitializerData data) { super.initializeInstance( data ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java index a7342275d..6a690f34e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java @@ -26,6 +26,7 @@ import org.hibernate.proxy.map.MapProxy; import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; +import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler; import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -253,47 +254,79 @@ public CompletionStage reactiveResolveInstance(EntityInitializerData origi } final RowProcessingState rowProcessingState = data.getRowProcessingState(); data.setState( State.RESOLVED ); + return assembleId( data, rowProcessingState ) + .thenCompose( unused -> { + if ( data.getState() == State.MISSING ) { + return voidFuture(); + } + else { + final PersistenceContext persistenceContext = rowProcessingState + .getSession().getPersistenceContextInternal(); + data.setEntityHolder( persistenceContext.claimEntityHolderIfPossible( + data.getEntityKey(), + null, + rowProcessingState.getJdbcValuesSourceProcessingState(), + this + ) ); + + if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { + data.setEntityInstanceForNotify( rowProcessingState.getEntityId() ); + data.setInstance( data.getEntityInstanceForNotify() ); + } + else { + return reactiveResolveEntityInstance1( data ) + .thenAccept( v -> { + if ( data.getUniqueKeyAttributePath() != null ) { + final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final EntityPersister concreteDescriptor = getConcreteDescriptor( data ); + final EntityUniqueKey euk = new EntityUniqueKey( + concreteDescriptor.getEntityName(), + data.getUniqueKeyAttributePath(), + rowProcessingState.getEntityUniqueKey(), + data.getUniqueKeyPropertyTypes()[concreteDescriptor.getSubclassId()], + session.getFactory() + ); + session.getPersistenceContextInternal().addEntity( + euk, + data.getInstance() + ); + } + postResolveInstance( data ); + } ); + } + postResolveInstance( data ); + return voidFuture(); + } + } ); + } + + private CompletionStage assembleId( + ReactiveEntityInitializerData data, + RowProcessingState rowProcessingState) { if ( data.getEntityKey() == null ) { - assert getIdentifierAssembler() != null; - final Object id = getIdentifierAssembler().assemble( rowProcessingState ); - if ( id == null ) { - setMissing( data ); + DomainResultAssembler identifierAssembler = getIdentifierAssembler(); + assert identifierAssembler != null; + if ( identifierAssembler instanceof ReactiveDomainResultsAssembler reactiveAssembler ) { + return reactiveAssembler + .reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState ) + .thenAccept( id -> { + if ( id == null ) { + setMissing( data ); + return ; + } + resolveEntityKey( data, id ); + } ); + } + else { + final Object id = identifierAssembler.assemble( rowProcessingState ); + if ( id == null ) { + setMissing( data ); + return voidFuture(); + } + resolveEntityKey( data, id ); return voidFuture(); } - resolveEntityKey( data, id ); - } - final PersistenceContext persistenceContext = rowProcessingState.getSession() - .getPersistenceContextInternal(); - data.setEntityHolder( persistenceContext.claimEntityHolderIfPossible( - data.getEntityKey(), - null, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ) ); - - if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { - data.setEntityInstanceForNotify( rowProcessingState.getEntityId() ); - data.setInstance( data.getEntityInstanceForNotify() ); - } - else { - return reactiveResolveEntityInstance1( data ) - .thenAccept( v -> { - if ( data.getUniqueKeyAttributePath() != null ) { - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final EntityPersister concreteDescriptor = getConcreteDescriptor( data ); - final EntityUniqueKey euk = new EntityUniqueKey( - concreteDescriptor.getEntityName(), - data.getUniqueKeyAttributePath(), - rowProcessingState.getEntityUniqueKey(), - data.getUniqueKeyPropertyTypes()[concreteDescriptor.getSubclassId()], - session.getFactory() - ); - session.getPersistenceContextInternal().addEntity( euk, data.getInstance() ); - } - postResolveInstance( data ); - } ); } - postResolveInstance( data ); return voidFuture(); } From 620dee408063995e7f3570dda46de07cf011cd41 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 29 Apr 2025 15:57:45 +0200 Subject: [PATCH 10/85] [#2230] Test embedded id with one-to-one --- .../reactive/EmbeddedIdWithOneToOneTest.java | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithOneToOneTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithOneToOneTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithOneToOneTest.java new file mode 100644 index 000000000..f5cbf5fd9 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithOneToOneTest.java @@ -0,0 +1,131 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EmbeddedIdWithOneToOneTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( FooEntity.class, BarEntity.class ); + } + + @Test + public void test(VertxTestContext context) { + BarEntity barEntity = new BarEntity( "1" ); + FooId fooId = new FooId( barEntity ); + FooEntity entity = new FooEntity( fooId ); + + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.persistAll( barEntity, entity ) ) + .chain( () -> getMutinySessionFactory() + .withTransaction( s -> s.find( FooEntity.class, fooId ) ) + ) + .invoke( result -> { + assertThat( result.getId() ).isEqualTo( fooId ); + assertThat( result.getId().getIdEntity() ).isEqualTo( fooId.getIdEntity() ); + assertThat( result.getId().getIdEntity().getId() ).isEqualTo( fooId.getIdEntity().getId() ); + } ) + ); + } + + @Entity(name = "bar") + public static class BarEntity { + + @Id + private String id; + + public BarEntity() { + } + + public BarEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + @Entity(name = "foo") + public static class FooEntity { + + @EmbeddedId + private FooId id; + + public FooEntity() { + } + + public FooEntity(FooId id) { + this.id = id; + } + + public FooId getId() { + return id; + } + + public void setId(FooId id) { + this.id = id; + } + } + + @Embeddable + public static class FooId { + + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "id", nullable = false) + private BarEntity barEntity; + + public FooId() { + } + + public FooId(BarEntity barEntity) { + this.barEntity = barEntity; + } + + public BarEntity getIdEntity() { + return barEntity; + } + + public void setIdEntity(BarEntity barEntity) { + this.barEntity = barEntity; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + FooId fooId = (FooId) o; + return Objects.equals( barEntity, fooId.barEntity ); + } + + @Override + public int hashCode() { + return Objects.hashCode( barEntity ); + } + } +} From 84781fec37ad56b8c99d27f974b2090c2c366c7c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 4 Jun 2025 10:53:49 +0200 Subject: [PATCH 11/85] [#2230] Small refactoring Make the structure of the code more similar to the one in Hibernate ORM (See EntityInitializerImpl) --- .../ReactiveEntityInitializerImpl.java | 121 ++++++++---------- 1 file changed, 51 insertions(+), 70 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java index 6a690f34e..c6ee36363 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityInitializerImpl.java @@ -254,82 +254,63 @@ public CompletionStage reactiveResolveInstance(EntityInitializerData origi } final RowProcessingState rowProcessingState = data.getRowProcessingState(); data.setState( State.RESOLVED ); - return assembleId( data, rowProcessingState ) - .thenCompose( unused -> { - if ( data.getState() == State.MISSING ) { - return voidFuture(); - } - else { - final PersistenceContext persistenceContext = rowProcessingState - .getSession().getPersistenceContextInternal(); - data.setEntityHolder( persistenceContext.claimEntityHolderIfPossible( - data.getEntityKey(), - null, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ) ); - - if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { - data.setEntityInstanceForNotify( rowProcessingState.getEntityId() ); - data.setInstance( data.getEntityInstanceForNotify() ); - } - else { - return reactiveResolveEntityInstance1( data ) - .thenAccept( v -> { - if ( data.getUniqueKeyAttributePath() != null ) { - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final EntityPersister concreteDescriptor = getConcreteDescriptor( data ); - final EntityUniqueKey euk = new EntityUniqueKey( - concreteDescriptor.getEntityName(), - data.getUniqueKeyAttributePath(), - rowProcessingState.getEntityUniqueKey(), - data.getUniqueKeyPropertyTypes()[concreteDescriptor.getSubclassId()], - session.getFactory() - ); - session.getPersistenceContextInternal().addEntity( - euk, - data.getInstance() - ); - } - postResolveInstance( data ); - } ); + if ( data.getEntityKey() == null ) { + return assembleId( rowProcessingState ) + .thenCompose( id -> { + if ( id == null ) { + setMissing( data ); + return voidFuture(); } - postResolveInstance( data ); - return voidFuture(); - } - } ); + resolveEntityKey( data, id ); + return postAssembleId( rowProcessingState, data ); + } ); + } + return postAssembleId( rowProcessingState, data ); } - private CompletionStage assembleId( - ReactiveEntityInitializerData data, - RowProcessingState rowProcessingState) { - if ( data.getEntityKey() == null ) { - DomainResultAssembler identifierAssembler = getIdentifierAssembler(); - assert identifierAssembler != null; - if ( identifierAssembler instanceof ReactiveDomainResultsAssembler reactiveAssembler ) { - return reactiveAssembler - .reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState ) - .thenAccept( id -> { - if ( id == null ) { - setMissing( data ); - return ; - } - resolveEntityKey( data, id ); - } ); - } - else { - final Object id = identifierAssembler.assemble( rowProcessingState ); - if ( id == null ) { - setMissing( data ); - return voidFuture(); - } - resolveEntityKey( data, id ); - return voidFuture(); - } + private CompletionStage postAssembleId(RowProcessingState rowProcessingState, ReactiveEntityInitializerData data) { + final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal(); + data.setEntityHolder( persistenceContext.claimEntityHolderIfPossible( + data.getEntityKey(), + null, + rowProcessingState.getJdbcValuesSourceProcessingState(), + this + ) ); + + if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { + data.setEntityInstanceForNotify( rowProcessingState.getEntityId() ); + data.setInstance( data.getEntityInstanceForNotify() ); + postResolveInstance( data ); + return voidFuture(); } - return voidFuture(); + + return reactiveResolveEntityInstance1( data ) + .thenAccept( v -> { + if ( data.getUniqueKeyAttributePath() != null ) { + final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final EntityPersister concreteDescriptor = getConcreteDescriptor( data ); + final EntityUniqueKey euk = new EntityUniqueKey( + concreteDescriptor.getEntityName(), + data.getUniqueKeyAttributePath(), + rowProcessingState.getEntityUniqueKey(), + data.getUniqueKeyPropertyTypes()[concreteDescriptor.getSubclassId()], + session.getFactory() + ); + session.getPersistenceContextInternal().addEntity( euk, data.getInstance() ); + } + postResolveInstance( data ); + } ); + } + + private CompletionStage assembleId(RowProcessingState rowProcessingState) { + final DomainResultAssembler identifierAssembler = getIdentifierAssembler(); + assert identifierAssembler != null; + return identifierAssembler instanceof ReactiveDomainResultsAssembler reactiveAssembler + ? reactiveAssembler.reactiveAssemble( (ReactiveRowProcessingState) rowProcessingState ) + : completedFuture( identifierAssembler.assemble( rowProcessingState ) ); } + // We could move this method in ORM private void postResolveInstance(ReactiveEntityInitializerData data) { if ( data.getInstance() != null ) { upgradeLockMode( data ); From 7779622d57921e1b3d73a8967a71fae882179fa0 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 5 Jun 2025 11:17:10 +0200 Subject: [PATCH 12/85] Add AUTHORS.txt --- AUTHORS.txt | 39 +++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 6 ++++++ 2 files changed, 45 insertions(+) create mode 100644 AUTHORS.txt diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 000000000..caf0817d1 --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,39 @@ +# This file lists copyright owners of the project. +# The list is not exhaustive: other copyright owners exist. +# See CONTRIBUTING.md for instructions regarding how to be added to this list. + +# Corporate contributors + +Red Hat, Inc. + +# Individual contributors + +Andrea Boriero +Andrew Guibert +Barry LaFond +Thinking Chen (cdmikechen) +Coding Xu (codingxu97) +Conor Farrell +Davide D'Alto +Derick Hermanson +Eric Dalquist +Eric Deandrea +Gail Badner +Gavin King +George Gastaldi +Georgios Andrianakis +Jay Erb +Julien Ponge +Marko Bekhta +Max Rydahl Andersen +(meepown) +Nesrin Aşan +Ravi Khadiwala +Renar Narubin +Sanne Grinovero +Scott Marlow +Stephane Epardaud +Steve Ebersole +Stuart Douglas +Thomas Segismont +Yoann Rodière diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2df00b224..bb825420f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,12 @@ All contributions are subject to the [Developer Certificate of Origin (DCO)](htt The DCO text is available verbatim in the [dco.txt](dco.txt) file in the root directory of the Hibernate Reactive repository. +Copyright owners are listed in [AUTHORS.txt](AUTHORS.txt). +Contributors with a valid copyright claim can request to be added to that list +by sending a pull request to the project's GitHub repository, +listing at least one relevant contribution in the pull request description. +Note: one-liner or repetitive patches may not be sufficient to claim copyright. + ## Guidelines While we try to keep requirements for contributing to a minimum, there are a few guidelines From b53a164202b9b2bf1e839c54e56ce95babbe62d2 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 5 Jun 2025 10:50:45 +0200 Subject: [PATCH 13/85] [#2284] ClassCastException with MapsId, EmbeddedId, and many-to-one --- .../internal/ReactiveEmbeddableFetchImpl.java | 3 +++ .../ReactiveEmbeddableInitializerImpl.java | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java index b83b49817..46a15c1e9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java @@ -57,6 +57,9 @@ public Fetch findFetch(Fetchable fetchable) { if ( fetch instanceof EntityFetchSelectImpl entityFetchSelect ) { return new ReactiveEntityFetchSelectImpl( entityFetchSelect ); } + else if ( fetch instanceof EmbeddableFetchImpl embeddableFetch ) { + return new ReactiveEmbeddableFetchImpl( embeddableFetch ); + } return fetch; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java index a9feb3fee..3df56721f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java @@ -214,16 +214,21 @@ public CompletionStage forEachReactiveSubInitializer( final ReactiveEmbeddableInitializerData embeddableInitializerData = (ReactiveEmbeddableInitializerData) data; final RowProcessingState rowProcessingState = embeddableInitializerData.getRowProcessingState(); if ( embeddableInitializerData.getConcreteEmbeddableType() == null ) { - return loop( subInitializers, subInitializer -> loop( subInitializer, initializer -> consumer - .apply( (ReactiveInitializer) initializer, rowProcessingState ) - ) ); + return loop( subInitializers, subInitializer -> + loop( subInitializer, initializer -> + initializer != null + ? consumer.apply( (ReactiveInitializer) initializer, rowProcessingState ) + : voidFuture() + ) + ); } else { Initializer[] initializers = subInitializers[embeddableInitializerData.getSubclassId()]; - return loop( 0, initializers.length, i -> { - ReactiveInitializer reactiveInitializer = (ReactiveInitializer) initializers[i]; - return consumer.apply( reactiveInitializer, rowProcessingState ); - } ); + return loop(0, initializers.length, i -> + initializers[i] != null + ? consumer.apply( (ReactiveInitializer) initializers[i], rowProcessingState ) + : voidFuture() + ); } } From f2c2222169543ebed6246dd3cc0327fc3a09e04a Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 30 May 2025 11:49:25 +0200 Subject: [PATCH 14/85] [#2284] Test Many-to-one with MapsId and EmbeddedId --- .../ManyToOneMapsIdAndEmbeddedIdTest.java | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneMapsIdAndEmbeddedIdTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneMapsIdAndEmbeddedIdTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneMapsIdAndEmbeddedIdTest.java new file mode 100644 index 000000000..314c69b48 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneMapsIdAndEmbeddedIdTest.java @@ -0,0 +1,189 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.hibernate.annotations.EmbeddedColumnNaming; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ManyToOneMapsIdAndEmbeddedIdTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( Link.class, GPSPoint.class, NetPoint.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + NetPointKey startKey = new NetPointKey( 1, NetPointType.STOP_POINT ); + NetPointKey endKey = new NetPointKey( 2, NetPointType.STOP_POINT ); + LinkKey linkKey = new LinkKey( startKey, endKey, "123" ); + + final NetPoint start = new NetPoint(); + fillWithBogusValues( start ); + start.key = startKey; + + final NetPoint end = new NetPoint(); + fillWithBogusValues( end ); + end.key = endKey; + + final Link link = new Link(); + link.key = linkKey; + link.addPoint( new GPSPoint( new GPSPointKey( linkKey, 0 ), link, 1, 1, 1 ) ); + link.addPoint( new GPSPoint( new GPSPointKey( linkKey, 1 ), link, 1, 1, 1 ) ); + link.addPoint( new GPSPoint( new GPSPointKey( linkKey, 2 ), link, 1, 1, 1 ) ); + + test( + context, getMutinySessionFactory().withTransaction( session -> session + .persistAll( start, end, link ) ) + ); + } + + @Test + public void test(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( session -> session + .createQuery( "from Link", Link.class ).getResultList() ) + .invoke( links -> assertThat( links ).hasSize( 1 ) ) + ); + } + + void fillWithBogusValues(NetPoint start) { + start.gpsLatitude = 1; + start.gpsLongitude = 1; + start.operatingDepartmentId = "123"; + start.operatingDepartmentShortName = "123 - 123"; + } + + @Entity(name = "GPSPoint") + public static class GPSPoint { + + @EmbeddedId + public GPSPointKey key; + + @ManyToOne + @MapsId("link") + public Link link; + + @Column(nullable = false) + public Integer latitude; + + @Column(nullable = false) + public Integer longitude; + + @Column(nullable = false) + public Integer distance; + + public GPSPoint() { + } + + public GPSPoint(GPSPointKey key, Link link, Integer latitude, Integer longitude, Integer distance) { + this.key = key; + this.link = link; + this.latitude = latitude; + this.longitude = longitude; + this.distance = distance; + } + } + + @Embeddable + public record GPSPointKey( + @Embedded + LinkKey link, + + Integer position + ) { + + } + + @Entity(name = "Link") + public static class Link { + + @EmbeddedId + public LinkKey key; + + @OneToMany(mappedBy = "link", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + public List gpsPoints = new ArrayList<>(); + + public void addPoint(GPSPoint point) { + gpsPoints.add( point ); + point.link = this; + } + } + + @Embeddable + public record LinkKey( + @Embedded + @EmbeddedColumnNaming("start_%s") + NetPointKey start, + + @Embedded + @EmbeddedColumnNaming("end_%s") + NetPointKey end, + + String operatingDepartmentId + ) { + + } + + @Embeddable + public record NetPointKey( + Integer id, + + @Enumerated(EnumType.ORDINAL) + NetPointType type + ) { + + } + + @Entity(name = "NetPoint") + public static class NetPoint { + + @EmbeddedId + public NetPointKey key; + + @Column(nullable = false) + public String operatingDepartmentId; + + @Column(nullable = false) + public String operatingDepartmentShortName; + + @Column(nullable = false) + public Integer gpsLatitude; + + @Column(nullable = false) + public Integer gpsLongitude; + } + + public enum NetPointType { + @Deprecated UNSPECIFIED, + STOP_POINT, + DEPOT_POINT, + BEACON, + SECTION_POINT + } +} From 4bec7b2ca68b53fe0130b995afbb59049361b040 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 6 Jun 2025 11:55:47 +0200 Subject: [PATCH 15/85] [#2186] Improve error message when asking for a JDBC connection --- .../main/java/org/hibernate/reactive/logging/impl/Log.java | 4 ++++ .../provider/service/NoJdbcConnectionProvider.java | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java index 4f205492e..fa29cd8b5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java @@ -7,6 +7,7 @@ +import java.sql.SQLException; import java.sql.SQLWarning; import jakarta.persistence.PersistenceException; @@ -266,6 +267,9 @@ public interface Log extends BasicLogger { @Message(id = 83, value = "Unexpected request of a non reactive connection") HibernateException unexpectedConnectionRequest(); + @Message(id = 84, value = "The application requested a JDBC connection, but Hibernate Reactive doesn't use JDBC. This could be caused by a bug or the use of an unsupported feature in Hibernate Reactive") + SQLException notUsingJdbc(); + // Same method that exists in CoreMessageLogger @LogMessage(level = WARN) @Message(id = 104, value = "firstResult/maxResults specified with collection fetch; applying in memory!" ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProvider.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProvider.java index 5c3380f65..a02c3e152 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProvider.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProvider.java @@ -6,10 +6,14 @@ package org.hibernate.reactive.provider.service; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.reactive.logging.impl.Log; import java.sql.Connection; import java.sql.SQLException; +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + /** * A dummy Hibernate {@link ConnectionProvider} throws an * exception if a JDBC connection is requested. @@ -17,12 +21,13 @@ * @author Gavin King */ public class NoJdbcConnectionProvider implements ConnectionProvider { + private static final Log LOG = make( Log.class, lookup() ); public static final NoJdbcConnectionProvider INSTANCE = new NoJdbcConnectionProvider(); @Override public Connection getConnection() throws SQLException { - throw new SQLException( "Not using JDBC" ); + throw LOG.notUsingJdbc(); } @Override From e05c3b87c9e2ed9c782cac910ab971bc4952de2e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 10 Jun 2025 19:26:09 +0200 Subject: [PATCH 16/85] [#2297] Upgrade Hibernate ORM to 7.0.1.Final --- README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 743540d6c..9176dedfb 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Hibernate Reactive has been tested with: - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 -- [Hibernate ORM][] 7.0.0.Final +- [Hibernate ORM][] 7.0.1.Final - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0 diff --git a/gradle.properties b/gradle.properties index 36bc0c5ac..6211b37e5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,7 +35,7 @@ org.gradle.java.installations.auto-download=false #enableMavenLocalRepo = true # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -hibernateOrmVersion = 7.0.0.Final +hibernateOrmVersion = 7.0.1.Final # Override default Hibernate ORM Gradle plugin version # Using the stable version because I don't know how to configure the build to download the snapshot version from From a0524feff87a42a2f4e8dde7c3d42607d3ad46c7 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 13 Jun 2025 12:00:51 +0200 Subject: [PATCH 17/85] [#2300] Upgrade Hibernate ORM to 7.0.2.Final --- README.md | 2 +- gradle.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9176dedfb..cb356cb19 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Hibernate Reactive has been tested with: - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 -- [Hibernate ORM][] 7.0.1.Final +- [Hibernate ORM][] 7.0.2.Final - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0 diff --git a/gradle.properties b/gradle.properties index 6211b37e5..08e84c8f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,12 +35,12 @@ org.gradle.java.installations.auto-download=false #enableMavenLocalRepo = true # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -hibernateOrmVersion = 7.0.1.Final +hibernateOrmVersion = 7.0.2.Final # Override default Hibernate ORM Gradle plugin version # Using the stable version because I don't know how to configure the build to download the snapshot version from # a remote repository -#hibernateOrmGradlePluginVersion = 7.0.0.Final +#hibernateOrmGradlePluginVersion = 7.0.2.Final # If set to true, skip Hibernate ORM version parsing (default is true, if set to null) # this is required when using intervals or weird versions or the build will fail From a8caac38ebc192d91585165c9693758d470631f6 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sat, 7 Jun 2025 16:08:10 +0200 Subject: [PATCH 18/85] [#2154] Composite key with generated value not working properly --- .../id/ReactiveIdentifierGenerator.java | 5 + ...ompositeNestedGeneratedValueGenerator.java | 130 ++++++++++++++++++ .../tuple/entity/ReactiveEntityMetamodel.java | 105 ++++++++++++-- 3 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveCompositeNestedGeneratedValueGenerator.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java index 7121ea5c4..83ae3290a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java @@ -6,6 +6,7 @@ package org.hibernate.reactive.id; import org.hibernate.Incubating; +import org.hibernate.generator.EventType; import org.hibernate.generator.Generator; import org.hibernate.id.IdentifierGenerator; import org.hibernate.reactive.session.ReactiveConnectionSupplier; @@ -33,4 +34,8 @@ public interface ReactiveIdentifierGenerator extends Generator { * @param session the reactive session */ CompletionStage generate(ReactiveConnectionSupplier session, Object entity); + + default CompletionStage generate(ReactiveConnectionSupplier session, Object owner, Object currentValue, EventType eventType) { + return generate( session, owner ); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveCompositeNestedGeneratedValueGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveCompositeNestedGeneratedValueGenerator.java new file mode 100644 index 000000000..cb6b03ab5 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveCompositeNestedGeneratedValueGenerator.java @@ -0,0 +1,130 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.id.impl; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.BeforeExecutionGenerator; +import org.hibernate.generator.Generator; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.CompositeNestedGeneratedValueGenerator; +import org.hibernate.id.IdentifierGenerationException; +import org.hibernate.mapping.Component; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.reactive.id.ReactiveIdentifierGenerator; +import org.hibernate.reactive.session.ReactiveConnectionSupplier; +import org.hibernate.reactive.tuple.entity.ReactiveEntityMetamodel; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; + +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; +import static org.hibernate.reactive.util.impl.CompletionStages.loop; + +public class ReactiveCompositeNestedGeneratedValueGenerator extends CompositeNestedGeneratedValueGenerator implements + ReactiveIdentifierGenerator { + + public ReactiveCompositeNestedGeneratedValueGenerator( + CompositeNestedGeneratedValueGenerator generator, + GeneratorCreationContext creationContext, + RuntimeModelCreationContext runtimeModelCreationContext) { + super( + generator.getGenerationContextLocator(), + generator.getCompositeType(), + reactivePlans( generator, creationContext, runtimeModelCreationContext ) + ); + } + + private static List reactivePlans( + CompositeNestedGeneratedValueGenerator generator, + GeneratorCreationContext creationContext, + RuntimeModelCreationContext runtimeModelCreationContext) { + final List plans = new ArrayList<>(); + for ( GenerationPlan plan : generator.getGenerationPlans() ) { + final GenerationPlan reactivePlane = new Component.ValueGenerationPlan( + (BeforeExecutionGenerator) ReactiveEntityMetamodel.augmentWithReactiveGenerator( + plan.getGenerator(), + creationContext, + runtimeModelCreationContext + ), + plan.getInjector(), + plan.getPropertyIndex() + ); + plans.add( reactivePlane ); + } + return plans; + } + + @Override + public CompletionStage generate(ReactiveConnectionSupplier reactiveConnectionSupplier, Object object) { + SharedSessionContractImplementor session = (SharedSessionContractImplementor) reactiveConnectionSupplier; + final Object context = getGenerationContextLocator().locateGenerationContext( session, object ); + + final List generatedValues = getCompositeType().isMutable() + ? null + : new ArrayList<>( getGenerationPlans().size() ); + return loop( getGenerationPlans(), generationPlan -> generateIdentifier( + reactiveConnectionSupplier, + object, + generationPlan, + session, + generatedValues, + context + ) ) + .thenCompose( v -> handleGeneratedValues( generatedValues, context, session ) ); + } + + private CompletionStage generateIdentifier( + ReactiveConnectionSupplier reactiveConnectionSupplier, + Object object, + GenerationPlan generationPlan, + SharedSessionContractImplementor session, + List generatedValues, + Object context) { + final Generator generator = generationPlan.getGenerator(); + if ( generator.generatedBeforeExecution( object, session ) ) { + if ( generator instanceof ReactiveIdentifierGenerator reactiveIdentifierGenerator ) { + return reactiveIdentifierGenerator + .generate( reactiveConnectionSupplier, object ) + .thenAccept( generated -> { + if ( generatedValues != null ) { + generatedValues.add( generated ); + } + else { + generationPlan.getInjector().set( context, generated ); + } + } ); + } + else { + final Object currentValue = generator.allowAssignedIdentifiers() + ? getCompositeType().getPropertyValue( context, generationPlan.getPropertyIndex(), session ) + : null; + return completedFuture( ( (BeforeExecutionGenerator) generator ) + .generate( session, object, currentValue, INSERT ) ); + } + } + else { + throw new IdentifierGenerationException( "Identity generation isn't supported for composite ids" ); + } + } + + private CompletionStage handleGeneratedValues( + List generatedValues, + Object context, + SharedSessionContractImplementor session) { + if ( generatedValues != null ) { + final Object[] values = getCompositeType().getPropertyValues( context ); + for ( int i = 0; i < generatedValues.size(); i++ ) { + values[getGenerationPlans().get( i ).getPropertyIndex()] = generatedValues.get( i ); + } + return completedFuture( getCompositeType().replacePropertyValues( context, values, session ) ); + } + else { + return completedFuture( context ); + } + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java index 2e20e7781..14e227358 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java @@ -8,8 +8,11 @@ import java.util.function.Function; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.generator.Generator; import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.SelectGenerator; @@ -18,18 +21,23 @@ import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.id.enhanced.TableStructure; -import org.hibernate.mapping.GeneratorCreator; +import org.hibernate.mapping.GeneratorSettings; import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; import org.hibernate.mapping.SimpleValue; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.id.ReactiveIdentifierGenerator; import org.hibernate.reactive.id.impl.EmulatedSequenceReactiveIdentifierGenerator; +import org.hibernate.reactive.id.impl.ReactiveCompositeNestedGeneratedValueGenerator; import org.hibernate.reactive.id.impl.ReactiveGeneratorWrapper; import org.hibernate.reactive.id.impl.ReactiveSequenceIdentifierGenerator; import org.hibernate.reactive.id.impl.TableReactiveIdentifierGenerator; import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.service.ServiceRegistry; import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.type.Type; import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; @@ -67,20 +75,22 @@ private static Generator buildIdGenerator( return existing; } else { - SimpleValue identifier = (SimpleValue) persistentClass.getIdentifier(); - GeneratorCreator customIdGeneratorCreator = identifier.getCustomIdGeneratorCreator(); - identifier.setCustomIdGeneratorCreator( context -> { - Generator generator = customIdGeneratorCreator.createGenerator( context ); - return augmentWithReactiveGenerator( generator, context, creationContext ); - } ); - final Generator idgenerator = identifier - // returns the cached Generator if it was already created - .createGenerator( + final SimpleValue identifier = (SimpleValue) persistentClass.getIdentifier(); + final Generator idgenerator = augmentWithReactiveGenerator( + identifier.createGenerator( creationContext.getDialect(), persistentClass.getRootClass(), persistentClass.getIdentifierProperty(), creationContext.getGeneratorSettings() - ); + ), + new IdGeneratorCreationContext( + persistentClass.getRootClass(), + persistentClass.getIdentifierProperty(), + creationContext.getGeneratorSettings(), + identifier, + creationContext + ), + creationContext ); creationContext.getGenerators().put( rootName, idgenerator ); return idgenerator; } @@ -90,8 +100,8 @@ public static Generator augmentWithReactiveGenerator( Generator generator, GeneratorCreationContext creationContext, RuntimeModelCreationContext runtimeModelCreationContext) { - if ( generator instanceof SequenceStyleGenerator ) { - final DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); + if ( generator instanceof SequenceStyleGenerator sequenceStyleGenerator) { + final DatabaseStructure structure = sequenceStyleGenerator.getDatabaseStructure(); if ( structure instanceof TableStructure ) { return initialize( (IdentifierGenerator) generator, new EmulatedSequenceReactiveIdentifierGenerator( (TableStructure) structure, runtimeModelCreationContext ), creationContext ); } @@ -100,16 +110,28 @@ public static Generator augmentWithReactiveGenerator( } throw LOG.unknownStructureType(); } - if ( generator instanceof TableGenerator ) { + if ( generator instanceof TableGenerator tableGenerator ) { return initialize( (IdentifierGenerator) generator, - new TableReactiveIdentifierGenerator( (TableGenerator) generator, runtimeModelCreationContext ), + new TableReactiveIdentifierGenerator( tableGenerator, runtimeModelCreationContext ), creationContext ); } if ( generator instanceof SelectGenerator ) { throw LOG.selectGeneratorIsNotSupportedInHibernateReactive(); } + if ( generator instanceof CompositeNestedGeneratedValueGenerator compositeNestedGeneratedValueGenerator ) { + final ReactiveCompositeNestedGeneratedValueGenerator reactiveCompositeNestedGeneratedValueGenerator = new ReactiveCompositeNestedGeneratedValueGenerator( + compositeNestedGeneratedValueGenerator, + creationContext, + runtimeModelCreationContext + ); + return initialize( + (IdentifierGenerator) generator, + reactiveCompositeNestedGeneratedValueGenerator, + creationContext + ); + } //nothing to do return generator; } @@ -121,4 +143,57 @@ private static Generator initialize( ( (Configurable) reactiveIdGenerator ).initialize( creationContext.getSqlStringGenerationContext() ); return new ReactiveGeneratorWrapper( reactiveIdGenerator, idGenerator ); } + + private record IdGeneratorCreationContext( + RootClass rootClass, + Property property, + GeneratorSettings defaults, + SimpleValue identifier, + RuntimeModelCreationContext buildingContext) implements GeneratorCreationContext { + + @Override + public Database getDatabase() { + return buildingContext.getBootModel().getDatabase(); + } + + @Override + public ServiceRegistry getServiceRegistry() { + return buildingContext.getBootstrapContext().getServiceRegistry(); + } + + @Override + public SqlStringGenerationContext getSqlStringGenerationContext() { + return defaults.getSqlStringGenerationContext(); + } + + @Override + public String getDefaultCatalog() { + return defaults.getDefaultCatalog(); + } + + @Override + public String getDefaultSchema() { + return defaults.getDefaultSchema(); + } + + @Override + public RootClass getRootClass() { + return rootClass; + } + + @Override + public PersistentClass getPersistentClass() { + return rootClass; + } + + @Override + public Property getProperty() { + return property; + } + + @Override + public Type getType() { + return identifier.getType(); + } + } } From 585b0c9062bdca65684335c86d2cd8750e7d86c5 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sat, 7 Jun 2025 16:07:10 +0200 Subject: [PATCH 19/85] [#2154] Test Composite Id with generated values --- .../CompositeIdWithGeneratedValuesTest.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/CompositeIdWithGeneratedValuesTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CompositeIdWithGeneratedValuesTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CompositeIdWithGeneratedValuesTest.java new file mode 100644 index 000000000..0192bbbf7 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CompositeIdWithGeneratedValuesTest.java @@ -0,0 +1,107 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.SequenceGenerator; + +import java.util.Collection; +import java.util.List; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 10, timeUnit = MINUTES) + +public class CompositeIdWithGeneratedValuesTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( Product.class ); + } + + @Test + public void testCompositeIdWithGeneratedValues(VertxTestContext context) { + final Product product = new Product( "name", 1150L ); + test( + context, + getMutinySessionFactory().withTransaction( session -> session.persist( product ) ) + .invoke( () -> assertThat( product.id ).isNotNull() ) + .chain( () -> getMutinySessionFactory().withTransaction( session -> session.find( + Product.class, + new ProductId( product.version, product.id ) + ) ) ) + .invoke( found -> { + assertThat( found ).hasFieldOrPropertyWithValue( "id", product.id ); + assertThat( found ).hasFieldOrPropertyWithValue( "version", product.version ); + assertThat( found ).hasFieldOrPropertyWithValue( "name", product.name ); + } ) + ); + } + + @Entity + @IdClass(ProductId.class) + public static class Product { + @Id + private Long version; + + @Id + @GeneratedValue + @SequenceGenerator(name = "product_seq", sequenceName = "product_seq") + private Long id; + + private String name; + + public Product() { + } + + public Product(String name, Long version) { + this.name = name; + this.version = version; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "(" + id + "," + version + "):" + name; + } + } + + public static class ProductId { + private Long version; + private Long id; + + private ProductId() { + } + + public ProductId(Long version, Long id) { + this.version = version; + this.id = id; + } + } +} From f26f2cb264f2a842a05c55fed49bf59e7f84bf0a Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 13 Jun 2025 23:45:33 +0200 Subject: [PATCH 20/85] [#2305] Switch to Maven Central publishing --- ci/release/Jenkinsfile | 8 +------- ci/snapshot-publish.Jenkinsfile | 2 +- publish.gradle | 6 +++--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 8627950a1..3bd5e82a3 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -195,13 +195,7 @@ pipeline { configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { withCredentials([ - // TODO: Once we switch to maven-central publishing (from nexus2) we need to add a new credentials - // to use the following env variable names to set the user/password: - // - JRELEASER_MAVENCENTRAL_USERNAME - // - JRELEASER_MAVENCENTRAL_TOKEN - // Also use the new `credentialsId` for Maven Central, e.g.: - // usernamePassword(credentialsId: '???????', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'), - usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'JRELEASER_NEXUS2_PASSWORD', usernameVariable: 'JRELEASER_NEXUS2_USERNAME'), + usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'), gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), string(credentialsId: 'release.gpg.passphrase', variable: 'JRELEASER_GPG_PASSPHRASE'), diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index fea160b23..e50eb4cac 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -43,7 +43,7 @@ pipeline { // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh // TODO: Once we switch to maven-central publishing (from nexus2) we need to update credentialsId: // https://docs.gradle.org/current/samples/sample_publishing_credentials.html#:~:text=via%20environment%20variables - usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'ORG_GRADLE_PROJECT_snapshotsPassword', usernameVariable: 'ORG_GRADLE_PROJECT_snapshotsUsername'), + usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'ORG_GRADLE_PROJECT_snapshotsPassword', usernameVariable: 'ORG_GRADLE_PROJECT_snapshotsUsername'), gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN') ]) { diff --git a/publish.gradle b/publish.gradle index f77b26959..6d05724fa 100644 --- a/publish.gradle +++ b/publish.gradle @@ -21,7 +21,7 @@ jar { 'Implementation-Version': project.version, 'Implementation-Vendor': 'Hibernate.org', 'Implementation-Vendor-Id': 'org.hibernate', - 'Implementation-Url': 'http://hibernate.org/reactive', + 'Implementation-Url': 'https://hibernate.org/reactive', ) } } @@ -50,7 +50,7 @@ publishing { license { name = 'Apache License Version 2.0' url = 'https://opensource.org/licenses/Apache-2.0' - comments = 'See discussion at http://hibernate.org/community/license/ for more details.' + comments = 'See discussion at https://hibernate.org/community/license/ for more details.' distribution = 'repo' } } @@ -81,7 +81,7 @@ publishing { } maven { name = 'snapshots' - url = "https://oss.sonatype.org/content/repositories/snapshots/" + url = "https://central.sonatype.com/repository/maven-snapshots/" // So that Gradle uses the `ORG_GRADLE_PROJECT_snapshotsPassword` / `ORG_GRADLE_PROJECT_snapshotsUsername` // env variables to read the username/password for the `snapshots` repository publishing: credentials(PasswordCredentials) From 5b84fb63495bc6e1123b25fac514b1b0aac81f77 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 17 Jun 2025 10:49:58 +0200 Subject: [PATCH 21/85] [#2309] Add JDK 26-ea to the build --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f818b94e8..fd4205415 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -196,6 +196,7 @@ jobs: - { name: "21", java_version_numeric: 21, jvm_args: '--enable-preview' } - { name: "24", java_version_numeric: 24, from: 'jdk.java.net', jvm_args: '--enable-preview' } - { name: "25-ea", java_version_numeric: 25, from: 'jdk.java.net', jvm_args: '--enable-preview' } + - { name: "26-ea", java_version_numeric: 26, from: 'jdk.java.net', jvm_args: '--enable-preview' } steps: - name: Checkout ${{ inputs.branch }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 From 4e9a73523f1350cc74d21e2ec61ca139c851a830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 20 Jun 2025 16:44:59 +0200 Subject: [PATCH 22/85] [#2314] Test setMaxResults() on native queries --- .../org/hibernate/reactive/QueryTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QueryTest.java index c65c79ee4..1011c0b19 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QueryTest.java @@ -43,6 +43,7 @@ import static jakarta.persistence.FetchType.LAZY; import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; import static org.hibernate.reactive.QueryTest.Author.AUTHOR_TABLE; import static org.hibernate.reactive.QueryTest.Author.HQL_NAMED_QUERY; import static org.hibernate.reactive.QueryTest.Author.SQL_NAMED_QUERY; @@ -395,6 +396,42 @@ public void testNativeEntityQueryWithParam(VertxTestContext context) { ); } + // https://github.com/hibernate/hibernate-reactive/issues/2314 + @Test + public void testNativeEntityQueryWithLimit(VertxTestContext context) { + Author author1 = new Author( "Iain M. Banks" ); + Author author2 = new Author( "Neal Stephenson" ); + Book book1 = new Book( "1-85723-235-6", "Feersum Endjinn", author1 ); + Book book2 = new Book( "0-380-97346-4", "Cryptonomicon", author2 ); + Book book3 = new Book( "0-553-08853-X", "Snow Crash", author2 ); + author1.books.add( book1 ); + author2.books.add( book2 ); + author2.books.add( book3 ); + + test( + context, + openSession() + .thenCompose( session -> session.persist( author1, author2 ) + .thenCompose( v -> session.flush() ) + ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session.createNativeQuery( + "select * from " + BOOK_TABLE + " order by isbn", + Book.class + ) + .setMaxResults( 2 ) + .getResultList() ) + .thenAccept( books -> { + assertThat( books ) + .extracting( b -> b.id, b -> b.title, b -> b.isbn ) + .containsExactly( + tuple( book2.id, book2.title, book2.isbn ), + tuple( book3.id, book3.title, book3.isbn ) + ); + } ) + ); + } + @Test public void testNativeEntityQueryWithNamedParam(VertxTestContext context) { Author author1 = new Author( "Iain M. Banks" ); From 821df5cd831a8e532c67fa6a96895a056416495f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 20 Jun 2025 17:25:25 +0200 Subject: [PATCH 23/85] [#2314] Fix '?' parameters in limit/offset not being replaced with native equivalent when using PostgreSQL/MSSQL Reverts some changes introduced in e257189f643346799703eab7805de6c4b74a8b33, probably assuming this was more efficient and didn't break anything (understandably, considering tests did pass). --- .../sql/internal/ReactiveNativeSelectQueryPlanImpl.java | 5 +---- .../internal/ReactiveDeferredResultSetAccess.java | 9 ++++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeSelectQueryPlanImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeSelectQueryPlanImpl.java index f1eca36df..bae87b8b3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeSelectQueryPlanImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeSelectQueryPlanImpl.java @@ -11,7 +11,6 @@ import java.util.Set; import java.util.concurrent.CompletionStage; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.spi.DomainQueryExecutionContext; @@ -23,7 +22,6 @@ import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; -import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.reactive.query.internal.ReactiveResultSetMappingProcessor; import org.hibernate.reactive.query.spi.ReactiveNativeSelectQueryPlan; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; @@ -61,8 +59,7 @@ public ReactiveNativeSelectQueryPlanImpl( resultSetMapping.addAffectedTableNames( affectedTableNames, sessionFactory ); } this.affectedTableNames = affectedTableNames; - Dialect dialect = sessionFactory.getJdbcServices().getDialect(); - this.sql = Parameters.instance( dialect ).process( parser.process() ); + this.sql = parser.process(); this.parameterList = parameterList; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java index 3f88bcffb..ac119a7d0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java @@ -14,6 +14,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockOptions; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -22,6 +23,7 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.util.impl.CompletionStages; @@ -172,6 +174,11 @@ private CompletionStage executeQuery() { return completedFuture( logicalConnection ) .thenCompose( lg -> { LOG.tracef( "Executing query to retrieve ResultSet : %s", getFinalSql() ); + + Dialect dialect = executionContext.getSession().getJdbcServices().getDialect(); + // This must happen at the very last minute in order to process parameters + // added in org.hibernate.dialect.pagination.OffsetFetchLimitHandler.processSql + final String sql = Parameters.instance( dialect ).process( getFinalSql() ); Object[] parameters = PreparedStatementAdaptor.bind( super::bindParameters ); final SessionEventListenerManager eventListenerManager = executionContext @@ -181,7 +188,7 @@ private CompletionStage executeQuery() { eventListenerManager.jdbcExecuteStatementStart(); return connection() - .selectJdbc( getFinalSql(), parameters ) + .selectJdbc( sql, parameters ) .thenCompose( this::validateResultSet ) .whenComplete( (resultSet, throwable) -> { // FIXME: I don't know if this event makes sense for Vert.x From c818376c268c28089273e0989b2c0d8ab0daf848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 23 Jun 2025 12:51:04 +0200 Subject: [PATCH 24/85] Release job: minor aligmnent on ORM's job --- ci/release/Jenkinsfile | 47 ++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 3bd5e82a3..867a249f7 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -1,9 +1,7 @@ #! /usr/bin/groovy /* - * 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 . + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors */ /* @@ -89,9 +87,13 @@ pipeline { ) } stages { - stage('Release check') { + stage('Check') { steps { script { + print "INFO: params.RELEASE_VERSION = ${params.RELEASE_VERSION}" + print "INFO: params.DEVELOPMENT_VERSION = ${params.DEVELOPMENT_VERSION}" + print "INFO: params.RELEASE_DRY_RUN? = ${params.RELEASE_DRY_RUN}" + checkoutReleaseScripts() def currentVersion = Version.parseDevelopmentVersion( sh( @@ -107,7 +109,9 @@ pipeline { echo "Release was requested manually" if ( !params.RELEASE_VERSION ) { - throw new IllegalArgumentException( 'Missing value for parameter RELEASE_VERSION. This parameter must be set explicitly to prevent mistakes.' ) + throw new IllegalArgumentException( + 'Missing value for parameter RELEASE_VERSION. This parameter must be set explicitly to prevent mistakes.' + ) } releaseVersion = Version.parseReleaseVersion( params.RELEASE_VERSION ) @@ -149,17 +153,12 @@ pipeline { env.RELEASE_VERSION = releaseVersion.toString() env.DEVELOPMENT_VERSION = developmentVersion.toString() - // Dry run is not supported at the moment - env.SCRIPT_OPTIONS = params.RELEASE_DRY_RUN ? "-d" : "" + env.SCRIPT_OPTIONS = params.RELEASE_DRY_RUN ? "-d" : "" env.JRELEASER_DRY_RUN = params.RELEASE_DRY_RUN - - // Determine version id to check if Jira version exists - // This step doesn't work for Hibernate Reactive (the project has been created with a different type on JIRA) - // sh ".release/scripts/determine-jira-version-id.sh ${env.JIRA_KEY} ${releaseVersion.withoutFinalQualifier}" } } } - stage('Release prepare') { + stage('Prepare') { steps { script { checkoutReleaseScripts() @@ -168,13 +167,13 @@ pipeline { configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"), configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { - - sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) { + sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) { // set release version // update changelog from JIRA // tags the version // changes the version to the provided development version withEnv([ + "DISABLE_REMOTE_GRADLE_CACHE=true", // Increase the amount of memory for this part since asciidoctor doc rendering consumes a lot of metaspace "GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8'" ]) { @@ -185,7 +184,7 @@ pipeline { } } } - stage('Publish release') { + stage('Publish') { steps { script { checkoutReleaseScripts() @@ -195,16 +194,20 @@ pipeline { configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { withCredentials([ - usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'), - gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), - file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'JRELEASER_GPG_PASSPHRASE'), - string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN') + usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'), + gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), + file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), + string(credentialsId: 'release.gpg.passphrase', variable: 'JRELEASER_GPG_PASSPHRASE'), + string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN') ]) { sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) { // performs documentation upload and Sonatype release // push to github - sh ".release/scripts/publish.sh -j ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH}" + withEnv([ + "DISABLE_REMOTE_GRADLE_CACHE=true" + ]) { + sh ".release/scripts/publish.sh -j ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH}" + } } } } From 5be05017d9a76008619172069988837e2e08d7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 23 Jun 2025 12:52:52 +0200 Subject: [PATCH 25/85] Release job: Avoid auto-release when there are no "releasable" commits The determination of "releasable" is in the release scripts, but currently it boils down to having an issue key in the commit message. --- ci/release/Jenkinsfile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 867a249f7..87a959714 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -122,14 +122,15 @@ pipeline { else { echo "Release was triggered automatically" - // Avoid doing an automatic release for commits from a release - def lastCommitter = sh(script: 'git show -s --format=\'%an\'', returnStdout: true).trim() - def secondLastCommitter = sh(script: 'git show -s --format=\'%an\' HEAD~1', returnStdout: true).trim() - echo "Last two commits were performed by '${lastCommitter}'/'${secondLastCommitter}'." - - if (lastCommitter == 'Hibernate-CI' && secondLastCommitter == 'Hibernate-CI') { - print "INFO: Automatic release skipped because last commits were for the previous release" - currentBuild.result = 'ABORTED' + // Avoid doing an automatic release if there are no "releasable" commits since the last release (see release scripts for determination) + def releasableCommitCount = sh( + script: ".release/scripts/count-releasable-commits.sh ${env.PROJECT}", + returnStdout: true + ).trim().toInteger() + if ( releasableCommitCount <= 0 ) { + print "INFO: Automatic release skipped because no releasable commits were pushed since the previous release" + currentBuild.getRawBuild().getExecutor().interrupt(Result.NOT_BUILT) + sleep(1) // Interrupt is not blocking and does not take effect immediately. return } From d405530bef0b110a14ae68cec1134d824c86dec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 23 Jun 2025 12:54:34 +0200 Subject: [PATCH 26/85] Release job: update the hibernate.org website automatically for micros --- ci/release/Jenkinsfile | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 87a959714..d4afbf6cf 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -215,6 +215,33 @@ pipeline { } } } + stage('Update website') { + steps { + script { + checkoutReleaseScripts() + + configFileProvider([ + configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"), + configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") + ]) { + withCredentials([ + gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default') + ]) { + sshagent( ['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net'] ) { + dir( '.release/hibernate.org' ) { + checkout scmGit( + branches: [[name: '*/production']], + extensions: [], + userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com', url: 'https://github.com/hibernate/hibernate.org.git']] + ) + sh "../scripts/website-release.sh ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION}" + } + } + } + } + } + } + } } post { always { From 15922506327b76574f4ec624e25d267bd68dad67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 23 Jun 2025 13:02:00 +0200 Subject: [PATCH 27/85] Release job: code for weekly release (disabled for now) --- ci/release/Jenkinsfile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index d4afbf6cf..6e4aacc33 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -15,11 +15,10 @@ import org.hibernate.jenkins.pipeline.helpers.version.Version // Global build configuration env.PROJECT = "reactive" env.JIRA_KEY = "HREACT" -def RELEASE_ON_PUSH = false // Set to `true` *only* on branches where you want a release on each push. +def RELEASE_ON_SCHEDULE = false // Set to `true` *only* on branches where you want a scheduled release. print "INFO: env.PROJECT = ${env.PROJECT}" print "INFO: env.JIRA_KEY = ${env.JIRA_KEY}" -print "INFO: RELEASE_ON_PUSH = ${RELEASE_ON_PUSH}" // -------------------------------------------- // Build conditions @@ -32,10 +31,17 @@ if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause')) { } def manualRelease = currentBuild.getBuildCauses().toString().contains( 'UserIdCause' ) +def cronRelease = currentBuild.getBuildCauses().toString().contains( 'TimerTriggerCause' ) // Only do automatic release on branches where we opted in -if ( !manualRelease && !RELEASE_ON_PUSH ) { - print "INFO: Build skipped because automated releases are disabled on this branch. See constant RELEASE_ON_PUSH in ci/release/Jenkinsfile" +if ( !manualRelease && !cronRelease ) { + print "INFO: Build skipped because automated releases on push are disabled on this branch." + currentBuild.result = 'NOT_BUILT' + return +} + +if ( !manualRelease && cronRelease && !RELEASE_ON_SCHEDULE ) { + print "INFO: Build skipped because automated releases are disabled on this branch. See constant RELEASE_ON_SCHEDULE in ci/release/Jenkinsfile" currentBuild.result = 'NOT_BUILT' return } @@ -51,6 +57,7 @@ def checkoutReleaseScripts() { } } + // -------------------------------------------- // Pipeline @@ -58,12 +65,15 @@ pipeline { agent { label 'Release' } + triggers { + // Run every week Sunday 1 AM + cron('0 1 * * 0') + } tools { jdk 'OpenJDK 17 Latest' } options { buildDiscarder logRotator(daysToKeepStr: '30', numToKeepStr: '10') - rateLimitBuilds(throttle: [count: 1, durationName: 'day', userBoost: true]) disableConcurrentBuilds(abortPrevious: false) preserveStashes() } From c254ebb5a6cda29843b8a3c1a8d6f8713450eec5 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 19 Jun 2025 15:10:49 +0200 Subject: [PATCH 28/85] [#2305] Update optional snapshot repositories to point to central snapshots instead of ossrh ones since now our snapshots are published to central. --- build.gradle | 6 +++--- examples/native-sql-example/build.gradle | 6 +++--- examples/session-example/build.gradle | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index f927371ef..1e442eb5b 100644 --- a/build.gradle +++ b/build.gradle @@ -56,9 +56,9 @@ subprojects { // Useful for local development, it should be disabled otherwise mavenLocal() } - // Example: ./gradlew build -PenableSonatypeOpenSourceSnapshotsRep - if ( project.hasProperty('enableSonatypeOpenSourceSnapshotsRep') ) { - maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + // Example: ./gradlew build -PenableCentralSonatypeSnapshotsRep + if ( project.hasProperty('enableCentralSonatypeSnapshotsRep') ) { + maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } } mavenCentral() diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index 3b650d34a..06c58a182 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -8,9 +8,9 @@ buildscript { mavenLocal() } // Optional: Enables snapshots repository - // Example: ./gradlew build -PenableSonatypeOpenSourceSnapshotsRep - if ( project.hasProperty('enableSonatypeOpenSourceSnapshotsRep') ) { - maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + // Example: ./gradlew build -PenableCentralSonatypeSnapshotsRep + if ( project.hasProperty('enableCentralSonatypeSnapshotsRep') ) { + maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } } mavenCentral() } diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index a001112d8..de82f3d5d 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -8,9 +8,9 @@ buildscript { mavenLocal() } // Optional: Enables snapshots repository - // Example: ./gradlew build -PenableSonatypeOpenSourceSnapshotsRep - if ( project.hasProperty('enableSonatypeOpenSourceSnapshotsRep') ) { - maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + // Example: ./gradlew build -PenableCentralSonatypeSnapshotsRep + if ( project.hasProperty('enableCentralSonatypeSnapshotsRep') ) { + maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } } mavenCentral() } From 51371baf5de43459a39066b4984ba04632681b67 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 7 Jul 2025 17:15:27 +0200 Subject: [PATCH 29/85] [#2333] Rename property for enabling the snapshot repository From `enableSonatypeOpenSourceSnapshotsRep` to `enableCentralSonatypeSnapshotsRep` (in [gradle.properties](https://github.com/hibernate/hibernate-reactive/blob/main/gradle.properties)). It was changed in commit c254ebb5a6cda29843b8a3c1a8d6f8713450eec5 as part of the move to Maven Central (#2305). --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 08e84c8f1..f1a8acda1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,8 +28,8 @@ org.gradle.java.installations.auto-download=false # Db2, MySql, PostgreSQL, CockroachDB, SqlServer, Oracle #db = MSSQL -# Enable the SonatypeOS maven repository (mainly for Vert.x snapshots) when present (value ignored) -#enableSonatypeOpenSourceSnapshotsRep = true +# Enable the maven Central Snapshot repository, when set to any value (the value is ignored) +#enableCentralSonatypeSnapshotsRep = true # Enable the maven local repository (for local development when needed) when present (value ignored) #enableMavenLocalRepo = true From 52b5b1fb0bc0c617bd5fec1d0b6d3e6f6c2a69fc Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Jul 2025 09:49:03 +0200 Subject: [PATCH 30/85] [#2333] Print the resolved Hibernate ORM and Vert.x version This is important to check what verison is actually resolved on the `wip/*` branches, where we test the snapshots, because we use a versions range. --- examples/native-sql-example/build.gradle | 33 ++++++++++++++++++++++++ examples/session-example/build.gradle | 32 +++++++++++++++++++++++ hibernate-reactive-core/build.gradle | 32 +++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index 06c58a182..9c11eaaa3 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -75,3 +75,36 @@ tasks.register( "runAllExamples" ) { dependsOn = ["runAllExamplesOnPostgreSQL"] description = "Run all examples on ${dbs}" } + +// Optional: Task to print the resolved versions of Hibernate ORM and Vert.x +tasks.register( "printResolvedVersions" ) { + description = "Print the resolved hibernate-orm-core and vert.x versions" + doLast { + def hibernateCoreVersion = "n/a" + def vertxVersion = "n/a" + + // Resolve Hibernate Core and Vert.x versions from compile classpath + configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> + if (artifact.moduleVersion.id.name == 'hibernate-core') { + hibernateCoreVersion = artifact.moduleVersion.id.version + } + if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { + vertxVersion = artifact.moduleVersion.id.version + } + } + + // Print the resolved versions + println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" + println "Resolved Vert.x SQL client Version: ${vertxVersion}" + } +} + +// Make the version printing task run before tests and JavaExec tasks +tasks.withType( Test ).configureEach { + dependsOn printResolvedVersions +} + +tasks.withType( JavaExec ).configureEach { + dependsOn printResolvedVersions +} + diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index de82f3d5d..8f5909aec 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -81,3 +81,35 @@ tasks.register( "runAllExamples" ) { dependsOn = ["runAllExamplesOnPostgreSQL", "runAllExamplesOnMySQL"] description = "Run all examples on ${dbs}" } + +// Optional: Task to print the resolved versions of Hibernate ORM and Vert.x +tasks.register( "printResolvedVersions" ) { + description = "Print the resolved hibernate-orm-core and vert.x versions" + doLast { + def hibernateCoreVersion = "n/a" + def vertxVersion = "n/a" + + // Resolve Hibernate Core and Vert.x versions from compile classpath + configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> + if (artifact.moduleVersion.id.name == 'hibernate-core') { + hibernateCoreVersion = artifact.moduleVersion.id.version + } + if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { + vertxVersion = artifact.moduleVersion.id.version + } + } + + // Print the resolved versions + println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" + println "Resolved Vert.x SQL client Version: ${vertxVersion}" + } +} + +// Make the version printing task run before tests and JavaExec tasks +tasks.withType( Test ).configureEach { + dependsOn printResolvedVersions +} + +tasks.withType( JavaExec ).configureEach { + dependsOn printResolvedVersions +} diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 26a4de751..a3bf0fdf5 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -167,3 +167,35 @@ tasks.register( "testAll", Test ) { description = "Run tests for ${dbs}" dependsOn = dbs.collect( [] as HashSet ) { db -> "testDb${db}" } } + +// Task to print the resolved versions of Hibernate ORM and Vert.x +tasks.register( "printResolvedVersions" ) { + description = "Print the resolved hibernate-orm-core and vert.x versions" + doLast { + def hibernateCoreVersion = "n/a" + def vertxVersion = "n/a" + + // Resolve Hibernate Core and Vert.x versions from compile classpath + configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> + if (artifact.moduleVersion.id.name == 'hibernate-core') { + hibernateCoreVersion = artifact.moduleVersion.id.version + } + if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { + vertxVersion = artifact.moduleVersion.id.version + } + } + + // Print the resolved versions + println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" + println "Resolved Vert.x SQL client Version: ${vertxVersion}" + } +} + +// Make the version printing task run before tests and JavaExec tasks +tasks.withType( Test ).configureEach { + dependsOn printResolvedVersions +} + +tasks.withType( JavaExec ).configureEach { + dependsOn printResolvedVersions +} From 0f53e6df86a172e5e918eef6ec419b6a54e8f0d5 Mon Sep 17 00:00:00 2001 From: exoego Date: Thu, 3 Jul 2025 18:10:19 +0900 Subject: [PATCH 31/85] [#1989] Migrate to Gradle version catalogs --- build.gradle | 31 +++------ documentation/build.gradle | 2 +- examples/native-sql-example/build.gradle | 14 ++-- examples/session-example/build.gradle | 16 ++--- gradle.properties | 10 +-- gradle/libs.versions.toml | 59 ++++++++++++++++ hibernate-reactive-core/build.gradle | 68 +++++++++---------- .../bytecode-enhancements-it/build.gradle | 21 +++--- .../build.gradle | 25 +++---- .../techempower-postgres-it/build.gradle | 30 +++----- .../verticle-postgres-it/build.gradle | 30 +++----- .../reactive/env/VersionsPlugin.java | 27 ++++++-- .../reactive/env/VersionsTomlParser.java | 68 +++++++++++++++++++ settings.gradle | 38 +++++++++++ 14 files changed, 289 insertions(+), 150 deletions(-) create mode 100644 gradle/libs.versions.toml create mode 100644 local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java diff --git a/build.gradle b/build.gradle index 1e442eb5b..532d932ef 100644 --- a/build.gradle +++ b/build.gradle @@ -3,33 +3,15 @@ plugins { id 'java-library' id 'maven-publish' - id 'com.diffplug.spotless' version '6.25.0' - id 'org.asciidoctor.jvm.convert' version '4.0.2' apply false - id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' + alias(libs.plugins.com.diffplug.spotless) + alias(libs.plugins.org.asciidoctor.jvm.convert) apply false + alias(libs.plugins.io.github.gradle.nexus.publish.plugin) } group = "org.hibernate.reactive" // leverage the ProjectVersion which comes from the `local.versions` plugin version = project.projectVersion.fullName -// Versions which need to be aligned across modules; this also -// allows overriding the build using a parameter, which can be -// useful to monitor compatibility for upcoming versions on CI: -// -// ./gradlew clean build -PhibernateOrmVersion=5.6.15-SNAPSHOT -ext { - // Mainly, to allow CI to test the latest versions of Vert.X - // Example: - // ./gradlew build -PvertxSqlClientVersion=4.0.0-SNAPSHOT - if ( !project.hasProperty( 'vertxSqlClientVersion' ) ) { - vertxSqlClientVersion = '5.0.0' - } - - testcontainersVersion = '1.21.0' - - logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion -} - subprojects { apply plugin: 'java-library' apply plugin: 'com.diffplug.spotless' @@ -135,3 +117,10 @@ subprojects { } } +rootProject.afterEvaluate { + // Workaround since "libs.versions.NAME" notation cannot be used here + def libs = project.extensions.getByType(VersionCatalogsExtension).named('libs') + logger.lifecycle "ORM version: ${libs.findVersion('hibernateOrmVersion').get().requiredVersion}" + logger.lifecycle "ORM Gradle plugin version: ${libs.findVersion('hibernateOrmGradlePluginVersion').get().requiredVersion}" + logger.lifecycle "Vert.x SQL Client version: ${libs.findVersion('vertxSqlClientVersion').get().requiredVersion}" +} diff --git a/documentation/build.gradle b/documentation/build.gradle index aa6a27622..95c3dcb2b 100644 --- a/documentation/build.gradle +++ b/documentation/build.gradle @@ -52,7 +52,7 @@ def aggregateJavadocsTask = tasks.register( 'aggregateJavadocs', Javadoc ) { use = true options.encoding = 'UTF-8' - def matcher = hibernateOrmVersion =~ /\d+\.\d+/ + def matcher = libs.versions.hibernateOrmVersion =~ /\d+\.\d+/ def ormMinorVersion = matcher.find() ? matcher.group() : "5.6"; links = [ diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index 9c11eaaa3..7c3b1d875 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -18,7 +18,7 @@ buildscript { plugins { // Optional: Hibernate Gradle plugin to enable bytecode enhancements - id "org.hibernate.orm" version "${hibernateOrmGradlePluginVersion}" + alias(libs.plugins.org.hibernate.orm) } description = 'Hibernate Reactive native SQL Example' @@ -27,20 +27,20 @@ dependencies { implementation project( ':hibernate-reactive-core' ) // Hibernate Validator (optional) - implementation 'org.hibernate.validator:hibernate-validator:8.0.2.Final' - runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' + implementation(libs.org.hibernate.validator.hibernate.validator) + runtimeOnly(libs.org.glassfish.expressly.expressly) // JPA metamodel generation for criteria queries (optional) - annotationProcessor "org.hibernate.orm:hibernate-jpamodelgen:${hibernateOrmVersion}" + annotationProcessor(libs.org.hibernate.orm.hibernate.jpamodelgen) // database driver for PostgreSQL - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) // logging (optional) - runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0" + runtimeOnly(libs.org.apache.logging.log4j.log4j.core) // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:scram-client:3.1' + runtimeOnly(libs.com.ongres.scram.scram.client) } // Optional: enable the bytecode enhancements diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index 8f5909aec..2fb7837b6 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -18,7 +18,7 @@ buildscript { plugins { // Optional: Hibernate Gradle plugin to enable bytecode enhancements - id "org.hibernate.orm" version "${hibernateOrmGradlePluginVersion}" + alias(libs.plugins.org.hibernate.orm) } description = 'Hibernate Reactive Session Examples' @@ -27,21 +27,21 @@ dependencies { implementation project( ':hibernate-reactive-core' ) // Hibernate Validator (optional) - implementation 'org.hibernate.validator:hibernate-validator:8.0.2.Final' - runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' + implementation(libs.org.hibernate.validator.hibernate.validator) + runtimeOnly(libs.org.glassfish.expressly.expressly) // JPA metamodel generation for criteria queries (optional) - annotationProcessor "org.hibernate.orm:hibernate-jpamodelgen:${hibernateOrmVersion}" + annotationProcessor(libs.org.hibernate.orm.hibernate.jpamodelgen) // database drivers for PostgreSQL and MySQL - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" - runtimeOnly "io.vertx:vertx-mysql-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) + runtimeOnly(libs.io.vertx.vertx.mysql.client) // logging (optional) - runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0" + runtimeOnly(libs.org.apache.logging.log4j.log4j.core) // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:scram-client:3.1' + runtimeOnly(libs.com.ongres.scram.scram.client) } // Optional: enable the bytecode enhancements diff --git a/gradle.properties b/gradle.properties index f1a8acda1..1fb1a63b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,9 @@ org.gradle.java.installations.auto-download=false ######################################################################### # Additional custom gradle build properties. -# Please, leave these properties commented upstream +# Please, leave these properties commented upstream. +# They are meant to be used for local builds or WIP branches. +# The same properties can be set from the command line. ########################################################################## # Enable Testcontainers + Docker when present (value ignored) @@ -34,12 +36,12 @@ org.gradle.java.installations.auto-download=false # Enable the maven local repository (for local development when needed) when present (value ignored) #enableMavenLocalRepo = true +### Settings the following properties will override the version defined in gradle/libs.versions.toml + # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -hibernateOrmVersion = 7.0.2.Final +#hibernateOrmVersion = 7.0.2.Final # Override default Hibernate ORM Gradle plugin version -# Using the stable version because I don't know how to configure the build to download the snapshot version from -# a remote repository #hibernateOrmGradlePluginVersion = 7.0.2.Final # If set to true, skip Hibernate ORM version parsing (default is true, if set to null) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..c1fa1d0b3 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,59 @@ +[versions] +assertjVersion = "3.27.3" +hibernateOrmVersion = "7.0.2.Final" +hibernateOrmGradlePluginVersion = "7.0.2.Final" +jacksonDatabindVersion = "2.15.2" +jbossLoggingAnnotationVersion = "3.0.4.Final" +jbossLoggingVersion = "3.6.1.Final" +junitVersion = "5.11.3" +log4jVersion = "2.20.0" +testcontainersVersion = "1.21.0" +vertxSqlClientVersion = "5.0.0" +vertxWebVersion= "5.0.0" +vertxWebClientVersion = "5.0.0" + +[libraries] +com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } +com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.0.0" } +com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "12.10.0.jre11" } +com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } +com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } +io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.0" } +io-vertx-vertx-db2-client = { group = "io.vertx", name = "vertx-db2-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-junit5 = { group = "io.vertx", name = "vertx-junit5", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-micrometer-metrics = { group = "io.vertx", name = "vertx-micrometer-metrics", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-mssql-client = { group = "io.vertx", name = "vertx-mssql-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-mysql-client = { group = "io.vertx", name = "vertx-mysql-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-oracle-client = { group = "io.vertx", name = "vertx-oracle-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-pg-client = { group = "io.vertx", name = "vertx-pg-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-sql-client = { group = "io.vertx", name = "vertx-sql-client", version.ref = "vertxSqlClientVersion" } +io-vertx-vertx-web = { group = "io.vertx", name = "vertx-web", version.ref = "vertxWebVersion" } +io-vertx-vertx-web-client = { group = "io.vertx", name = "vertx-web-client", version.ref = "vertxWebClientVersion" } +org-apache-logging-log4j-log4j-core = { group = "org.apache.logging.log4j", name = "log4j-core", version.ref = "log4jVersion" } +org-assertj-assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "assertjVersion" } +org-ehcache-ehcache = { group = "org.ehcache", name = "ehcache", version = "3.10.8" } +org-glassfish-expressly-expressly = { group = "org.glassfish.expressly", name = "expressly", version = "5.0.0" } +org-hibernate-orm-hibernate-core = { group = "org.hibernate.orm", name = "hibernate-core", version.ref = "hibernateOrmVersion" } +org-hibernate-orm-hibernate-jcache = { group = "org.hibernate.orm", name = "hibernate-jcache", version.ref = "hibernateOrmVersion" } +org-hibernate-orm-hibernate-jpamodelgen = { group = "org.hibernate.orm", name = "hibernate-jpamodelgen", version.ref = "hibernateOrmVersion" } +org-hibernate-validator-hibernate-validator = { group = "org.hibernate.validator", name = "hibernate-validator", version = "8.0.2.Final" } +org-jboss-logging-jboss-logging = { group = "org.jboss.logging", name = "jboss-logging", version.ref = "jbossLoggingVersion" } +org-jboss-logging-jboss-logging-annotations = { group = "org.jboss.logging", name = "jboss-logging-annotations", version.ref = "jbossLoggingAnnotationVersion" } +org-jboss-logging-jboss-logging-processor = { group = "org.jboss.logging", name = "jboss-logging-processor", version.ref = "jbossLoggingAnnotationVersion" } +org-junit-jupiter-junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitVersion" } +org-junit-jupiter-junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitVersion" } +org-mariadb-jdbc-mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version = "3.5.3" } +org-postgresql-postgresql = { group = "org.postgresql", name = "postgresql", version = "42.7.5" } +org-testcontainers-cockroachdb = { group = "org.testcontainers", name = "cockroachdb", version.ref = "testcontainersVersion" } +org-testcontainers-db2 = { group = "org.testcontainers", name = "db2", version.ref = "testcontainersVersion" } +org-testcontainers-mariadb = { group = "org.testcontainers", name = "mariadb", version.ref = "testcontainersVersion" } +org-testcontainers-mssqlserver = { group = "org.testcontainers", name = "mssqlserver", version.ref = "testcontainersVersion" } +org-testcontainers-mysql = { group = "org.testcontainers", name = "mysql", version.ref = "testcontainersVersion" } +org-testcontainers-oracle-xe = { group = "org.testcontainers", name = "oracle-xe", version.ref = "testcontainersVersion" } +org-testcontainers-postgresql = { group = "org.testcontainers", name = "postgresql", version.ref = "testcontainersVersion" } + +[plugins] +com-diffplug-spotless = { id = "com.diffplug.spotless", version = "6.25.0" } +io-github-gradle-nexus-publish-plugin = { id = "io.github.gradle-nexus.publish-plugin", version = "1.3.0" } +org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.2" } +org-hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernateOrmGradlePluginVersion" } diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index a3bf0fdf5..4407a4341 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -8,77 +8,77 @@ apply from: publishScript dependencies { - api "org.hibernate.orm:hibernate-core:${hibernateOrmVersion}" + api(libs.org.hibernate.orm.hibernate.core) - api 'io.smallrye.reactive:mutiny:2.9.0' + api(libs.io.smallrye.reactive.mutiny) //Logging - implementation 'org.jboss.logging:jboss-logging:3.6.1.Final' - annotationProcessor 'org.jboss.logging:jboss-logging:3.6.1.Final' + implementation(libs.org.jboss.logging.jboss.logging) + annotationProcessor(libs.org.jboss.logging.jboss.logging) - compileOnly 'org.jboss.logging:jboss-logging-annotations:3.0.4.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-annotations:3.0.4.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-processor:3.0.4.Final' + compileOnly(libs.org.jboss.logging.jboss.logging.annotations) + annotationProcessor(libs.org.jboss.logging.jboss.logging.annotations) + annotationProcessor(libs.org.jboss.logging.jboss.logging.processor) //Specific implementation details of Hibernate Reactive: - implementation "io.vertx:vertx-sql-client:${vertxSqlClientVersion}" + implementation(libs.io.vertx.vertx.sql.client) // Testing - testImplementation 'org.assertj:assertj-core:3.27.3' - testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" + testImplementation(libs.org.assertj.assertj.core) + testImplementation(libs.io.vertx.vertx.junit5) // Drivers - testImplementation "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" - testImplementation "io.vertx:vertx-mysql-client:${vertxSqlClientVersion}" - testImplementation "io.vertx:vertx-db2-client:${vertxSqlClientVersion}" - testImplementation "io.vertx:vertx-mssql-client:${vertxSqlClientVersion}" - testImplementation "io.vertx:vertx-oracle-client:${vertxSqlClientVersion}" + testImplementation(libs.io.vertx.vertx.pg.client) + testImplementation(libs.io.vertx.vertx.mysql.client) + testImplementation(libs.io.vertx.vertx.db2.client) + testImplementation(libs.io.vertx.vertx.mssql.client) + testImplementation(libs.io.vertx.vertx.oracle.client) // Metrics - testImplementation "io.vertx:vertx-micrometer-metrics:${vertxSqlClientVersion}" + testImplementation(libs.io.vertx.vertx.micrometer.metrics) // Optional dependency of vertx-pg-client, essential when connecting via SASL SCRAM - testImplementation 'com.ongres.scram:scram-client:3.1' + testImplementation(libs.com.ongres.scram.scram.client) // JUnit Jupiter - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.3' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.3' + testImplementation(libs.org.junit.jupiter.junit.jupiter.api) + testRuntimeOnly(libs.org.junit.jupiter.junit.jupiter.engine) // JDBC driver to test with ORM and PostgreSQL - testRuntimeOnly "org.postgresql:postgresql:42.7.5" + testRuntimeOnly(libs.org.postgresql.postgresql) // JDBC driver for Testcontainers with MS SQL Server - testRuntimeOnly "com.microsoft.sqlserver:mssql-jdbc:12.10.0.jre11" + testRuntimeOnly(libs.com.microsoft.sqlserver.mssql.jdbc) // JDBC driver for Testcontainers with MariaDB Server - testRuntimeOnly "org.mariadb.jdbc:mariadb-java-client:3.5.3" + testRuntimeOnly(libs.org.mariadb.jdbc.mariadb.java.client) // JDBC driver for Testcontainers with MYSQL Server - testRuntimeOnly "com.mysql:mysql-connector-j:9.3.0" + testRuntimeOnly(libs.com.mysql.mysql.connector.j) // JDBC driver for Db2 server, for testing - testRuntimeOnly "com.ibm.db2:jcc:12.1.0.0" + testRuntimeOnly(libs.com.ibm.db2.jcc) // EHCache - testRuntimeOnly ("org.ehcache:ehcache:3.10.8") { + testRuntimeOnly(libs.org.ehcache.ehcache) { capabilities { requireCapability 'org.ehcache.modules:ehcache-xml-jakarta' } } - testRuntimeOnly ("org.hibernate.orm:hibernate-jcache:${hibernateOrmVersion}") + testRuntimeOnly(libs.org.hibernate.orm.hibernate.jcache) // log4j - testRuntimeOnly 'org.apache.logging.log4j:log4j-core:2.20.0' + testRuntimeOnly(libs.org.apache.logging.log4j.log4j.core) // Testcontainers - testImplementation "org.testcontainers:postgresql:${testcontainersVersion}" - testImplementation "org.testcontainers:mysql:${testcontainersVersion}" - testImplementation "org.testcontainers:mariadb:${testcontainersVersion}" - testImplementation "org.testcontainers:db2:${testcontainersVersion}" - testImplementation "org.testcontainers:cockroachdb:${testcontainersVersion}" - testImplementation "org.testcontainers:mssqlserver:${testcontainersVersion}" - testImplementation "org.testcontainers:oracle-xe:${testcontainersVersion}" + testImplementation(libs.org.testcontainers.postgresql) + testImplementation(libs.org.testcontainers.mysql) + testImplementation(libs.org.testcontainers.mariadb) + testImplementation(libs.org.testcontainers.db2) + testImplementation(libs.org.testcontainers.cockroachdb) + testImplementation(libs.org.testcontainers.mssqlserver) + testImplementation(libs.org.testcontainers.oracle.xe) } // Reproducible Builds diff --git a/integration-tests/bytecode-enhancements-it/build.gradle b/integration-tests/bytecode-enhancements-it/build.gradle index 071745993..a796557dd 100644 --- a/integration-tests/bytecode-enhancements-it/build.gradle +++ b/integration-tests/bytecode-enhancements-it/build.gradle @@ -10,37 +10,32 @@ buildscript { } plugins { - id "org.hibernate.orm" version "${hibernateOrmGradlePluginVersion}" + alias(libs.plugins.org.hibernate.orm) } description = 'Bytecode enhancements integration tests' -ext { - log4jVersion = '2.20.0' - assertjVersion = '3.27.3' -} - dependencies { implementation project(':hibernate-reactive-core') // JPA metamodel generation for criteria queries (optional) - annotationProcessor "org.hibernate.orm:hibernate-jpamodelgen:${hibernateOrmVersion}" + annotationProcessor(libs.org.hibernate.orm.hibernate.jpamodelgen) // Testing on one database should be enough - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:scram-client:3.1' + runtimeOnly(libs.com.ongres.scram.scram.client) // logging - runtimeOnly "org.apache.logging.log4j:log4j-core:${log4jVersion}" + runtimeOnly(libs.org.apache.logging.log4j.log4j.core) // Testcontainers - testImplementation "org.testcontainers:postgresql:${testcontainersVersion}" + testImplementation(libs.org.testcontainers.postgresql) // Testing - testImplementation "org.assertj:assertj-core:${assertjVersion}" - testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" + testImplementation(libs.org.assertj.assertj.core) + testImplementation(libs.io.vertx.vertx.junit5) } // Optional: enable the bytecode enhancements diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index abd6e2bc0..38165df4d 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -10,39 +10,34 @@ buildscript { } plugins { - id "org.hibernate.orm" version "${hibernateOrmGradlePluginVersion}" + alias(libs.plugins.org.hibernate.orm) } description = 'Quarkus QE integration tests' -ext { - log4jVersion = '2.20.0' - assertjVersion = '3.27.3' -} - dependencies { implementation project(':hibernate-reactive-core') - implementation "org.hibernate.validator:hibernate-validator:8.0.2.Final" - runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' + implementation(libs.org.hibernate.validator.hibernate.validator) + runtimeOnly(libs.org.glassfish.expressly.expressly) // JPA metamodel generation for criteria queries (optional) - annotationProcessor "org.hibernate.orm:hibernate-jpamodelgen:${hibernateOrmVersion}" + annotationProcessor(libs.org.hibernate.orm.hibernate.jpamodelgen) // Testing on one database should be enough - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) // Allow authentication to PostgreSQL using SCRAM: - runtimeOnly 'com.ongres.scram:scram-client:3.1' + runtimeOnly(libs.com.ongres.scram.scram.client) // logging - runtimeOnly "org.apache.logging.log4j:log4j-core:${log4jVersion}" + runtimeOnly(libs.org.apache.logging.log4j.log4j.core) // Testcontainers - testImplementation "org.testcontainers:postgresql:${testcontainersVersion}" + testImplementation(libs.org.testcontainers.postgresql) // Testing - testImplementation "org.assertj:assertj-core:${assertjVersion}" - testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" + testImplementation(libs.org.assertj.assertj.core) + testImplementation(libs.io.vertx.vertx.junit5) } // Optional: enable the bytecode enhancements diff --git a/integration-tests/techempower-postgres-it/build.gradle b/integration-tests/techempower-postgres-it/build.gradle index 603a60db5..b5f74f4d2 100644 --- a/integration-tests/techempower-postgres-it/build.gradle +++ b/integration-tests/techempower-postgres-it/build.gradle @@ -11,37 +11,25 @@ buildscript { description = 'TechEmpower integration tests' -ext { - jacksonDatabindVersion = '2.15.2' - jbossLoggingVersion = '3.5.0.Final' - assertjVersion = '3.27.3' - vertxWebVersion = project.hasProperty( 'vertxWebVersion' ) - ? project.property( 'vertxWebVersion' ) - : vertxSqlClientVersion - vertxWebClientVersion = project.hasProperty( 'vertxWebClientVersion' ) - ? project.property( 'vertxWebClientVersion' ) - : vertxSqlClientVersion -} - dependencies { implementation project( ':hibernate-reactive-core' ) - implementation "io.vertx:vertx-web:${vertxWebVersion}" - implementation "io.vertx:vertx-web-client:${vertxWebClientVersion}" + implementation(libs.io.vertx.vertx.web) + implementation(libs.io.vertx.vertx.web.client) - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) // The Pg client requires this dependency - runtimeOnly "com.ongres.scram:scram-client:3.1" - runtimeOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}" + runtimeOnly(libs.com.ongres.scram.scram.client) + runtimeOnly(libs.com.fasterxml.jackson.core.jackson.databind) // logging - implementation "org.jboss.logging:jboss-logging:${jbossLoggingVersion}" + implementation(libs.org.jboss.logging.jboss.logging) // Testcontainers - implementation "org.testcontainers:postgresql:${testcontainersVersion}" + implementation(libs.org.testcontainers.postgresql) // Testing - testImplementation "org.assertj:assertj-core:${assertjVersion}" - testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" + testImplementation(libs.org.assertj.assertj.core) + testImplementation(libs.io.vertx.vertx.junit5) } // Configuration for the tests diff --git a/integration-tests/verticle-postgres-it/build.gradle b/integration-tests/verticle-postgres-it/build.gradle index cf98e8f56..7d2322306 100644 --- a/integration-tests/verticle-postgres-it/build.gradle +++ b/integration-tests/verticle-postgres-it/build.gradle @@ -11,37 +11,25 @@ buildscript { description = 'Bytecode enhancements integration tests' -ext { - jacksonDatabindVersion = '2.15.2' - jbossLoggingVersion = '3.5.0.Final' - assertjVersion = '3.27.3' - vertxWebVersion = project.hasProperty( 'vertxWebVersion' ) - ? project.property( 'vertxWebVersion' ) - : vertxSqlClientVersion - vertxWebClientVersion = project.hasProperty( 'vertxWebClientVersion' ) - ? project.property( 'vertxWebClientVersion' ) - : vertxSqlClientVersion -} - dependencies { implementation project(':hibernate-reactive-core') - implementation "io.vertx:vertx-web:${vertxWebVersion}" - implementation "io.vertx:vertx-web-client:${vertxWebClientVersion}" + implementation(libs.io.vertx.vertx.web) + implementation(libs.io.vertx.vertx.web.client) - runtimeOnly "io.vertx:vertx-pg-client:${vertxSqlClientVersion}" + runtimeOnly(libs.io.vertx.vertx.pg.client) // The Pg client requires this dependency - runtimeOnly "com.ongres.scram:scram-client:3.1" - runtimeOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}" + runtimeOnly(libs.com.ongres.scram.scram.client) + runtimeOnly(libs.com.fasterxml.jackson.core.jackson.databind) // logging - implementation "org.jboss.logging:jboss-logging:${jbossLoggingVersion}" + implementation(libs.org.jboss.logging.jboss.logging) // Testcontainers - implementation "org.testcontainers:postgresql:${testcontainersVersion}" + implementation(libs.org.testcontainers.postgresql) // Testing - testImplementation "org.assertj:assertj-core:${assertjVersion}" - testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" + testImplementation(libs.org.assertj.assertj.core) + testImplementation(libs.io.vertx.vertx.junit5) } // Configuration for the tests diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java index 36b5ae10a..c34a4b479 100644 --- a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java @@ -28,6 +28,7 @@ public class VersionsPlugin implements Plugin { public static final String SKIP_ORM_VERSION_PARSING = "skipOrmVersionParsing"; public static final String RELATIVE_FILE = "gradle/version.properties"; + public static final String RELATIVE_CATALOG = "gradle/libs.versions.toml"; @Override public void apply(Project project) { @@ -57,12 +58,13 @@ public void apply(Project project) { project.getLogger().lifecycle( "Development version: n/a" ); } - final String ormVersionString = determineOrmVersion( project ); + final VersionsTomlParser tomlParser = new VersionsTomlParser( RELATIVE_CATALOG ); + final String ormVersionString = determineOrmVersion( project, tomlParser ); final Object ormVersion = resolveOrmVersion( ormVersionString, project ); project.getLogger().lifecycle( "ORM version: {}", ormVersion ); project.getExtensions().add( ORM_VERSION, ormVersion ); - final Object ormPluginVersion = determineOrmPluginVersion( ormVersion, project ); + final Object ormPluginVersion = determineOrmPluginVersion( ormVersion, project, tomlParser ); project.getLogger().lifecycle( "ORM Gradle plugin version: {}", ormPluginVersion ); project.getExtensions().add( ORM_PLUGIN_VERSION, ormPluginVersion ); } @@ -123,10 +125,17 @@ private static void withInputStream(File file, Consumer action) { } } - private String determineOrmVersion(Project project) { + private String determineOrmVersion(Project project, VersionsTomlParser parser) { + // Check if it has been set in the project if ( project.hasProperty( ORM_VERSION ) ) { return (String) project.property( ORM_VERSION ); } + + // Check in the catalog + final String version = parser.read( ORM_VERSION ); + if ( version != null ) { + return version; + } throw new IllegalStateException( "Hibernate ORM version not specified on project" ); } @@ -138,10 +147,18 @@ private Object resolveOrmVersion(String stringForm, Project project) { return new ProjectVersion( stringForm ); } - private Object determineOrmPluginVersion(Object ormVersion, Project project) { + private Object determineOrmPluginVersion(Object ormVersion, Project project, VersionsTomlParser parser) { + // Check if it has been set in the project if ( project.hasProperty( ORM_PLUGIN_VERSION ) ) { return project.property( ORM_PLUGIN_VERSION ); } - return ormVersion; + + // Check in the catalog + final String version = parser.read( ORM_PLUGIN_VERSION ); + if ( version != null ) { + return version; + } + + throw new IllegalStateException( "Hibernate ORM Gradle plugin version not specified on project" ); } } diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java new file mode 100644 index 000000000..b0adafd6d --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java @@ -0,0 +1,68 @@ +package org.hibernate.reactive.env; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Read the versions section of the library catalog + */ +public class VersionsTomlParser { + + private final Map data = new HashMap<>(); + + public VersionsTomlParser(String filePath) { + parse( filePath ); + } + + private void parse(String filePath) { + try ( BufferedReader reader = new BufferedReader( new FileReader( filePath ) ) ) { + String line; + String currentSection = null; + while ( ( line = reader.readLine() ) != null ) { + line = line.trim(); + + // Skip comments and blank lines + if ( line.isEmpty() || line.startsWith( "#" ) ) { + continue; + } + + // Handle [section] + if ( line.startsWith( "[" ) && line.endsWith( "]" ) ) { + currentSection = line.substring( 1, line.length() - 1 ).trim(); + continue; + } + + if ( "versions".equalsIgnoreCase( currentSection ) ) { + // Handle key = value + int equalsIndex = line.indexOf( '=' ); + if ( equalsIndex == -1 ) { + continue; + } + + String key = line.substring( 0, equalsIndex ).trim(); + String value = line.substring( equalsIndex + 1 ).trim(); + + // Remove optional quotes around string values + if ( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) { + value = value.substring( 1, value.length() - 1 ); + } + + data.put( key, value ); + } + } + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + + /** + * Read the value of the property in the versions section of a toml file + */ + public String read(String property) { + return data.get( property ); + } +} diff --git a/settings.gradle b/settings.gradle index e997f3e73..159d91e20 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,6 +19,44 @@ gradle.ext.baselineJavaVersion = JavaLanguageVersion.of( 17 ) // You can't use bytecode higher than what Gradle supports, even with toolchains. def GRADLE_MAX_SUPPORTED_BYTECODE_VERSION = 23 +// This overrides the default version catalog in gradle/libs.versions.toml, which can be +// useful to monitor compatibility for upcoming versions on CI: +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + // ./gradlew build -PhibernateOrmVersion=7.0.2.Final + def hibernateOrmVersion = settings.ext.find("hibernateOrmVersion") ?: "" + if ( hibernateOrmVersion != "" ) { + version("hibernateOrmVersion", hibernateOrmVersion) + } + + // ./gradlew build -PhibernateOrmGradlePluginVersion=7.0.2.Final + def hibernateOrmGradlePluginVersion = settings.ext.find("hibernateOrmGradlePluginVersion") ?: "" + if ( hibernateOrmGradlePluginVersion ) { + version("hibernateOrmGradlePluginVersion", hibernateOrmGradlePluginVersion) + } + + // ./gradlew build -PvertxSqlClientVersion=4.0.0-SNAPSHOT + def vertxSqlClientVersion = settings.ext.find("vertxSqlClientVersion") ?: "" + if ( vertxSqlClientVersion ) { + version("vertxSqlClientVersion", vertxSqlClientVersion) + } + + // ./gradlew build -PvertxWebVersion=4.0.0-SNAPSHOT + def vertxWebVersion = settings.ext.find("vertxWebVersion") ?: vertxSqlClientVersion + if ( vertxWebVersion ) { + version("vertxWebVersion", vertxWebVersion) + } + + // ./gradlew build -PvertxWebClientVersion=4.0.0-SNAPSHOT + def vertxWebClientVersion = settings.ext.find("vertxWebClientVersion") ?: vertxSqlClientVersion + if ( vertxWebClientVersion ) { + version("vertxWebClientVersion", vertxWebClientVersion) + } + } + } +} + // If either 'main.jdk.version' or 'test.jdk.version' is set, enable the toolchain and use the selected jdk. // If only one property is set, the other defaults to the baseline Java version (8). // Note that when toolchain is enabled, you also need to specify From ad4631d90827f930b32a60579624e1f6d04b4daf Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 10 Jul 2025 14:05:15 +0200 Subject: [PATCH 32/85] [#2269] chore: Fix imports in MutinySessionTest --- .../java/org/hibernate/reactive/MutinySessionTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinySessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinySessionTest.java index bf46f835a..31bcf2380 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinySessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinySessionTest.java @@ -27,7 +27,13 @@ import jakarta.persistence.metamodel.EntityType; import static java.util.concurrent.TimeUnit.MINUTES; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; @Timeout(value = 10, timeUnit = MINUTES) From 126bca2d54b7456d70453b1fff44b843b1a3a6c8 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 9 Jul 2025 15:10:08 +0200 Subject: [PATCH 33/85] [#2269] chore: Upgrade Testcontainers to 1.21.3 --- gradle/libs.versions.toml | 2 +- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/Db2ReactiveTest.java.qute | 2 +- tooling/jbang/MariaDBReactiveTest.java.qute | 2 +- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/PostgreSQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c1fa1d0b3..99b4a30f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" junitVersion = "5.11.3" log4jVersion = "2.20.0" -testcontainersVersion = "1.21.0" +testcontainersVersion = "1.21.3" vertxSqlClientVersion = "5.0.0" vertxWebVersion= "5.0.0" vertxWebClientVersion = "5.0.0" diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index d0ebda569..498892f67 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -10,7 +10,7 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:cockroachdb:1.21.0 +//DEPS org.testcontainers:cockroachdb:1.21.3 //DEPS org.slf4j:slf4j-simple:2.0.7 //// Testcontainer needs the JDBC drivers to start the container diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index d9396c13f..d0ee70229 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -10,7 +10,7 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:db2:1.21.0 +//DEPS org.testcontainers:db2:1.21.3 //DEPS org.slf4j:slf4j-simple:2.0.7 import jakarta.persistence.Entity; diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index d3f304147..b0129f086 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -10,7 +10,7 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mariadb:1.21.0 +//DEPS org.testcontainers:mariadb:1.21.3 //DEPS org.slf4j:slf4j-simple:2.0.7 //// Testcontainer needs the JDBC drivers to start the container diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 67925f99a..34865636f 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -10,7 +10,7 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mysql:1.21.0 +//DEPS org.testcontainers:mysql:1.21.3 //DEPS org.slf4j:slf4j-simple:2.0.7 //// Testcontainer needs the JDBC drivers to start the container diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 0993b9227..b5c6c8cbd 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -10,7 +10,7 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.21.0 +//DEPS org.testcontainers:postgresql:1.21.3 //DEPS org.slf4j:slf4j-simple:2.0.7 //DESCRIPTION Allow authentication to PostgreSQL using SCRAM: //DEPS com.ongres.scram:client:2.1 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 28656a504..8db2eedc9 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -13,11 +13,11 @@ //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.21.0 -//DEPS org.testcontainers:mysql:1.21.0 -//DEPS org.testcontainers:db2:1.21.0 -//DEPS org.testcontainers:mariadb:1.21.0 -//DEPS org.testcontainers:cockroachdb:1.21.0 +//DEPS org.testcontainers:postgresql:1.21.3 +//DEPS org.testcontainers:mysql:1.21.3 +//DEPS org.testcontainers:db2:1.21.3 +//DEPS org.testcontainers:mariadb:1.21.3 +//DEPS org.testcontainers:cockroachdb:1.21.3 // //// Testcontainer needs the JDBC drivers to start the containers //// Hibernate Reactive doesn't use them From 95cda946095c385a3574c868fabf4fa0eb8e16b3 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 10 Jul 2025 17:00:11 +0200 Subject: [PATCH 34/85] [#2269] Keep containers image and version in docker files We are trying to achieve two things: * Make it possible for dependabot to upgrade the containers automatically * Collect the image and version of the containers we use for testing in one place Note that the test suite will still create and start the containers programmatically, but it will read the first FROM line in each Dockerfile to extract the image and version to use. It will ignore everything else. My initial plan was to configure each container using the Dockerfile directly, but I prefer to reuse the exsisting Testcontainers classes for each database (for example, PostgreSQLContainer) because they contain out-of-the-box configuration that I would need to copy somewhere else. In any case, this is a good starting point and we can improve it later. --- build.gradle | 6 + .../containers/CockroachDBDatabase.java | 4 +- .../reactive/containers/DB2Database.java | 4 +- .../reactive/containers/DockerImage.java | 103 ++++++++++++++- .../containers/MSSQLServerDatabase.java | 4 +- .../reactive/containers/MariaDatabase.java | 4 +- .../reactive/containers/MySQLDatabase.java | 4 +- .../reactive/containers/OracleDatabase.java | 6 +- .../containers/PostgreSQLDatabase.java | 8 +- .../hibernate/reactive/it/BaseReactiveIT.java | 4 +- .../hibernate/reactive/it/DockerImage.java | 125 ++++++++++++++++++ .../quarkus/qe/database/BaseReactiveIT.java | 4 +- .../it/quarkus/qe/database/DockerImage.java | 125 ++++++++++++++++++ .../reactive/it/verticle/DockerImage.java | 125 ++++++++++++++++++ .../reactive/it/verticle/VertxServer.java | 4 +- tooling/docker/README.md | 6 + tooling/docker/cockroachdb.Dockerfile | 3 + tooling/docker/db2.Dockerfile | 3 + tooling/docker/maria.Dockerfile | 3 + tooling/docker/mysql.Dockerfile | 3 + tooling/docker/oracle.Dockerfile | 3 + tooling/docker/postgresql.Dockerfile | 3 + tooling/docker/sqlserver.Dockerfile | 3 + 23 files changed, 528 insertions(+), 29 deletions(-) create mode 100644 integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/DockerImage.java create mode 100644 integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/DockerImage.java create mode 100644 integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/DockerImage.java create mode 100644 tooling/docker/README.md create mode 100644 tooling/docker/cockroachdb.Dockerfile create mode 100644 tooling/docker/db2.Dockerfile create mode 100644 tooling/docker/maria.Dockerfile create mode 100644 tooling/docker/mysql.Dockerfile create mode 100644 tooling/docker/oracle.Dockerfile create mode 100644 tooling/docker/postgresql.Dockerfile create mode 100644 tooling/docker/sqlserver.Dockerfile diff --git a/build.gradle b/build.gradle index 532d932ef..2615b335d 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,12 @@ subprojects { options.encoding = 'UTF-8' } + // Configure test tasks for all subprojects + tasks.withType( Test ).configureEach { + // Set the project root for finding Docker files - available to all modules + systemProperty 'hibernate.reactive.project.root', rootProject.projectDir.absolutePath + } + if ( !gradle.ext.javaToolchainEnabled ) { sourceCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) targetCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/CockroachDBDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/CockroachDBDatabase.java index e1a0928d1..172ace2bc 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/CockroachDBDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/CockroachDBDatabase.java @@ -12,7 +12,7 @@ import org.testcontainers.containers.CockroachContainer; import org.testcontainers.containers.Container; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; class CockroachDBDatabase extends PostgreSQLDatabase { @@ -25,7 +25,7 @@ class CockroachDBDatabase extends PostgreSQLDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final CockroachContainer cockroachDb = new CockroachContainer( imageName( "cockroachdb/cockroach", "v24.3.13" ) ) + public static final CockroachContainer cockroachDb = new CockroachContainer( fromDockerfile( "cockroachdb" ) ) // Username, password and database are not supported by test container at the moment // Testcontainers will use a database named 'postgres' and the 'root' user .withReuse( true ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DB2Database.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DB2Database.java index dfeeaf15c..c2b60e3d6 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DB2Database.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DB2Database.java @@ -28,7 +28,7 @@ import org.testcontainers.containers.Db2Container; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; class DB2Database implements TestableDatabase { @@ -87,7 +87,7 @@ class DB2Database implements TestableDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - static final Db2Container db2 = new Db2Container( imageName( "icr.io", "db2_community/db2", "12.1.0.0" ) ) + static final Db2Container db2 = new Db2Container( fromDockerfile( "db2" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DockerImage.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DockerImage.java index 1b2f3f6a7..e8f40f34d 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DockerImage.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DockerImage.java @@ -5,10 +5,17 @@ */ package org.hibernate.reactive.containers; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + import org.testcontainers.utility.DockerImageName; + /** - * A utility class with methods to generate {@link DockerImageName} for testcontainers. + * A utility class with methods to generate a {@link DockerImageName} for Testcontainers. *

* Testcontainers might not work if the image required is available in multiple different registries (for example when * using podman instead of docker). @@ -17,10 +24,28 @@ */ public final class DockerImage { - public static final String DEFAULT_REGISTRY = "docker.io"; + /** + * The absolute path of the project root that we have set in Gradle. + */ + private static final String PROJECT_ROOT = System.getProperty( "hibernate.reactive.project.root" ); + + /** + * The path to the directory containing all the Dockerfile files + */ + private static final Path DOCKERFILE_DIR_PATH = Path.of( PROJECT_ROOT ).resolve( "tooling" ).resolve( "docker" ); - public static DockerImageName imageName(String image, String version) { - return imageName( DEFAULT_REGISTRY, image, version ); + /** + * Extract the image name and version from the first FROM instruction in the Dockerfile. + * Note that everything else is ignored. + */ + public static DockerImageName fromDockerfile(String databaseName) { + try { + final ImageInformation imageInformation = readFromInstruction( databaseName.toLowerCase() ); + return imageName( imageInformation.getRegistry(), imageInformation.getImage(), imageInformation.getVersion() ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } } public static DockerImageName imageName(String registry, String image, String version) { @@ -28,4 +53,74 @@ public static DockerImageName imageName(String registry, String image, String ve .parse( registry + "/" + image + ":" + version ) .asCompatibleSubstituteFor( image ); } + + private static class ImageInformation { + private final String registry; + private final String image; + private final String version; + + public ImageInformation(String fullImageInfo) { + // FullImageInfo pattern: /: + // For example: "docker.io/cockroachdb/cockroach:v24.3.13" becomes registry = "docker.io", image = "cockroachdb/cockroach", version = "v24.3.13" + final int registryEndPos = fullImageInfo.indexOf( '/' ); + final int imageEndPos = fullImageInfo.lastIndexOf( ':' ); + this.registry = fullImageInfo.substring( 0, registryEndPos ); + this.image = fullImageInfo.substring( registryEndPos + 1, imageEndPos ); + this.version = fullImageInfo.substring( imageEndPos + 1 ); + } + + public String getRegistry() { + return registry; + } + + public String getImage() { + return image; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return registry + "/" + image + ":" + version; + } + } + + private static Path dockerFilePath(String database) { + // Get project root from system property set by Gradle, with fallback + return DOCKERFILE_DIR_PATH.resolve( database.toLowerCase() + ".Dockerfile" ); + } + + private static ImageInformation readFromInstruction(String database) throws IOException { + return readFromInstruction( dockerFilePath( database ) ); + } + + /** + * Read a Dockerfile and extract the first FROM instruction. + * + * @param dockerfilePath path to the Dockerfile + * @return the first FROM instruction found, or empty if none found + * @throws IOException if the file cannot be read + */ + private static ImageInformation readFromInstruction(Path dockerfilePath) throws IOException { + if ( !Files.exists( dockerfilePath ) ) { + throw new FileNotFoundException( "Dockerfile not found: " + dockerfilePath ); + } + + List lines = Files.readAllLines( dockerfilePath ); + for ( String line : lines ) { + // Skip comments and empty lines + String trimmedLine = line.trim(); + if ( trimmedLine.isEmpty() || trimmedLine.startsWith( "#" ) ) { + continue; + } + + if ( trimmedLine.startsWith( "FROM " ) ) { + return new ImageInformation( trimmedLine.substring( "FROM ".length() ) ); + } + } + + throw new IOException( " Missing FROM instruction in " + dockerfilePath ); + } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MSSQLServerDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MSSQLServerDatabase.java index aeb1b8fb0..eaab2e8fb 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MSSQLServerDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MSSQLServerDatabase.java @@ -28,7 +28,7 @@ import org.testcontainers.containers.MSSQLServerContainer; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; /** * The JDBC driver syntax is: @@ -96,7 +96,7 @@ class MSSQLServerDatabase implements TestableDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final MSSQLServerContainer mssqlserver = new MSSQLServerContainer<>( imageName( "mcr.microsoft.com", "mssql/server", "2022-latest" ) ) + public static final MSSQLServerContainer mssqlserver = new MSSQLServerContainer<>( fromDockerfile( "sqlserver" ) ) .acceptLicense() .withPassword( PASSWORD ) .withReuse( true ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MariaDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MariaDatabase.java index 16a5f97ff..14d60c264 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MariaDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MariaDatabase.java @@ -13,7 +13,7 @@ import org.testcontainers.containers.MariaDBContainer; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; class MariaDatabase extends MySQLDatabase { @@ -36,7 +36,7 @@ class MariaDatabase extends MySQLDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final MariaDBContainer maria = new MariaDBContainer<>( imageName( "mariadb", "11.7.2" ) ) + public static final MariaDBContainer maria = new MariaDBContainer<>( fromDockerfile( "maria" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java index e0aa9ef3a..f5cd8f7c7 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java @@ -5,7 +5,7 @@ */ package org.hibernate.reactive.containers; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; import java.io.Serializable; import java.math.BigDecimal; @@ -87,7 +87,7 @@ class MySQLDatabase implements TestableDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final MySQLContainer mysql = new MySQLContainer<>( imageName( "mysql", "9.2.0") ) + public static final MySQLContainer mysql = new MySQLContainer<>( fromDockerfile( "mysql" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java index d57f9103a..bbde95e6c 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java @@ -29,7 +29,7 @@ import org.testcontainers.containers.OracleContainer; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; /** * Connection string for Oracle thin should be something like: @@ -88,9 +88,7 @@ class OracleDatabase implements TestableDatabase { } } - public static final OracleContainer oracle = new OracleContainer( - imageName( "gvenzl/oracle-free", "23-slim-faststart" ) - .asCompatibleSubstituteFor( "gvenzl/oracle-xe" ) ) + public static final OracleContainer oracle = new OracleContainer( fromDockerfile( "oracle" ).asCompatibleSubstituteFor( "gvenzl/oracle-xe" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/PostgreSQLDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/PostgreSQLDatabase.java index 56ef4f878..f6a974ba2 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/PostgreSQLDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/PostgreSQLDatabase.java @@ -5,8 +5,6 @@ */ package org.hibernate.reactive.containers; -import static org.hibernate.reactive.containers.DockerImage.imageName; - import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; @@ -30,9 +28,11 @@ import org.testcontainers.containers.PostgreSQLContainer; +import static org.hibernate.reactive.containers.DockerImage.fromDockerfile; + class PostgreSQLDatabase implements TestableDatabase { - public static PostgreSQLDatabase INSTANCE = new PostgreSQLDatabase(); + public static final PostgreSQLDatabase INSTANCE = new PostgreSQLDatabase(); private static Map, String> expectedDBTypeForClass = new HashMap<>(); @@ -87,7 +87,7 @@ class PostgreSQLDatabase implements TestableDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>( imageName( "postgres", "17.5" ) ) + public static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>( fromDockerfile( "postgresql" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java index 70251e20e..261fa380d 100644 --- a/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java +++ b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java @@ -51,9 +51,7 @@ public abstract class BaseReactiveIT { // These properties are in DatabaseConfiguration in core public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); - public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:17.5" ) - .asCompatibleSubstituteFor( "postgres" ); + public static final DockerImageName IMAGE_NAME = DockerImage.fromDockerfile( "postgresql" ); public static final String USERNAME = "hreact"; public static final String PASSWORD = "hreact"; diff --git a/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/DockerImage.java b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/DockerImage.java new file mode 100644 index 000000000..b5621249d --- /dev/null +++ b/integration-tests/bytecode-enhancements-it/src/test/java/org/hibernate/reactive/it/DockerImage.java @@ -0,0 +1,125 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.it; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.testcontainers.utility.DockerImageName; + +/** + * A utility class with methods to generate a {@link DockerImageName} for Testcontainers. + *

+ * Testcontainers might not work if the image required is available in multiple different registries (for example when + * using podman instead of docker). + * These methods make sure to pick a registry as default. + *

+ */ +public final class DockerImage { + + /** + * The absolute path of the project root that we have set in Gradle. + */ + private static final String PROJECT_ROOT = System.getProperty( "hibernate.reactive.project.root" ); + + /** + * The path to the directory containing all the Dockerfile files + */ + private static final Path DOCKERFILE_DIR_PATH = Path.of( PROJECT_ROOT ).resolve( "tooling" ).resolve( "docker" ); + + /** + * Extract the image name and version from the first FROM instruction in the Dockerfile. + * Note that everything else is ignored. + */ + public static DockerImageName fromDockerfile(String databaseName) { + try { + final ImageInformation imageInformation = readFromInstruction( databaseName.toLowerCase() ); + return imageName( imageInformation.getRegistry(), imageInformation.getImage(), imageInformation.getVersion() ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + + public static DockerImageName imageName(String registry, String image, String version) { + return DockerImageName + .parse( registry + "/" + image + ":" + version ) + .asCompatibleSubstituteFor( image ); + } + + private static class ImageInformation { + private final String registry; + private final String image; + private final String version; + + public ImageInformation(String fullImageInfo) { + // FullImageInfo pattern: /: + // For example: "docker.io/cockroachdb/cockroach:v24.3.13" becomes registry = "docker.io", image = "cockroachdb/cockroach", version = "v24.3.13" + final int registryEndPos = fullImageInfo.indexOf( '/' ); + final int imageEndPos = fullImageInfo.lastIndexOf( ':' ); + this.registry = fullImageInfo.substring( 0, registryEndPos ); + this.image = fullImageInfo.substring( registryEndPos + 1, imageEndPos ); + this.version = fullImageInfo.substring( imageEndPos + 1 ); + } + + public String getRegistry() { + return registry; + } + + public String getImage() { + return image; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return registry + "/" + image + ":" + version; + } + } + + private static Path dockerFilePath(String database) { + // Get project root from system property set by Gradle, with fallback + return DOCKERFILE_DIR_PATH.resolve( database.toLowerCase() + ".Dockerfile" ); + } + + private static ImageInformation readFromInstruction(String database) throws IOException { + return readFromInstruction( dockerFilePath( database ) ); + } + + /** + * Read a Dockerfile and extract the first FROM instruction. + * + * @param dockerfilePath path to the Dockerfile + * @return the first FROM instruction found, or empty if none found + * @throws IOException if the file cannot be read + */ + private static ImageInformation readFromInstruction(Path dockerfilePath) throws IOException { + if ( !Files.exists( dockerfilePath ) ) { + throw new FileNotFoundException( "Dockerfile not found: " + dockerfilePath ); + } + + List lines = Files.readAllLines( dockerfilePath ); + for ( String line : lines ) { + // Skip comments and empty lines + String trimmedLine = line.trim(); + if ( trimmedLine.isEmpty() || trimmedLine.startsWith( "#" ) ) { + continue; + } + + if ( trimmedLine.startsWith( "FROM " ) ) { + return new ImageInformation( trimmedLine.substring( "FROM ".length() ) ); + } + } + + throw new IOException( " Missing FROM instruction in " + dockerfilePath ); + } +} diff --git a/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java index f65358ce3..cb94e0547 100644 --- a/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java +++ b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/BaseReactiveIT.java @@ -51,9 +51,7 @@ public abstract class BaseReactiveIT { // These properties are in DatabaseConfiguration in core public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); - public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:17.5" ) - .asCompatibleSubstituteFor( "postgres" ); + public static final DockerImageName IMAGE_NAME = DockerImage.fromDockerfile( "postgresql" ); public static final String USERNAME = "hreact"; public static final String PASSWORD = "hreact"; diff --git a/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/DockerImage.java b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/DockerImage.java new file mode 100644 index 000000000..7770da76c --- /dev/null +++ b/integration-tests/hibernate-validator-postgres-it/src/test/java/org/hibernate/reactive/it/quarkus/qe/database/DockerImage.java @@ -0,0 +1,125 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.it.quarkus.qe.database; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.testcontainers.utility.DockerImageName; + +/** + * A utility class with methods to generate a {@link DockerImageName} for Testcontainers. + *

+ * Testcontainers might not work if the image required is available in multiple different registries (for example when + * using podman instead of docker). + * These methods make sure to pick a registry as default. + *

+ */ +public final class DockerImage { + + /** + * The absolute path of the project root that we have set in Gradle. + */ + private static final String PROJECT_ROOT = System.getProperty( "hibernate.reactive.project.root" ); + + /** + * The path to the directory containing all the Dockerfile files + */ + private static final Path DOCKERFILE_DIR_PATH = Path.of( PROJECT_ROOT ).resolve( "tooling" ).resolve( "docker" ); + + /** + * Extract the image name and version from the first FROM instruction in the Dockerfile. + * Note that everything else is ignored. + */ + public static DockerImageName fromDockerfile(String databaseName) { + try { + final ImageInformation imageInformation = readFromInstruction( databaseName.toLowerCase() ); + return imageName( imageInformation.getRegistry(), imageInformation.getImage(), imageInformation.getVersion() ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + + public static DockerImageName imageName(String registry, String image, String version) { + return DockerImageName + .parse( registry + "/" + image + ":" + version ) + .asCompatibleSubstituteFor( image ); + } + + private static class ImageInformation { + private final String registry; + private final String image; + private final String version; + + public ImageInformation(String fullImageInfo) { + // FullImageInfo pattern: /: + // For example: "docker.io/cockroachdb/cockroach:v24.3.13" becomes registry = "docker.io", image = "cockroachdb/cockroach", version = "v24.3.13" + final int registryEndPos = fullImageInfo.indexOf( '/' ); + final int imageEndPos = fullImageInfo.lastIndexOf( ':' ); + this.registry = fullImageInfo.substring( 0, registryEndPos ); + this.image = fullImageInfo.substring( registryEndPos + 1, imageEndPos ); + this.version = fullImageInfo.substring( imageEndPos + 1 ); + } + + public String getRegistry() { + return registry; + } + + public String getImage() { + return image; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return registry + "/" + image + ":" + version; + } + } + + private static Path dockerFilePath(String database) { + // Get project root from system property set by Gradle, with fallback + return DOCKERFILE_DIR_PATH.resolve( database.toLowerCase() + ".Dockerfile" ); + } + + private static ImageInformation readFromInstruction(String database) throws IOException { + return readFromInstruction( dockerFilePath( database ) ); + } + + /** + * Read a Dockerfile and extract the first FROM instruction. + * + * @param dockerfilePath path to the Dockerfile + * @return the first FROM instruction found, or empty if none found + * @throws IOException if the file cannot be read + */ + private static ImageInformation readFromInstruction(Path dockerfilePath) throws IOException { + if ( !Files.exists( dockerfilePath ) ) { + throw new FileNotFoundException( "Dockerfile not found: " + dockerfilePath ); + } + + List lines = Files.readAllLines( dockerfilePath ); + for ( String line : lines ) { + // Skip comments and empty lines + String trimmedLine = line.trim(); + if ( trimmedLine.isEmpty() || trimmedLine.startsWith( "#" ) ) { + continue; + } + + if ( trimmedLine.startsWith( "FROM " ) ) { + return new ImageInformation( trimmedLine.substring( "FROM ".length() ) ); + } + } + + throw new IOException( " Missing FROM instruction in " + dockerfilePath ); + } +} diff --git a/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/DockerImage.java b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/DockerImage.java new file mode 100644 index 000000000..10026d020 --- /dev/null +++ b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/DockerImage.java @@ -0,0 +1,125 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.it.verticle; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.testcontainers.utility.DockerImageName; + +/** + * A utility class with methods to generate a {@link DockerImageName} for Testcontainers. + *

+ * Testcontainers might not work if the image required is available in multiple different registries (for example when + * using podman instead of docker). + * These methods make sure to pick a registry as default. + *

+ */ +public final class DockerImage { + + /** + * The absolute path of the project root that we have set in Gradle. + */ + private static final String PROJECT_ROOT = System.getProperty( "hibernate.reactive.project.root" ); + + /** + * The path to the directory containing all the Dockerfile files + */ + private static final Path DOCKERFILE_DIR_PATH = Path.of( PROJECT_ROOT ).resolve( "tooling" ).resolve( "docker" ); + + /** + * Extract the image name and version from the first FROM instruction in the Dockerfile. + * Note that everything else is ignored. + */ + public static DockerImageName fromDockerfile(String databaseName) { + try { + final ImageInformation imageInformation = readFromInstruction( databaseName.toLowerCase() ); + return imageName( imageInformation.getRegistry(), imageInformation.getImage(), imageInformation.getVersion() ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + + public static DockerImageName imageName(String registry, String image, String version) { + return DockerImageName + .parse( registry + "/" + image + ":" + version ) + .asCompatibleSubstituteFor( image ); + } + + private static class ImageInformation { + private final String registry; + private final String image; + private final String version; + + public ImageInformation(String fullImageInfo) { + // FullImageInfo pattern: /: + // For example: "docker.io/cockroachdb/cockroach:v24.3.13" becomes registry = "docker.io", image = "cockroachdb/cockroach", version = "v24.3.13" + final int registryEndPos = fullImageInfo.indexOf( '/' ); + final int imageEndPos = fullImageInfo.lastIndexOf( ':' ); + this.registry = fullImageInfo.substring( 0, registryEndPos ); + this.image = fullImageInfo.substring( registryEndPos + 1, imageEndPos ); + this.version = fullImageInfo.substring( imageEndPos + 1 ); + } + + public String getRegistry() { + return registry; + } + + public String getImage() { + return image; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return registry + "/" + image + ":" + version; + } + } + + private static Path dockerFilePath(String database) { + // Get project root from system property set by Gradle, with fallback + return DOCKERFILE_DIR_PATH.resolve( database.toLowerCase() + ".Dockerfile" ); + } + + private static ImageInformation readFromInstruction(String database) throws IOException { + return readFromInstruction( dockerFilePath( database ) ); + } + + /** + * Read a Dockerfile and extract the first FROM instruction. + * + * @param dockerfilePath path to the Dockerfile + * @return the first FROM instruction found, or empty if none found + * @throws IOException if the file cannot be read + */ + private static ImageInformation readFromInstruction(Path dockerfilePath) throws IOException { + if ( !Files.exists( dockerfilePath ) ) { + throw new FileNotFoundException( "Dockerfile not found: " + dockerfilePath ); + } + + List lines = Files.readAllLines( dockerfilePath ); + for ( String line : lines ) { + // Skip comments and empty lines + String trimmedLine = line.trim(); + if ( trimmedLine.isEmpty() || trimmedLine.startsWith( "#" ) ) { + continue; + } + + if ( trimmedLine.startsWith( "FROM " ) ) { + return new ImageInformation( trimmedLine.substring( "FROM ".length() ) ); + } + } + + throw new IOException( " Missing FROM instruction in " + dockerfilePath ); + } +} diff --git a/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/VertxServer.java b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/VertxServer.java index 990fef754..f11254c6d 100644 --- a/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/VertxServer.java +++ b/integration-tests/verticle-postgres-it/src/main/java/org/hibernate/reactive/it/verticle/VertxServer.java @@ -21,6 +21,7 @@ import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; @@ -36,7 +37,8 @@ public class VertxServer { // These properties are in DatabaseConfiguration in core public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); - public static final String IMAGE_NAME = "postgres:17.5"; + public static final DockerImageName IMAGE_NAME = DockerImage.fromDockerfile( "postgresql" ); + public static final String USERNAME = "hreact"; public static final String PASSWORD = "hreact"; public static final String DB_NAME = "hreact"; diff --git a/tooling/docker/README.md b/tooling/docker/README.md new file mode 100644 index 000000000..4de2451e0 --- /dev/null +++ b/tooling/docker/README.md @@ -0,0 +1,6 @@ +Our test suite will only read the first FROM instruction from each Dockerfile to extract the base image and the version +of the container to run. It will ignore everything else. + +The reason we have these files is that we want to automate the upgrade of the containers using dependabot. + +See the class `DockerImage`. diff --git a/tooling/docker/cockroachdb.Dockerfile b/tooling/docker/cockroachdb.Dockerfile new file mode 100644 index 000000000..a4710ad5b --- /dev/null +++ b/tooling/docker/cockroachdb.Dockerfile @@ -0,0 +1,3 @@ +# CockroachDB +# See https://hub.docker.com/r/cockroachdb/cockroach +FROM docker.io/cockroachdb/cockroach:v24.3.13 diff --git a/tooling/docker/db2.Dockerfile b/tooling/docker/db2.Dockerfile new file mode 100644 index 000000000..1ccb34906 --- /dev/null +++ b/tooling/docker/db2.Dockerfile @@ -0,0 +1,3 @@ +# IBM DB2 +# See https://hub.docker.com/r/ibmcom/db2 +FROM icr.io/db2_community/db2:12.1.0.0 diff --git a/tooling/docker/maria.Dockerfile b/tooling/docker/maria.Dockerfile new file mode 100644 index 000000000..4ccb397c8 --- /dev/null +++ b/tooling/docker/maria.Dockerfile @@ -0,0 +1,3 @@ +# MariaDB +# See https://hub.docker.com/_/mariadb +FROM docker.io/mariadb:11.7.2 diff --git a/tooling/docker/mysql.Dockerfile b/tooling/docker/mysql.Dockerfile new file mode 100644 index 000000000..966350a87 --- /dev/null +++ b/tooling/docker/mysql.Dockerfile @@ -0,0 +1,3 @@ +# MySQL +# See https://hub.docker.com/_/mysql +FROM docker.io/mysql:9.2.0 diff --git a/tooling/docker/oracle.Dockerfile b/tooling/docker/oracle.Dockerfile new file mode 100644 index 000000000..7dfc6447d --- /dev/null +++ b/tooling/docker/oracle.Dockerfile @@ -0,0 +1,3 @@ +# Oracle Database Free +# See https://hub.docker.com/r/gvenzl/oracle-free +FROM docker.io/gvenzl/oracle-free:23-slim-faststart diff --git a/tooling/docker/postgresql.Dockerfile b/tooling/docker/postgresql.Dockerfile new file mode 100644 index 000000000..fb36f48e8 --- /dev/null +++ b/tooling/docker/postgresql.Dockerfile @@ -0,0 +1,3 @@ +# PostgreSQL +# See https://hub.docker.com/_/postgres +FROM docker.io/postgres:17.5 diff --git a/tooling/docker/sqlserver.Dockerfile b/tooling/docker/sqlserver.Dockerfile new file mode 100644 index 000000000..b4fb34f2f --- /dev/null +++ b/tooling/docker/sqlserver.Dockerfile @@ -0,0 +1,3 @@ +# Microsoft SQL Server +# See https://hub.docker.com/_/microsoft-mssql-server +FROM mcr.microsoft.com/mssql/server:2022-latest From d70996d31010d0a4fe40869443e17a476150715f Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 11 Jul 2025 16:29:19 +0200 Subject: [PATCH 35/85] [#1136] Update dependabot configuration Includes: * Update project dependencies * Update images in Dockerfile files under tooling/docker/ * Update images we use as services in the GitHub workflow (mysql and postgres to test the examples) --- .github/dependabot.yml | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4d1c7e327..fa4919d37 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,3 +16,48 @@ updates: allow: - dependency-name: "actions/*" - dependency-name: "redhat-actions/*" + + - package-ecosystem: "gradle" + directory: "/" + registries: + - gradle-plugin-portal + schedule: + interval: "weekly" + day: "tuesday" + open-pull-requests-limit: 20 + groups: + hibernate: + patterns: + - "org.hibernate*" + vertx: + patterns: + - "io.vertx*" + mutiny: + patterns: + - "io.smallrye.reactive*" + # Testcontainers plus the JDBC driver we need for testing + testcontainers: + patterns: + - "org.testcontainers*" + - "com.ibm.db2*" + - "com.microsoft.sqlserver*" + - "org.postgresql*" + - "con.ongres.scram*" + - "com.fasterxml.jackson.core*" + - "com.mysql*" + - "org.mariadb.jdbc*" + + ignore: + # Only patches for Hibernate ORM and Vert.x + - dependency-name: "org.hibernate*" + update-types: ["version-update:semver-major", "version-update:semver-minor"] + - dependency-name: "io.vertx*" + update-types: ["version-update:semver-major", "version-update:semver-minor"] + + # Dockerfiles in tooling/docker/, and database services we use for examples (MySQL and PostgreSQL) + - package-ecosystem: "docker" + directory: "/tooling/docker" + schedule: + interval: "weekly" + allow: + - dependency-type: "all" From 6e202df2be03bd5a7ba0f7a5e7e5173ed331d6ba Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 14 Jul 2025 12:46:43 +0200 Subject: [PATCH 36/85] [#1136] Remove invalid gradle-plugin-portal registry from dependabot --- .github/dependabot.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fa4919d37..0d4bc0776 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -19,8 +19,6 @@ updates: - package-ecosystem: "gradle" directory: "/" - registries: - - gradle-plugin-portal schedule: interval: "weekly" day: "tuesday" From 31199651c5fbf8d15624a809bc5fede735fb4d20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 12:59:23 +0000 Subject: [PATCH 37/85] Bump org.asciidoctor.jvm.convert from 4.0.2 to 4.0.4 Bumps org.asciidoctor.jvm.convert from 4.0.2 to 4.0.4. --- updated-dependencies: - dependency-name: org.asciidoctor.jvm.convert dependency-version: 4.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 99b4a30f2..bdba4e88d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,5 +55,5 @@ org-testcontainers-postgresql = { group = "org.testcontainers", name = "postgres [plugins] com-diffplug-spotless = { id = "com.diffplug.spotless", version = "6.25.0" } io-github-gradle-nexus-publish-plugin = { id = "io.github.gradle-nexus.publish-plugin", version = "1.3.0" } -org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.2" } +org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.4" } org-hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernateOrmGradlePluginVersion" } From 6d0bb14cab86bebed6e9337a79b4b474e6624443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:08:18 +0000 Subject: [PATCH 38/85] Bump the testcontainers group with 5 updates Bumps the testcontainers group with 5 updates: | Package | From | To | | --- | --- | --- | | [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) | `2.15.2` | `2.19.1` | | com.ibm.db2:jcc | `12.1.0.0` | `12.1.2.0` | | [com.microsoft.sqlserver:mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) | `12.10.0.jre11` | `13.1.0.jre11-preview` | | [org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) | `3.5.3` | `3.5.4` | | [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) | `42.7.5` | `42.7.7` | Updates `com.fasterxml.jackson.core:jackson-databind` from 2.15.2 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.ibm.db2:jcc` from 12.1.0.0 to 12.1.2.0 Updates `com.microsoft.sqlserver:mssql-jdbc` from 12.10.0.jre11 to 13.1.0.jre11-preview - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) Updates `org.mariadb.jdbc:mariadb-java-client` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/main/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.5.3...3.5.4) Updates `org.postgresql:postgresql` from 42.7.5 to 42.7.7 - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.5...REL42.7.7) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: testcontainers - dependency-name: com.ibm.db2:jcc dependency-version: 12.1.2.0 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: testcontainers - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-version: 13.1.0.jre11-preview dependency-type: direct:production update-type: version-update:semver-major dependency-group: testcontainers - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: testcontainers - dependency-name: org.postgresql:postgresql dependency-version: 42.7.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: testcontainers ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdba4e88d..4b99dbb99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ assertjVersion = "3.27.3" hibernateOrmVersion = "7.0.2.Final" hibernateOrmGradlePluginVersion = "7.0.2.Final" -jacksonDatabindVersion = "2.15.2" +jacksonDatabindVersion = "2.19.1" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" junitVersion = "5.11.3" @@ -14,8 +14,8 @@ vertxWebClientVersion = "5.0.0" [libraries] com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } -com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.0.0" } -com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "12.10.0.jre11" } +com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.2.0" } +com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.0.jre11-preview" } com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.0" } @@ -42,8 +42,8 @@ org-jboss-logging-jboss-logging-annotations = { group = "org.jboss.logging", nam org-jboss-logging-jboss-logging-processor = { group = "org.jboss.logging", name = "jboss-logging-processor", version.ref = "jbossLoggingAnnotationVersion" } org-junit-jupiter-junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitVersion" } org-junit-jupiter-junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitVersion" } -org-mariadb-jdbc-mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version = "3.5.3" } -org-postgresql-postgresql = { group = "org.postgresql", name = "postgresql", version = "42.7.5" } +org-mariadb-jdbc-mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version = "3.5.4" } +org-postgresql-postgresql = { group = "org.postgresql", name = "postgresql", version = "42.7.7" } org-testcontainers-cockroachdb = { group = "org.testcontainers", name = "cockroachdb", version.ref = "testcontainersVersion" } org-testcontainers-db2 = { group = "org.testcontainers", name = "db2", version.ref = "testcontainersVersion" } org-testcontainers-mariadb = { group = "org.testcontainers", name = "mariadb", version.ref = "testcontainersVersion" } From 8ad0767c4039174bc2d5b55df0ac007e05805c82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 12:59:32 +0000 Subject: [PATCH 39/85] Bump org.apache.logging.log4j:log4j-core from 2.20.0 to 2.25.1 Bumps org.apache.logging.log4j:log4j-core from 2.20.0 to 2.25.1. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b99dbb99..af9789d6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ jacksonDatabindVersion = "2.19.1" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" junitVersion = "5.11.3" -log4jVersion = "2.20.0" +log4jVersion = "2.25.1" testcontainersVersion = "1.21.3" vertxSqlClientVersion = "5.0.0" vertxWebVersion= "5.0.0" From c604141589ffd327ae9e1f8fa5dee7da68f7f659 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:17:47 +0000 Subject: [PATCH 40/85] Bump cockroachdb/cockroach from v24.3.13 to v25.2.2 in /tooling/docker Bumps cockroachdb/cockroach from v24.3.13 to v25.2.2. --- updated-dependencies: - dependency-name: cockroachdb/cockroach dependency-version: v25.2.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tooling/docker/cockroachdb.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/cockroachdb.Dockerfile b/tooling/docker/cockroachdb.Dockerfile index a4710ad5b..5f92dd720 100644 --- a/tooling/docker/cockroachdb.Dockerfile +++ b/tooling/docker/cockroachdb.Dockerfile @@ -1,3 +1,3 @@ # CockroachDB # See https://hub.docker.com/r/cockroachdb/cockroach -FROM docker.io/cockroachdb/cockroach:v24.3.13 +FROM docker.io/cockroachdb/cockroach:v25.2.2 From d8e96e6fb90f1e7d4109402bb893d449434bfcee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:18:48 +0000 Subject: [PATCH 41/85] Bump db2_community/db2 from 12.1.0.0 to 12.1.2.0 in /tooling/docker Bumps db2_community/db2 from 12.1.0.0 to 12.1.2.0. --- updated-dependencies: - dependency-name: db2_community/db2 dependency-version: 12.1.2.0 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- tooling/docker/db2.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/db2.Dockerfile b/tooling/docker/db2.Dockerfile index 1ccb34906..b70ff8a80 100644 --- a/tooling/docker/db2.Dockerfile +++ b/tooling/docker/db2.Dockerfile @@ -1,3 +1,3 @@ # IBM DB2 # See https://hub.docker.com/r/ibmcom/db2 -FROM icr.io/db2_community/db2:12.1.0.0 +FROM icr.io/db2_community/db2:12.1.2.0 From 97335272d3d3c36887fe11e5f965d33165803676 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:34:30 +0000 Subject: [PATCH 42/85] Bump com.diffplug.spotless from 6.25.0 to 7.1.0 Bumps com.diffplug.spotless from 6.25.0 to 7.1.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index af9789d6b..9026adc79 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -53,7 +53,7 @@ org-testcontainers-oracle-xe = { group = "org.testcontainers", name = "oracle-xe org-testcontainers-postgresql = { group = "org.testcontainers", name = "postgresql", version.ref = "testcontainersVersion" } [plugins] -com-diffplug-spotless = { id = "com.diffplug.spotless", version = "6.25.0" } +com-diffplug-spotless = { id = "com.diffplug.spotless", version = "7.1.0" } io-github-gradle-nexus-publish-plugin = { id = "io.github.gradle-nexus.publish-plugin", version = "1.3.0" } org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.4" } org-hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernateOrmGradlePluginVersion" } From 7c171af301744c4836fbd554efcdddde634dd852 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 03:37:56 +0000 Subject: [PATCH 43/85] Bump the vertx group with 10 updates Bumps the vertx group with 10 updates: | Package | From | To | | --- | --- | --- | | [io.vertx:vertx-db2-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-junit5](https://github.com/eclipse-vertx/vertx-junit5) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-micrometer-metrics](https://github.com/vert-x3/vertx-micrometer-metrics) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-mssql-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-mysql-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-oracle-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-pg-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-sql-client](https://github.com/eclipse-vertx/vertx-sql-client) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-web](https://github.com/vert-x3/vertx-web) | `5.0.0` | `5.0.1` | | [io.vertx:vertx-web-client](https://github.com/vert-x3/vertx-web) | `5.0.0` | `5.0.1` | Updates `io.vertx:vertx-db2-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-junit5` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-junit5/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-micrometer-metrics` from 5.0.0 to 5.0.1 - [Commits](https://github.com/vert-x3/vertx-micrometer-metrics/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-mssql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-mysql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-oracle-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-pg-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-sql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-junit5` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-junit5/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-micrometer-metrics` from 5.0.0 to 5.0.1 - [Commits](https://github.com/vert-x3/vertx-micrometer-metrics/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-mssql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-mysql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-oracle-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-pg-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-sql-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/eclipse-vertx/vertx-sql-client/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-web` from 5.0.0 to 5.0.1 - [Commits](https://github.com/vert-x3/vertx-web/compare/5.0.0...5.0.1) Updates `io.vertx:vertx-web-client` from 5.0.0 to 5.0.1 - [Commits](https://github.com/vert-x3/vertx-web/compare/5.0.0...5.0.1) --- updated-dependencies: - dependency-name: io.vertx:vertx-db2-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-junit5 dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-micrometer-metrics dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-mssql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-mysql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-oracle-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-pg-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-sql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-junit5 dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-micrometer-metrics dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-mssql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-mysql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-oracle-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-pg-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-sql-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-web dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx - dependency-name: io.vertx:vertx-web-client dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vertx ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9026adc79..5ce004d52 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,9 +8,9 @@ jbossLoggingVersion = "3.6.1.Final" junitVersion = "5.11.3" log4jVersion = "2.25.1" testcontainersVersion = "1.21.3" -vertxSqlClientVersion = "5.0.0" -vertxWebVersion= "5.0.0" -vertxWebClientVersion = "5.0.0" +vertxSqlClientVersion = "5.0.1" +vertxWebVersion= "5.0.1" +vertxWebClientVersion = "5.0.1" [libraries] com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } From be3c66e3f1209bc18aeb1c17f7c067dbe9f4f9e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:34:42 +0000 Subject: [PATCH 44/85] Bump junitVersion from 5.11.3 to 5.13.3 Requires extra dependency org.junit.platform:junit-platform-launcher:1.13.3 --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 +++- hibernate-reactive-core/build.gradle | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5ce004d52..8c9b47036 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,8 @@ hibernateOrmGradlePluginVersion = "7.0.2.Final" jacksonDatabindVersion = "2.19.1" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" -junitVersion = "5.11.3" +junitVersion = "5.13.3" +junitPlatformVersion = "1.13.3" log4jVersion = "2.25.1" testcontainersVersion = "1.21.3" vertxSqlClientVersion = "5.0.1" @@ -42,6 +43,7 @@ org-jboss-logging-jboss-logging-annotations = { group = "org.jboss.logging", nam org-jboss-logging-jboss-logging-processor = { group = "org.jboss.logging", name = "jboss-logging-processor", version.ref = "jbossLoggingAnnotationVersion" } org-junit-jupiter-junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitVersion" } org-junit-jupiter-junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitVersion" } +org-junit-platform-junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version = "junitPlatformVersion" } org-mariadb-jdbc-mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version = "3.5.4" } org-postgresql-postgresql = { group = "org.postgresql", name = "postgresql", version = "42.7.7" } org-testcontainers-cockroachdb = { group = "org.testcontainers", name = "cockroachdb", version.ref = "testcontainersVersion" } diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 4407a4341..91ee72f89 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -44,6 +44,7 @@ dependencies { // JUnit Jupiter testImplementation(libs.org.junit.jupiter.junit.jupiter.api) testRuntimeOnly(libs.org.junit.jupiter.junit.jupiter.engine) + testRuntimeOnly(libs.org.junit.platform.junit.platform.launcher) // JDBC driver to test with ORM and PostgreSQL testRuntimeOnly(libs.org.postgresql.postgresql) From eabf863d8e6759ce94450a121e0547a16cc15fb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:36:28 +0000 Subject: [PATCH 45/85] Bump the hibernate group with 4 updates Bumps the hibernate group with 4 updates: [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jcache](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jpamodelgen](https://github.com/hibernate/hibernate-orm) and org.hibernate.orm. Updates `org.hibernate.orm:hibernate-core` from 7.0.2.Final to 7.0.6.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.6/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.2...7.0.6) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.2.Final to 7.0.6.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.6/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.2...7.0.6) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.2.Final to 7.0.6.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.6/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.2...7.0.6) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.2.Final to 7.0.6.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.6/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.2...7.0.6) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.2.Final to 7.0.6.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.6/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.2...7.0.6) Updates `org.hibernate.orm` from 7.0.2.Final to 7.0.6.Final --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm dependency-version: 7.0.6.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c9b47036..c9ad20e2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.0.2.Final" -hibernateOrmGradlePluginVersion = "7.0.2.Final" +hibernateOrmVersion = "7.0.6.Final" +hibernateOrmGradlePluginVersion = "7.0.6.Final" jacksonDatabindVersion = "2.19.1" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" From cfde55b60ab43cbb90b72abae91b6fdc70f44516 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 03:37:48 +0000 Subject: [PATCH 46/85] Bump io.smallrye.reactive:mutiny from 2.9.0 to 2.9.3 in the mutiny group Bumps the mutiny group with 1 update: [io.smallrye.reactive:mutiny](https://github.com/smallrye/smallrye-mutiny). Updates `io.smallrye.reactive:mutiny` from 2.9.0 to 2.9.3 - [Release notes](https://github.com/smallrye/smallrye-mutiny/releases) - [Commits](https://github.com/smallrye/smallrye-mutiny/compare/2.9.0...2.9.3) --- updated-dependencies: - dependency-name: io.smallrye.reactive:mutiny dependency-version: 2.9.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: mutiny ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9ad20e2d..27ea34bf6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.2.0" } com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.0.jre11-preview" } com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } -io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.0" } +io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.3" } io-vertx-vertx-db2-client = { group = "io.vertx", name = "vertx-db2-client", version.ref = "vertxSqlClientVersion" } io-vertx-vertx-junit5 = { group = "io.vertx", name = "vertx-junit5", version.ref = "vertxSqlClientVersion" } io-vertx-vertx-micrometer-metrics = { group = "io.vertx", name = "vertx-micrometer-metrics", version.ref = "vertxSqlClientVersion" } From ebedd9fcdfdfbe449337cb9c5ade82dc0406cec7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 06:18:18 +0000 Subject: [PATCH 47/85] Bump mariadb from 11.7.2 to 11.8.2 in /tooling/docker Bumps mariadb from 11.7.2 to 11.8.2. --- updated-dependencies: - dependency-name: mariadb dependency-version: 11.8.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- tooling/docker/maria.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/maria.Dockerfile b/tooling/docker/maria.Dockerfile index 4ccb397c8..fc954df6e 100644 --- a/tooling/docker/maria.Dockerfile +++ b/tooling/docker/maria.Dockerfile @@ -1,3 +1,3 @@ # MariaDB # See https://hub.docker.com/_/mariadb -FROM docker.io/mariadb:11.7.2 +FROM docker.io/mariadb:11.8.2 From 14dc7d112d58f94ba041ed84aa5afd4988429da2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:38:54 +0000 Subject: [PATCH 48/85] Bump mssql/server from 2022-latest to 2025-latest in /tooling/docker Bumps mssql/server from 2022-latest to 2025-latest. --- updated-dependencies: - dependency-name: mssql/server dependency-version: 2025-latest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tooling/docker/sqlserver.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/sqlserver.Dockerfile b/tooling/docker/sqlserver.Dockerfile index b4fb34f2f..94fae1429 100644 --- a/tooling/docker/sqlserver.Dockerfile +++ b/tooling/docker/sqlserver.Dockerfile @@ -1,3 +1,3 @@ # Microsoft SQL Server # See https://hub.docker.com/_/microsoft-mssql-server -FROM mcr.microsoft.com/mssql/server:2022-latest +FROM mcr.microsoft.com/mssql/server:2025-latest From 203cb0644201c8f3edbea86c7eb8e291bdd92ba7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:45:45 +0000 Subject: [PATCH 49/85] [#2270] Change docker image to container-registry.oracle.com/mysql/community-server:9.3.0 from `docker.io/mysql:9.2.0` to `container-registry.oracle.com/mysql/community-server:9.3.0` I found the registry in the official MySQL documentation: https://dev.mysql.com/doc/refman/9.3/en/docker-mysql-getting-started.html#docker-download-image The one from docker.io doesn't seem to work with testcontainers. --- .github/workflows/build.yml | 3 +-- .../java/org/hibernate/reactive/containers/MySQLDatabase.java | 2 +- tooling/docker/mysql.Dockerfile | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd4205415..9e1f766dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,8 +52,7 @@ jobs: services: # Label used to access the service container mysql: - # Docker Hub image - image: mysql:9.2.0 + image: container-registry.oracle.com/mysql/community-server:9.3.0 env: MYSQL_ROOT_PASSWORD: hreact MYSQL_DATABASE: hreact diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java index f5cd8f7c7..5b5ad5d2c 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/MySQLDatabase.java @@ -87,7 +87,7 @@ class MySQLDatabase implements TestableDatabase { * TIP: To reuse the same containers across multiple runs, set `testcontainers.reuse.enable=true` in a file located * at `$HOME/.testcontainers.properties` (create the file if it does not exist). */ - public static final MySQLContainer mysql = new MySQLContainer<>( fromDockerfile( "mysql" ) ) + public static final MySQLContainer mysql = new MySQLContainer<>( fromDockerfile( "mysql" ).asCompatibleSubstituteFor( "mysql" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/tooling/docker/mysql.Dockerfile b/tooling/docker/mysql.Dockerfile index 966350a87..69b12ca62 100644 --- a/tooling/docker/mysql.Dockerfile +++ b/tooling/docker/mysql.Dockerfile @@ -1,3 +1,3 @@ # MySQL # See https://hub.docker.com/_/mysql -FROM docker.io/mysql:9.2.0 +FROM container-registry.oracle.com/mysql/community-server:9.3.0 From 0d836fec0c84f145603a4b2d8c070c87cd41cf0f Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 15 Jul 2025 10:14:05 +0200 Subject: [PATCH 50/85] [#2270] Update JBang templates MySQL docker image to `container-registry.oracle.com/mysql/community-server:9.3.0` --- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 34865636f..00d82a12b 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -72,7 +72,7 @@ public class {baseName} { } @ClassRule - public final static MySQLContainer database = new MySQLContainer<>( imageName( "docker.io", "mysql", "9.2.0" ) ); + public final static MySQLContainer database = new MySQLContainer<>( imageName( "container-registry.oracle.com", "mysql/community-server", "9.3.0" ).asCompatibleSubstituteFor( "mysql" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 8db2eedc9..87c8659a3 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -229,7 +229,7 @@ public String toString() { */ enum Database { POSTGRESQL( () -> new PostgreSQLContainer( "postgres:17.5" ) ), - MYSQL( () -> new MySQLContainer( "mysql:9.2.0" ) ), + MYSQL( () -> new MySQLContainer( "container-registry.oracle.com/mysql/community-server:9.3.0" ) ), DB2( () -> new Db2Container( "docker.io/icr.io/db2_community/db2:12.1.0.0" ).acceptLicense() ), MARIADB( () -> new MariaDBContainer( "mariadb:11.7.2" ) ), COCKROACHDB( () -> new CockroachContainer( "cockroachdb/cockroach:v24.3.13" ) ); From afc30861f08d755c32f172fe5a55a7f104474dce Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 15 Jul 2025 11:17:04 +0200 Subject: [PATCH 51/85] [#1136] Keep Hibernate Validator and expressively consistent Expressly 5 for Hibernate Validator 8 Expressly 6 for Hibernate Validator 9 I've decided to keep it simple and just ignore the upgrades for majors. I will update manually as needed for now, and think of something else if it becomes too much of a chore. We could remove the dependency to expressly by adding an extra configuration file for Hibernate Validator, but then I would have to explain why we need the extra configuraiton file somewhere in the examples. --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0d4bc0776..fbc73fcb5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,10 @@ updates: day: "tuesday" open-pull-requests-limit: 20 groups: + hibernate-validator: + patterns: + - "org.hibernate.validator*" + - "org.glassfish.expressly*" hibernate: patterns: - "org.hibernate*" @@ -46,6 +50,9 @@ updates: - "org.mariadb.jdbc*" ignore: + # For Hibernate Validator, we will need to update major version manually as needed (but we only use it in tests) + - dependency-name: "org.glassfish.expressly*" + update-types: ["version-update-:semver-major"] # Only patches for Hibernate ORM and Vert.x - dependency-name: "org.hibernate*" update-types: ["version-update:semver-major", "version-update:semver-minor"] From f9d4c84cf9b2bf1d22f955dc315ccb92f5a795ca Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 16 Jul 2025 09:46:50 +0200 Subject: [PATCH 52/85] [#2368] Remove gradle-nexus:publish-plugin from the catalog We don't use it anymore since the switch to JRelease. This was just a left over. --- build.gradle | 1 - ci/snapshot-publish.Jenkinsfile | 1 - gradle/libs.versions.toml | 1 - 3 files changed, 3 deletions(-) diff --git a/build.gradle b/build.gradle index 2615b335d..d6a172b6f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,6 @@ plugins { id 'maven-publish' alias(libs.plugins.com.diffplug.spotless) alias(libs.plugins.org.asciidoctor.jvm.convert) apply false - alias(libs.plugins.io.github.gradle.nexus.publish.plugin) } group = "org.hibernate.reactive" diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index e50eb4cac..7fb8f9444 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -40,7 +40,6 @@ pipeline { steps { script { withCredentials([ - // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh // TODO: Once we switch to maven-central publishing (from nexus2) we need to update credentialsId: // https://docs.gradle.org/current/samples/sample_publishing_credentials.html#:~:text=via%20environment%20variables usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'ORG_GRADLE_PROJECT_snapshotsPassword', usernameVariable: 'ORG_GRADLE_PROJECT_snapshotsUsername'), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27ea34bf6..172a9d5f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,6 +56,5 @@ org-testcontainers-postgresql = { group = "org.testcontainers", name = "postgres [plugins] com-diffplug-spotless = { id = "com.diffplug.spotless", version = "7.1.0" } -io-github-gradle-nexus-publish-plugin = { id = "io.github.gradle-nexus.publish-plugin", version = "1.3.0" } org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.4" } org-hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernateOrmGradlePluginVersion" } From 4b7fdad1ed30369aa1b85e0afc8bf9a6b9d1304a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 03:14:24 +0000 Subject: [PATCH 53/85] Bump com.fasterxml.jackson.core:jackson-databind --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: testcontainers ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 172a9d5f7..366174c80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ assertjVersion = "3.27.3" hibernateOrmVersion = "7.0.6.Final" hibernateOrmGradlePluginVersion = "7.0.6.Final" -jacksonDatabindVersion = "2.19.1" +jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" junitVersion = "5.13.3" From 7836e261908fe1399b44476256e589baa2ff8b8b Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 22 Jul 2025 16:47:05 +0200 Subject: [PATCH 54/85] [#2381] Fix chaining in ReactiveEmbeddableInitializerImpl#reactiveResolveInstance The code was using .thenAccept instead of .thenCompose --- .../embeddable/internal/ReactiveEmbeddableInitializerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java index 3df56721f..02b3a76d1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java @@ -85,7 +85,7 @@ public CompletionStage reactiveResolveInstance(EmbeddableInitializerData d data.setState( State.RESOLVED ); return extractRowState( (ReactiveEmbeddableInitializerData) data ) - .thenAccept( unused -> prepareCompositeInstance( (ReactiveEmbeddableInitializerData) data ) ); + .thenCompose( unused -> prepareCompositeInstance( (ReactiveEmbeddableInitializerData) data ) ); } private CompletionStage extractRowState(ReactiveEmbeddableInitializerData data) { From e2db93ae7693406c17ee173a44f1eff84d31ec26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 04:22:03 +0000 Subject: [PATCH 55/85] Bump mysql/community-server from 9.3.0 to 9.4.0 in /tooling/docker Bumps mysql/community-server from 9.3.0 to 9.4.0. --- updated-dependencies: - dependency-name: mysql/community-server dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- tooling/docker/mysql.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/mysql.Dockerfile b/tooling/docker/mysql.Dockerfile index 69b12ca62..897fbd5ee 100644 --- a/tooling/docker/mysql.Dockerfile +++ b/tooling/docker/mysql.Dockerfile @@ -1,3 +1,3 @@ # MySQL # See https://hub.docker.com/_/mysql -FROM container-registry.oracle.com/mysql/community-server:9.3.0 +FROM container-registry.oracle.com/mysql/community-server:9.4.0 From 8c0c8d4c0fc8ac50f19b89009d27b53893cbc19a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 05:27:38 +0000 Subject: [PATCH 56/85] Bump the hibernate group with 4 updates Bumps the hibernate group with 4 updates: [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jcache](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jpamodelgen](https://github.com/hibernate/hibernate-orm) and org.hibernate.orm. Updates `org.hibernate.orm:hibernate-core` from 7.0.6.Final to 7.0.8.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.8/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.6...7.0.8) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.6.Final to 7.0.8.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.8/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.6...7.0.8) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.6.Final to 7.0.8.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.8/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.6...7.0.8) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.6.Final to 7.0.8.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.8/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.6...7.0.8) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.6.Final to 7.0.8.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.8/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.6...7.0.8) Updates `org.hibernate.orm` from 7.0.6.Final to 7.0.8.Final --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm dependency-version: 7.0.8.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 366174c80..47dc9a7b9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.0.6.Final" -hibernateOrmGradlePluginVersion = "7.0.6.Final" +hibernateOrmVersion = "7.0.8.Final" +hibernateOrmGradlePluginVersion = "7.0.8.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" From ef0d95f5de5000c2870abad49816710f7a7bd0a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:48:35 +0000 Subject: [PATCH 57/85] Bump com.microsoft.sqlserver:mssql-jdbc in the testcontainers group Bumps the testcontainers group with 1 update: [com.microsoft.sqlserver:mssql-jdbc](https://github.com/Microsoft/mssql-jdbc). Updates `com.microsoft.sqlserver:mssql-jdbc` from 13.1.0.jre11-preview to 13.1.1.jre11-preview - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-version: 13.1.1.jre11-preview dependency-type: direct:production update-type: version-update:semver-patch dependency-group: testcontainers ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 47dc9a7b9..5d56a7a7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ vertxWebClientVersion = "5.0.1" [libraries] com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.2.0" } -com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.0.jre11-preview" } +com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.1.jre11-preview" } com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.3" } From 43d0738438131444b39d24d5e6da226cf37fc691 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 05:50:38 +0000 Subject: [PATCH 58/85] Bump junitVersion from 5.13.3 to 5.13.4 Bumps `junitVersion` from 5.13.3 to 5.13.4. Updates `org.junit.jupiter:junit-jupiter-api` from 5.13.3 to 5.13.4 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.3...r5.13.4) Updates `org.junit.jupiter:junit-jupiter-engine` from 5.13.3 to 5.13.4 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.3...r5.13.4) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d56a7a7e..a04713993 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ hibernateOrmGradlePluginVersion = "7.0.8.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" -junitVersion = "5.13.3" +junitVersion = "5.13.4" junitPlatformVersion = "1.13.3" log4jVersion = "2.25.1" testcontainersVersion = "1.21.3" From 55869ac731571efacdf98d4e41511a015c675559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:20:01 +0000 Subject: [PATCH 59/85] Bump io.smallrye.reactive:mutiny from 2.9.3 to 2.9.4 in the mutiny group Bumps the mutiny group with 1 update: [io.smallrye.reactive:mutiny](https://github.com/smallrye/smallrye-mutiny). Updates `io.smallrye.reactive:mutiny` from 2.9.3 to 2.9.4 - [Release notes](https://github.com/smallrye/smallrye-mutiny/releases) - [Commits](https://github.com/smallrye/smallrye-mutiny/compare/2.9.3...2.9.4) --- updated-dependencies: - dependency-name: io.smallrye.reactive:mutiny dependency-version: 2.9.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: mutiny ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a04713993..34f8bd3fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.2.0" } com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.1.jre11-preview" } com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } -io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.3" } +io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.4" } io-vertx-vertx-db2-client = { group = "io.vertx", name = "vertx-db2-client", version.ref = "vertxSqlClientVersion" } io-vertx-vertx-junit5 = { group = "io.vertx", name = "vertx-junit5", version.ref = "vertxSqlClientVersion" } io-vertx-vertx-micrometer-metrics = { group = "io.vertx", name = "vertx-micrometer-metrics", version.ref = "vertxSqlClientVersion" } From 189f857f3dd9f78f03b7493514b893207322ab81 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 30 Jul 2025 09:55:06 +0200 Subject: [PATCH 60/85] Fix db2 podman script in the documentation (podman.md) * Remove docker.io from the image source (it's wrong) * Upgrade the image to 12.1.2.0 --- podman.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/podman.md b/podman.md index bc4d96ba7..9b9a71af2 100644 --- a/podman.md +++ b/podman.md @@ -158,7 +158,7 @@ and schema to run the tests: podman run --rm -e LICENSE=accept --privileged=true --group-add keep-groups \ --name HibernateTestingDB2 -e DBNAME=hreact -e DB2INSTANCE=hreact \ -e DB2INST1_PASSWORD=hreact -e PERSISTENT_HOME=false -e ARCHIVE_LOGS=false \ - -e AUTOCONFIG=false -p 50000:50000 docker.io/icr.io/db2_community/db2:12.1.0.0 + -e AUTOCONFIG=false -p 50000:50000 icr.io/db2_community/db2:12.1.2.0 ``` When the database has started, you can run the tests on Db2 with: From e31e3592048f9e56fe425885f3a200ec95ee55c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 05:59:07 +0000 Subject: [PATCH 61/85] Bump org.junit.platform:junit-platform-launcher Bumps [org.junit.platform:junit-platform-launcher](https://github.com/junit-team/junit-framework) from junitPlatformVersion to 1.13.4. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/commits) --- updated-dependencies: - dependency-name: org.junit.platform:junit-platform-launcher dependency-version: 1.13.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34f8bd3fa..4bbb1abb7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,7 +43,7 @@ org-jboss-logging-jboss-logging-annotations = { group = "org.jboss.logging", nam org-jboss-logging-jboss-logging-processor = { group = "org.jboss.logging", name = "jboss-logging-processor", version.ref = "jbossLoggingAnnotationVersion" } org-junit-jupiter-junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitVersion" } org-junit-jupiter-junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junitVersion" } -org-junit-platform-junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version = "junitPlatformVersion" } +org-junit-platform-junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version = "1.13.4" } org-mariadb-jdbc-mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version = "3.5.4" } org-postgresql-postgresql = { group = "org.postgresql", name = "postgresql", version = "42.7.7" } org-testcontainers-cockroachdb = { group = "org.testcontainers", name = "cockroachdb", version.ref = "testcontainersVersion" } From c510fbe215bd105768c0881988366e36fca89f33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:33:34 +0000 Subject: [PATCH 62/85] Bump com.diffplug.spotless from 7.1.0 to 7.2.1 Bumps com.diffplug.spotless from 7.1.0 to 7.2.1. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-version: 7.2.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4bbb1abb7..6263356cf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,6 +55,6 @@ org-testcontainers-oracle-xe = { group = "org.testcontainers", name = "oracle-xe org-testcontainers-postgresql = { group = "org.testcontainers", name = "postgresql", version.ref = "testcontainersVersion" } [plugins] -com-diffplug-spotless = { id = "com.diffplug.spotless", version = "7.1.0" } +com-diffplug-spotless = { id = "com.diffplug.spotless", version = "7.2.1" } org-asciidoctor-jvm-convert = { id = "org.asciidoctor.jvm.convert", version = "4.0.4" } org-hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernateOrmGradlePluginVersion" } From 24e1159a2ba8e52de40058abfbc150d10241c2d6 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 31 Jul 2025 10:55:09 +0200 Subject: [PATCH 63/85] Schedule regular builds for wip/3.1 on CI --- .github/workflows/scheduler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scheduler.yml b/.github/workflows/scheduler.yml index 24299c8a7..476be2550 100644 --- a/.github/workflows/scheduler.yml +++ b/.github/workflows/scheduler.yml @@ -11,7 +11,7 @@ jobs: build-snapshots: strategy: matrix: - branch: [ 'wip/2.4', 'wip/3.0', 'wip/4.0' ] + branch: [ 'wip/2.4', 'wip/3.0', 'wip/3.1', 'wip/4.0' ] uses: ./.github/workflows/build.yml with: branch: ${{ matrix.branch }} From dbdfa8fc042806f43fe535db10e508de0a51ed21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 05:10:45 +0000 Subject: [PATCH 64/85] Bump cockroachdb/cockroach from v25.2.2 to v25.2.4 in /tooling/docker Bumps cockroachdb/cockroach from v25.2.2 to v25.2.4. --- updated-dependencies: - dependency-name: cockroachdb/cockroach dependency-version: v25.2.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tooling/docker/cockroachdb.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/docker/cockroachdb.Dockerfile b/tooling/docker/cockroachdb.Dockerfile index 5f92dd720..3783d7f28 100644 --- a/tooling/docker/cockroachdb.Dockerfile +++ b/tooling/docker/cockroachdb.Dockerfile @@ -1,3 +1,3 @@ # CockroachDB # See https://hub.docker.com/r/cockroachdb/cockroach -FROM docker.io/cockroachdb/cockroach:v25.2.2 +FROM docker.io/cockroachdb/cockroach:v25.2.4 From 75d1bcac470ccbe4ef3b783094ba9bb774b0960f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:28:32 +0000 Subject: [PATCH 65/85] Bump the hibernate group with 4 updates Bumps the hibernate group with 4 updates: [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jcache](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jpamodelgen](https://github.com/hibernate/hibernate-orm) and org.hibernate.orm. Updates `org.hibernate.orm:hibernate-core` from 7.0.8.Final to 7.0.9.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.9/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.8...7.0.9) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.8.Final to 7.0.9.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.9/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.8...7.0.9) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.8.Final to 7.0.9.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.9/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.8...7.0.9) Updates `org.hibernate.orm:hibernate-jcache` from 7.0.8.Final to 7.0.9.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.9/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.8...7.0.9) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 7.0.8.Final to 7.0.9.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.0.9/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/7.0.8...7.0.9) Updates `org.hibernate.orm` from 7.0.8.Final to 7.0.9.Final --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm dependency-version: 7.0.9.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6263356cf..7ec93023d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.0.8.Final" -hibernateOrmGradlePluginVersion = "7.0.8.Final" +hibernateOrmVersion = "7.0.9.Final" +hibernateOrmGradlePluginVersion = "7.0.9.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" From 154be85cc7d2d305cba319205bc1bdce242140ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:25:35 +0000 Subject: [PATCH 66/85] Bump com.mysql:mysql-connector-j in the testcontainers group Bumps the testcontainers group with 1 update: [com.mysql:mysql-connector-j](https://github.com/mysql/mysql-connector-j). Updates `com.mysql:mysql-connector-j` from 9.3.0 to 9.4.0 - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/9.x/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/9.3.0...9.4.0) --- updated-dependencies: - dependency-name: com.mysql:mysql-connector-j dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: testcontainers ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ec93023d..d59faf7a0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ vertxWebClientVersion = "5.0.1" com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } com-ibm-db2-jcc = { group = "com.ibm.db2", name = "jcc", version = "12.1.2.0" } com-microsoft-sqlserver-mssql-jdbc = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version = "13.1.1.jre11-preview" } -com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.3.0" } +com-mysql-mysql-connector-j = { group = "com.mysql", name = "mysql-connector-j", version = "9.4.0" } com-ongres-scram-scram-client = { group = "com.ongres.scram", name = "scram-client", version = "3.1" } io-smallrye-reactive-mutiny = { group = "io.smallrye.reactive", name = "mutiny", version = "2.9.4" } io-vertx-vertx-db2-client = { group = "io.vertx", name = "vertx-db2-client", version.ref = "vertxSqlClientVersion" } From b0be42ded3d10f6c48575f79f1e09f92dd24b3ed Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 24 Jul 2025 19:10:15 +0200 Subject: [PATCH 67/85] [#1990] Consider moving away from subprojects block for subproject configuration --- build.gradle | 111 ---------------- documentation/build.gradle | 1 + examples/native-sql-example/build.gradle | 36 +----- examples/session-example/build.gradle | 35 +----- hibernate-reactive-core/build.gradle | 118 ++---------------- .../bytecode-enhancements-it/build.gradle | 72 +---------- .../build.gradle | 72 +---------- .../techempower-postgres-it/build.gradle | 55 ++------ .../verticle-postgres-it/build.gradle | 74 +---------- local-build-plugins/build.gradle | 1 + .../src/main/groovy/hr-java-library.gradle | 110 ++++++++++++++++ .../groovy/hr-print-resolved-version.gradle | 31 +++++ .../src/main/groovy/hr-test-containers.gradle | 79 ++++++++++++ release/build.gradle | 3 + 14 files changed, 259 insertions(+), 539 deletions(-) create mode 100644 local-build-plugins/src/main/groovy/hr-java-library.gradle create mode 100644 local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle create mode 100644 local-build-plugins/src/main/groovy/hr-test-containers.gradle diff --git a/build.gradle b/build.gradle index d6a172b6f..7a45d616b 100644 --- a/build.gradle +++ b/build.gradle @@ -11,117 +11,6 @@ group = "org.hibernate.reactive" // leverage the ProjectVersion which comes from the `local.versions` plugin version = project.projectVersion.fullName -subprojects { - apply plugin: 'java-library' - apply plugin: 'com.diffplug.spotless' - - group = rootProject.group - version = rootProject.version - - spotless { - //Don't fail during the check: rather than enforcing guidelines, we use this plugin to fix mistakes automatically. - enforceCheck false - java { - licenseHeaderFile rootProject.file('spotless.license.java') - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() - } - } - - tasks.compileJava.dependsOn(spotlessApply) - - repositories { - // Example: ./gradlew build -PenableMavenLocalRepo - if ( project.hasProperty('enableMavenLocalRepo') ) { - // Useful for local development, it should be disabled otherwise - mavenLocal() - } - // Example: ./gradlew build -PenableCentralSonatypeSnapshotsRep - if ( project.hasProperty('enableCentralSonatypeSnapshotsRep') ) { - maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } - } - - mavenCentral() - } - - ext.publishScript = rootProject.rootDir.absolutePath + '/publish.gradle' - - tasks.withType( JavaCompile ).configureEach { - options.encoding = 'UTF-8' - } - - // Configure test tasks for all subprojects - tasks.withType( Test ).configureEach { - // Set the project root for finding Docker files - available to all modules - systemProperty 'hibernate.reactive.project.root', rootProject.projectDir.absolutePath - } - - if ( !gradle.ext.javaToolchainEnabled ) { - sourceCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) - targetCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) - } - else { - // Configure generated bytecode - // "sourceCompatibility" is not supported with toolchains. We have to work around that limitation. - tasks.compileJava.configure { - options.release = gradle.ext.javaVersions.main.release.asInt() - } - tasks.compileTestJava.configure { - options.release = gradle.ext.javaVersions.test.release.asInt() - } - - // Configure version of Java tools - java { - toolchain { - languageVersion = gradle.ext.javaVersions.main.compiler - } - } - tasks.compileTestJava { - javaCompiler = javaToolchains.compilerFor { - languageVersion = gradle.ext.javaVersions.test.compiler - } - } - tasks.test { - javaLauncher = javaToolchains.launcherFor { - languageVersion = gradle.ext.javaVersions.test.launcher - } - } - - // Configure JVM Options - tasks.withType( JavaCompile ).configureEach { - options.forkOptions.jvmArgs.addAll( getProperty( 'toolchain.compiler.jvmargs' ).toString().split( ' ' ) ) - } - tasks.withType( Javadoc ).configureEach { - options.setJFlags( getProperty( 'toolchain.javadoc.jvmargs' ).toString().split( ' ' ).toList().findAll( { !it.isEmpty() } ) ) - } - tasks.test { - // Configure JVM Options - jvmArgs(getProperty('toolchain.launcher.jvmargs').toString().split(' ')) - if ( project.hasProperty( 'test.jdk.launcher.args' ) ) { - jvmArgs( project.getProperty( 'test.jdk.launcher.args' ).toString().split( ' ' ) ) - } - } - - // Display version of Java tools - tasks.withType( JavaCompile ).configureEach { - doFirst { - logger.lifecycle "Compiling with '${javaCompiler.get().metadata.installationPath}'" - } - } - tasks.withType( Javadoc ).configureEach { - doFirst { - logger.lifecycle "Generating javadoc with '${javadocTool.get().metadata.installationPath}'" - } - } - tasks.test { - doFirst { - logger.lifecycle "Testing with '${javaLauncher.get().metadata.installationPath}'" - } - } - } -} - rootProject.afterEvaluate { // Workaround since "libs.versions.NAME" notation cannot be used here def libs = project.extensions.getByType(VersionCatalogsExtension).named('libs') diff --git a/documentation/build.gradle b/documentation/build.gradle index 95c3dcb2b..01c67f967 100644 --- a/documentation/build.gradle +++ b/documentation/build.gradle @@ -4,6 +4,7 @@ import java.time.Year plugins { id "org.asciidoctor.jvm.convert" + id "hr-java-library" } ext { diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index 7c3b1d875..c1239deeb 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -17,6 +17,9 @@ buildscript { } plugins { + id "hr-java-library" + id "hr-print-resolved-version" + // Optional: Hibernate Gradle plugin to enable bytecode enhancements alias(libs.plugins.org.hibernate.orm) } @@ -75,36 +78,3 @@ tasks.register( "runAllExamples" ) { dependsOn = ["runAllExamplesOnPostgreSQL"] description = "Run all examples on ${dbs}" } - -// Optional: Task to print the resolved versions of Hibernate ORM and Vert.x -tasks.register( "printResolvedVersions" ) { - description = "Print the resolved hibernate-orm-core and vert.x versions" - doLast { - def hibernateCoreVersion = "n/a" - def vertxVersion = "n/a" - - // Resolve Hibernate Core and Vert.x versions from compile classpath - configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> - if (artifact.moduleVersion.id.name == 'hibernate-core') { - hibernateCoreVersion = artifact.moduleVersion.id.version - } - if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { - vertxVersion = artifact.moduleVersion.id.version - } - } - - // Print the resolved versions - println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" - println "Resolved Vert.x SQL client Version: ${vertxVersion}" - } -} - -// Make the version printing task run before tests and JavaExec tasks -tasks.withType( Test ).configureEach { - dependsOn printResolvedVersions -} - -tasks.withType( JavaExec ).configureEach { - dependsOn printResolvedVersions -} - diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index 2fb7837b6..795fc3fb0 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -17,6 +17,9 @@ buildscript { } plugins { + id "hr-java-library" + id "hr-print-resolved-version" + // Optional: Hibernate Gradle plugin to enable bytecode enhancements alias(libs.plugins.org.hibernate.orm) } @@ -81,35 +84,3 @@ tasks.register( "runAllExamples" ) { dependsOn = ["runAllExamplesOnPostgreSQL", "runAllExamplesOnMySQL"] description = "Run all examples on ${dbs}" } - -// Optional: Task to print the resolved versions of Hibernate ORM and Vert.x -tasks.register( "printResolvedVersions" ) { - description = "Print the resolved hibernate-orm-core and vert.x versions" - doLast { - def hibernateCoreVersion = "n/a" - def vertxVersion = "n/a" - - // Resolve Hibernate Core and Vert.x versions from compile classpath - configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> - if (artifact.moduleVersion.id.name == 'hibernate-core') { - hibernateCoreVersion = artifact.moduleVersion.id.version - } - if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { - vertxVersion = artifact.moduleVersion.id.version - } - } - - // Print the resolved versions - println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" - println "Resolved Vert.x SQL client Version: ${vertxVersion}" - } -} - -// Make the version printing task run before tests and JavaExec tasks -tasks.withType( Test ).configureEach { - dependsOn printResolvedVersions -} - -tasks.withType( JavaExec ).configureEach { - dependsOn printResolvedVersions -} diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 91ee72f89..97c1b8bd8 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -1,5 +1,11 @@ +plugins { + id "hr-java-library" + id "hr-test-containers" + id "hr-print-resolved-version" +} + ext { - mavenPomName = 'Hibernate Reactive Core' + mavenPomName = 'Hibernate Reactive Core' } description = 'The core module of Hibernate Reactive' @@ -90,113 +96,3 @@ tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true } - -// Print a summary of the results of the tests (number of failures, successes and skipped) -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -// Example: -// gradle test -Pdb=MySQL -test { - def selectedDb = project.hasProperty( 'db' ) - ? project.properties['db'] - : 'PostgreSQL' - doFirst { - systemProperty 'db', selectedDb - } - afterSuite { desc, result -> - loggingSummary( selectedDb, result, desc ) - } -} - -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - showStandardStreams = project.hasProperty('showStandardOutput') - showStackTraces = true - exceptionFormat = 'full' - displayGranularity = 1 - events = ['PASSED', 'FAILED', 'SKIPPED'] - } - systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false' - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' - - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.properties['includeTests'] ?: '*' as String - } - } -} - -def createTestDbTask(dbName) { - tasks.register( "testDb${dbName}", Test ) { - description = "Run tests for ${dbName}" - - doFirst() { - systemProperty 'db', dbName - } - afterSuite { desc, result -> - loggingSummary( dbName, result, desc ) - } - } -} - -// Rule to recognize calls to testDb -// and run the tests on the selected db -// Example: -// gradle testDbMySQL testDbDB2 -tasks.addRule( "Pattern testDb" ) { String taskName -> - if ( taskName.startsWith( "testDb" ) ) { - def dbName = taskName.substring( "testDb".length() ) - createTestDbTask( dbName ) - } -} - -// The dbs we want to test when running testAll -def dbs = ['MariaDB', 'MySQL', 'PostgreSQL', 'DB2', 'CockroachDB', 'MSSQLServer', 'Oracle'] -dbs.forEach { createTestDbTask it } - -tasks.register( "testAll", Test ) { - description = "Run tests for ${dbs}" - dependsOn = dbs.collect( [] as HashSet ) { db -> "testDb${db}" } -} - -// Task to print the resolved versions of Hibernate ORM and Vert.x -tasks.register( "printResolvedVersions" ) { - description = "Print the resolved hibernate-orm-core and vert.x versions" - doLast { - def hibernateCoreVersion = "n/a" - def vertxVersion = "n/a" - - // Resolve Hibernate Core and Vert.x versions from compile classpath - configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> - if (artifact.moduleVersion.id.name == 'hibernate-core') { - hibernateCoreVersion = artifact.moduleVersion.id.version - } - if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { - vertxVersion = artifact.moduleVersion.id.version - } - } - - // Print the resolved versions - println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" - println "Resolved Vert.x SQL client Version: ${vertxVersion}" - } -} - -// Make the version printing task run before tests and JavaExec tasks -tasks.withType( Test ).configureEach { - dependsOn printResolvedVersions -} - -tasks.withType( JavaExec ).configureEach { - dependsOn printResolvedVersions -} diff --git a/integration-tests/bytecode-enhancements-it/build.gradle b/integration-tests/bytecode-enhancements-it/build.gradle index a796557dd..74ee2d563 100644 --- a/integration-tests/bytecode-enhancements-it/build.gradle +++ b/integration-tests/bytecode-enhancements-it/build.gradle @@ -10,6 +10,9 @@ buildscript { } plugins { + id "hr-java-library" + id "hr-test-containers" + alias(libs.plugins.org.hibernate.orm) } @@ -40,72 +43,3 @@ dependencies { // Optional: enable the bytecode enhancements hibernate { enhancement } - -// Print a summary of the results of the tests (number of failures, successes and skipped) -// This is the same as the one in hibernate-reactive-core -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -// Example: -// gradle test -Pdb=MySQL -test { - def selectedDb = project.hasProperty( 'db' ) - ? project.properties['db'] - : 'PostgreSQL' - doFirst { - systemProperty 'db', selectedDb - } - afterSuite { desc, result -> - loggingSummary( selectedDb, result, desc ) - } -} - -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - displayGranularity 1 - showStandardStreams = project.hasProperty('showStandardOutput') - showStackTraces = true - exceptionFormat = 'full' - events 'PASSED', 'FAILED', 'SKIPPED' - } - systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false' - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' - - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.properties['includeTests'] ?: '*' as String - } - } -} - -// Rule to recognize calls to testDb -// and run the tests on the selected db -// Example: -// gradle testDbMySQL testDbDB2 -tasks.addRule( "Pattern testDb" ) { String taskName -> - if ( taskName.startsWith( "testDb" ) ) { - tasks.register( taskName, Test ) { - def dbName = taskName.substring( "testDb".length() ) - description = "Run tests for ${dbName}" - - // We only want to test this on Postgres - onlyIf { dbName.toLowerCase().startsWith( 'p' ) } - doFirst() { - systemProperty 'db', dbName - } - afterSuite { desc, result -> - loggingSummary( dbName, result, desc ) - } - - } - } -} \ No newline at end of file diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index 38165df4d..828228394 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -10,6 +10,9 @@ buildscript { } plugins { + id "hr-java-library" + id "hr-test-containers" + alias(libs.plugins.org.hibernate.orm) } @@ -43,72 +46,3 @@ dependencies { // Optional: enable the bytecode enhancements //hibernate { enhancement } -// Print a summary of the results of the tests (number of failures, successes and skipped) -// This is the same as the one in hibernate-reactive-core -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -// Example: -// gradle test -Pdb=MySQL -test { - def selectedDb = project.hasProperty( 'db' ) - ? project.properties['db'] - : 'PostgreSQL' - doFirst { - systemProperty 'db', selectedDb - } - afterSuite { desc, result -> - loggingSummary( selectedDb, result, desc ) - } -} - -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - displayGranularity 1 - showStandardStreams = project.hasProperty('showStandardOutput') - showStackTraces = true - exceptionFormat = 'full' - events 'PASSED', 'FAILED', 'SKIPPED' - } - systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false' - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' - - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.properties['includeTests'] ?: '*' as String - } - } -} - - -// Rule to recognize calls to testDb -// and run the tests on the selected db -// Example: -// gradle testDbMySQL testDbDB2 -tasks.addRule( "Pattern testDb" ) { String taskName -> - if ( taskName.startsWith( "testDb" ) ) { - tasks.register( taskName, Test ) { - def dbName = taskName.substring( "testDb".length() ) - description = "Run tests for ${dbName}" - - // We only want to test this on Postgres - onlyIf { dbName.toLowerCase().startsWith( 'p' ) } - doFirst() { - systemProperty 'db', dbName - } - afterSuite { desc, result -> - loggingSummary( dbName, result, desc ) - } - - } - } -} \ No newline at end of file diff --git a/integration-tests/techempower-postgres-it/build.gradle b/integration-tests/techempower-postgres-it/build.gradle index b5f74f4d2..b44b3f7a6 100644 --- a/integration-tests/techempower-postgres-it/build.gradle +++ b/integration-tests/techempower-postgres-it/build.gradle @@ -9,6 +9,16 @@ buildscript { } } +plugins { + id "hr-java-library" + id "hr-test-containers" +} + +test { + def db = project.properties['db'] + enabled = db == null || db.equals( 'PostgreSQL' ) +} + description = 'TechEmpower integration tests' dependencies { @@ -32,51 +42,6 @@ dependencies { testImplementation(libs.io.vertx.vertx.junit5) } -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - showStandardStreams = project.hasProperty( 'showStandardOutput' ) - showStackTraces = true - exceptionFormat = 'full' - displayGranularity = 1 - events = ['PASSED', 'FAILED', 'SKIPPED'] - } - - // We need a to use an instance of PostgreSQL with a specific configuration. - // So, for this particular integration-test module, we default to true unless docker is disabled. - systemProperty 'docker', project.hasProperty('docker') - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' - - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.properties['includeTests'] ?: '*' as String - } - } -} - -// Print a summary of the results of the tests (number of failures, successes and skipped) -// This is the same as the one in hibernate-reactive-core -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -test { - doFirst { - systemProperty 'db', 'PostgreSQL' - } - // We only want to test this on Postgres - afterSuite { desc, result -> - loggingSummary( 'PostgreSQL', result, desc ) - } -} - def javaMainClass = "org.hibernate.reactive.it.techempower.VertxServer" jar { dependsOn = [':hibernate-reactive-core:jar'] diff --git a/integration-tests/verticle-postgres-it/build.gradle b/integration-tests/verticle-postgres-it/build.gradle index 7d2322306..ea2f8e157 100644 --- a/integration-tests/verticle-postgres-it/build.gradle +++ b/integration-tests/verticle-postgres-it/build.gradle @@ -9,6 +9,11 @@ buildscript { } } +plugins { + id 'hr-java-library' + id 'hr-test-containers' +} + description = 'Bytecode enhancements integration tests' dependencies { @@ -32,75 +37,6 @@ dependencies { testImplementation(libs.io.vertx.vertx.junit5) } -// Configuration for the tests -tasks.withType( Test ).configureEach { - defaultCharacterEncoding = "UTF-8" - useJUnitPlatform() - testLogging { - showStandardStreams = project.hasProperty( 'showStandardOutput' ) - showStackTraces = true - exceptionFormat = 'full' - displayGranularity = 1 - events = ['PASSED', 'FAILED', 'SKIPPED'] - } - systemProperty 'docker', project.hasProperty( 'docker' ) - systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' - - if ( project.hasProperty( 'includeTests' ) ) { - // Example: ./gradlew testAll -PincludeTests=DefaultPortTest - filter { - includeTestsMatching project.getProperty( 'includeTests' ) ?: '*' - } - } -} - -// Print a summary of the results of the tests (number of failures, successes and skipped) -// This is the same as the one in hibernate-reactive-core -def loggingSummary(db, result, desc) { - if ( !desc.parent ) { // will match the outermost suite - def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" - def repeatLength = output.length() + 1 - logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) - } -} - -// Example: -// gradle test -Pdb=MySQL -test { - def selectedDb = project.hasProperty( 'db' ) - ? project.properties['db'] - : 'PostgreSQL' - doFirst { - systemProperty 'db', selectedDb - } - afterSuite { desc, result -> - loggingSummary( selectedDb, result, desc ) - } -} - -// Rule to recognize calls to testDb -// and run the tests on the selected db -// Example: -// gradle testDbMySQL testDbDB2 -tasks.addRule( "Pattern testDb" ) { String taskName -> - if ( taskName.startsWith( "testDb" ) ) { - tasks.register( taskName, Test ) { - def dbName = taskName.substring( "testDb".length() ) - description = "Run tests for ${dbName}" - - // We only want to test this on Postgres - onlyIf { dbName.toLowerCase().startsWith( 'p' ) } - doFirst() { - systemProperty 'db', dbName - } - afterSuite { desc, result -> - loggingSummary( dbName, result, desc ) - } - - } - } -} - tasks.register( "startVertx", JavaExec ) { description = "Starts the Vert.x Server app" classpath = sourceSets.main.runtimeClasspath diff --git a/local-build-plugins/build.gradle b/local-build-plugins/build.gradle index 7cc4db2f9..2389343d6 100644 --- a/local-build-plugins/build.gradle +++ b/local-build-plugins/build.gradle @@ -1,5 +1,6 @@ plugins { id "java-gradle-plugin" + id 'groovy-gradle-plugin' } repositories { diff --git a/local-build-plugins/src/main/groovy/hr-java-library.gradle b/local-build-plugins/src/main/groovy/hr-java-library.gradle new file mode 100644 index 000000000..4f372fe21 --- /dev/null +++ b/local-build-plugins/src/main/groovy/hr-java-library.gradle @@ -0,0 +1,110 @@ +plugins{ + id 'java-library' + id 'com.diffplug.spotless' +} + +group = rootProject.group +version = rootProject.version + +spotless { + //Don't fail during the check: rather than enforcing guidelines, we use this plugin to fix mistakes automatically. + enforceCheck false + java { + licenseHeaderFile rootProject.file('spotless.license.java') + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } +} + +tasks.compileJava.dependsOn(spotlessApply) + +repositories { + // Example: ./gradlew build -PenableMavenLocalRepo + if ( project.hasProperty('enableMavenLocalRepo') ) { + // Useful for local development, it should be disabled otherwise + mavenLocal() + } + // Example: ./gradlew build -PenableCentralSonatypeSnapshotsRep + if ( project.hasProperty('enableCentralSonatypeSnapshotsRep') ) { + maven { url 'https://central.sonatype.com/repository/maven-snapshots/' } + } + + mavenCentral() +} + +ext.publishScript = rootProject.rootDir.absolutePath + '/publish.gradle' + +tasks.withType( JavaCompile ).configureEach { + options.encoding = 'UTF-8' +} + +// Configure test tasks for all subprojects +tasks.withType( Test ).configureEach { + // Set the project root for finding Docker files - available to all modules + systemProperty 'hibernate.reactive.project.root', rootProject.projectDir.absolutePath +} + +if ( !gradle.ext.javaToolchainEnabled ) { + sourceCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) + targetCompatibility = JavaVersion.toVersion( gradle.ext.baselineJavaVersion ) +} +else { + // Configure generated bytecode + // "sourceCompatibility" is not supported with toolchains. We have to work around that limitation. + tasks.compileJava.configure { + options.release = gradle.ext.javaVersions.main.release.asInt() + } + tasks.compileTestJava.configure { + options.release = gradle.ext.javaVersions.test.release.asInt() + } + + // Configure version of Java tools + java { + toolchain { + languageVersion = gradle.ext.javaVersions.main.compiler + } + } + tasks.compileTestJava { + javaCompiler = javaToolchains.compilerFor { + languageVersion = gradle.ext.javaVersions.test.compiler + } + } + tasks.test { + javaLauncher = javaToolchains.launcherFor { + languageVersion = gradle.ext.javaVersions.test.launcher + } + } + + // Configure JVM Options + tasks.withType( JavaCompile ).configureEach { + options.forkOptions.jvmArgs.addAll( getProperty( 'toolchain.compiler.jvmargs' ).toString().split( ' ' ) ) + } + tasks.withType( Javadoc ).configureEach { + options.setJFlags( getProperty( 'toolchain.javadoc.jvmargs' ).toString().split( ' ' ).toList().findAll( { !it.isEmpty() } ) ) + } + tasks.test { + // Configure JVM Options + jvmArgs(getProperty('toolchain.launcher.jvmargs').toString().split(' ')) + if ( project.hasProperty( 'test.jdk.launcher.args' ) ) { + jvmArgs( project.getProperty( 'test.jdk.launcher.args' ).toString().split( ' ' ) ) + } + } + + // Display version of Java tools + tasks.withType( JavaCompile ).configureEach { + doFirst { + logger.lifecycle "Compiling with '${javaCompiler.get().metadata.installationPath}'" + } + } + tasks.withType( Javadoc ).configureEach { + doFirst { + logger.lifecycle "Generating javadoc with '${javadocTool.get().metadata.installationPath}'" + } + } + tasks.test { + doFirst { + logger.lifecycle "Testing with '${javaLauncher.get().metadata.installationPath}'" + } + } +} diff --git a/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle b/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle new file mode 100644 index 000000000..9dea09b82 --- /dev/null +++ b/local-build-plugins/src/main/groovy/hr-print-resolved-version.gradle @@ -0,0 +1,31 @@ +// Task to print the resolved versions of Hibernate ORM and Vert.x +tasks.register( "printResolvedVersions" ) { + description = "Print the resolved hibernate-orm-core and vert.x versions" + doLast { + def hibernateCoreVersion = "n/a" + def vertxVersion = "n/a" + + // Resolve Hibernate Core and Vert.x versions from compile classpath + configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact -> + if (artifact.moduleVersion.id.name == 'hibernate-core') { + hibernateCoreVersion = artifact.moduleVersion.id.version + } + if (artifact.moduleVersion.id.group == 'io.vertx' && artifact.moduleVersion.id.name == 'vertx-sql-client') { + vertxVersion = artifact.moduleVersion.id.version + } + } + + // Print the resolved versions + println "Resolved Hibernate ORM Core Version: ${hibernateCoreVersion}" + println "Resolved Vert.x SQL client Version: ${vertxVersion}" + } +} + +// Make the version printing task run before tests and JavaExec tasks +tasks.withType( Test ).configureEach { + dependsOn printResolvedVersions +} + +tasks.withType( JavaExec ).configureEach { + dependsOn printResolvedVersions +} \ No newline at end of file diff --git a/local-build-plugins/src/main/groovy/hr-test-containers.gradle b/local-build-plugins/src/main/groovy/hr-test-containers.gradle new file mode 100644 index 000000000..fad66398f --- /dev/null +++ b/local-build-plugins/src/main/groovy/hr-test-containers.gradle @@ -0,0 +1,79 @@ +// Print a summary of the results of the tests (number of failures, successes and skipped) +def loggingSummary(db, result, desc) { + if ( !desc.parent ) { // will match the outermost suite + def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)" + def repeatLength = output.length() + 1 + logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength) + } +} + +// Example: +// gradle test -Pdb=MySQL +test { + def selectedDb + + doFirst { + selectedDb = project.hasProperty( 'db' ) + ? project.properties['db'] + : 'PostgreSQL' + systemProperty 'db', selectedDb + } + afterSuite { desc, result -> + loggingSummary( selectedDb, result, desc ) + } +} + +// Configuration for the tests +tasks.withType( Test ).configureEach { + defaultCharacterEncoding = "UTF-8" + useJUnitPlatform() + testLogging { + showStandardStreams = project.hasProperty('showStandardOutput') + showStackTraces = true + exceptionFormat = 'full' + displayGranularity = 1 + events = ['PASSED', 'FAILED', 'SKIPPED'] + } + systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false' + systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true' + + if ( project.hasProperty( 'includeTests' ) ) { + // Example: ./gradlew testAll -PincludeTests=DefaultPortTest + filter { + includeTestsMatching project.properties['includeTests'] ?: '*' as String + } + } +} + +def createTestDbTask(dbName) { + tasks.register( "testDb${dbName}", Test ) { + description = "Run tests for ${dbName}" + + doFirst() { + systemProperty 'db', dbName + } + afterSuite { desc, result -> + loggingSummary( dbName, result, desc ) + } + } +} + +// Rule to recognize calls to testDb +// and run the tests on the selected db +// Example: +// gradle testDbMySQL testDbDB2 +tasks.addRule( "Pattern testDb" ) { String taskName -> + if ( taskName.startsWith( "testDb" ) ) { + def dbName = taskName.substring( "testDb".length() ) + createTestDbTask( dbName ) + } +} + +// The dbs we want to test when running testAll +def dbs = ['MariaDB', 'MySQL', 'PostgreSQL', 'DB2', 'CockroachDB', 'MSSQLServer', 'Oracle'] +dbs.forEach { createTestDbTask it } + +tasks.register( "testAll", Test ) { + description = "Run tests for ${dbs}" + dependsOn = dbs.collect( [] as HashSet ) { db -> "testDb${db}" } +} diff --git a/release/build.gradle b/release/build.gradle index 690534750..4e5321f9a 100644 --- a/release/build.gradle +++ b/release/build.gradle @@ -1,5 +1,8 @@ import java.nio.charset.StandardCharsets +plugins { + id "hr-java-library" +} ext { // Select which repository to use for publishing the documentation // Example: From 3fff5b081f59860e059ac5b9dc0649436ca79bd8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 28 Jul 2025 19:16:08 +0200 Subject: [PATCH 68/85] Run validator-postgres and verticle-postgres integration tests only if projetc property is not set or is equal to PostgreSQL --- .../hibernate-validator-postgres-it/build.gradle | 5 +++++ integration-tests/verticle-postgres-it/build.gradle | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index 828228394..aae2818e6 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -16,6 +16,11 @@ plugins { alias(libs.plugins.org.hibernate.orm) } +test { + def db = project.properties['db'] + enabled = db == null || db.equals( 'PostgreSQL' ) +} + description = 'Quarkus QE integration tests' dependencies { diff --git a/integration-tests/verticle-postgres-it/build.gradle b/integration-tests/verticle-postgres-it/build.gradle index ea2f8e157..f8622ab95 100644 --- a/integration-tests/verticle-postgres-it/build.gradle +++ b/integration-tests/verticle-postgres-it/build.gradle @@ -14,6 +14,11 @@ plugins { id 'hr-test-containers' } +test { + def db = project.properties['db'] + enabled = db == null || db.equals( 'PostgreSQL' ) +} + description = 'Bytecode enhancements integration tests' dependencies { From a7c22434ddbc9a9ed86000c8692e85b4d832c007 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 28 Jul 2025 18:58:22 +0200 Subject: [PATCH 69/85] Fix file path issue with VersionsTomlParser --- .../java/org/hibernate/reactive/env/VersionsPlugin.java | 2 +- .../java/org/hibernate/reactive/env/VersionsTomlParser.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java index c34a4b479..883317d4a 100644 --- a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsPlugin.java @@ -58,7 +58,7 @@ public void apply(Project project) { project.getLogger().lifecycle( "Development version: n/a" ); } - final VersionsTomlParser tomlParser = new VersionsTomlParser( RELATIVE_CATALOG ); + final VersionsTomlParser tomlParser = new VersionsTomlParser( project.getRootProject().file( RELATIVE_CATALOG ) ); final String ormVersionString = determineOrmVersion( project, tomlParser ); final Object ormVersion = resolveOrmVersion( ormVersionString, project ); project.getLogger().lifecycle( "ORM version: {}", ormVersion ); diff --git a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java index b0adafd6d..8833de6a6 100644 --- a/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java +++ b/local-build-plugins/src/main/java/org/hibernate/reactive/env/VersionsTomlParser.java @@ -1,6 +1,7 @@ package org.hibernate.reactive.env; import java.io.BufferedReader; +import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; @@ -13,11 +14,12 @@ public class VersionsTomlParser { private final Map data = new HashMap<>(); - public VersionsTomlParser(String filePath) { + public VersionsTomlParser(File filePath) { parse( filePath ); } - private void parse(String filePath) { + private void parse(File filePath) { + try ( BufferedReader reader = new BufferedReader( new FileReader( filePath ) ) ) { String line; String currentSection = null; From c1a30dd26717b2cbd8ba9add4740e7225ab0f7bc Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Jul 2025 15:23:12 +0200 Subject: [PATCH 70/85] [#2387] Upgrade Hibernate ORM to 7.1.0.CR1 --- README.md | 8 +- gradle.properties | 4 +- gradle/libs.versions.toml | 4 +- gradle/version.properties | 6 +- .../ReactiveInsertReturningDelegate.java | 2 +- .../impl/ReactiveAbstractEntityPersister.java | 27 +- ...ReactiveJoinedSubclassEntityPersister.java | 4 +- .../ReactiveSingleTableEntityPersister.java | 4 +- .../ReactiveUnionSubclassEntityPersister.java | 4 +- .../impl/ReactiveServiceInitiators.java | 4 + .../sql/internal/ReactiveNativeQueryImpl.java | 11 +- .../query/sqm/ReactiveSqmSelectionQuery.java | 2 +- .../sqm/internal/ReactiveQuerySqmImpl.java | 6 +- .../cte/ReactiveCteInsertHandler.java | 43 +-- .../cte/ReactiveInsertExecutionDelegate.java | 10 +- ...activeExecuteWithTemporaryTableHelper.java | 61 ++-- ...iveGlobalTemporaryTableInsertStrategy.java | 3 +- ...eGlobalTemporaryTableMutationStrategy.java | 6 +- ...tiveLocalTemporaryTableInsertStrategy.java | 3 +- ...veLocalTemporaryTableMutationStrategy.java | 6 +- ...ReactivePersistentTableInsertStrategy.java | 3 +- ...activePersistentTableMutationStrategy.java | 6 +- ...tiveRestrictedDeleteExecutionDelegate.java | 149 ++++---- .../ReactiveSoftDeleteExecutionDelegate.java | 320 ++++++++++++++++++ .../ReactiveTableBasedDeleteHandler.java | 40 ++- .../ReactiveTableBasedInsertHandler.java | 15 +- .../ReactiveTableBasedUpdateHandler.java | 13 +- .../ReactiveTemporaryTableHelper.java | 22 +- .../ReactiveUpdateExecutionDelegate.java | 9 +- .../impl/ReactiveStatelessSessionImpl.java | 115 ++++--- .../sql/results/ReactiveResultSetMapping.java | 7 +- .../internal/ReactiveEmbeddableFetchImpl.java | 6 +- ...eactiveEmbeddableForeignKeyResultImpl.java | 4 +- .../ReactiveEmbeddableInitializerImpl.java | 4 +- .../ReactiveDeferredResultSetAccess.java | 28 +- .../ReactiveDirectResultSetAccess.java | 18 +- .../internal/ReactiveResultSetAccess.java | 49 +-- .../jdbc/ReactiveJsonArrayJdbcType.java | 46 +-- .../reactive/ImplicitSoftDeleteTests.java | 225 ++++++++++++ .../hibernate/reactive/LazyPropertyTest.java | 4 +- .../org/hibernate/reactive/UpsertTest.java | 12 +- .../bytecode-enhancements-it/build.gradle | 7 +- .../reactive/it/LocalContextTest.java | 2 +- settings.gradle | 4 +- 44 files changed, 944 insertions(+), 382 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/ImplicitSoftDeleteTests.java diff --git a/README.md b/README.md index cb356cb19..7cd62b47f 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,14 @@ Learn more at . Hibernate Reactive has been tested with: - Java 17, 21, 24 -- PostgreSQL 16 +- PostgreSQL 17 - MySQL 9 - MariaDB 11 - Db2 12 -- CockroachDB v24 -- MS SQL Server 2022 +- CockroachDB v25 +- MS SQL Server 2025 - Oracle 23 -- [Hibernate ORM][] 7.0.2.Final +- [Hibernate ORM][] 7.1.0.Final - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0 diff --git a/gradle.properties b/gradle.properties index 1fb1a63b1..d081888e0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -39,10 +39,10 @@ org.gradle.java.installations.auto-download=false ### Settings the following properties will override the version defined in gradle/libs.versions.toml # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -#hibernateOrmVersion = 7.0.2.Final +#hibernateOrmVersion = 7.1.0.CR1 # Override default Hibernate ORM Gradle plugin version -#hibernateOrmGradlePluginVersion = 7.0.2.Final +#hibernateOrmGradlePluginVersion = 7.1.0.CR1 # If set to true, skip Hibernate ORM version parsing (default is true, if set to null) # this is required when using intervals or weird versions or the build will fail diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d59faf7a0..6a884473b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.0.9.Final" -hibernateOrmGradlePluginVersion = "7.0.9.Final" +hibernateOrmVersion = "7.1.0.CR1" +hibernateOrmGradlePluginVersion = "7.1.0.CR1" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" diff --git a/gradle/version.properties b/gradle/version.properties index 618fb2d72..7cc669e01 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1,5 @@ -projectVersion=4.0.0-SNAPSHOT \ No newline at end of file +<<<<<<< HEAD +projectVersion=4.0.0-SNAPSHOT +======= +projectVersion=3.1.0-SNAPSHOT +>>>>>>> 2a11b54c ([#2387] Upgrade Hibernate ORM to 7.1.0.CR1) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java index 2b4063173..90b098652 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java @@ -91,7 +91,7 @@ public TableMutationBuilder createTableMutationBuilder( return new TableInsertReturningBuilder( persister, tableReference, generatedColumns, sessionFactory ); } else { - return new TableUpdateReturningBuilder<>( persister, tableReference, generatedColumns, sessionFactory ); + return new TableUpdateReturningBuilder( persister, tableReference, generatedColumns, sessionFactory ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java index 0ebebe603..8e96291eb 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java @@ -474,13 +474,9 @@ default CompletionStage reactiveInitializeEnhancedEntityUsedAsProxy( Object entity, String nameOfAttributeBeingAccessed, SharedSessionContractImplementor session) { - final BytecodeEnhancementMetadata enhancementMetadata = getEntityPersister().getBytecodeEnhancementMetadata(); final BytecodeLazyAttributeInterceptor currentInterceptor = enhancementMetadata.extractLazyInterceptor( entity ); - if ( currentInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - final EnhancementAsProxyLazinessInterceptor proxyInterceptor = - (EnhancementAsProxyLazinessInterceptor) currentInterceptor; - + if ( currentInterceptor instanceof EnhancementAsProxyLazinessInterceptor proxyInterceptor ) { final EntityKey entityKey = proxyInterceptor.getEntityKey(); final Object identifier = entityKey.getIdentifier(); @@ -494,19 +490,17 @@ default CompletionStage reactiveInitializeEnhancedEntityUsedAsProxy( .handleEntityNotFound( entityKey.getEntityName(), identifier ); } + final LazyAttributeLoadingInterceptor interceptor = enhancementMetadata + .injectInterceptor( entity, identifier, session ); + if ( nameOfAttributeBeingAccessed == null ) { return null; } else { - final LazyAttributeLoadingInterceptor interceptor = enhancementMetadata - .injectInterceptor( entity, identifier, session ); return interceptor.readObject( - entity, - nameOfAttributeBeingAccessed, - interceptor.isAttributeLoaded( nameOfAttributeBeingAccessed ) - ? getPropertyValue( entity, nameOfAttributeBeingAccessed ) - : ( (LazyPropertyInitializer) this ) - .initializeLazyProperty( nameOfAttributeBeingAccessed, entity, session ) + entity, nameOfAttributeBeingAccessed, interceptor.isAttributeLoaded( nameOfAttributeBeingAccessed ) + ? getPropertyValue( entity, nameOfAttributeBeingAccessed ) + : ( (LazyPropertyInitializer) this ).initializeLazyProperty( nameOfAttributeBeingAccessed, entity, session ) ); } } ); @@ -528,11 +522,12 @@ private CompletionStage loadFromDatabaseOrCache( return completedFuture( loaded ); } } - return ( (ReactiveSingleIdEntityLoader) determineLoaderToUse( session ) ) - .load( identifier, entity, LockOptions.NONE, session ); + final LockOptions lockOptions = new LockOptions(); + return ( (ReactiveSingleIdEntityLoader) determineLoaderToUse( session, lockOptions ) ) + .load( identifier, entity, lockOptions, session ); } - SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session); + SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions); boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveJoinedSubclassEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveJoinedSubclassEntityPersister.java index 0182ef403..2443d4d9b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveJoinedSubclassEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveJoinedSubclassEntityPersister.java @@ -139,8 +139,8 @@ protected AttributeMapping buildPluralAttributeMapping( } @Override - public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { - return super.determineLoaderToUse( session ); + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions) { + return super.determineLoaderToUse( session, lockOptions ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveSingleTableEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveSingleTableEntityPersister.java index 3006a0eae..2f5878737 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveSingleTableEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveSingleTableEntityPersister.java @@ -86,8 +86,8 @@ public GeneratedValuesMutationDelegate createInsertDelegate() { } @Override - public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { - return super.determineLoaderToUse( session ); + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions) { + return super.determineLoaderToUse( session, lockOptions ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveUnionSubclassEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveUnionSubclassEntityPersister.java index 52871e30f..bba3cf86d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveUnionSubclassEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveUnionSubclassEntityPersister.java @@ -139,8 +139,8 @@ protected AttributeMapping buildPluralAttributeMapping( } @Override - public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { - return super.determineLoaderToUse( session ); + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions) { + return super.determineLoaderToUse( session, lockOptions ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveServiceInitiators.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveServiceInitiators.java index 2343a9644..13b0ae6b8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveServiceInitiators.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveServiceInitiators.java @@ -22,6 +22,7 @@ import org.hibernate.engine.jdbc.internal.SqlStatementLoggerInitiator; import org.hibernate.engine.jndi.internal.JndiServiceInitiator; import org.hibernate.event.internal.EntityCopyObserverFactoryInitiator; +import org.hibernate.internal.util.cache.InternalCacheFactoryInitiator; import org.hibernate.persister.internal.PersisterFactoryInitiator; import org.hibernate.property.access.internal.PropertyAccessStrategyResolverInitiator; import org.hibernate.reactive.context.impl.VertxContextInitiator; @@ -161,6 +162,9 @@ private static List> buildInitialServiceInitiatorLis // Custom for Hibernate Reactive: BatchLoaderFactory serviceInitiators.add( ReactiveBatchLoaderFactoryInitiator.INSTANCE ); + // [standard] InternalCacheFactoryService + serviceInitiators.add( InternalCacheFactoryInitiator.INSTANCE ); + // --- end of services defined by Hibernate ORM // --- custom ones follow: diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java index 6ca5b2024..d58ae57a9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java @@ -142,19 +142,14 @@ private ReactiveSelectQueryPlan reactiveSelectPlan() { private ReactiveNonSelectQueryPlan reactiveNonSelectPlan() { final QueryInterpretationCache.Key cacheKey = generateNonSelectInterpretationsKey(); if ( cacheKey != null ) { - NonSelectQueryPlan queryPlan = getSession().getFactory().getQueryEngine() - .getInterpretationCache().getNonSelectQueryPlan( cacheKey ); + NonSelectQueryPlan queryPlan = getSession().getFactory().getQueryEngine().getInterpretationCache().getNonSelectQueryPlan( cacheKey ); if ( queryPlan != null ) { return (ReactiveNonSelectQueryPlan) queryPlan; } } - final String sqlString = expandParameterLists(); - ReactiveNonSelectQueryPlan queryPlan = new ReactiveNativeNonSelectQueryPlan( - sqlString, - getQuerySpaces(), - getParameterOccurrences() - ); + final String sqlString = expandParameterLists( 1 ); + ReactiveNonSelectQueryPlan queryPlan = new ReactiveNativeNonSelectQueryPlan( sqlString, getQuerySpaces(), getParameterOccurrences() ); if ( cacheKey != null ) { getSession().getFactory().getQueryEngine().getInterpretationCache() .cacheNonSelectQueryPlan( cacheKey, queryPlan ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/ReactiveSqmSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/ReactiveSqmSelectionQuery.java index 0794ae435..a43d917e9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/ReactiveSqmSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/ReactiveSqmSelectionQuery.java @@ -24,7 +24,7 @@ /** * @see org.hibernate.query.sqm.SqmSelectionQuery */ -public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery, SqmQuery { +public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery, SqmQuery { @Override ReactiveSqmSelectionQuery setParameter(String name, Object value); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java index 5242ab747..755448cbd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java @@ -44,8 +44,8 @@ import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.spi.QueryOptions; -import org.hibernate.query.sqm.internal.QuerySqmImpl; import org.hibernate.query.sqm.internal.SqmInterpretationsKey; +import org.hibernate.query.sqm.internal.SqmQueryImpl; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; @@ -71,9 +71,9 @@ import jakarta.persistence.metamodel.Type; /** - * A reactive {@link QuerySqmImpl} + * A reactive {@link SqmQueryImpl} */ -public class ReactiveQuerySqmImpl extends QuerySqmImpl implements ReactiveSqmQueryImplementor { +public class ReactiveQuerySqmImpl extends SqmQueryImpl implements ReactiveSqmQueryImplementor { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java index 603563e0f..e16ad0abd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java @@ -144,27 +144,10 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = sqmConverter.visitInsertionTargetPaths( (assignable, columnReferences) -> { final SqmPathInterpretation pathInterpretation = (SqmPathInterpretation) assignable; - final int offset = CteTable.determineModelPartStartIndex( - entityDescriptor, - pathInterpretation.getExpressionType() - ); - if ( offset == -1 ) { - throw new IllegalStateException( "Couldn't find matching cte column for: " + ( (Expression) assignable ).getExpressionType() ); - } - final int end = offset + pathInterpretation.getExpressionType().getJdbcTypeCount(); - // Find a matching cte table column and set that at the current index - final List columns = getCteTable().getCteColumns().subList( offset, end ); + final List columns = getCteTable().findCteColumns( pathInterpretation.getExpressionType() ); insertStatement.addTargetColumnReferences( columnReferences ); targetPathCteColumns.addAll( columns ); - targetPathColumns.add( - new AbstractMap.SimpleEntry<>( - columns, - new Assignment( - assignable, - (Expression) assignable - ) - ) - ); + targetPathColumns.add( new AbstractMap.SimpleEntry<>( columns, new Assignment( assignable, (Expression) assignable ) ) ); }, sqmInsertStatement, entityDescriptor, @@ -205,15 +188,8 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec ); } if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - SqmInsertStrategyHelper.createRowNumberingExpression( - querySpec, - sessionFactory - ) - ) - ); + querySpec.getSelectClause() + .addSqlSelection( new SqlSelectionImpl( 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, sessionFactory ) ) ); } } ); @@ -250,6 +226,17 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec ); } } + if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + 0, + SqmInsertStrategyHelper.createRowNumberingExpression( + querySpec, + sessionFactory + ) + ) + ); + } final ValuesTableGroup valuesTableGroup = new ValuesTableGroup( navigablePath, entityDescriptor.getEntityPersister(), diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java index 33acc38f6..399e8b30a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java @@ -11,12 +11,12 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.InsertExecutionDelegate; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTableBasedInsertHandler; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -34,12 +34,14 @@ public class ReactiveInsertExecutionDelegate extends InsertExecutionDelegate imp public ReactiveInsertExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup insertingTableGroup, Map tableReferenceByAlias, List assignments, + boolean assignedId, InsertSelectStatement insertStatement, ConflictClause conflictClause, JdbcParameter sessionUidParameter, @@ -47,12 +49,14 @@ public ReactiveInsertExecutionDelegate( super( sqmConverter, entityTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, assignments, + assignedId, insertStatement, conflictClause, sessionUidParameter, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java index e8f7bbea6..d936c590f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java @@ -17,6 +17,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableColumn; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -33,6 +34,7 @@ import org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTemporaryTableHelper.TemporaryTableCreationWork; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstTranslatorFactory; @@ -52,6 +54,8 @@ import org.hibernate.sql.results.internal.SqlSelectionImpl; import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTemporaryTableHelper.cleanTemporaryTableRows; +import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; +import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -104,15 +108,11 @@ public static CompletionStage saveMatchingIdsIntoIdTable( mutatingEntityDescriptor.getIdentifierMapping().forEachSelectable( (selectionIndex, selection) -> { - final TableReference tableReference = mutatingTableGroup.resolveTableReference( - mutatingTableGroup.getNavigablePath(), - selection.getContainingTableExpression() - ); matchingIdSelection.getSelectClause().addSqlSelection( new SqlSelectionImpl( selectionIndex + 1, sqmConverter.getSqlExpressionResolver().resolveSqlExpression( - tableReference, + mutatingTableGroup.resolveTableReference( mutatingTableGroup.getNavigablePath(), selection.getContainingTableExpression() ), selection ) ) @@ -155,7 +155,8 @@ public static CompletionStage saveIntoTemporaryTable( temporaryTableInsert.getSourceSelectStatement().visitQuerySpecs( querySpec -> querySpec.getFromClause().visitTableJoins( tableJoin -> { - if ( tableJoin.getJoinType() != SqlAstJoinType.INNER ) { + if ( tableJoin.isInitialized() + && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { lockOptions.setLockMode( lockMode ); } } @@ -210,7 +211,7 @@ public static QuerySpec createIdTableSelectQuerySpec( querySpec.getFromClause().addRoot( idTableGroup ); - applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart ); + applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart, entityDescriptor ); applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidAccess, executionContext ); return querySpec; @@ -221,9 +222,10 @@ private static void applyIdTableSelections( QuerySpec querySpec, TableReference tableReference, TemporaryTable idTable, - ModelPart fkModelPart) { + ModelPart fkModelPart, + EntityMappingType entityDescriptor) { if ( fkModelPart == null ) { - final int size = idTable.getEntityDescriptor().getIdentifierMapping().getJdbcTypeCount(); + final int size = entityDescriptor.getIdentifierMapping().getJdbcTypeCount(); for ( int i = 0; i < size; i++ ) { final TemporaryTableColumn temporaryTableColumn = idTable.getColumns().get( i ); if ( temporaryTableColumn != idTable.getSessionUidColumn() ) { @@ -285,12 +287,31 @@ private static void applyIdTableRestrictions( } } + @Deprecated(forRemoval = true, since = "3.1") public static CompletionStage performBeforeTemporaryTableUseActions( TemporaryTable temporaryTable, ExecutionContext executionContext) { + return performBeforeTemporaryTableUseActions( + temporaryTable, + executionContext.getSession().getDialect().getTemporaryTableBeforeUseAction(), + executionContext + ).thenCompose( CompletionStages::voidFuture ); + } + + public static CompletionStage performBeforeTemporaryTableUseActions( + TemporaryTable temporaryTable, + TemporaryTableStrategy temporaryTableStrategy, + ExecutionContext executionContext) { + return performBeforeTemporaryTableUseActions( temporaryTable, temporaryTableStrategy.getTemporaryTableBeforeUseAction(), executionContext ); + } + + public static CompletionStage performBeforeTemporaryTableUseActions( + TemporaryTable temporaryTable, + BeforeUseAction beforeUseAction, + ExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final Dialect dialect = factory.getJdbcServices().getDialect(); - if ( dialect.getTemporaryTableBeforeUseAction() == BeforeUseAction.CREATE ) { + if ( beforeUseAction == BeforeUseAction.CREATE ) { final TemporaryTableCreationWork temporaryTableCreationWork = new TemporaryTableCreationWork( temporaryTable, factory ); final TempTableDdlTransactionHandling ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) { @@ -298,7 +319,7 @@ public static CompletionStage performBeforeTemporaryTableUseActions( } throw LOG.notYetImplemented(); } - return voidFuture(); + return falseFuture(); } public static CompletionStage performAfterTemporaryTableUseActions( @@ -308,14 +329,11 @@ public static CompletionStage performAfterTemporaryTableUseActions( ExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final Dialect dialect = factory.getJdbcServices().getDialect(); - switch ( afterUseAction ) { - case CLEAN: - return cleanTemporaryTableRows( temporaryTable, dialect.getTemporaryTableExporter(), sessionUidAccess, executionContext.getSession() ); - case DROP: - return dropAction( temporaryTable, executionContext, factory, dialect ); - default: - return voidFuture(); - } + return switch ( afterUseAction ) { + case CLEAN -> cleanTemporaryTableRows( temporaryTable, dialect.getTemporaryTableExporter(), sessionUidAccess, executionContext.getSession() ); + case DROP -> dropAction( temporaryTable, executionContext, factory, dialect ); + default -> voidFuture(); + }; } private static CompletionStage dropAction( @@ -327,10 +345,11 @@ private static CompletionStage dropAction( if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) { return new ReactiveTemporaryTableHelper .TemporaryTableDropWork( temporaryTable, factory ) - .reactiveExecute( ( (ReactiveConnectionSupplier) executionContext.getSession() ).getReactiveConnection() ); + .reactiveExecute( ( (ReactiveConnectionSupplier) executionContext.getSession() ).getReactiveConnection() ) + .thenCompose( CompletionStages::voidFuture ); } - throw LOG.notYetImplemented(); + return failedFuture( LOG.notYetImplemented() ); } private static void doNothing(Integer integer, PreparedStatement preparedStatement) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java index 2f7f7bb37..708a7b072 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java @@ -56,7 +56,8 @@ public CompletionStage reactiveExecuteInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, // generally a global temp table should already track a Connection-specific uid, // but just in case a particular env needs it... ReactiveGlobalTemporaryTableStrategy::sessionIdentifier, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java index 66b001520..baa4ed9a5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java @@ -59,7 +59,8 @@ public CompletionStage reactiveExecuteUpdate( sqmUpdateStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, ReactivePersistentTableStrategy::sessionIdentifier, getSessionFactory() ).reactiveExecute( context ) ); @@ -75,7 +76,8 @@ public CompletionStage reactiveExecuteDelete( sqmDeleteStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, ReactiveGlobalTemporaryTableStrategy::sessionIdentifier, getSessionFactory() ).reactiveExecute( context ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java index ae2298ddd..322127730 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java @@ -31,7 +31,8 @@ public CompletionStage reactiveExecuteInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - afterUserAction(), + getTemporaryTableStrategy(), + isDropIdTables(), ReactiveLocalTemporaryTableInsertStrategy::throwUnexpectedCallToSessionUIDError, getSessionFactory() ).reactiveExecute( context ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java index fe07871eb..d22d50e5b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java @@ -37,7 +37,8 @@ public CompletionStage reactiveExecuteUpdate( sqmUpdate, domainParameterXref, getTemporaryTable(), - afterUseAction(), + getTemporaryTableStrategy(), + isDropIdTables(), ReactiveLocalTemporaryTableMutationStrategy::throwUnexpectedAccessToSessionUID, getSessionFactory() ).reactiveExecute( context ); @@ -52,7 +53,8 @@ public CompletionStage reactiveExecuteDelete( sqmDelete, domainParameterXref, getTemporaryTable(), - afterUseAction(), + getTemporaryTableStrategy(), + isDropIdTables(), ReactiveLocalTemporaryTableMutationStrategy::throwUnexpectedAccessToSessionUID, getSessionFactory() ).reactiveExecute( context ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java index 9a44a44cf..951cabce1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java @@ -53,7 +53,8 @@ public CompletionStage reactiveExecuteInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, ReactivePersistentTableStrategy::sessionIdentifier, getSessionFactory() ).reactiveExecute( context ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java index 57cd14f4b..8f23ce727 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java @@ -56,7 +56,8 @@ public CompletionStage reactiveExecuteUpdate( sqmUpdateStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, ReactivePersistentTableStrategy::sessionIdentifier, getSessionFactory() ).reactiveExecute( context ) ); @@ -72,7 +73,8 @@ public CompletionStage reactiveExecuteDelete( sqmDeleteStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, ReactivePersistentTableStrategy::sessionIdentifier, getSessionFactory() ).reactiveExecute( context ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java index 0e1097b63..c119e718a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; -import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -16,6 +15,7 @@ import java.util.function.Supplier; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -33,17 +33,15 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector; +import org.hibernate.query.sqm.mutation.internal.temptable.AbstractDeleteExecutionDelegate; import org.hibernate.query.sqm.mutation.internal.temptable.ColumnReferenceCheckingSqlAstWalker; import org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithoutIdTableHelper; import org.hibernate.query.sqm.mutation.internal.temptable.RestrictedDeleteExecutionDelegate; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; import org.hibernate.reactive.util.impl.CompletionStages; @@ -68,67 +66,40 @@ import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** * The reactive version of {@link RestrictedDeleteExecutionDelegate} */ -// Basically a copy of RestrictedDeleteExecutionDelegate, we will probably need to refactor this code to avoid -// duplication -public class ReactiveRestrictedDeleteExecutionDelegate +// Basically a copy of RestrictedDeleteExecutionDelegate, we will probably need to refactor this code to avoid duplication +public class ReactiveRestrictedDeleteExecutionDelegate extends AbstractDeleteExecutionDelegate implements ReactiveTableBasedDeleteHandler.ReactiveExecutionDelegate { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - private final EntityMappingType entityDescriptor; - private final TemporaryTable idTable; - private final AfterUseAction afterUseAction; - private final SqmDeleteStatement sqmDelete; - private final DomainParameterXref domainParameterXref; - private final SessionFactoryImplementor sessionFactory; - - private final Function sessionUidAccess; - private final MultiTableSqmMutationConverter converter; + private static final Log LOG = make( Log.class, lookup() ); public ReactiveRestrictedDeleteExecutionDelegate( - EntityMappingType entityDescriptor, - TemporaryTable idTable, - AfterUseAction afterUseAction, - SqmDeleteStatement sqmDelete, - DomainParameterXref domainParameterXref, - Function sessionUidAccess, - QueryOptions queryOptions, - LoadQueryInfluencers loadQueryInfluencers, - QueryParameterBindings queryParameterBindings, - SessionFactoryImplementor sessionFactory) { - this.entityDescriptor = entityDescriptor; - this.idTable = idTable; - this.afterUseAction = afterUseAction; - this.sqmDelete = sqmDelete; - this.domainParameterXref = domainParameterXref; - this.sessionUidAccess = sessionUidAccess; - this.sessionFactory = sessionFactory; - this.converter = new MultiTableSqmMutationConverter( - entityDescriptor, - sqmDelete, - sqmDelete.getTarget(), - domainParameterXref, - queryOptions, - loadQueryInfluencers, - queryParameterBindings, - sessionFactory.getSqlTranslationEngine() + EntityMappingType entityDescriptor, TemporaryTable idTable, TemporaryTableStrategy temporaryTableStrategy, boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, + DomainParameterXref domainParameterXref, QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings, + Function sessionUidAccess, SessionFactoryImplementor sessionFactory + ) { + super( + entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess, + sessionFactory ); } + @Override public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { - final EntityPersister entityDescriptor = sessionFactory.getRuntimeMetamodels() + final EntityPersister entityDescriptor = getSessionFactory().getRuntimeMetamodels() .getMappingMetamodel() - .getEntityDescriptor( sqmDelete.getTarget().getEntityName() ); + .getEntityDescriptor( getSqmDelete().getTarget().getEntityName() ); final String hierarchyRootTableName = entityDescriptor.getTableName(); - final TableGroup deletingTableGroup = converter.getMutatingTableGroup(); + final TableGroup deletingTableGroup = getConverter().getMutatingTableGroup(); final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference( deletingTableGroup.getNavigablePath(), @@ -141,7 +112,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec // 2) we also inspect each ColumnReference that is part of the where-clause to see which // table it comes from. If all the referenced columns (if any at all) are from the root table // we can perform all the deletes without using an id-table - final Predicate specifiedRestriction = converter.visitWhereClause( sqmDelete.getWhereClause() ); + final Predicate specifiedRestriction = getConverter().visitWhereClause( getSqmDelete().getWhereClause() ); final PredicateCollector predicateCollector = new PredicateCollector( specifiedRestriction ); entityDescriptor.applyBaseRestrictions( @@ -151,10 +122,10 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), false, null, - converter + getConverter() ); - converter.pruneTableGroupJoins(); + getConverter().pruneTableGroupJoins(); final ColumnReferenceCheckingSqlAstWalker walker = new ColumnReferenceCheckingSqlAstWalker( hierarchyRootTableReference.getIdentificationVariable() ); @@ -175,8 +146,8 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec if ( needsIdTable ) { return executeWithIdTable( predicateCollector.getPredicate(), - converter.getJdbcParamsBySqmParam(), - converter.getSqmParameterMappingModelExpressibleResolutions(), + getConverter().getJdbcParamsBySqmParam(), + getConverter().getSqmParameterMappingModelExpressibleResolutions(), executionContextAdapter ); } @@ -184,9 +155,9 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec return executeWithoutIdTable( predicateCollector.getPredicate(), deletingTableGroup, - converter.getJdbcParamsBySqmParam(), - converter.getSqmParameterMappingModelExpressibleResolutions(), - converter.getSqlExpressionResolver(), + getConverter().getJdbcParamsBySqmParam(), + getConverter().getSqmParameterMappingModelExpressibleResolutions(), + getConverter().getSqlExpressionResolver(), executionContextAdapter ); } @@ -199,9 +170,9 @@ private CompletionStage executeWithoutIdTable( Map, MappingModelExpressible> paramTypeResolutions, SqlExpressionResolver sqlExpressionResolver, ExecutionContext executionContext) { - assert entityDescriptor == entityDescriptor.getRootEntityDescriptor(); + assert getEntityDescriptor() == getEntityDescriptor().getRootEntityDescriptor(); - final EntityPersister rootEntityPersister = entityDescriptor.getEntityPersister(); + final EntityPersister rootEntityPersister = getEntityDescriptor().getEntityPersister(); final String rootTableName = rootEntityPersister.getTableName(); final NamedTableReference rootTableReference = (NamedTableReference) tableGroup.resolveTableReference( tableGroup.getNavigablePath(), @@ -214,14 +185,14 @@ private CompletionStage executeWithoutIdTable( suppliedPredicate, rootEntityPersister, sqlExpressionResolver, - sessionFactory + getSessionFactory() ); final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), - domainParameterXref, + getDomainParameterXref(), SqmUtil.generateJdbcParamsXref( - domainParameterXref, + getDomainParameterXref(), () -> restrictionSqmParameterResolutions ), new SqmParameterMappingModelResolutionAccess() { @@ -235,7 +206,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter cleanUpCollectionTablesStage = ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - entityDescriptor, + getEntityDescriptor(), (tableReference, attributeMapping) -> { // No need for a predicate if there is no supplied predicate i.e. this is a full cleanup if ( suppliedPredicate == null ) { @@ -254,7 +225,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter MappingModelExpressible getResolvedMappingModelType(SqmParameter MappingModelExpressible getResolvedMappingModelType(SqmParameter rows.get() ); } else { - entityDescriptor.visitConstraintOrderedTables( + getEntityDescriptor().visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> { if ( !tableExpression.equals( rootTableName ) ) { final NamedTableReference tableReference = (NamedTableReference) tableGroup.getTableReference( @@ -354,7 +325,7 @@ private CompletionStage visitUnionTableReferences( JdbcParameterBindings jdbcParameterBindings, CompletionStage[] deleteFromNonRootStages, MutableInteger rows) { - entityDescriptor.visitConstraintOrderedTables( + getEntityDescriptor().visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> { final NamedTableReference tableReference = new NamedTableReference( tableExpression, @@ -449,7 +420,7 @@ private CompletionStage deleteFromNonRootTableWithoutIdTable( final Expression deletingTableColumnRefsExpression = deletingTableColumnRefs.size() == 1 ? deletingTableColumnRefs.get( 0 ) - : new SqlTuple( deletingTableColumnRefs, entityDescriptor.getIdentifierMapping() ); + : new SqlTuple( deletingTableColumnRefs, getEntityDescriptor().getIdentifierMapping() ); tableDeletePredicate = new InSubQueryPredicate( deletingTableColumnRefsExpression, @@ -502,9 +473,9 @@ private CompletionStage executeWithIdTable( ExecutionContext executionContext) { final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), - domainParameterXref, + getDomainParameterXref(), SqmUtil.generateJdbcParamsXref( - domainParameterXref, + getDomainParameterXref(), () -> restrictionSqmParameterResolutions ), new SqmParameterMappingModelResolutionAccess() { @@ -517,14 +488,14 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter executeUsingIdTable( predicate, executionContext, jdbcParameterBindings ) .handle( CompletionStages::handle ) .thenCompose( resultHandler -> ReactiveExecuteWithTemporaryTableHelper .performAfterTemporaryTableUseActions( - idTable, - sessionUidAccess, - afterUseAction, + getIdTable(), + getSessionUidAccess(), + getAfterUseAction(), executionContext ) .thenCompose( resultHandler::getResultAsCompletionStage ) @@ -537,28 +508,27 @@ private CompletionStage executeUsingIdTable( ExecutionContext executionContext, JdbcParameterBindings jdbcParameterBindings) { return ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable( - converter, + getConverter(), predicate, - idTable, - sessionUidAccess, + getIdTable(), + getSessionUidAccess(), jdbcParameterBindings, executionContext ) .thenCompose( rows -> { final QuerySpec idTableIdentifierSubQuery = createIdTableSelectQuerySpec( - idTable, - sessionUidAccess, - entityDescriptor, + getIdTable(), + getSessionUidAccess(), + getEntityDescriptor(), executionContext ); return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - entityDescriptor, + getEntityDescriptor(), (tableReference, attributeMapping) -> { final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); - final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart() - .isEntityIdentifierMapping() - ? idTableIdentifierSubQuery - : createIdTableSelectQuerySpec( idTable, fkDescriptor.getTargetPart(), sessionUidAccess, entityDescriptor, executionContext ); + final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart().isEntityIdentifierMapping() + ? idTableIdentifierSubQuery + : createIdTableSelectQuerySpec( getIdTable(), fkDescriptor.getTargetPart(), getSessionUidAccess(), getEntityDescriptor(), executionContext ); return new InSubQueryPredicate( MappingModelCreationHelper.buildColumnReferenceExpression( new MutatingTableReferenceGroupWrapper( @@ -568,7 +538,7 @@ private CompletionStage executeUsingIdTable( ), fkDescriptor, null, - sessionFactory + getSessionFactory() ), idTableFkSubQuery, false @@ -586,7 +556,7 @@ private CompletionStage visitConstraintOrderedTables( QuerySpec idTableIdentifierSubQuery, ExecutionContext executionContext) { final Completable completable = new Completable<>(); - entityDescriptor + getEntityDescriptor() .visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> deleteFromTableUsingIdTable( tableExpression, tableKeyColumnVisitationSupplier, @@ -605,12 +575,8 @@ private CompletionStage deleteFromTableUsingIdTable( ExecutionContext executionContext) { LOG.tracef( "deleteFromTableUsingIdTable - %s", tableExpression ); - final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( entityDescriptor ); - final NamedTableReference targetTable = new NamedTableReference( - tableExpression, - DeleteStatement.DEFAULT_ALIAS, - true - ); + final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( getEntityDescriptor() ); + final NamedTableReference targetTable = new NamedTableReference( tableExpression, DeleteStatement.DEFAULT_ALIAS, true ); tableKeyColumnVisitationSupplier.get().accept( (columnIndex, selection) -> { @@ -636,5 +602,4 @@ private CompletionStage deleteFromTableUsingIdTable( executionContext ); } - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java new file mode 100644 index 000000000..607812f93 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java @@ -0,0 +1,320 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.query.sqm.mutation.internal.temptable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; + +import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.metamodel.mapping.TableDetails; +import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; +import org.hibernate.query.sqm.internal.SqmUtil; +import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; +import org.hibernate.query.sqm.mutation.internal.temptable.AbstractDeleteExecutionDelegate; +import org.hibernate.query.sqm.mutation.internal.temptable.ColumnReferenceCheckingSqlAstWalker; +import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.spi.NavigablePath; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; +import org.hibernate.sql.ast.tree.from.NamedTableReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.predicate.PredicateCollector; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.ast.tree.update.Assignment; +import org.hibernate.sql.ast.tree.update.UpdateStatement; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.results.internal.SqlSelectionImpl; + +import static java.util.Collections.singletonList; +import static org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter.omittingLockingAndPaging; +import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; + +public class ReactiveSoftDeleteExecutionDelegate extends AbstractDeleteExecutionDelegate + implements ReactiveTableBasedDeleteHandler.ReactiveExecutionDelegate { + + public ReactiveSoftDeleteExecutionDelegate( + EntityMappingType entityDescriptor, TemporaryTable idTable, TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, + QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings, + Function sessionUidAccess, SessionFactoryImplementor sessionFactory + ) { + super( + entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess, + sessionFactory + ); + } + + @Override + public CompletionStage reactiveExecute(DomainQueryExecutionContext domainQueryExecutionContext) { + final String targetEntityName = getSqmDelete().getTarget().getEntityName(); + final EntityPersister targetEntityDescriptor = getSessionFactory().getMappingMetamodel().getEntityDescriptor( targetEntityName ); + + final EntityMappingType rootEntityDescriptor = targetEntityDescriptor.getRootEntityDescriptor(); + + // determine if we need to use a sub-query for matching ids - + // 1. if the target is not the root we will + // 2. if the supplied predicate (if any) refers to columns from a table + // other than the identifier table we will + final SqmJdbcExecutionContextAdapter executionContext = omittingLockingAndPaging( domainQueryExecutionContext ); + + final TableGroup deletingTableGroup = getConverter().getMutatingTableGroup(); + final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails(); + final NamedTableReference rootTableReference = (NamedTableReference) deletingTableGroup.resolveTableReference( + deletingTableGroup.getNavigablePath(), + softDeleteTable.getTableName() + ); + assert rootTableReference != null; + + // NOTE : `converter.visitWhereClause` already applies the soft-delete restriction + final Predicate specifiedRestriction = getConverter().visitWhereClause( getSqmDelete().getWhereClause() ); + + final PredicateCollector predicateCollector = new PredicateCollector( specifiedRestriction ); + targetEntityDescriptor.applyBaseRestrictions( + predicateCollector, + deletingTableGroup, + true, + executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), + false, + null, + getConverter() + ); + + getConverter().pruneTableGroupJoins(); + final ColumnReferenceCheckingSqlAstWalker walker = new ColumnReferenceCheckingSqlAstWalker( + rootTableReference.getIdentificationVariable() + ); + if ( predicateCollector.getPredicate() != null ) { + predicateCollector.getPredicate().accept( walker ); + } + + final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( + executionContext.getQueryParameterBindings(), + getDomainParameterXref(), + SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), getConverter() ), + new SqmParameterMappingModelResolutionAccess() { + @Override + @SuppressWarnings("unchecked") + public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { + return (MappingModelExpressible) getConverter().getSqmParameterMappingModelExpressibleResolutions().get( parameter ); + } + }, + executionContext.getSession() + ); + + final boolean needsSubQuery = !walker.isAllColumnReferencesFromIdentificationVariable() + || targetEntityDescriptor != rootEntityDescriptor; + + if ( needsSubQuery ) { + return getSessionFactory().getJdbcServices().getDialect().supportsSubqueryOnMutatingTable() + ? performDeleteWithSubQuery( rootEntityDescriptor, deletingTableGroup, rootTableReference, predicateCollector, jdbcParameterBindings, getConverter(), executionContext ) + : performDeleteWithIdTable( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext ); + } + return performDirectDelete( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext ); + } + + private CompletionStage performDeleteWithIdTable( + EntityMappingType rootEntityDescriptor, + NamedTableReference targetTableReference, + PredicateCollector predicateCollector, + JdbcParameterBindings jdbcParameterBindings, + SqmJdbcExecutionContextAdapter executionContext + ) { + ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( getIdTable(), getTemporaryTableStrategy(), executionContext ); + try { + return deleteUsingIdTable( rootEntityDescriptor, targetTableReference, predicateCollector, jdbcParameterBindings, executionContext ); + } + finally { + ReactiveExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions( getIdTable(), getSessionUidAccess(), getAfterUseAction(), executionContext ); + } + } + + private CompletionStage deleteUsingIdTable( + EntityMappingType rootEntityDescriptor, + NamedTableReference targetTableReference, + PredicateCollector predicateCollector, + JdbcParameterBindings jdbcParameterBindings, + SqmJdbcExecutionContextAdapter executionContext + ) { + return ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable( getConverter(), + predicateCollector.getPredicate(), + getIdTable(), + getSessionUidAccess(), + jdbcParameterBindings, + executionContext + ) + .thenCompose( rows -> { + final QuerySpec idTableIdentifierSubQuery = createIdTableSelectQuerySpec( + getIdTable(), + getSessionUidAccess(), + getEntityDescriptor(), + executionContext + ); + + return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( + getEntityDescriptor(), + (tableReference, attributeMapping) -> { + final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); + final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart().isEntityIdentifierMapping() + ? idTableIdentifierSubQuery + : createIdTableSelectQuerySpec( getIdTable(), fkDescriptor.getTargetPart(), getSessionUidAccess(), getEntityDescriptor(), executionContext ); + return new InSubQueryPredicate( + MappingModelCreationHelper.buildColumnReferenceExpression( + new MutatingTableReferenceGroupWrapper( + new NavigablePath( attributeMapping.getRootPathName() ), + attributeMapping, + (NamedTableReference) tableReference + ), + fkDescriptor, + null, + getSessionFactory() + ), + idTableFkSubQuery, + false + ); + + }, + JdbcParameterBindings.NO_BINDINGS, + executionContext + ).thenCompose( unused -> { + final Assignment softDeleteAssignment = rootEntityDescriptor + .getSoftDeleteMapping() + .createSoftDeleteAssignment( targetTableReference ); + final Expression idExpression = createIdExpression( rootEntityDescriptor, targetTableReference ); + final UpdateStatement updateStatement = new UpdateStatement( + targetTableReference, + singletonList( softDeleteAssignment ), + new InSubQueryPredicate( idExpression, idTableIdentifierSubQuery, false ) + ); + + return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); + } ) + .thenApply( v -> rows ); + } ); + } + + private static Expression createIdExpression(EntityMappingType rootEntityDescriptor, NamedTableReference targetTableReference) { + final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails(); + final TableDetails.KeyDetails keyDetails = softDeleteTable.getKeyDetails(); + final List idExpressions = new ArrayList<>( keyDetails.getColumnCount() ); + keyDetails.forEachKeyColumn( (position, column) -> idExpressions.add( new ColumnReference( targetTableReference, column ) ) ); + return idExpressions.size() == 1 + ? idExpressions.get( 0 ) + : new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() ); + } + + private CompletionStage performDeleteWithSubQuery( + EntityMappingType rootEntityDescriptor, + TableGroup deletingTableGroup, + NamedTableReference rootTableReference, + PredicateCollector predicateCollector, + JdbcParameterBindings jdbcParameterBindings, + MultiTableSqmMutationConverter converter, + SqmJdbcExecutionContextAdapter executionContext + ) { + final QuerySpec matchingIdSubQuery = new QuerySpec( false, 1 ); + matchingIdSubQuery.getFromClause().addRoot( deletingTableGroup ); + + final TableDetails identifierTableDetails = rootEntityDescriptor.getIdentifierTableDetails(); + final TableDetails.KeyDetails keyDetails = identifierTableDetails.getKeyDetails(); + + final NamedTableReference targetTable = new NamedTableReference( + identifierTableDetails.getTableName(), + DeleteStatement.DEFAULT_ALIAS, + false + ); + + final List idExpressions = new ArrayList<>( keyDetails.getColumnCount() ); + keyDetails.forEachKeyColumn( (position, column) -> { + final Expression columnReference = converter.getSqlExpressionResolver().resolveSqlExpression( + rootTableReference, + column + ); + matchingIdSubQuery.getSelectClause().addSqlSelection( + new SqlSelectionImpl( position, columnReference ) + ); + idExpressions.add( new ColumnReference( targetTable, column ) ); + } ); + + matchingIdSubQuery.applyPredicate( predicateCollector.getPredicate() ); + final Expression idExpression = idExpressions.size() == 1 + ? idExpressions.get( 0 ) + : new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() ); + + final Assignment softDeleteAssignment = rootEntityDescriptor + .getSoftDeleteMapping() + .createSoftDeleteAssignment( targetTable ); + + final UpdateStatement updateStatement = new UpdateStatement( + targetTable, + singletonList( softDeleteAssignment ), + new InSubQueryPredicate( idExpression, matchingIdSubQuery, false ) + ); + + return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); + } + + private CompletionStage performDirectDelete( + EntityMappingType rootEntityDescriptor, + NamedTableReference rootTableReference, + PredicateCollector predicateCollector, + JdbcParameterBindings jdbcParameterBindings, + SqmJdbcExecutionContextAdapter executionContext + ) { + + final Assignment softDeleteAssignment = rootEntityDescriptor.getSoftDeleteMapping().createSoftDeleteAssignment( rootTableReference ); + final UpdateStatement updateStatement = new UpdateStatement( rootTableReference, singletonList( softDeleteAssignment ), predicateCollector.getPredicate() ); + return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); + } + + private CompletionStage executeUpdate(UpdateStatement updateStatement, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + final JdbcServices jdbcServices = factory.getJdbcServices(); + + final JdbcOperationQueryMutation jdbcUpdate = jdbcServices.getJdbcEnvironment() + .getSqlAstTranslatorFactory() + .buildMutationTranslator( factory, updateStatement ) + .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); + + return StandardReactiveJdbcMutationExecutor.INSTANCE + .executeReactive( + jdbcUpdate, + jdbcParameterBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext + ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java index bb9ceff9c..d916e4910 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java @@ -10,12 +10,12 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedDeleteHandler; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; @@ -38,34 +38,56 @@ public ReactiveTableBasedDeleteHandler( SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { - super( sqmDeleteStatement, domainParameterXref, idTable, afterUseAction, sessionUidAccess, sessionFactory ); + super( + sqmDeleteStatement, + domainParameterXref, + idTable, + temporaryTableStrategy, + forceDropAfterUse, + sessionUidAccess, + sessionFactory + ); } @Override public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { if ( LOG.isTraceEnabled() ) { - LOG.tracef( - "Starting multi-table delete execution - %s", - getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() - ); + LOG.tracef( "Starting multi-table delete execution - %s", getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() ); } return resolveDelegate( executionContext ).reactiveExecute( executionContext ); } protected ReactiveExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { + if ( getEntityDescriptor().getSoftDeleteMapping() != null ) { + return new ReactiveSoftDeleteExecutionDelegate( + getEntityDescriptor(), + getIdTable(), + getTemporaryTableStrategy(), + isForceDropAfterUse(), + getSqmDeleteOrUpdateStatement(), + getDomainParameterXref(), + executionContext.getQueryOptions(), + executionContext.getSession().getLoadQueryInfluencers(), + executionContext.getQueryParameterBindings(), + getSessionUidAccess(), + getSessionFactory() + ); + } return new ReactiveRestrictedDeleteExecutionDelegate( getEntityDescriptor(), getIdTable(), - getAfterUseAction(), + getTemporaryTableStrategy(), + isForceDropAfterUse(), getSqmDeleteOrUpdateStatement(), getDomainParameterXref(), - getSessionUidAccess(), executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), + getSessionUidAccess(), getSessionFactory() ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java index d697758ed..eb10dc8a4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java @@ -12,6 +12,7 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; @@ -19,7 +20,6 @@ import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedInsertHandler; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; @@ -50,10 +50,11 @@ public ReactiveTableBasedInsertHandler( SqmInsertStatement sqmInsert, DomainParameterXref domainParameterXref, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { - super( sqmInsert, domainParameterXref, entityTable, afterUseAction, sessionUidAccess, sessionFactory ); + super( sqmInsert, domainParameterXref, entityTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, sessionFactory ); } @Override @@ -81,12 +82,14 @@ protected ExecutionDelegate buildExecutionDelegate( SqmInsertStatement sqmInsert, MultiTableSqmMutationConverter sqmConverter, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup insertingTableGroup, Map tableReferenceByAlias, List assignments, + boolean assignsId, InsertSelectStatement insertStatement, ConflictClause conflictClause, JdbcParameter sessionUidParameter, @@ -94,12 +97,14 @@ protected ExecutionDelegate buildExecutionDelegate( return new ReactiveInsertExecutionDelegate( sqmConverter, entityTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, assignments, + assignsId, insertStatement, conflictClause, sessionUidParameter, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java index 7379f962e..05a6d1511 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java @@ -12,6 +12,7 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; @@ -19,7 +20,6 @@ import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedUpdateHandler; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; @@ -47,10 +47,11 @@ public ReactiveTableBasedUpdateHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { - super( sqmUpdate, domainParameterXref, idTable, afterUseAction, sessionUidAccess, sessionFactory ); + super( sqmUpdate, domainParameterXref, idTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, sessionFactory ); } @Override @@ -80,7 +81,8 @@ protected ReactiveExecutionDelegate resolveDelegate(DomainQueryExecutionContext protected ReactiveUpdateExecutionDelegate buildExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup updatingTableGroup, @@ -91,7 +93,8 @@ protected ReactiveUpdateExecutionDelegate buildExecutionDelegate( return new ReactiveUpdateExecutionDelegate( sqmConverter, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, updatingTableGroup, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java index 126c168f8..2b3eda5f5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java @@ -23,7 +23,7 @@ import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.util.impl.CompletionStages; -import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture; /** * @see org.hibernate.dialect.temptable.TemporaryTableHelper @@ -38,7 +38,7 @@ public class ReactiveTemporaryTableHelper { * @see org.hibernate.jdbc.Work */ public interface ReactiveWork { - CompletionStage reactiveExecute(ReactiveConnection connection); + CompletionStage reactiveExecute(ReactiveConnection connection); } public static class TemporaryTableCreationWork implements ReactiveWork { @@ -66,19 +66,22 @@ public TemporaryTableCreationWork( } @Override - public CompletionStage reactiveExecute(ReactiveConnection connection) { + public CompletionStage reactiveExecute(ReactiveConnection connection) { try { final String creationCommand = exporter.getSqlCreateCommand( temporaryTable ); return connection.executeUnprepared( creationCommand ) .handle( (integer, throwable) -> { + if ( throwable == null ) { + return true; + } logException( "create", creationCommand, temporaryTable, throwable ); - return null; + return false; } ); } catch (Exception e) { logException( "create", null, temporaryTable, e ); - return voidFuture(); + return falseFuture(); } } } @@ -111,19 +114,22 @@ public TemporaryTableDropWork( } @Override - public CompletionStage reactiveExecute(ReactiveConnection connection) { + public CompletionStage reactiveExecute(ReactiveConnection connection) { try { final String dropCommand = exporter.getSqlDropCommand( temporaryTable ); return connection.update( dropCommand ) .handle( (integer, throwable) -> { + if ( throwable == null ) { + return true; + } logException( "drop", dropCommand, temporaryTable, throwable ); - return null; + return false; } ); } catch (Exception e) { logException( "drop", null, temporaryTable, e ); - return voidFuture(); + return falseFuture(); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java index 927105dfa..454864b80 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java @@ -17,6 +17,7 @@ import java.util.function.Supplier; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.SelectableConsumer; @@ -24,7 +25,6 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.UpdateExecutionDelegate; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; @@ -60,7 +60,8 @@ public class ReactiveUpdateExecutionDelegate extends UpdateExecutionDelegate imp public ReactiveUpdateExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup updatingTableGroup, @@ -71,7 +72,8 @@ public ReactiveUpdateExecutionDelegate( super( sqmConverter, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, updatingTableGroup, @@ -94,6 +96,7 @@ public int execute(ExecutionContext executionContext) { public CompletionStage reactiveExecute(ExecutionContext executionContext) { return performBeforeTemporaryTableUseActions( getIdTable(), + getTemporaryTableStrategy(), executionContext ) .thenCompose( v -> saveMatchingIdsIntoIdTable( diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java index d67cc2018..63babefd0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java @@ -319,11 +319,12 @@ public void whenComplete(BiConsumer consumer) { private CompletionStage recreateCollections(Object entity, Object id, EntityPersister persister) { final Completable stage = new Completable<>(); + final String entityName = persister.getEntityName(); + final EventMonitor eventMonitor = getEventMonitor(); final Loop loop = new Loop(); forEachOwnedCollection( entity, id, persister, (descriptor, collection) -> { - firePreRecreate( collection, descriptor ); - final EventMonitor eventMonitor = getEventMonitor(); + firePreRecreate( collection, descriptor, entityName, entity ); final DiagnosticEvent event = eventMonitor.beginCollectionRecreateEvent(); loop.then( () -> supplyStage( () -> ( (ReactiveCollectionPersister) descriptor ) .reactiveRecreate( collection, id, this ) ) @@ -335,7 +336,7 @@ private CompletionStage recreateCollections(Object entity, Object id, Enti if ( statistics.isStatisticsEnabled() ) { statistics.recreateCollection( descriptor.getRole() ); } - firePostRecreate( collection, descriptor ); + firePostRecreate( collection, id, entityName, descriptor ); } ) ); } @@ -480,29 +481,35 @@ public CompletionStage reactiveDelete(Object entity) { } private CompletionStage removeCollections(Object entity, Object id, EntityPersister persister) { - final Completable stage = new Completable<>(); - final Loop loop = new Loop(); - forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> { - firePreRemove( collection, entity, descriptor ); - final EventMonitor eventMonitor = getEventMonitor(); - final DiagnosticEvent event = eventMonitor.beginCollectionRemoveEvent(); - loop.then( () -> supplyStage( () -> ( (ReactiveCollectionPersister) descriptor ) - .reactiveRemove( id, this ) ) - .whenComplete( (unused, throwable) -> eventMonitor - .completeCollectionRemoveEvent( event, id, descriptor.getRole(), throwable != null, this ) - ) - .thenAccept( v -> { - firePostRemove( collection, entity, descriptor ); - final StatisticsImplementor statistics = getFactory().getStatistics(); - if ( statistics.isStatisticsEnabled() ) { - statistics.removeCollection( descriptor.getRole() ); - } - } ) - ); - } ); - loop.whenComplete( stage::complete ); - return stage.getStage(); + if ( persister.hasOwnedCollections() ) { + final Loop loop = new Loop(); + final Completable stage = new Completable<>(); + final String entityName = persister.getEntityName(); + forEachOwnedCollection( + entity, id, persister, + (descriptor, collection) -> { + firePreRemove( collection, id, entityName, entity ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginCollectionRemoveEvent(); + loop.then( () -> supplyStage( () -> ( (ReactiveCollectionPersister) descriptor ) + .reactiveRemove( id, this ) ) + .whenComplete( (unused, throwable) -> eventMonitor + .completeCollectionRemoveEvent( event, id, descriptor.getRole(), throwable != null, this ) + ) + .thenAccept( v -> { + firePostRemove( collection, id, entityName, entity ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.removeCollection( descriptor.getRole() ); + } + } ) + ); + } + ); + loop.whenComplete( stage::complete ); + return stage.getStage(); + } + return voidFuture(); } @Override @@ -561,31 +568,37 @@ private CompletionStage executeReactiveUpdate(Object entity) { } private CompletionStage removeAndRecreateCollections(Object entity, Object id, EntityPersister persister) { - final Completable stage = new Completable<>(); - final Loop loop = new Loop(); - forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> { - firePreUpdate( collection, descriptor ); - final EventMonitor eventMonitor = getEventMonitor(); - final DiagnosticEvent event = eventMonitor.beginCollectionRemoveEvent(); - ReactiveCollectionPersister reactivePersister = (ReactiveCollectionPersister) persister; - loop.then( () -> supplyStage( () -> reactivePersister - .reactiveRemove( id, this ) - .thenCompose( v -> reactivePersister.reactiveRecreate( collection, id, this ) ) ) - .whenComplete( (unused, throwable) -> eventMonitor - .completeCollectionRemoveEvent( event, id, descriptor.getRole(), throwable != null, this ) - ) - .thenAccept( v -> { - firePostUpdate( collection, descriptor ); - final StatisticsImplementor statistics = getFactory().getStatistics(); - if ( statistics.isStatisticsEnabled() ) { - statistics.updateCollection( descriptor.getRole() ); - } - } ) - ); - } ); - loop.whenComplete( stage::complete ); - return stage.getStage(); + if ( persister.hasOwnedCollections() ) { + final String entityName = persister.getEntityName(); + final Completable stage = new Completable<>(); + final Loop loop = new Loop(); + forEachOwnedCollection( + entity, id, persister, + (descriptor, collection) -> { + firePreUpdate( collection, id, entityName, entity ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginCollectionRemoveEvent(); + ReactiveCollectionPersister reactivePersister = (ReactiveCollectionPersister) persister; + loop.then( () -> supplyStage( () -> reactivePersister + .reactiveRemove( id, this ) + .thenCompose( v -> reactivePersister.reactiveRecreate( collection, id, this ) ) ) + .whenComplete( (unused, throwable) -> eventMonitor + .completeCollectionRemoveEvent( event, id, descriptor.getRole(), throwable != null, this ) + ) + .thenAccept( v -> { + firePostUpdate( collection, id, entityName, entity); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.updateCollection( descriptor.getRole() ); + } + } ) + ); + } + ); + loop.whenComplete( stage::complete ); + return stage.getStage(); + } + return voidFuture(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/ReactiveResultSetMapping.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/ReactiveResultSetMapping.java index a4b45101f..8b66a3bfb 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/ReactiveResultSetMapping.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/ReactiveResultSetMapping.java @@ -27,7 +27,7 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; /** - * @see org.hibernate.query.results.ResultSetMappingImpl + * @see org.hibernate.query.results.internal.ResultSetMappingImpl */ public class ReactiveResultSetMapping implements ResultSetMapping, ReactiveValuesMappingProducer { @@ -62,6 +62,11 @@ public CompletionStage reactiveResolve( .thenApply( columnCount -> delegate.resolve( jdbcResultsMetadata, loadQueryInfluencers, sessionFactory ) ); } + @Override + public ResultSetMapping cacheKeyInstance() { + return new ReactiveResultSetMapping( delegate.cacheKeyInstance() ); + } + @Override public String getMappingIdentifier() { return delegate.getMappingIdentifier(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java index 46a15c1e9..685717f35 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableFetchImpl.java @@ -38,10 +38,8 @@ public ReactiveEmbeddableFetchImpl(EmbeddableFetchImpl original) { } @Override - public EmbeddableInitializer createInitializer( - InitializerParent parent, - AssemblerCreationState creationState) { - return new ReactiveEmbeddableInitializerImpl( this, getDiscriminatorFetch(), parent, creationState, true ); + public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) { + return new ReactiveEmbeddableInitializerImpl( this, getDiscriminatorFetch(), getNullIndicatorResult(), parent, creationState, true ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableForeignKeyResultImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableForeignKeyResultImpl.java index 1e3433320..426345ab5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableForeignKeyResultImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableForeignKeyResultImpl.java @@ -20,7 +20,7 @@ public ReactiveEmbeddableForeignKeyResultImpl(EmbeddableForeignKeyResultImpl @Override public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) { return getReferencedModePart() instanceof NonAggregatedIdentifierMapping - ? new ReactiveNonAggregatedIdentifierMappingInitializer( this, null, creationState, true ) - : new ReactiveEmbeddableInitializerImpl( this, null, null, creationState, true ); + ? new ReactiveNonAggregatedIdentifierMappingInitializer( this, null, creationState, true ) + : new ReactiveEmbeddableInitializerImpl( this, null, null, null, creationState, true ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java index 02b3a76d1..6d5fbcd5e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveEmbeddableInitializerImpl.java @@ -16,6 +16,7 @@ import org.hibernate.reactive.sql.results.graph.ReactiveDomainResultsAssembler; import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; import org.hibernate.sql.results.graph.AssemblerCreationState; +import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; @@ -66,10 +67,11 @@ public EmbeddableMappingType.ConcreteEmbeddableType getConcreteEmbeddableType() public ReactiveEmbeddableInitializerImpl( EmbeddableResultGraphNode resultDescriptor, BasicFetch discriminatorFetch, + DomainResult nullIndicatorResult, InitializerParent parent, AssemblerCreationState creationState, boolean isResultInitializer) { - super( resultDescriptor, discriminatorFetch, parent, creationState, isResultInitializer ); + super( resultDescriptor, discriminatorFetch, nullIndicatorResult, parent, creationState, isResultInitializer ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java index ac119a7d0..8de288c0e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDeferredResultSetAccess.java @@ -7,14 +7,15 @@ import java.lang.invoke.MethodHandles; import java.sql.ResultSet; -import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import org.hibernate.HibernateException; +import org.hibernate.JDBCException; import org.hibernate.LockOptions; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -65,6 +66,17 @@ public ReactiveDeferredResultSetAccess( this.sqlStatementLogger = executionContext.getSession().getJdbcServices().getSqlStatementLogger(); } + @Override + public JdbcServices getJdbcServices() { + return getFactory().getJdbcServices(); + } + + @Override + public JDBCException convertSqlException(SQLException e, String message) { + return getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper() + .convert( e, message ); + } + /** * Reactive version of {@link org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess#registerAfterLoadAction(ExecutionContext, LockOptions)} * calling {@link ReactiveSession#reactiveLock(String, Object, LockOptions)} @@ -141,20 +153,6 @@ private JdbcValuesMetadata convertToMetadata(ResultSet resultSet) { return (JdbcValuesMetadata) resultSet; } - @Override - public CompletionStage getReactiveMetadata() { - return getReactiveResultSet().thenApply( this::reactiveMetadata ); - } - - private ResultSetMetaData reactiveMetadata(ResultSet resultSet) { - try { - return resultSet.getMetaData(); - } - catch (SQLException e) { - throw new RuntimeException( e ); - } - } - private static int columnCount(ResultSet resultSet) { try { return resultSet.getMetaData().getColumnCount(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDirectResultSetAccess.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDirectResultSetAccess.java index 2412f3941..80d3217aa 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDirectResultSetAccess.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveDirectResultSetAccess.java @@ -8,9 +8,11 @@ import java.lang.invoke.MethodHandles; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.ResultSetMetaData; +import java.sql.SQLException; import java.util.concurrent.CompletionStage; +import org.hibernate.JDBCException; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.reactive.logging.impl.Log; @@ -40,8 +42,13 @@ public ReactiveDirectResultSetAccess( } @Override - public SessionFactoryImplementor getFactory() { - return getPersistenceContext().getFactory(); + public JdbcServices getJdbcServices() { + return getFactory().getJdbcServices(); + } + + @Override + public JDBCException convertSqlException(SQLException e, String message) { + return getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper().convert( e, message ); } @Override @@ -73,11 +80,6 @@ public CompletionStage getReactiveResultSet() { return completedFuture( resultSet ); } - @Override - public CompletionStage getReactiveMetadata() { - return completedFuture( getMetaData() ); - } - @Override public CompletionStage getReactiveColumnCount() { return completedFuture( getColumnCount() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveResultSetAccess.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveResultSetAccess.java index 75a265ed5..ad9be5a6a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveResultSetAccess.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveResultSetAccess.java @@ -10,9 +10,9 @@ import java.sql.SQLException; import java.util.concurrent.CompletionStage; +import org.hibernate.JDBCException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaType; @@ -27,14 +27,14 @@ */ public interface ReactiveResultSetAccess extends JdbcValuesMetadata { CompletionStage getReactiveResultSet(); - CompletionStage getReactiveMetadata(); CompletionStage getReactiveColumnCount(); CompletionStage resolveJdbcValueMetadata(); ResultSet getResultSet(); - SessionFactoryImplementor getFactory(); + JdbcServices getJdbcServices(); + void release(); /** @@ -51,43 +51,35 @@ default int getColumnCount() { return getResultSet().getMetaData().getColumnCount(); } catch (SQLException e) { - throw getFactory().getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper().convert( - e, - "Unable to access ResultSet column count" - ); + throw convertSqlException( e, "Unable to access ResultSet column count" ); } } + JDBCException convertSqlException(SQLException e, String message); + default int resolveColumnPosition(String columnName) { try { return getResultSet().findColumn( columnName ); } catch (SQLException e) { - throw getFactory().getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper().convert( - e, - "Unable to find column position by name" - ); + throw convertSqlException( e, "Unable to find column position by name" ); } } default String resolveColumnName(int position) { try { - return getFactory().getJdbcServices().getJdbcEnvironment() + return getJdbcServices().getJdbcEnvironment() .getDialect() .getColumnAliasExtractor() .extractColumnAlias( getResultSet().getMetaData(), position ); } catch (SQLException e) { - throw getFactory().getJdbcServices().getJdbcEnvironment().getSqlExceptionHelper().convert( - e, - "Unable to find column name by position" - ); + throw convertSqlException( e, "Unable to find column name by position" ); } } @Override default BasicType resolveType(int position, JavaType explicitJavaType, TypeConfiguration typeConfiguration) { - final JdbcServices jdbcServices = getFactory().getJdbcServices(); try { final ResultSetMetaData metaData = getResultSet().getMetaData(); final String columnTypeName = metaData.getColumnTypeName( position ); @@ -95,7 +87,7 @@ default BasicType resolveType(int position, JavaType explicitJavaType, final int scale = metaData.getScale( position ); final int precision = metaData.getPrecision( position ); final int displaySize = metaData.getColumnDisplaySize( position ); - final Dialect dialect = jdbcServices.getDialect(); + final Dialect dialect = getJdbcServices().getDialect(); final int length = dialect.resolveSqlTypeLength( columnTypeName, columnType, @@ -104,13 +96,7 @@ default BasicType resolveType(int position, JavaType explicitJavaType, displaySize ); final JdbcType resolvedJdbcType = dialect - .resolveSqlTypeDescriptor( - columnTypeName, - columnType, - length, - scale, - typeConfiguration.getJdbcTypeRegistry() - ); + .resolveSqlTypeDescriptor( columnTypeName, columnType, length, scale, typeConfiguration.getJdbcTypeRegistry() ); final JavaType javaType; final JdbcType jdbcType; // If there is an explicit JavaType, then prefer its recommended JDBC type @@ -145,26 +131,19 @@ public EnumType getEnumeratedType() { @Override public Dialect getDialect() { - return getFactory().getJdbcServices().getDialect(); + return getJdbcServices().getDialect(); } } ); } else { jdbcType = resolvedJdbcType; - javaType = jdbcType.getJdbcRecommendedJavaTypeMapping( - length, - scale, - typeConfiguration - ); + javaType = jdbcType.getJdbcRecommendedJavaTypeMapping( length, scale, typeConfiguration ); } return typeConfiguration.getBasicTypeRegistry().resolve( javaType, jdbcType ); } catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( - e, - "Unable to determine JDBC type code for ResultSet position " + position - ); + throw convertSqlException( e, "Unable to determine JDBC type code for ResultSet position " + position ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java index 81af936ba..ab84952ef 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java @@ -5,26 +5,27 @@ */ package org.hibernate.reactive.type.descriptor.jdbc; -import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + 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.BasicPluralJavaType; import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; +import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType; import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.BasicExtractor; import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JsonHelper; -import org.hibernate.type.descriptor.jdbc.JsonJdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JsonGeneratingVisitor; +import org.hibernate.type.format.StringJsonDocumentWriter; import io.vertx.core.json.JsonArray; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; /** * @see org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType @@ -65,21 +66,24 @@ protected X fromString(String string, JavaType javaType, WrapperOptions o if ( string == null ) { return null; } - - return JsonHelper.arrayFromString( javaType, getElementJdbcType(), string, options ); + if ( ((BasicPluralJavaType) javaType).getElementJavaType() instanceof UnknownBasicJavaType ) { + return options.getJsonFormatMapper().fromString( string, javaType, options ); + } + else { + return JsonHelper.arrayFromString( javaType, this.getElementJdbcType(), string, options ); + } } protected String toString(X value, JavaType javaType, WrapperOptions options) { - final JdbcType elementJdbcType = getElementJdbcType(); - final Object[] domainObjects = javaType.unwrap( value, Object[].class, options ); - if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) { - final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType(); - return JsonHelper.arrayToString( embeddableMappingType, domainObjects, options ); + final JavaType elementJavaType = ( (BasicPluralJavaType) javaType ).getElementJavaType(); + if ( elementJavaType instanceof UnknownBasicJavaType ) { + return options.getJsonFormatMapper().toString( value, javaType, options); } else { - assert !( elementJdbcType instanceof AggregateJdbcType ); - final JavaType elementJavaType = ( (BasicPluralJavaType) javaType ).getElementJavaType(); - return JsonHelper.arrayToString( elementJavaType, elementJdbcType, domainObjects, options ); + final Object[] domainObjects = javaType.unwrap( value, Object[].class, options ); + final StringJsonDocumentWriter writer = new StringJsonDocumentWriter(); + JsonGeneratingVisitor.INSTANCE.visitArray( elementJavaType, getElementJdbcType(), domainObjects, options, writer ); + return writer.getJson(); } } @@ -121,12 +125,8 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o } private X getObject(Object array, WrapperOptions options) throws SQLException { - if ( array == null ) { - return null; - } - - return ( (ReactiveJsonArrayJdbcType) getJdbcType() ) - .fromString( ( (JsonArray) array ).encode(), getJavaType(), options ); + final String json = array == null ? null : ( (JsonArray) array ).encode(); + return ( (ReactiveJsonArrayJdbcType) getJdbcType() ).fromString( json, getJavaType(), options ); } }; } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ImplicitSoftDeleteTests.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ImplicitSoftDeleteTests.java new file mode 100644 index 000000000..f1c08d4ce --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ImplicitSoftDeleteTests.java @@ -0,0 +1,225 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletionStage; + +import org.hibernate.ObjectNotFoundException; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.SoftDelete; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Tuple; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.reactive.common.Identifier.id; +import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; + +@Timeout(value = 10, timeUnit = MINUTES) +public class ImplicitSoftDeleteTests extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( ImplicitEntity.class ); + } + + @Override + protected CompletionStage cleanDb() { + return voidFuture(); + } + + @BeforeEach + void createTestData(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createNativeQuery( "delete from implicit_entities" ).executeUpdate() ) + .call( () -> getMutinySessionFactory().withTransaction( s -> s + .persistAll( new ImplicitEntity( 1, "first" ), new ImplicitEntity( 2, "second" ), new ImplicitEntity( 3, "third" ) ) + ) ) + .chain( () -> getMutinySessionFactory().withTransaction( s -> { + final ImplicitEntity first = s.getReference( ImplicitEntity.class, 1 ); + return s.remove( first ).call( s::flush ); + } ) ) + .call( () -> getMutinySessionFactory() + .withTransaction( s -> s.createNativeQuery( "select * from implicit_entities e order by id", Tuple.class ).getResultList() ) + .invoke( tuples -> assertThat( tuples ).hasSize( 3 ) ) + ) + ); + } + + @Test + void testSelectionQuery(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createQuery( "from ImplicitEntity", ImplicitEntity.class ).getResultList() ) + .invoke( list -> assertThat( list ).hasSize( 2 ) ) + ); + } + + @Test + void testLoading(VertxTestContext context) { + test( + context, getMutinySessionFactory() + // Load + .withTransaction( s -> s + .find( ImplicitEntity.class, 1 ).invoke( entity -> assertThat( entity ).isNull() ) + .call( () -> s.find( ImplicitEntity.class, 2 ).invoke( entity -> assertThat( entity ).isNotNull() ) ) + .call( () -> s.find( ImplicitEntity.class, 3 ).invoke( entity -> assertThat( entity ).isNotNull() ) ) + ) + // Proxy + // We deleted the entity, so we expect an ObjectNotFoundException + .chain( () -> assertThrown( ObjectNotFoundException.class, getMutinySessionFactory().withTransaction( s -> { + final ImplicitEntity reference = s.getReference( ImplicitEntity.class, 1 ); + return s.fetch( reference ).map( ImplicitEntity::getName ); + } ) ) ) + .chain( () -> getMutinySessionFactory().withTransaction( s -> { + final ImplicitEntity reference = s.getReference( ImplicitEntity.class, 2 ); + return s.fetch( reference ).map( ImplicitEntity::getName ); + } ) ) + .invoke( name -> assertThat( name ).isEqualTo( "second" ) ) + .chain( () -> getMutinySessionFactory().withTransaction( s -> { + final ImplicitEntity reference = s.getReference( ImplicitEntity.class, 3 ); + return s.fetch( reference ).map( ImplicitEntity::getName ); + } ) ) + .invoke( name -> assertThat( name ).isEqualTo( "third" ) ) + ); + } + + @Test + void testMultiLoading(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( ImplicitEntity.class, 1, 2, 3 ) ) + .invoke( list -> assertThat( list ) + .containsExactly( null, new ImplicitEntity( null, "second" ), new ImplicitEntity( null, "third" ) ) ) + ); + } + + @Test + void testNaturalIdLoading(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( ImplicitEntity.class, id( "name", "first" ) ) ) + .invoke( entity -> assertThat( entity ).isNull() ) + .chain( () -> getMutinySessionFactory().withTransaction( s -> s.find( ImplicitEntity.class, id( "name", "second" ) ) ) ) + .invoke( entity -> assertThat( entity ).extracting( ImplicitEntity::getId ).isEqualTo( 2 ) ) + .chain( () -> getMutinySessionFactory().withTransaction( s -> s.find( ImplicitEntity.class, id( "name", "third" ) ) ) ) + .invoke( entity -> assertThat( entity ).extracting( ImplicitEntity::getId ).isEqualTo( 3 ) ) + ); + } + + @Test + void testDeletion(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> { + final ImplicitEntity reference = s.getReference( ImplicitEntity.class, 2 ); + return s.remove( reference ).call( s::flush ) + .call( () -> s.createSelectionQuery( "from ImplicitEntity", ImplicitEntity.class ).getResultList() + // #1 was "deleted" up front and we just "deleted" #2... only #3 should be active + .invoke( list -> { + assertThat( list ).extracting( ImplicitEntity::getId ).containsExactly( 3 ); + assertThat( list ).extracting( ImplicitEntity::getName ).containsExactly( "third" ); + } ) + ); + } ) + ); + } + + @Test + void testFullUpdateMutationQuery(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createMutationQuery( "update ImplicitEntity set name = null" ).executeUpdate() ) + .invoke( affected -> assertThat( affected ).isEqualTo( 2 ) ) + ); + } + + @Test + void testRestrictedUpdateMutationQuery(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createMutationQuery( "update ImplicitEntity set name = null where name = 'second'" ).executeUpdate() ) + .invoke( affected -> assertThat( affected ).isEqualTo( 1 ) ) + ); + } + + @Test + void testFullDeleteMutationQuery(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createMutationQuery( "delete ImplicitEntity" ).executeUpdate() ) + // only #2 and #3 + .invoke( affected -> assertThat( affected ).isEqualTo( 2 ) ) + ); + } + + @Test + void testRestrictedDeleteMutationQuery(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.createMutationQuery( "delete ImplicitEntity where name = 'second'" ).executeUpdate() ) + // only #2 + .invoke( affected -> assertThat( affected ).isEqualTo( 1 ) ) + ); + } + + + @Entity(name = "ImplicitEntity") + @Table(name = "implicit_entities") + @SoftDelete + public static class ImplicitEntity { + @Id + private Integer id; + @NaturalId + private String name; + + public ImplicitEntity() { + } + + public ImplicitEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object object) { + if ( object == null || getClass() != object.getClass() ) { + return false; + } + ImplicitEntity that = (ImplicitEntity) object; + return Objects.equals( name, that.name ); + } + + @Override + public int hashCode() { + return Objects.hashCode( name ); + } + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyPropertyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyPropertyTest.java index 3d22d0e71..73233b5e9 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyPropertyTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyPropertyTest.java @@ -146,14 +146,14 @@ private String getIsbn() { private Author author; public Book(String isbn, String title, Author author) { - super( "Book", 1, singleton( "isbn" ), null ); + super( new EntityRelatedState( "Book", singleton( "isbn" ) ), 1, null ); this.title = title; this.isbn = isbn; this.author = author; } public Book() { - super( "Book", 1, singleton( "isbn" ), null ); + super( new EntityRelatedState( "Book", singleton( "isbn" ) ), 1, null ); } @Override diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java index 8dcc07a6a..8cf8e6931 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UpsertTest.java @@ -144,14 +144,10 @@ private void assertQueries() { } private boolean hasMergeOperator() { - switch ( dbType() ) { - case SQLSERVER: - case ORACLE: - case POSTGRESQL: - return true; - default: - return false; - } + return switch ( dbType() ) { + case SQLSERVER, ORACLE, POSTGRESQL, DB2 -> true; + default -> false; + }; } @Entity(name = "Record") diff --git a/integration-tests/bytecode-enhancements-it/build.gradle b/integration-tests/bytecode-enhancements-it/build.gradle index 74ee2d563..aaf1f5d8a 100644 --- a/integration-tests/bytecode-enhancements-it/build.gradle +++ b/integration-tests/bytecode-enhancements-it/build.gradle @@ -41,5 +41,8 @@ dependencies { testImplementation(libs.io.vertx.vertx.junit5) } -// Optional: enable the bytecode enhancements -hibernate { enhancement } +hibernate { + enhancement { + // We want everything enabled for the tests + } +} diff --git a/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java b/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java index c2300977e..eef39ba37 100644 --- a/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java +++ b/integration-tests/verticle-postgres-it/src/test/java/org/hibernate/reactive/it/LocalContextTest.java @@ -49,7 +49,7 @@ * that's been close ahead of time by someone else. * Theoretically, everything could happen in the right order because of chance, * but it's unlikely and at the moment I don't have a better solution. - * See the the related issue + * See the related issue * for more details. *

*/ diff --git a/settings.gradle b/settings.gradle index 159d91e20..9e3962b53 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,13 +24,13 @@ def GRADLE_MAX_SUPPORTED_BYTECODE_VERSION = 23 dependencyResolutionManagement { versionCatalogs { create("libs") { - // ./gradlew build -PhibernateOrmVersion=7.0.2.Final + // ./gradlew build -PhibernateOrmVersion=7.1.0.CR1 def hibernateOrmVersion = settings.ext.find("hibernateOrmVersion") ?: "" if ( hibernateOrmVersion != "" ) { version("hibernateOrmVersion", hibernateOrmVersion) } - // ./gradlew build -PhibernateOrmGradlePluginVersion=7.0.2.Final + // ./gradlew build -PhibernateOrmGradlePluginVersion=7.1.0.CR1 def hibernateOrmGradlePluginVersion = settings.ext.find("hibernateOrmGradlePluginVersion") ?: "" if ( hibernateOrmGradlePluginVersion ) { version("hibernateOrmGradlePluginVersion", hibernateOrmGradlePluginVersion) From 19bf867c9377c43702ac13633fd5ec86bcd1e1c9 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 1 Aug 2025 09:53:27 +0200 Subject: [PATCH 71/85] [#2387] Downgrade Hibernate Gradle plugin to 7.0.8.Final The latest Hibernate Gradle plugin requires at list JVM 22. This downgrade won't affect the released artifact. --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a884473b..c4f8c1e4b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" hibernateOrmVersion = "7.1.0.CR1" -hibernateOrmGradlePluginVersion = "7.1.0.CR1" +hibernateOrmGradlePluginVersion = "7.0.8.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" From e61fd1f8560747a8e96de2926103f37c2371dd49 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 4 Aug 2025 11:08:53 +0200 Subject: [PATCH 72/85] [#2404] Upgrade Hibernate ORM to 7.1.0.CR2 --- gradle/libs.versions.toml | 2 +- .../impl/ReactiveAbstractEntityPersister.java | 19 +- .../sql/spi/ReactiveNamedSqmQueryMemento.java | 6 +- .../ConcreteSqmSelectReactiveQueryPlan.java | 274 +++----- ...veAbstractMultiTableMutationQueryPlan.java | 35 + .../ReactiveMultiTableDeleteQueryPlan.java | 25 +- .../ReactiveMultiTableInsertQueryPlan.java | 25 +- .../ReactiveMultiTableUpdateQueryPlan.java | 28 +- .../ReactiveSimpleDeleteQueryPlan.java | 232 ++----- .../ReactiveSimpleInsertQueryPlan.java | 121 ---- .../ReactiveSimpleNonSelectQueryPlan.java | 56 ++ .../ReactiveSimpleUpdateQueryPlan.java | 117 ---- ...SqmImpl.java => ReactiveSqmQueryImpl.java} | 169 ++--- .../mutation/internal/ReactiveHandler.java | 32 +- .../ReactiveSqmMutationStrategyHelper.java | 161 ----- .../ReactiveAbstractCteMutationHandler.java | 180 +----- .../cte/ReactiveCteDeleteHandler.java | 41 +- .../cte/ReactiveCteInsertHandler.java | 534 +--------------- .../cte/ReactiveCteInsertStrategy.java | 23 +- .../cte/ReactiveCteMutationStrategy.java | 67 +- .../cte/ReactiveCteSoftDeleteHandler.java | 43 ++ .../cte/ReactiveCteUpdateHandler.java | 42 +- .../cte/ReactiveInsertExecutionDelegate.java | 72 --- ...activeExecuteWithTemporaryTableHelper.java | 249 ++----- ...iveGlobalTemporaryTableInsertStrategy.java | 22 +- ...eGlobalTemporaryTableMutationStrategy.java | 95 ++- ...tiveLocalTemporaryTableInsertStrategy.java | 35 +- ...veLocalTemporaryTableMutationStrategy.java | 79 ++- ...ReactivePersistentTableInsertStrategy.java | 20 +- ...activePersistentTableMutationStrategy.java | 83 ++- ...tiveRestrictedDeleteExecutionDelegate.java | 605 ------------------ .../ReactiveSoftDeleteExecutionDelegate.java | 320 --------- .../ReactiveTableBasedDeleteHandler.java | 142 ++-- .../ReactiveTableBasedInsertHandler.java | 422 ++++++++++-- .../ReactiveTableBasedSoftDeleteHandler.java | 129 ++++ .../ReactiveTableBasedUpdateHandler.java | 125 ++-- .../ReactiveTemporaryTableHelper.java | 2 - .../ReactiveUpdateExecutionDelegate.java | 295 --------- .../spi/ReactiveAbstractMutationHandler.java | 3 - .../ReactiveSqmMultiTableInsertStrategy.java | 16 +- ...ReactiveSqmMultiTableMutationStrategy.java | 32 +- .../session/impl/ReactiveSessionImpl.java | 18 +- .../impl/ReactiveStatelessSessionImpl.java | 14 +- 43 files changed, 1543 insertions(+), 3467 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveAbstractMultiTableMutationQueryPlan.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleInsertQueryPlan.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleNonSelectQueryPlan.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleUpdateQueryPlan.java rename hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/{ReactiveQuerySqmImpl.java => ReactiveSqmQueryImpl.java} (80%) delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveSqmMutationStrategyHelper.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteSoftDeleteHandler.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedSoftDeleteHandler.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c4f8c1e4b..694d04bcd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.1.0.CR1" +hibernateOrmVersion = "7.1.0.CR2" hibernateOrmGradlePluginVersion = "7.0.8.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java index 8e96291eb..129b9e236 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractEntityPersister.java @@ -80,7 +80,6 @@ import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; -import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.logSqlException; import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; @@ -303,25 +302,25 @@ default CompletionStage reactiveGetCurrentVersion(Object id, SharedSessi return getReactiveConnection( session ) .selectJdbc( delegate().getVersionSelectString(), params ) - .thenCompose( resultSet -> currentVersion( session, resultSet ) ); + .thenApply( resultSet -> currentVersion( session, resultSet ) ); } - private CompletionStage currentVersion(SharedSessionContractImplementor session, ResultSet resultSet) { + private Object currentVersion(SharedSessionContractImplementor session, ResultSet resultSet) { try { if ( !resultSet.next() ) { - return nullFuture(); + return null; } if ( !isVersioned() ) { - return completedFuture( this ); + return this; } - return completedFuture( getVersionType() - .getJdbcMapping() - .getJdbcValueExtractor() - .extract( resultSet, 1, session ) ); + return getVersionType() + .getJdbcMapping() + .getJdbcValueExtractor() + .extract( resultSet, 1, session ); } catch (SQLException sqle) { //can never happen - return failedFuture( new JDBCException( "error reading version", sqle ) ); + throw new JDBCException( "error reading version", sqle ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedSqmQueryMemento.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedSqmQueryMemento.java index 388488b26..aae4316a3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedSqmQueryMemento.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedSqmQueryMemento.java @@ -20,7 +20,7 @@ import org.hibernate.query.sqm.spi.NamedSqmQueryMemento; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; -import org.hibernate.reactive.query.sqm.internal.ReactiveQuerySqmImpl; +import org.hibernate.reactive.query.sqm.internal.ReactiveSqmQueryImpl; import org.hibernate.reactive.query.sqm.internal.ReactiveSqmSelectionQueryImpl; /** @@ -49,10 +49,10 @@ public SqmQueryImplementor toQuery(SharedSessionContractImplementor session) public SqmQueryImplementor toQuery(SharedSessionContractImplementor session, Class resultType) { // A bit of a hack, I'm sure that if we have a better look at this we can avoid the instanceof if ( delegate instanceof NamedHqlQueryMementoImpl ) { - return new ReactiveQuerySqmImpl<>( (NamedHqlQueryMementoImpl) delegate, resultType, session ); + return new ReactiveSqmQueryImpl<>( (NamedHqlQueryMementoImpl) delegate, resultType, session ); } if ( delegate instanceof NamedCriteriaQueryMementoImpl ) { - return new ReactiveQuerySqmImpl<>( (NamedCriteriaQueryMementoImpl) delegate, resultType, session ); + return new ReactiveSqmQueryImpl<>( (NamedCriteriaQueryMementoImpl) delegate, resultType, session ); } else { throw new UnsupportedOperationException( "NamedSqmQueryMemento not recognized: " + delegate.getClass() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java index 7c7cfd062..81f1addf5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java @@ -6,40 +6,26 @@ package org.hibernate.reactive.query.sqm.internal; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.hibernate.ScrollMode; -import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; -import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.Query; import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryOptions; -import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; +import org.hibernate.query.sqm.internal.CacheableSqmInterpretation; import org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan; import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.SqmTranslation; -import org.hibernate.query.sqm.sql.SqmTranslator; -import org.hibernate.query.sqm.sql.SqmTranslatorFactory; -import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.reactive.sql.results.spi.ReactiveResultsConsumer; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.FromClauseAccess; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -68,7 +54,7 @@ public class ConcreteSqmSelectReactiveQueryPlan extends ConcreteSqmSelectQuer private final SqmSelectStatement sqm; private final DomainParameterXref domainParameterXref; - private volatile CacheableSqmInterpretation cacheableSqmInterpretation; + private volatile CacheableSqmInterpretation cacheableSqmInterpretation; public ConcreteSqmSelectReactiveQueryPlan( SqmSelectStatement sqm, @@ -91,27 +77,46 @@ private static CompletionStage> listInterpreter( String hql, DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext, - CacheableSqmInterpretation sqmInterpretation, + CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings, RowTransformer rowTransformer) { final ReactiveSharedSessionContractImplementor session = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); - final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect(); - // I'm using a supplier so that the whenComplete at the end will catch any errors, like a finally-block - Supplier fetchHandlerSupplier = () -> SubselectFetch - .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.selectStatement, JdbcParametersList.empty(), jdbcParameterBindings ); - return completedFuture( fetchHandlerSupplier ) - .thenApply( Supplier::get ) - .thenCompose( subSelectFetchKeyHandler -> session + final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation(); + + return CompletionStages + .supplyStage( () -> { + final var subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext() + .getBatchFetchQueue(), + sqmInterpretation.statement(), + JdbcParametersList.empty(), + jdbcParameterBindings + ); + return session .reactiveAutoFlushIfRequired( jdbcSelect.getAffectedTableNames() ) - .thenCompose( required -> StandardReactiveSelectExecutor.INSTANCE - .list( jdbcSelect, - jdbcParameterBindings, - ConcreteSqmSelectQueryPlan.listInterpreterExecutionContext( hql, executionContext, jdbcSelect, subSelectFetchKeyHandler ), - rowTransformer, - ReactiveListResultsConsumer.UniqueSemantic.ALLOW - ) - ) - ) + .thenCompose( required -> { + final Expression fetchExpression = sqmInterpretation.statement() + .getQueryPart() + .getFetchClauseExpression(); + final int resultCountEstimate = fetchExpression != null + ? interpretIntExpression( fetchExpression, jdbcParameterBindings ) + : -1; + return StandardReactiveSelectExecutor.INSTANCE.list( + jdbcSelect, + jdbcParameterBindings, + ConcreteSqmSelectQueryPlan.listInterpreterExecutionContext( + hql, + executionContext, + jdbcSelect, + subSelectFetchKeyHandler + ), + rowTransformer, + (Class) executionContext.getResultType(), + ReactiveListResultsConsumer.UniqueSemantic.ALLOW, + resultCountEstimate + ); + } ); + } ) .whenComplete( (rs, t) -> domainParameterXref.clearExpansions() ); } @@ -119,43 +124,50 @@ private static CompletionStage executeQueryInterpreter( String hql, DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext, - CacheableSqmInterpretation sqmInterpretation, + CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings, RowTransformer rowTransformer, ReactiveResultsConsumer resultsConsumer) { final ReactiveSharedSessionContractImplementor session = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); - final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect(); + final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation(); // I'm using a supplier so that the whenComplete at the end will catch any errors, like a finally-block - Supplier fetchHandlerSupplier = () -> SubselectFetch - .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.selectStatement, JdbcParametersList.empty(), jdbcParameterBindings ); - return completedFuture( fetchHandlerSupplier ) - .thenApply( Supplier::get ) - .thenCompose( subSelectFetchKeyHandler -> session - .reactiveAutoFlushIfRequired( jdbcSelect.getAffectedTableNames() ) - .thenCompose( required -> StandardReactiveSelectExecutor.INSTANCE - .executeQuery( - jdbcSelect, - jdbcParameterBindings, - ConcreteSqmSelectQueryPlan.listInterpreterExecutionContext( - hql, - executionContext, - jdbcSelect, - subSelectFetchKeyHandler - ), - rowTransformer, - null, - resultCountEstimate( sqmInterpretation, jdbcParameterBindings ), - resultsConsumer - ) - ) - ) + final Supplier fetchHandlerSupplier = () -> SubselectFetch + .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.statement(), JdbcParametersList.empty(), jdbcParameterBindings ); + return CompletionStages + .supplyStage( () -> { + final var subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext() + .getBatchFetchQueue(), + sqmInterpretation.statement(), + JdbcParametersList.empty(), + jdbcParameterBindings + ); + return session + .reactiveAutoFlushIfRequired( jdbcSelect.getAffectedTableNames() ) + .thenCompose( required -> StandardReactiveSelectExecutor.INSTANCE + .executeQuery( + jdbcSelect, + jdbcParameterBindings, + ConcreteSqmSelectQueryPlan.listInterpreterExecutionContext( + hql, + executionContext, + jdbcSelect, + subSelectFetchKeyHandler + ), + rowTransformer, + null, + resultCountEstimate( sqmInterpretation, jdbcParameterBindings ), + resultsConsumer + ) + ); + }) .whenComplete( (rs, t) -> domainParameterXref.clearExpansions() ); } private static int resultCountEstimate( - CacheableSqmInterpretation sqmInterpretation, + CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings) { - final Expression fetchExpression = sqmInterpretation.selectStatement.getQueryPart() + final Expression fetchExpression = sqmInterpretation.statement().getQueryPart() .getFetchClauseExpression(); return fetchExpression != null ? interpretIntExpression( fetchExpression, jdbcParameterBindings ) @@ -191,16 +203,16 @@ private CompletionStage withCacheableSqmInterpretation(DomainQueryExec // to protect access. However, synchronized is much simpler here. We will verify // during throughput testing whether this is an issue and consider changes then - CacheableSqmInterpretation localCopy = cacheableSqmInterpretation; + CacheableSqmInterpretation localCopy = cacheableSqmInterpretation; JdbcParameterBindings jdbcParameterBindings = null; if ( localCopy == null ) { synchronized ( this ) { localCopy = cacheableSqmInterpretation; if ( localCopy == null ) { - localCopy = buildCacheableSqmInterpretation( sqm, domainParameterXref, executionContext ); - jdbcParameterBindings = localCopy.firstParameterBindings; - localCopy.firstParameterBindings = null; + final MutableObject mutableValue = new MutableObject<>(); + localCopy = buildInterpretation( sqm, domainParameterXref, executionContext, mutableValue ); + jdbcParameterBindings = mutableValue.get(); cacheableSqmInterpretation = localCopy; } } @@ -208,15 +220,15 @@ private CompletionStage withCacheableSqmInterpretation(DomainQueryExec else { // If the translation depends on parameter bindings, or it isn't compatible with the current query options, // we have to rebuild the JdbcSelect, which is still better than having to translate from SQM to SQL AST again - if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) { + if ( localCopy.jdbcOperation().dependsOnParameterBindings() ) { jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); } // If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect // We could avoid this by putting the lock options into the cache key - if ( !localCopy.jdbcSelect.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { - localCopy = buildCacheableSqmInterpretation( sqm, domainParameterXref, executionContext ); - jdbcParameterBindings = localCopy.firstParameterBindings; - localCopy.firstParameterBindings = null; + if ( !localCopy.jdbcOperation().isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { + final MutableObject mutableValue = new MutableObject<>(); + localCopy = buildInterpretation( sqm, domainParameterXref, executionContext, mutableValue ); + jdbcParameterBindings = mutableValue.get(); cacheableSqmInterpretation = localCopy; } } @@ -228,130 +240,12 @@ private CompletionStage withCacheableSqmInterpretation(DomainQueryExec return interpreter.interpret( context, executionContext, localCopy, jdbcParameterBindings ); } - // Copy and paste from ORM - private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, DomainQueryExecutionContext executionContext) { - return SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - domainParameterXref, - sqmInterpretation.getJdbcParamsXref(), - new SqmParameterMappingModelResolutionAccess() { - //this is pretty ugly! - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypes().get(parameter); - } - }, - executionContext.getSession() - ); - } - - private static CacheableSqmInterpretation buildCacheableSqmInterpretation( - SqmSelectStatement sqm, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext executionContext) { - final SharedSessionContractImplementor session = executionContext.getSession(); - final SessionFactoryImplementor sessionFactory = session.getFactory(); - final QueryEngine queryEngine = sessionFactory.getQueryEngine(); - - final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory(); - - final SqmTranslator sqmConverter = sqmTranslatorFactory.createSelectTranslator( - sqm, - executionContext.getQueryOptions(), - domainParameterXref, - executionContext.getQueryParameterBindings(), - executionContext.getSession().getLoadQueryInfluencers(), - sessionFactory.getSqlTranslationEngine(), - true - ); - -// tableGroupAccess = sqmConverter.getFromClauseAccess(); - final SqmTranslation sqmInterpretation = sqmConverter.translate(); - final FromClauseAccess tableGroupAccess = sqmConverter.getFromClauseAccess(); - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - final SqlAstTranslator selectTranslator = sqlAstTranslatorFactory - .buildSelectTranslator( sessionFactory, sqmInterpretation.getSqlAst() ); - final Map, Map, List>> jdbcParamsXref = SqmUtil - .generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam ); - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - domainParameterXref, - jdbcParamsXref, - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get(parameter); - } - }, - session - ); - - final JdbcOperationQuerySelect jdbcSelect = selectTranslator.translate( - jdbcParameterBindings, - executionContext.getQueryOptions() - ); - - return new CacheableSqmInterpretation( - sqmInterpretation.getSqlAst(), - jdbcSelect, - tableGroupAccess, - jdbcParamsXref, - sqmInterpretation.getSqmParameterMappingModelTypeResolutions(), - jdbcParameterBindings - ); - } - private interface SqmInterpreter { CompletionStage interpret( X context, DomainQueryExecutionContext executionContext, - CacheableSqmInterpretation sqmInterpretation, + CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings); } - private static class CacheableSqmInterpretation { - private final SelectStatement selectStatement; - private final JdbcOperationQuerySelect jdbcSelect; - private final FromClauseAccess tableGroupAccess; - private final Map, Map, List>> jdbcParamsXref; - private final Map, MappingModelExpressible> sqmParameterMappingModelTypes; - private transient JdbcParameterBindings firstParameterBindings; - - CacheableSqmInterpretation( - SelectStatement selectStatement, - JdbcOperationQuerySelect jdbcSelect, - FromClauseAccess tableGroupAccess, - Map, Map, List>> jdbcParamsXref, - Map, MappingModelExpressible> sqmParameterMappingModelTypes, - JdbcParameterBindings firstParameterBindings) { - this.selectStatement = selectStatement; - this.jdbcSelect = jdbcSelect; - this.tableGroupAccess = tableGroupAccess; - this.jdbcParamsXref = jdbcParamsXref; - this.sqmParameterMappingModelTypes = sqmParameterMappingModelTypes; - this.firstParameterBindings = firstParameterBindings; - } - - SelectStatement getSelectStatement() { - return selectStatement; - } - - JdbcOperationQuerySelect getJdbcSelect() { - return jdbcSelect; - } - - FromClauseAccess getTableGroupAccess() { - return tableGroupAccess; - } - - Map, Map, List>> getJdbcParamsXref() { - return jdbcParamsXref; - } - - public Map, MappingModelExpressible> getSqmParameterMappingModelTypes() { - return sqmParameterMappingModelTypes; - } - } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveAbstractMultiTableMutationQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveAbstractMultiTableMutationQueryPlan.java new file mode 100644 index 000000000..a1759ccbc --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveAbstractMultiTableMutationQueryPlan.java @@ -0,0 +1,35 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.query.sqm.internal; + +import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.internal.AbstractMultiTableMutationQueryPlan; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.tree.SqmDmlStatement; +import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; + +import java.util.concurrent.CompletionStage; + +public abstract class ReactiveAbstractMultiTableMutationQueryPlan, F> + extends AbstractMultiTableMutationQueryPlan implements ReactiveNonSelectQueryPlan { + + public ReactiveAbstractMultiTableMutationQueryPlan( + S statement, + DomainParameterXref domainParameterXref, + F strategy) { + super( statement, domainParameterXref, strategy ); + } + + @Override + public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext context) { + BulkOperationCleanupAction.schedule( context.getSession(), getStatement() ); + final Interpretation interpretation = getInterpretation( context ); + return ((ReactiveHandler)interpretation.handler()).reactiveExecute( interpretation.jdbcParameterBindings(), context ); + } + +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableDeleteQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableDeleteQueryPlan.java index 90bdf6c49..30a77ba2d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableDeleteQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableDeleteQueryPlan.java @@ -5,35 +5,32 @@ */ package org.hibernate.reactive.query.sqm.internal; -import java.util.concurrent.CompletionStage; - -import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; /** * @see org.hibernate.query.sqm.internal.MultiTableDeleteQueryPlan */ -public class ReactiveMultiTableDeleteQueryPlan implements ReactiveNonSelectQueryPlan { - private final SqmDeleteStatement sqmDelete; - private final DomainParameterXref domainParameterXref; - private final ReactiveSqmMultiTableMutationStrategy deleteStrategy; +public class ReactiveMultiTableDeleteQueryPlan + extends ReactiveAbstractMultiTableMutationQueryPlan, SqmMultiTableMutationStrategy> { public ReactiveMultiTableDeleteQueryPlan( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, ReactiveSqmMultiTableMutationStrategy deleteStrategy) { - this.sqmDelete = sqmDelete; - this.domainParameterXref = domainParameterXref; - this.deleteStrategy = deleteStrategy; + super( sqmDelete, domainParameterXref, deleteStrategy ); } @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDelete ); - return deleteStrategy.reactiveExecuteDelete( sqmDelete, domainParameterXref, executionContext ); + protected MultiTableHandlerBuildResult buildHandler( + SqmDeleteStatement statement, + DomainParameterXref domainParameterXref, + SqmMultiTableMutationStrategy strategy, + DomainQueryExecutionContext context) { + return strategy.buildHandler( statement, domainParameterXref, context ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableInsertQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableInsertQueryPlan.java index ed0e68b01..502f4f53c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableInsertQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableInsertQueryPlan.java @@ -5,35 +5,32 @@ */ package org.hibernate.reactive.query.sqm.internal; -import java.util.concurrent.CompletionStage; - -import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; -import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; /** * @see org.hibernate.query.sqm.internal.MultiTableInsertQueryPlan */ -public class ReactiveMultiTableInsertQueryPlan implements ReactiveNonSelectQueryPlan { - private final SqmInsertStatement sqmInsert; - private final DomainParameterXref domainParameterXref; - private final ReactiveSqmMultiTableInsertStrategy mutationStrategy; +public class ReactiveMultiTableInsertQueryPlan + extends ReactiveAbstractMultiTableMutationQueryPlan, SqmMultiTableInsertStrategy> { public ReactiveMultiTableInsertQueryPlan( SqmInsertStatement sqmInsert, DomainParameterXref domainParameterXref, ReactiveSqmMultiTableInsertStrategy mutationStrategy) { - this.sqmInsert = sqmInsert; - this.domainParameterXref = domainParameterXref; - this.mutationStrategy = mutationStrategy; + super( sqmInsert, domainParameterXref, mutationStrategy ); } @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmInsert ); - return mutationStrategy.reactiveExecuteInsert( sqmInsert, domainParameterXref, executionContext ); + protected MultiTableHandlerBuildResult buildHandler( + SqmInsertStatement statement, + DomainParameterXref domainParameterXref, + SqmMultiTableInsertStrategy strategy, + DomainQueryExecutionContext context) { + return strategy.buildHandler( statement, domainParameterXref, context ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableUpdateQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableUpdateQueryPlan.java index 605186079..0715e06b5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableUpdateQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveMultiTableUpdateQueryPlan.java @@ -5,35 +5,31 @@ */ package org.hibernate.reactive.query.sqm.internal; -import java.util.concurrent.CompletionStage; - -import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; -import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; /** * @see org.hibernate.query.sqm.internal.MultiTableUpdateQueryPlan */ -public class ReactiveMultiTableUpdateQueryPlan implements ReactiveNonSelectQueryPlan { - private final SqmUpdateStatement sqmUpdate; - private final DomainParameterXref domainParameterXref; - private final ReactiveSqmMultiTableMutationStrategy mutationStrategy; +public class ReactiveMultiTableUpdateQueryPlan + extends ReactiveAbstractMultiTableMutationQueryPlan, SqmMultiTableMutationStrategy> { public ReactiveMultiTableUpdateQueryPlan( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ReactiveSqmMultiTableMutationStrategy mutationStrategy) { - this.sqmUpdate = sqmUpdate; - this.domainParameterXref = domainParameterXref; - this.mutationStrategy = mutationStrategy; + SqmMultiTableMutationStrategy mutationStrategy) { + super( sqmUpdate, domainParameterXref, mutationStrategy ); } @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmUpdate ); - return mutationStrategy.reactiveExecuteUpdate( sqmUpdate, domainParameterXref, executionContext ); + protected MultiTableHandlerBuildResult buildHandler( + SqmUpdateStatement statement, + DomainParameterXref domainParameterXref, + SqmMultiTableMutationStrategy strategy, + DomainQueryExecutionContext context) { + return strategy.buildHandler( statement, domainParameterXref, context ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleDeleteQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleDeleteQueryPlan.java index eb055a38a..83113911b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleDeleteQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleDeleteQueryPlan.java @@ -5,215 +5,89 @@ */ package org.hibernate.reactive.query.sqm.internal; -import java.sql.PreparedStatement; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; - import org.hibernate.action.internal.BulkOperationCleanupAction; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.mapping.SoftDeleteMapping; -import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sqm.internal.CacheableSqmInterpretation; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SimpleDeleteQueryPlan; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.SqmTranslation; -import org.hibernate.query.sqm.sql.SqmTranslator; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; -import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.sql.ast.tree.MutationStatement; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.ast.tree.update.Assignment; -import org.hibernate.sql.ast.tree.update.UpdateStatement; +import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.exec.spi.JdbcParametersList; -import org.hibernate.sql.results.internal.SqlSelectionImpl; -import static org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter.usingLockingAndPaging; - -public class ReactiveSimpleDeleteQueryPlan extends SimpleDeleteQueryPlan implements ReactiveNonSelectQueryPlan { - private final EntityMappingType entityDescriptor; - private final SqmDeleteStatement sqmDelete; - private final DomainParameterXref domainParameterXref; +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CompletionStage; - private JdbcOperationQueryMutation jdbcOperation; +public class ReactiveSimpleDeleteQueryPlan + extends SimpleDeleteQueryPlan implements ReactiveNonSelectQueryPlan { - private SqmTranslation sqmInterpretation; - private Map, Map, List>> jdbcParamsXref; + private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); public ReactiveSimpleDeleteQueryPlan( - EntityMappingType entityDescriptor, + EntityPersister entityDescriptor, SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref) { super( entityDescriptor, sqmDelete, domainParameterXref ); - this.entityDescriptor = entityDescriptor; - this.sqmDelete = sqmDelete; - this.domainParameterXref = domainParameterXref; } @Override - protected SqlAstTranslator createTranslator(DomainQueryExecutionContext executionContext) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final SqmTranslator translator = factory.getQueryEngine().getSqmTranslatorFactory().createMutationTranslator( - sqmDelete, - executionContext.getQueryOptions(), - domainParameterXref, - executionContext.getQueryParameterBindings(), - executionContext.getSession().getLoadQueryInfluencers(), - factory.getSqlTranslationEngine() - ); - - sqmInterpretation = (SqmTranslation) translator.translate(); - - this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref( - domainParameterXref, - sqmInterpretation::getJdbcParamsBySqmParam - ); - - return factory.getJdbcServices() - .getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, createDeleteAst() ); + protected int execute(CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { + throw LOG.nonReactiveMethodCall( "executeReactiveUpdate" ); } - // Copy and paste from superclass - private MutationStatement createDeleteAst() { - final MutationStatement ast; - if ( entityDescriptor.getSoftDeleteMapping() == null ) { - ast = sqmInterpretation.getSqlAst(); - } - else { - final AbstractUpdateOrDeleteStatement sqlDeleteAst = sqmInterpretation.getSqlAst(); - final NamedTableReference targetTable = sqlDeleteAst.getTargetTable(); - final SoftDeleteMapping columnMapping = getEntityDescriptor().getSoftDeleteMapping(); - final Assignment assignment = columnMapping.createSoftDeleteAssignment( targetTable ); - - ast = new UpdateStatement( - targetTable, - Collections.singletonList( assignment ), - sqlDeleteAst.getRestriction() - ); - } - return ast; + @Override + public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext context) { + BulkOperationCleanupAction.schedule( context.getSession(), getStatement() ); + final Interpretation interpretation = getInterpretation( context ); + final ExecutionContext executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + return executeReactive( interpretation.interpretation(), interpretation.jdbcParameterBindings(), executionContext ); } - @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDelete ); + protected CompletionStage executeReactive(CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { final SharedSessionContractImplementor session = executionContext.getSession(); - final SessionFactoryImplementor factory = session.getFactory(); - SqlAstTranslator sqlAstTranslator = null; - if ( jdbcOperation == null ) { - sqlAstTranslator = createTranslator( executionContext ); - } - - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - domainParameterXref, - jdbcParamsXref, - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get(parameter); - } - }, - session + return CompletionStages.loop(getCollectionTableDeletes(), delete -> + executeReactive( delete, jdbcParameterBindings, executionContext.getSession(), executionContext ) + ).thenCompose( v -> CompletionStages.loop( + getCollectionTableDeletes(), delete -> + executeReactive( + delete, + jdbcParameterBindings, + executionContext.getSession(), + executionContext + ) + ) + .thenCompose( unused -> executeReactive( + sqmInterpretation.jdbcOperation(), + jdbcParameterBindings, + session, + executionContext + ) ) ); - - if ( jdbcOperation != null - && !jdbcOperation.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { - sqlAstTranslator = createTranslator( executionContext ); - } - - if ( sqlAstTranslator != null ) { - jdbcOperation = sqlAstTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - } - - final boolean missingRestriction = sqmDelete.getWhereClause() == null - || sqmDelete.getWhereClause().getPredicate() == null; - if ( missingRestriction ) { - assert domainParameterXref.getSqmParameterCount() == 0; - assert jdbcParamsXref.isEmpty(); - } - - final SqmJdbcExecutionContextAdapter executionContextAdapter = usingLockingAndPaging( executionContext ); - - return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - entityDescriptor, - (tableReference, attributeMapping) -> { - if ( missingRestriction ) { - return null; - } - - final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); - final Expression fkColumnExpression = MappingModelCreationHelper.buildColumnReferenceExpression( - new MutatingTableReferenceGroupWrapper( - new NavigablePath( attributeMapping.getRootPathName() ), - attributeMapping, - (NamedTableReference) tableReference - ), - fkDescriptor.getKeyPart(), - null, - factory - ); - - final QuerySpec matchingIdSubQuery = new QuerySpec( false ); - - final MutatingTableReferenceGroupWrapper tableGroup = new MutatingTableReferenceGroupWrapper( - new NavigablePath( attributeMapping.getRootPathName() ), - attributeMapping, - sqmInterpretation.getSqlAst().getTargetTable() - ); - final Expression fkTargetColumnExpression = MappingModelCreationHelper.buildColumnReferenceExpression( - tableGroup, - fkDescriptor.getTargetPart(), - sqmInterpretation.getSqlExpressionResolver(), - factory - ); - matchingIdSubQuery.getSelectClause() - .addSqlSelection( new SqlSelectionImpl( 0, fkTargetColumnExpression ) ); - - matchingIdSubQuery.getFromClause().addRoot( - tableGroup - ); - - matchingIdSubQuery.applyPredicate( sqmInterpretation.getSqlAst().getRestriction() ); - - return new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false ); - }, - ( missingRestriction ? JdbcParameterBindings.NO_BINDINGS : jdbcParameterBindings ), - executionContextAdapter - ) - .thenCompose( unused -> StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcOperation, - jdbcParameterBindings, - session.getJdbcCoordinator().getStatementPreparer()::prepareStatement, - ReactiveSimpleDeleteQueryPlan::doNothing, - executionContextAdapter ) - ); } - private static void doNothing(Integer i, PreparedStatement ps) { + private static CompletionStage executeReactive( + JdbcOperationQueryMutation delete, + JdbcParameterBindings jdbcParameterBindings, + SharedSessionContractImplementor executionContext, + ExecutionContext executionContext1) { + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + delete, + jdbcParameterBindings, + sql -> executionContext + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext1 + ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleInsertQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleInsertQueryPlan.java deleted file mode 100644 index bf67d16ea..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleInsertQueryPlan.java +++ /dev/null @@ -1,121 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.internal; - - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; - -import org.hibernate.action.internal.BulkOperationCleanupAction; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.SqmTranslation; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; -import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.tree.MutationStatement; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.exec.spi.JdbcParametersList; - -/** - * @see org.hibernate.query.sqm.internal.SimpleInsertQueryPlan - */ -public class ReactiveSimpleInsertQueryPlan implements ReactiveNonSelectQueryPlan { - - private final SqmInsertStatement sqmInsert; - - private final DomainParameterXref domainParameterXref; - - private Map, MappingModelExpressible> paramTypeResolutions; - - private JdbcOperationQueryMutation jdbcInsert; - private Map, Map, List>> jdbcParamsXref; - - public ReactiveSimpleInsertQueryPlan( - SqmInsertStatement sqmInsert, - DomainParameterXref domainParameterXref) { - this.sqmInsert = sqmInsert; - this.domainParameterXref = domainParameterXref; - } - - @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmInsert ); - final SharedSessionContractImplementor session = executionContext.getSession(); - SqlAstTranslator insertTranslator = jdbcInsert == null - ? createInsertTranslator( executionContext ) - : null; - - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - domainParameterXref, - jdbcParamsXref, - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) paramTypeResolutions.get(parameter); - } - }, - session - ); - - if ( jdbcInsert != null && !jdbcInsert.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { - insertTranslator = createInsertTranslator( executionContext ); - } - - if ( insertTranslator != null ) { - jdbcInsert = insertTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - } - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcInsert, - jdbcParameterBindings, - session.getJdbcCoordinator().getStatementPreparer()::prepareStatement, - (i, ps) -> {}, - SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ) - ); - } - - // Copied from Hibernate ORM SimpleInsertQueryPlan#createInsertTranslator - private SqlAstTranslator createInsertTranslator(DomainQueryExecutionContext executionContext) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - - final SqmTranslation sqmInterpretation = factory.getQueryEngine().getSqmTranslatorFactory() - .createMutationTranslator( - sqmInsert, - executionContext.getQueryOptions(), - domainParameterXref, - executionContext.getQueryParameterBindings(), - executionContext.getSession().getLoadQueryInfluencers(), - factory.getSqlTranslationEngine() - ) - .translate(); - - this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref( - domainParameterXref, - sqmInterpretation::getJdbcParamsBySqmParam - ); - - this.paramTypeResolutions = sqmInterpretation.getSqmParameterMappingModelTypeResolutions(); - - return factory.getJdbcServices() - .getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, sqmInterpretation.getSqlAst() ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleNonSelectQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleNonSelectQueryPlan.java new file mode 100644 index 000000000..1a2a942f5 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleNonSelectQueryPlan.java @@ -0,0 +1,56 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.query.sqm.internal; + +import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.internal.CacheableSqmInterpretation; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SimpleNonSelectQueryPlan; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; +import org.hibernate.query.sqm.tree.SqmDmlStatement; +import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.sql.ast.tree.MutationStatement; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +import java.util.concurrent.CompletionStage; + +public class ReactiveSimpleNonSelectQueryPlan extends + SimpleNonSelectQueryPlan implements ReactiveNonSelectQueryPlan { + + public ReactiveSimpleNonSelectQueryPlan( + SqmDmlStatement statement, + DomainParameterXref domainParameterXref) { + super( statement, domainParameterXref ); + } + + @Override + public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext context) { + BulkOperationCleanupAction.schedule( context.getSession(), getStatement() ); + final Interpretation interpretation = getInterpretation( context ); + final ExecutionContext executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + return executeReactive( interpretation.interpretation(), interpretation.jdbcParameterBindings(), executionContext ); + } + + protected CompletionStage executeReactive(CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { + final SharedSessionContractImplementor session = executionContext.getSession(); + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + sqmInterpretation.jdbcOperation(), + jdbcParameterBindings, + sql -> session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleUpdateQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleUpdateQueryPlan.java deleted file mode 100644 index f49f848f5..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSimpleUpdateQueryPlan.java +++ /dev/null @@ -1,117 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.internal; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; - -import org.hibernate.action.internal.BulkOperationCleanupAction; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryEngine; -import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.SqmTranslation; -import org.hibernate.query.sqm.sql.SqmTranslator; -import org.hibernate.query.sqm.sql.SqmTranslatorFactory; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.FromClauseAccess; -import org.hibernate.sql.ast.tree.MutationStatement; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.exec.spi.JdbcParametersList; - - -/** - * @see org.hibernate.query.sqm.internal.SimpleUpdateQueryPlan - */ -public class ReactiveSimpleUpdateQueryPlan implements ReactiveNonSelectQueryPlan { - - private final SqmUpdateStatement sqmUpdate; - private final DomainParameterXref domainParameterXref; - - private JdbcOperationQueryMutation jdbcUpdate; - private FromClauseAccess tableGroupAccess; - private Map, Map, List>> jdbcParamsXref; - private Map, MappingModelExpressible> sqmParamMappingTypeResolutions; - - public ReactiveSimpleUpdateQueryPlan(SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref) { - this.sqmUpdate = sqmUpdate; - this.domainParameterXref = domainParameterXref; - } - - @Override - public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmUpdate ); - final SharedSessionContractImplementor session = executionContext.getSession(); - SqlAstTranslator updateTranslator = jdbcUpdate == null - ? createUpdateTranslator( executionContext ) - : null; - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - domainParameterXref, - jdbcParamsXref, - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmParamMappingTypeResolutions.get(parameter); - } - }, - session - ); - - if ( jdbcUpdate != null && !jdbcUpdate.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { - updateTranslator = createUpdateTranslator( executionContext ); - } - - if ( updateTranslator != null ) { - jdbcUpdate = updateTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - } - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcUpdate, - jdbcParameterBindings, - session.getJdbcCoordinator().getStatementPreparer()::prepareStatement, - (i, ps) -> {}, - SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ) - ); - } - - // I can probably change ORM to reuse this - private SqlAstTranslator createUpdateTranslator(DomainQueryExecutionContext executionContext) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final QueryEngine queryEngine = factory.getQueryEngine(); - - final SqmTranslatorFactory translatorFactory = queryEngine.getSqmTranslatorFactory(); - final SqmTranslator translator = translatorFactory.createMutationTranslator( - sqmUpdate, - executionContext.getQueryOptions(), - domainParameterXref, - executionContext.getQueryParameterBindings(), - executionContext.getSession().getLoadQueryInfluencers(), - factory.getSqlTranslationEngine() - ); - - final SqmTranslation sqmInterpretation = translator.translate(); - tableGroupAccess = sqmInterpretation.getFromClauseAccess(); - this.jdbcParamsXref = SqmUtil - .generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam ); - this.sqmParamMappingTypeResolutions = sqmInterpretation.getSqmParameterMappingModelTypeResolutions(); - return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, sqmInterpretation.getSqlAst() ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmQueryImpl.java similarity index 80% rename from hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java rename to hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmQueryImpl.java index 755448cbd..b0bc9f823 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmQueryImpl.java @@ -46,9 +46,12 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.internal.SqmInterpretationsKey; import org.hibernate.query.sqm.internal.SqmQueryImpl; +import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; +import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; +import org.hibernate.query.sqm.tree.insert.SqmValues; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.logging.impl.Log; @@ -73,13 +76,13 @@ /** * A reactive {@link SqmQueryImpl} */ -public class ReactiveQuerySqmImpl extends SqmQueryImpl implements ReactiveSqmQueryImplementor { +public class ReactiveSqmQueryImpl extends SqmQueryImpl implements ReactiveSqmQueryImplementor { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); private final ReactiveAbstractSelectionQuery selectionQueryDelegate; - public ReactiveQuerySqmImpl( + public ReactiveSqmQueryImpl( NamedHqlQueryMementoImpl memento, Class expectedResultType, SharedSessionContractImplementor session) { @@ -87,7 +90,7 @@ public ReactiveQuerySqmImpl( this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveQuerySqmImpl( + public ReactiveSqmQueryImpl( NamedCriteriaQueryMementoImpl memento, Class resultType, SharedSessionContractImplementor session) { @@ -95,7 +98,7 @@ public ReactiveQuerySqmImpl( this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveQuerySqmImpl( + public ReactiveSqmQueryImpl( String hql, HqlInterpretation hqlInterpretation, Class resultType, @@ -104,7 +107,7 @@ public ReactiveQuerySqmImpl( this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveQuerySqmImpl( + public ReactiveSqmQueryImpl( SqmStatement criteria, Class resultType, SharedSessionContractImplementor session) { @@ -415,13 +418,13 @@ private ReactiveNonSelectQueryPlan buildUpdateQueryPlan() { final ReactiveSqmMultiTableMutationStrategy multiTableStrategy = (ReactiveSqmMultiTableMutationStrategy) entityDescriptor.getSqmMultiTableMutationStrategy(); return multiTableStrategy == null - ? new ReactiveSimpleUpdateQueryPlan( sqmUpdate, getDomainParameterXref() ) + ? new ReactiveSimpleNonSelectQueryPlan( sqmUpdate, getDomainParameterXref() ) : new ReactiveMultiTableUpdateQueryPlan( sqmUpdate, getDomainParameterXref(), multiTableStrategy ); } private ReactiveNonSelectQueryPlan buildInsertQueryPlan() { //noinspection rawtypes - final SqmInsertStatement sqmInsert = (SqmInsertStatement) getSqmStatement(); + final SqmInsertStatement sqmInsert = (SqmInsertStatement) getSqmStatement(); final String entityNameToInsert = sqmInsert.getTarget().getModel().getHibernateEntityName(); final EntityPersister persister = getSessionFactory() @@ -438,87 +441,101 @@ private ReactiveNonSelectQueryPlan buildInsertQueryPlan() { } } } - if ( !useMultiTableInsert ) { - return new ReactiveSimpleInsertQueryPlan( sqmInsert, getDomainParameterXref() ); - } - else { + if ( useMultiTableInsert ) { return new ReactiveMultiTableInsertQueryPlan( sqmInsert, getDomainParameterXref(), (ReactiveSqmMultiTableInsertStrategy) persister.getSqmMultiTableInsertStrategy() ); } + else if ( sqmInsert instanceof SqmInsertValuesStatement insertValues + && insertValues.getValuesList().size() != 1 + && !getSessionFactory().getJdbcServices().getDialect().supportsValuesListForInsert() ) { + // Split insert-values queries if the dialect doesn't support values lists + final List valuesList = insertValues.getValuesList(); + final ReactiveNonSelectQueryPlan[] planParts = new ReactiveNonSelectQueryPlan[valuesList.size()]; + for ( int i = 0; i < valuesList.size(); i++ ) { + final SqmInsertValuesStatement subInsert = + insertValues.copyWithoutValues( SqmCopyContext.simpleContext() ); + subInsert.values( valuesList.get( i ) ); + planParts[i] = new ReactiveSimpleNonSelectQueryPlan( subInsert, getDomainParameterXref() ); + } + + return new ReactiveAggregatedNonSelectQueryPlan( planParts ); + } + + return new ReactiveSimpleNonSelectQueryPlan( sqmInsert, getDomainParameterXref() ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // QueryOptions @Override - public ReactiveQuerySqmImpl setHint(String hintName, Object value) { + public ReactiveSqmQueryImpl setHint(String hintName, Object value) { super.setHint( hintName, value ); return this; } @Override - public ReactiveQuerySqmImpl addQueryHint(String hint) { + public ReactiveSqmQueryImpl addQueryHint(String hint) { super.addQueryHint( hint ); return this; } @Override - public ReactiveQuerySqmImpl setLockOptions(LockOptions lockOptions) { + public ReactiveSqmQueryImpl setLockOptions(LockOptions lockOptions) { super.setLockOptions( lockOptions ); return this; } @Override - public ReactiveQuerySqmImpl setLockMode(String alias, LockMode lockMode) { + public ReactiveSqmQueryImpl setLockMode(String alias, LockMode lockMode) { super.setLockMode( alias, lockMode ); return this; } @Override - public ReactiveQuerySqmImpl setTupleTransformer(TupleTransformer transformer) { + public ReactiveSqmQueryImpl setTupleTransformer(TupleTransformer transformer) { throw new UnsupportedOperationException(); } @Override - public ReactiveQuerySqmImpl setResultListTransformer(ResultListTransformer transformer) { + public ReactiveSqmQueryImpl setResultListTransformer(ResultListTransformer transformer) { super.setResultListTransformer( transformer ); return this; } @Override @Deprecated - public ReactiveQuerySqmImpl setResultTransformer(ResultTransformer transformer) { + public ReactiveSqmQueryImpl setResultTransformer(ResultTransformer transformer) { throw new UnsupportedOperationException(); } @Override - public ReactiveQuerySqmImpl setMaxResults(int maxResult) { + public ReactiveSqmQueryImpl setMaxResults(int maxResult) { super.setMaxResults( maxResult ); return this; } @Override - public ReactiveQuerySqmImpl setFirstResult(int startPosition) { + public ReactiveSqmQueryImpl setFirstResult(int startPosition) { super.setFirstResult( startPosition ); return this; } @Override - public ReactiveQuerySqmImpl setHibernateFlushMode(FlushMode flushMode) { + public ReactiveSqmQueryImpl setHibernateFlushMode(FlushMode flushMode) { super.setHibernateFlushMode( flushMode ); return this; } @Override - public ReactiveQuerySqmImpl setFlushMode(FlushModeType flushMode) { + public ReactiveSqmQueryImpl setFlushMode(FlushModeType flushMode) { super.setFlushMode( flushMode ); return this; } @Override - public ReactiveQuerySqmImpl setLockMode(LockModeType lockMode) { + public ReactiveSqmQueryImpl setLockMode(LockModeType lockMode) { super.setLockMode( lockMode ); return this; } @@ -527,313 +544,313 @@ public ReactiveQuerySqmImpl setLockMode(LockModeType lockMode) { // covariance @Override - public ReactiveQuerySqmImpl applyGraph(RootGraph graph, GraphSemantic semantic) { + public ReactiveSqmQueryImpl applyGraph(RootGraph graph, GraphSemantic semantic) { super.applyGraph( graph, semantic ); return this; } @Override - public ReactiveQuerySqmImpl applyLoadGraph(RootGraph graph) { + public ReactiveSqmQueryImpl applyLoadGraph(RootGraph graph) { super.applyLoadGraph( graph ); return this; } @Override - public ReactiveQuerySqmImpl applyFetchGraph(RootGraph graph) { + public ReactiveSqmQueryImpl applyFetchGraph(RootGraph graph) { super.applyFetchGraph( graph ); return this; } @Override - public ReactiveQuerySqmImpl setComment(String comment) { + public ReactiveSqmQueryImpl setComment(String comment) { super.setComment( comment ); return this; } @Override - public ReactiveQuerySqmImpl setCacheMode(CacheMode cacheMode) { + public ReactiveSqmQueryImpl setCacheMode(CacheMode cacheMode) { super.setCacheMode( cacheMode ); return this; } @Override - public ReactiveQuerySqmImpl setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { + public ReactiveSqmQueryImpl setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { super.setCacheRetrieveMode( cacheRetrieveMode ); return this; } @Override - public ReactiveQuerySqmImpl setCacheStoreMode(CacheStoreMode cacheStoreMode) { + public ReactiveSqmQueryImpl setCacheStoreMode(CacheStoreMode cacheStoreMode) { super.setCacheStoreMode( cacheStoreMode ); return this; } @Override - public ReactiveQuerySqmImpl setCacheable(boolean cacheable) { + public ReactiveSqmQueryImpl setCacheable(boolean cacheable) { super.setCacheable( cacheable ); return this; } @Override - public ReactiveQuerySqmImpl setCacheRegion(String cacheRegion) { + public ReactiveSqmQueryImpl setCacheRegion(String cacheRegion) { super.setCacheRegion( cacheRegion ); return this; } @Override - public ReactiveQuerySqmImpl setHibernateLockMode(LockMode lockMode) { + public ReactiveSqmQueryImpl setHibernateLockMode(LockMode lockMode) { super.setHibernateLockMode( lockMode ); return this; } @Override - public ReactiveQuerySqmImpl setTimeout(int timeout) { + public ReactiveSqmQueryImpl setTimeout(int timeout) { super.setTimeout( timeout ); return this; } @Override - public ReactiveQuerySqmImpl setFetchSize(int fetchSize) { + public ReactiveSqmQueryImpl setFetchSize(int fetchSize) { super.setFetchSize( fetchSize ); return this; } @Override - public ReactiveQuerySqmImpl setReadOnly(boolean readOnly) { + public ReactiveSqmQueryImpl setReadOnly(boolean readOnly) { super.setReadOnly( readOnly ); return this; } @Override - public ReactiveQuerySqmImpl setProperties(Object bean) { + public ReactiveSqmQueryImpl setProperties(Object bean) { super.setProperties( bean ); return this; } @Override - public ReactiveQuerySqmImpl setProperties(Map bean) { + public ReactiveSqmQueryImpl setProperties(Map bean) { super.setProperties( bean ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(String name, Object value) { + public ReactiveSqmQueryImpl setParameter(String name, Object value) { super.setParameter( name, value ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(String name, P value, Class

javaType) { + public

ReactiveSqmQueryImpl setParameter(String name, P value, Class

javaType) { super.setParameter( name, value, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(String name, P value, Type

type) { + public

ReactiveSqmQueryImpl setParameter(String name, P value, Type

type) { super.setParameter( name, value, type ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(String name, Instant value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(String name, Instant value, TemporalType temporalType) { super.setParameter( name, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(int position, Object value) { + public ReactiveSqmQueryImpl setParameter(int position, Object value) { super.setParameter( position, value ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(int position, P value, Class

javaType) { + public

ReactiveSqmQueryImpl setParameter(int position, P value, Class

javaType) { super.setParameter( position, value, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(int position, P value, Type

type) { + public

ReactiveSqmQueryImpl setParameter(int position, P value, Type

type) { super.setParameter( position, value, type ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(int position, Instant value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(int position, Instant value, TemporalType temporalType) { super.setParameter( position, value, temporalType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(QueryParameter

parameter, P value) { + public

ReactiveSqmQueryImpl setParameter(QueryParameter

parameter, P value) { super.setParameter( parameter, value ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(QueryParameter

parameter, P value, Class

javaType) { + public

ReactiveSqmQueryImpl setParameter(QueryParameter

parameter, P value, Class

javaType) { super.setParameter( parameter, value, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(QueryParameter

parameter, P value, Type

type) { + public

ReactiveSqmQueryImpl setParameter(QueryParameter

parameter, P value, Type

type) { super.setParameter( parameter, value, type ); return this; } @Override - public

ReactiveQuerySqmImpl setParameter(Parameter

parameter, P value) { + public

ReactiveSqmQueryImpl setParameter(Parameter

parameter, P value) { super.setParameter( parameter, value ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(Parameter param, Calendar value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(Parameter param, Calendar value, TemporalType temporalType) { super.setParameter( param, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(Parameter param, Date value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(Parameter param, Date value, TemporalType temporalType) { super.setParameter( param, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(String name, Calendar value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(String name, Calendar value, TemporalType temporalType) { super.setParameter( name, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(String name, Date value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(String name, Date value, TemporalType temporalType) { super.setParameter( name, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(int position, Calendar value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(int position, Calendar value, TemporalType temporalType) { super.setParameter( position, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameter(int position, Date value, TemporalType temporalType) { + public ReactiveSqmQueryImpl setParameter(int position, Date value, TemporalType temporalType) { super.setParameter( position, value, temporalType ); return this; } @Override - public ReactiveQuerySqmImpl setParameterList(String name, Collection values) { + public ReactiveSqmQueryImpl setParameterList(String name, Collection values) { super.setParameterList( name, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(String name, Collection values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(String name, Collection values, Class

javaType) { super.setParameterList( name, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(String name, Collection values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(String name, Collection values, Type

type) { super.setParameterList( name, values, type ); return this; } @Override - public ReactiveQuerySqmImpl setParameterList(String name, Object[] values) { + public ReactiveSqmQueryImpl setParameterList(String name, Object[] values) { super.setParameterList( name, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(String name, P[] values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(String name, P[] values, Class

javaType) { super.setParameterList( name, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(String name, P[] values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(String name, P[] values, Type

type) { super.setParameterList( name, values, type ); return this; } @Override - public ReactiveQuerySqmImpl setParameterList(int position, Collection values) { + public ReactiveSqmQueryImpl setParameterList(int position, Collection values) { super.setParameterList( position, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(int position, Collection values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(int position, Collection values, Class

javaType) { super.setParameterList( position, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(int position, Collection values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(int position, Collection values, Type

type) { super.setParameterList( position, values, type ); return this; } @Override - public ReactiveQuerySqmImpl setParameterList(int position, Object[] values) { + public ReactiveSqmQueryImpl setParameterList(int position, Object[] values) { super.setParameterList( position, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(int position, P[] values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(int position, P[] values, Class

javaType) { super.setParameterList( position, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(int position, P[] values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(int position, P[] values, Type

type) { super.setParameterList( position, values, type ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, Collection values) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, Collection values) { super.setParameterList( parameter, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, Collection values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, Collection values, Class

javaType) { super.setParameterList( parameter, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, Collection values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, Collection values, Type

type) { super.setParameterList( parameter, values, type ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, P[] values) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, P[] values) { super.setParameterList( parameter, values ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, P[] values, Class

javaType) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, P[] values, Class

javaType) { super.setParameterList( parameter, values, javaType ); return this; } @Override - public

ReactiveQuerySqmImpl setParameterList(QueryParameter

parameter, P[] values, Type

type) { + public

ReactiveSqmQueryImpl setParameterList(QueryParameter

parameter, P[] values, Type

type) { super.setParameterList( parameter, values, type ); return this; } @Override - public ReactiveQuerySqmImpl setFollowOnLocking(boolean enable) { + public ReactiveSqmQueryImpl setFollowOnLocking(boolean enable) { super.setFollowOnLocking( enable ); return this; } @@ -844,7 +861,7 @@ public void applyGraph(RootGraphImplementor graph, GraphSemantic semantic) { } @Override - public ReactiveQuerySqmImpl enableFetchProfile(String profileName) { + public ReactiveSqmQueryImpl enableFetchProfile(String profileName) { selectionQueryDelegate.enableFetchProfile( profileName ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveHandler.java index d23ae3225..cab920f73 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveHandler.java @@ -5,14 +5,30 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal; +import java.lang.invoke.MethodHandles; import java.util.concurrent.CompletionStage; import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.mutation.internal.Handler; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; /** * @see org.hibernate.query.sqm.mutation.internal.Handler */ -public interface ReactiveHandler { +public interface ReactiveHandler extends Handler { + Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + + @Override + default int execute(DomainQueryExecutionContext executionContext) { + throw LOG.nonReactiveMethodCall( "reactiveExecute" ); + } + + @Override + default int execute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecutionContext executionContext) { + throw LOG.nonReactiveMethodCall( "reactiveExecute" ); + } /** * Execute the multi-table update or delete indicated by the SQM AST @@ -22,5 +38,17 @@ public interface ReactiveHandler { * * @return The "number of rows affected" count */ - CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext); + default CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext){ + return reactiveExecute( createJdbcParameterBindings( executionContext ), executionContext ); + } + + /** + * Execute the multi-table update or delete indicated by the SQM AST + * passed in when this Handler was created. + * + * @param jdbcParameterBindings The parameter bindings for JDBC parameters + * @param executionContext Contextual information needed for execution + * @return The "number of rows affected" count + */ + CompletionStage reactiveExecute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecutionContext executionContext); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveSqmMutationStrategyHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveSqmMutationStrategyHelper.java deleted file mode 100644 index b2e36a296..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/ReactiveSqmMutationStrategyHelper.java +++ /dev/null @@ -1,161 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.mutation.internal; - -import java.sql.PreparedStatement; -import java.util.concurrent.CompletionStage; -import java.util.function.BiFunction; - -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.reactive.util.impl.CompletionStages; -import org.hibernate.reactive.util.impl.CompletionStages.Completable; -import org.hibernate.sql.ast.tree.delete.DeleteStatement; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.spi.JdbcParameterBindings; - -import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; -import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; - -/** - * @see org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper - */ -public class ReactiveSqmMutationStrategyHelper { - - private ReactiveSqmMutationStrategyHelper() { - } - - public static CompletionStage cleanUpCollectionTables( - EntityMappingType entityDescriptor, - BiFunction restrictionProducer, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - if ( !entityDescriptor.getEntityPersister().hasCollections() ) { - // none to clean-up - return voidFuture(); - } - - try { - final Completable stage = new Completable<>(); - entityDescriptor - .visitSubTypeAttributeMappings( attributeMapping -> { - if ( attributeMapping instanceof PluralAttributeMapping ) { - cleanUpCollectionTable( - (PluralAttributeMapping) attributeMapping, - restrictionProducer, - jdbcParameterBindings, - executionContext - ).handle( stage::complete ); - } - else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { - cleanUpCollectionTables( - (EmbeddedAttributeMapping) attributeMapping, - restrictionProducer, - jdbcParameterBindings, - executionContext - ).handle( stage::complete ); - } - else { - stage.complete( null, null ); - } - } - ); - return stage.getStage(); - } - catch (Throwable throwable) { - return failedFuture( throwable ); - } - } - - private static CompletionStage cleanUpCollectionTables( - EmbeddedAttributeMapping attributeMapping, - BiFunction restrictionProducer, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - try { - final Completable completable = new Completable<>(); - attributeMapping.visitSubParts( - modelPart -> { - if ( modelPart instanceof PluralAttributeMapping ) { - cleanUpCollectionTable( - (PluralAttributeMapping) modelPart, - restrictionProducer, - jdbcParameterBindings, - executionContext - ).handle( completable::complete ); - } - else if ( modelPart instanceof EmbeddedAttributeMapping ) { - cleanUpCollectionTables( - (EmbeddedAttributeMapping) modelPart, - restrictionProducer, - jdbcParameterBindings, - executionContext - ).handle( completable::complete ); - } - }, - null - ); - return completable.getStage(); - } - catch (Throwable throwable) { - return failedFuture( throwable ); - } - } - - private static CompletionStage cleanUpCollectionTable( - PluralAttributeMapping attributeMapping, - BiFunction restrictionProducer, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - final String separateCollectionTable = attributeMapping.getSeparateCollectionTable(); - if ( separateCollectionTable == null ) { - // one-to-many - update the matching rows in the associated table setting the fk column(s) to null - // not yet implemented - do nothing - return voidFuture(); - } - - final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory(); - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - - // element-collection or many-to-many - delete the collection-table row - - final NamedTableReference tableReference = new NamedTableReference( - separateCollectionTable, - DeleteStatement.DEFAULT_ALIAS, - true - ); - - final DeleteStatement sqlAstDelete = new DeleteStatement( - tableReference, - restrictionProducer.apply( tableReference, attributeMapping ) - ); - - JdbcOperationQueryMutation jdbcDelete = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( sessionFactory, sqlAstDelete ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcDelete, - jdbcParameterBindings, - executionContext.getSession().getJdbcCoordinator().getStatementPreparer()::prepareStatement, - ReactiveSqmMutationStrategyHelper::doNothing, - executionContext - ) - .thenCompose( CompletionStages::voidFuture ); - } - - private static void doNothing(Integer i, PreparedStatement ps) { - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveAbstractCteMutationHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveAbstractCteMutationHandler.java index 806b75718..c0eb4ee3a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveAbstractCteMutationHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveAbstractCteMutationHandler.java @@ -5,189 +5,49 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.util.ArrayList; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; - import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.cte.AbstractCteMutationHandler; -import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; -import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveAbstractMutationHandler; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.tree.cte.CteContainer; -import org.hibernate.sql.ast.tree.cte.CteMaterialization; -import org.hibernate.sql.ast.tree.cte.CteStatement; -import org.hibernate.sql.ast.tree.cte.CteTable; -import org.hibernate.sql.ast.tree.cte.CteTableGroup; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.sql.results.internal.SqlSelectionImpl; + +import java.util.concurrent.CompletionStage; /** * @see org.hibernate.query.sqm.mutation.internal.cte.AbstractCteMutationHandler */ public interface ReactiveAbstractCteMutationHandler extends ReactiveAbstractMutationHandler { - CteTable getCteTable(); - - DomainParameterXref getDomainParameterXref(); - - CteMutationStrategy getStrategy(); - - void addDmlCtes( - CteContainer statement, - CteStatement idSelectCte, - MultiTableSqmMutationConverter sqmConverter, - Map, List> parameterResolutions, - SessionFactoryImplementor factory); - /** - * @see org.hibernate.query.sqm.mutation.internal.cte.AbstractCteMutationHandler#execute(DomainQueryExecutionContext) + * @see org.hibernate.query.sqm.mutation.internal.cte.AbstractCteMutationHandler#execute(JdbcParameterBindings, DomainQueryExecutionContext) */ @Override - default CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { - final SqmDeleteOrUpdateStatement sqmMutationStatement = getSqmDeleteOrUpdateStatement(); - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final EntityMappingType entityDescriptor = getEntityDescriptor(); - final String explicitDmlTargetAlias; - // We need an alias because we try to acquire a WRITE lock for these rows in the CTE - if ( sqmMutationStatement.getTarget().getExplicitAlias() == null ) { - explicitDmlTargetAlias = "dml_target"; - } - else { - explicitDmlTargetAlias = sqmMutationStatement.getTarget().getExplicitAlias(); - } - - final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( - entityDescriptor, - sqmMutationStatement, - sqmMutationStatement.getTarget(), - explicitDmlTargetAlias, - getDomainParameterXref(), - executionContext.getQueryOptions(), - executionContext.getSession().getLoadQueryInfluencers(), - executionContext.getQueryParameterBindings(), - factory.getSqlTranslationEngine() - ); - final Map, List> parameterResolutions; - if ( getDomainParameterXref().getSqmParameterCount() == 0 ) { - parameterResolutions = Collections.emptyMap(); - } - else { - parameterResolutions = new IdentityHashMap<>(); - } - - final Predicate restriction = sqmConverter.visitWhereClause( sqmMutationStatement.getWhereClause() ); - sqmConverter.pruneTableGroupJoins(); - - final CteStatement idSelectCte = new CteStatement( - getCteTable(), - MatchingIdSelectionHelper.generateMatchingIdSelectStatement( - entityDescriptor, - sqmMutationStatement, - true, - restriction, - sqmConverter, - executionContext - ), - // The id-select cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - - // Create the main query spec that will return the count of - final QuerySpec querySpec = new QuerySpec( true, 1 ); - final List> domainResults = new ArrayList<>( 1 ); - final SelectStatement statement = new SelectStatement( querySpec, domainResults ); - final JdbcServices jdbcServices = factory.getJdbcServices(); - final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildSelectTranslator( factory, statement ); - - final Expression count = createCountStar( factory, sqmConverter ); - domainResults.add( - new BasicResult<>( - 0, - null, - ( (SqlExpressible) count ).getJdbcMapping() - ) - ); - querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, count ) ); - querySpec.getFromClause().addRoot( - new CteTableGroup( - new NamedTableReference( - idSelectCte.getCteTable().getTableExpression(), - AbstractCteMutationHandler.CTE_TABLE_IDENTIFIER - ) - ) - ); - - // Add all CTEs - statement.addCteStatement( idSelectCte ); - addDmlCtes( statement, idSelectCte, sqmConverter, parameterResolutions, factory ); - - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - getDomainParameterXref(), - SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), sqmConverter ), - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmConverter.getSqmParameterMappingModelExpressibleResolutions().get( parameter ); - } - }, - executionContext.getSession() - ); + default CompletionStage reactiveExecute( + JdbcParameterBindings jdbcParameterBindings, + DomainQueryExecutionContext executionContext){ final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); - final LockMode lockMode = lockOptions.getAliasSpecificLockMode( explicitDmlTargetAlias ); // Acquire a WRITE lock for the rows that are about to be modified - lockOptions.setAliasSpecificLockMode( explicitDmlTargetAlias, LockMode.WRITE ); - final JdbcOperationQuerySelect select = translator.translate( - jdbcParameterBindings, - executionContext.getQueryOptions() - ); - lockOptions.setAliasSpecificLockMode( explicitDmlTargetAlias, lockMode ); - + lockOptions.setLockMode( LockMode.WRITE ); return ( (ReactiveSharedSessionContractImplementor) executionContext.getSession() ) - .reactiveAutoFlushIfRequired( select.getAffectedTableNames() ) - .thenCompose( v -> StandardReactiveSelectExecutor.INSTANCE.list( - select, - jdbcParameterBindings, - SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), - row -> row[0], - ReactiveListResultsConsumer.UniqueSemantic.NONE - ) - .thenApply( list -> ( (Number) list.get( 0 ) ).intValue() ) + .reactiveAutoFlushIfRequired( getSelect().getAffectedTableNames() ) + .thenCompose( v -> StandardReactiveSelectExecutor.INSTANCE + .list( + getSelect(), + jdbcParameterBindings, + SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), + row -> row[0], + null, + ReactiveListResultsConsumer.UniqueSemantic.NONE, + 1 + ) + .thenApply( list -> ( (Number) list.get( 0 ) ).intValue() ) ); } - Expression createCountStar(SessionFactoryImplementor factory, MultiTableSqmMutationConverter sqmConverter); + JdbcOperationQuerySelect getSelect(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteDeleteHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteDeleteHandler.java index a1f555253..67c3641b8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteDeleteHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteDeleteHandler.java @@ -5,60 +5,37 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.cte.CteDeleteHandler; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.sql.ast.tree.cte.CteContainer; -import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteTable; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; /** * @see org.hibernate.query.sqm.mutation.internal.cte.CteDeleteHandler */ public class ReactiveCteDeleteHandler extends CteDeleteHandler implements ReactiveAbstractCteMutationHandler { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - protected ReactiveCteDeleteHandler( CteTable cteTable, SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, CteMutationStrategy strategy, - SessionFactoryImplementor sessionFactory) { - super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, sessionFactory ); - } - - @Override - public void addDmlCtes( - CteContainer statement, - CteStatement idSelectCte, - MultiTableSqmMutationConverter sqmConverter, - Map, List> parameterResolutions, - SessionFactoryImplementor factory) { - super.addDmlCtes( statement, idSelectCte, sqmConverter, parameterResolutions, factory ); - } - - @Override - public int execute(DomainQueryExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); + SessionFactoryImplementor sessionFactory, + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, sessionFactory, context, firstJdbcParameterBindingsConsumer ); } @Override - public Expression createCountStar(SessionFactoryImplementor factory, MultiTableSqmMutationConverter sqmConverter) { - return super.createCountStar( factory, sqmConverter ); + public JdbcOperationQuerySelect getSelect() { + return super.getSelect(); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java index e16ad0abd..9361df67f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertHandler.java @@ -5,93 +5,35 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.lang.invoke.MethodHandles; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; - -import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; -import org.hibernate.id.OptimizableGenerator; -import org.hibernate.id.enhanced.Optimizer; -import org.hibernate.internal.util.collections.Stack; -import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.mapping.SqlExpressible; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.results.internal.TableGroupImpl; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.sqm.BinaryArithmeticOperator; -import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertHandler; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; -import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; -import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; -import org.hibernate.query.sqm.tree.insert.SqmValues; import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; -import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.SqlAstJoinType; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.spi.SqlAstProcessingState; -import org.hibernate.sql.ast.tree.Statement; -import org.hibernate.sql.ast.tree.cte.CteColumn; -import org.hibernate.sql.ast.tree.cte.CteMaterialization; -import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteTable; -import org.hibernate.sql.ast.tree.cte.CteTableGroup; -import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; -import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; -import org.hibernate.sql.ast.tree.from.ValuesTableGroup; -import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.insert.Values; -import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; -import org.hibernate.sql.ast.tree.select.QueryPart; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.ast.tree.update.Assignment; -import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.sql.results.internal.SqlSelectionImpl; + +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CompletionStage; public class ReactiveCteInsertHandler extends CteInsertHandler implements ReactiveHandler { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final SessionFactoryImplementor sessionFactory; - public ReactiveCteInsertHandler( CteTable cteTable, SqmInsertStatement sqmStatement, DomainParameterXref domainParameterXref, - SessionFactoryImplementor sessionFactory) { - super( cteTable, sqmStatement, domainParameterXref, sessionFactory ); - this.sessionFactory = sessionFactory; + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( cteTable, sqmStatement, domainParameterXref, context, firstJdbcParameterBindingsConsumer ); } @Override @@ -99,457 +41,23 @@ public int execute(DomainQueryExecutionContext executionContext) { throw LOG.nonReactiveMethodCall( "reactiveExecute" ); } - // Pretty much a copy and paste of the method in the super class - // We should refactor this @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { - final SqmInsertStatement sqmInsertStatement = getSqmStatement(); - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final EntityPersister entityDescriptor = getEntityDescriptor().getEntityPersister(); - final String explicitDmlTargetAlias; - if ( sqmInsertStatement.getTarget().getExplicitAlias() == null ) { - explicitDmlTargetAlias = "dml_target"; - } - else { - explicitDmlTargetAlias = sqmInsertStatement.getTarget().getExplicitAlias(); - } - - final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( - entityDescriptor, - sqmInsertStatement, - sqmInsertStatement.getTarget(), - explicitDmlTargetAlias, - getDomainParameterXref(), - executionContext.getQueryOptions(), - executionContext.getSession().getLoadQueryInfluencers(), - executionContext.getQueryParameterBindings(), - factory.getSqlTranslationEngine() - ); - final TableGroup insertingTableGroup = sqmConverter.getMutatingTableGroup(); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // visit the insertion target using our special converter, collecting - // information about the target paths - - final int size = getSqmStatement().getInsertionTargetPaths().size(); - final List, Assignment>> targetPathColumns = new ArrayList<>( size ); - final List targetPathCteColumns = new ArrayList<>( size ); - final NamedTableReference entityTableReference = new NamedTableReference( - getCteTable().getTableExpression(), - TemporaryTable.DEFAULT_ALIAS, - true - ); - final InsertSelectStatement insertStatement = new InsertSelectStatement( entityTableReference ); - - final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = sqmConverter.visitInsertionTargetPaths( - (assignable, columnReferences) -> { - final SqmPathInterpretation pathInterpretation = (SqmPathInterpretation) assignable; - final List columns = getCteTable().findCteColumns( pathInterpretation.getExpressionType() ); - insertStatement.addTargetColumnReferences( columnReferences ); - targetPathCteColumns.addAll( columns ); - targetPathColumns.add( new AbstractMap.SimpleEntry<>( columns, new Assignment( assignable, (Expression) assignable ) ) ); - }, - sqmInsertStatement, - entityDescriptor, - insertingTableGroup - ); - - final boolean assignsId = targetPathCteColumns.contains( getCteTable().getCteColumns().get( 0 ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Create the statement that represent the source for the entity cte - - final Stack processingStateStack = sqmConverter.getProcessingStateStack(); - final SqlAstProcessingState oldState = processingStateStack.pop(); - final Statement queryStatement; - if ( sqmInsertStatement instanceof SqmInsertSelectStatement ) { - final QueryPart queryPart = sqmConverter.visitQueryPart( ( (SqmInsertSelectStatement) sqmInsertStatement ).getSelectQueryPart() ); - queryPart.visitQuerySpecs( - querySpec -> { - // This returns true if the insertion target uses a sequence with an optimizer - // in which case we will fill the row_number column instead of the id column - if ( additionalInsertValues.applySelections( querySpec, sessionFactory ) ) { - final CteColumn rowNumberColumn = getCteTable().getCteColumns() - .get( getCteTable().getCteColumns().size() - 1 ); - final ColumnReference columnReference = new ColumnReference( - (String) null, - rowNumberColumn.getColumnExpression(), - false, - null, - rowNumberColumn.getJdbcMapping() - ); - insertStatement.getTargetColumns().set( - insertStatement.getTargetColumns().size() - 1, - columnReference - ); - targetPathCteColumns.set( - targetPathCteColumns.size() - 1, - rowNumberColumn - ); - } - if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { - querySpec.getSelectClause() - .addSqlSelection( new SqlSelectionImpl( 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, sessionFactory ) ) ); - } - } - ); - queryStatement = new SelectStatement( queryPart ); - } - else { - final List sqmValuesList = ( (SqmInsertValuesStatement) sqmInsertStatement ).getValuesList(); - final List valuesList = new ArrayList<>( sqmValuesList.size() ); - for ( SqmValues sqmValues : sqmValuesList ) { - final Values values = sqmConverter.visitValues( sqmValues ); - additionalInsertValues.applyValues( values ); - valuesList.add( values ); - } - final QuerySpec querySpec = new QuerySpec( true ); - final NavigablePath navigablePath = new NavigablePath( entityDescriptor.getRootPathName() ); - final List columnNames = new ArrayList<>( targetPathColumns.size() ); - final String valuesAlias = insertingTableGroup.getPrimaryTableReference().getIdentificationVariable(); - for ( Map.Entry, Assignment> entry : targetPathColumns ) { - for ( ColumnReference columnReference : entry.getValue().getAssignable().getColumnReferences() ) { - columnNames.add( columnReference.getColumnExpression() ); - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - columnReference.getQualifier().equals( valuesAlias ) - ? columnReference - : new ColumnReference( - valuesAlias, - columnReference.getColumnExpression(), - false, - null, - columnReference.getJdbcMapping() - ) - ) - ); - } - } - if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - SqmInsertStrategyHelper.createRowNumberingExpression( - querySpec, - sessionFactory - ) - ) - ); - } - final ValuesTableGroup valuesTableGroup = new ValuesTableGroup( - navigablePath, - entityDescriptor.getEntityPersister(), - valuesList, - insertingTableGroup.getPrimaryTableReference().getIdentificationVariable(), - columnNames, - true, - factory - ); - querySpec.getFromClause().addRoot( valuesTableGroup ); - queryStatement = new SelectStatement( querySpec ); - } - processingStateStack.push( oldState ); - sqmConverter.pruneTableGroupJoins(); - - if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { - // Add the row number to the assignments - final CteColumn rowNumberColumn = getCteTable().getCteColumns() - .get( getCteTable().getCteColumns().size() - 1 ); - final ColumnReference columnReference = new ColumnReference( - (String) null, - rowNumberColumn.getColumnExpression(), - false, - null, - rowNumberColumn.getJdbcMapping() - ); - insertStatement.getTargetColumns().add( columnReference ); - targetPathCteColumns.add( rowNumberColumn ); - } - - final CteTable entityCteTable = createCteTable( getCteTable(), targetPathCteColumns ); - - // Create the main query spec that will return the count of rows - final QuerySpec querySpec = new QuerySpec( true, 1 ); - final List> domainResults = new ArrayList<>( 1 ); - final SelectStatement statement = new SelectStatement( querySpec, domainResults ); - - final CteStatement entityCte; - if ( additionalInsertValues.requiresRowNumberIntermediate() ) { - final CteTable fullEntityCteTable = getCteTable(); - final String baseTableName = "base_" + entityCteTable.getTableExpression(); - final CteStatement baseEntityCte = new CteStatement( - entityCteTable.withName( baseTableName ), - queryStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - statement.addCteStatement( baseEntityCte ); - - final CteColumn rowNumberColumn = fullEntityCteTable.getCteColumns().get( - fullEntityCteTable.getCteColumns().size() - 1 - ); - final ColumnReference rowNumberColumnReference = new ColumnReference( - "e", - rowNumberColumn.getColumnExpression(), - false, - null, - rowNumberColumn.getJdbcMapping() - ); - final CteColumn idColumn = fullEntityCteTable.getCteColumns().get( 0 ); - final BasicValuedMapping idType = (BasicValuedMapping) idColumn.getJdbcMapping(); - final Optimizer optimizer = ( (OptimizableGenerator) entityDescriptor.getGenerator() ).getOptimizer(); - final BasicValuedMapping integerType = (BasicValuedMapping) rowNumberColumn.getJdbcMapping(); - final Expression rowNumberMinusOneModuloIncrement = new BinaryArithmeticExpression( - new BinaryArithmeticExpression( - rowNumberColumnReference, - BinaryArithmeticOperator.SUBTRACT, - new QueryLiteral<>( - 1, - (BasicValuedMapping) rowNumberColumn.getJdbcMapping() - ), - integerType - ), - BinaryArithmeticOperator.MODULO, - new QueryLiteral<>( - optimizer.getIncrementSize(), - integerType - ), - integerType - ); - - // Create the CTE that fetches a new sequence value for the row numbers that need it - { - final QuerySpec rowsWithSequenceQuery = new QuerySpec( true ); - rowsWithSequenceQuery.getFromClause().addRoot( - new CteTableGroup( new NamedTableReference( baseTableName, "e" ) ) - ); - rowsWithSequenceQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - rowNumberColumnReference - ) - ); - final String fragment = ( (BulkInsertionCapableIdentifierGenerator) entityDescriptor.getGenerator() ) - .determineBulkInsertionIdentifierGenerationSelectFragment( - sessionFactory.getSqlStringGenerationContext() - ); - rowsWithSequenceQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 1, - new SelfRenderingSqlFragmentExpression( fragment ) - ) - ); - rowsWithSequenceQuery.applyPredicate( - new ComparisonPredicate( - rowNumberMinusOneModuloIncrement, - ComparisonOperator.EQUAL, - new QueryLiteral<>( - 0, - integerType - ) - ) - ); - final CteTable rowsWithSequenceCteTable = new CteTable( - ROW_NUMBERS_WITH_SEQUENCE_VALUE, - List.of( rowNumberColumn, idColumn ) - ); - final SelectStatement rowsWithSequenceStatement = new SelectStatement( rowsWithSequenceQuery ); - final CteStatement rowsWithSequenceCte = new CteStatement( - rowsWithSequenceCteTable, - rowsWithSequenceStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - statement.addCteStatement( rowsWithSequenceCte ); - } - - // Create the CTE that represents the entity cte - { - final QuerySpec entityQuery = new QuerySpec( true ); - final NavigablePath navigablePath = new NavigablePath( baseTableName ); - final TableGroup baseTableGroup = new TableGroupImpl( - navigablePath, - null, - new NamedTableReference( baseTableName, "e" ), - null - ); - final TableGroup rowsWithSequenceTableGroup = new CteTableGroup( - new NamedTableReference( - ROW_NUMBERS_WITH_SEQUENCE_VALUE, - "t" - ) - ); - baseTableGroup.addTableGroupJoin( - new TableGroupJoin( - rowsWithSequenceTableGroup.getNavigablePath(), - SqlAstJoinType.LEFT, - rowsWithSequenceTableGroup, - new ComparisonPredicate( - new BinaryArithmeticExpression( - rowNumberColumnReference, - BinaryArithmeticOperator.SUBTRACT, - rowNumberMinusOneModuloIncrement, - integerType - ), - ComparisonOperator.EQUAL, - new ColumnReference( - "t", - rowNumberColumn.getColumnExpression(), - false, - null, - rowNumberColumn.getJdbcMapping() - ) - ) - ) - ); - entityQuery.getFromClause().addRoot( baseTableGroup ); - entityQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - new BinaryArithmeticExpression( - new ColumnReference( - "t", - idColumn.getColumnExpression(), - false, - null, - idColumn.getJdbcMapping() - ), - BinaryArithmeticOperator.ADD, - new BinaryArithmeticExpression( - rowNumberColumnReference, - BinaryArithmeticOperator.SUBTRACT, - new ColumnReference( - "t", - rowNumberColumn.getColumnExpression(), - false, - null, - rowNumberColumn.getJdbcMapping() - ), - integerType - ), - idType - ) - ) - ); - final CteTable finalEntityCteTable; - if ( targetPathCteColumns.contains( getCteTable().getCteColumns().get( 0 ) ) ) { - finalEntityCteTable = entityCteTable; - } - else { - targetPathCteColumns.add( 0, getCteTable().getCteColumns().get( 0 ) ); - finalEntityCteTable = createCteTable( getCteTable(), targetPathCteColumns ); - } - final List cteColumns = finalEntityCteTable.getCteColumns(); - for ( int i = 1; i < cteColumns.size(); i++ ) { - final CteColumn cteColumn = cteColumns.get( i ); - entityQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - i, - new ColumnReference( - "e", - cteColumn.getColumnExpression(), - false, - null, - cteColumn.getJdbcMapping() - ) - ) - ); - } - - final SelectStatement entityStatement = new SelectStatement( entityQuery ); - entityCte = new CteStatement( - finalEntityCteTable, - entityStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - statement.addCteStatement( entityCte ); - } - } - else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { - final String baseTableName = "base_" + entityCteTable.getTableExpression(); - final CteStatement baseEntityCte = new CteStatement( - entityCteTable.withName( baseTableName ), - queryStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - statement.addCteStatement( baseEntityCte ); - targetPathCteColumns.add( 0, getCteTable().getCteColumns().get( 0 ) ); - final CteTable finalEntityCteTable = createCteTable( getCteTable(), targetPathCteColumns ); - final QuerySpec finalQuerySpec = new QuerySpec( true ); - final SelectStatement finalQueryStatement = new SelectStatement( finalQuerySpec ); - entityCte = new CteStatement( - finalEntityCteTable, - finalQueryStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - } - else { - entityCte = new CteStatement( - entityCteTable, - queryStatement, - // The query cte will be reused multiple times - CteMaterialization.MATERIALIZED - ); - statement.addCteStatement( entityCte ); - } - - // Add all CTEs - final String baseInsertCte = addDmlCtes( - statement, - entityCte, - targetPathColumns, - assignsId, - sqmConverter, - factory - ); - - final Expression count = createCountStar( factory, sqmConverter ); - domainResults - .add( new BasicResult<>( 0, null, ( (SqlExpressible) count ).getJdbcMapping() ) ); - querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, count ) ); - querySpec.getFromClause().addRoot( - new CteTableGroup( - new NamedTableReference( - // We want to return the insertion count of the base table - baseInsertCte, - CTE_TABLE_IDENTIFIER - ) - ) - ); - - // Execute the statement - final JdbcServices jdbcServices = factory.getJdbcServices(); - final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildSelectTranslator( factory, statement ); - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - getDomainParameterXref(), - SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), sqmConverter ), - new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmConverter.getSqmParameterMappingModelExpressibleResolutions() - .get( parameter ); - } - }, - executionContext.getSession() - ); - final JdbcOperationQuerySelect select = translator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - return ( (ReactiveSharedSessionContractImplementor) executionContext.getSession() ) - .reactiveAutoFlushIfRequired( select.getAffectedTableNames() ) - .thenCompose( v -> StandardReactiveSelectExecutor.INSTANCE.list( - select, + public CompletionStage reactiveExecute( + JdbcParameterBindings jdbcParameterBindings, + DomainQueryExecutionContext context) { + return ( (ReactiveSharedSessionContractImplementor) context.getSession() ) + .reactiveAutoFlushIfRequired( getSelect().getAffectedTableNames() ) + .thenCompose( v -> StandardReactiveSelectExecutor.INSTANCE + .list( + getSelect(), jdbcParameterBindings, - SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), + SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ), row -> row[0], - ReactiveListResultsConsumer.UniqueSemantic.NONE + null, + ReactiveListResultsConsumer.UniqueSemantic.NONE, + 1 ) - .thenApply( list -> ( (Number) list.get( 0 ) ).intValue() ) ); + .thenApply( list -> ( (Number) list.get( 0 ) ).intValue() ) + ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertStrategy.java index d7259ebb1..e0a0e10fd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteInsertStrategy.java @@ -5,16 +5,18 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.util.concurrent.CompletionStage; - +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.internal.InsertHandler; import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; public class ReactiveCteInsertStrategy extends CteInsertStrategy implements ReactiveSqmMultiTableInsertStrategy { @@ -31,11 +33,16 @@ public ReactiveCteInsertStrategy( } @Override - public CompletionStage reactiveExecuteInsert( - SqmInsertStatement sqmInsertStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return new ReactiveCteInsertHandler( getEntityCteTable(), sqmInsertStatement, domainParameterXref, getSessionFactory() ) - .reactiveExecute( context ); + public MultiTableHandlerBuildResult buildHandler(SqmInsertStatement sqmInsertStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final InsertHandler multiTableHandler = new ReactiveCteInsertHandler( + getEntityCteTable(), + sqmInsertStatement, + domainParameterXref, + context, + firstJdbcParameterBindings + ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } + } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java index 6da230aad..be67bd8ad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java @@ -5,17 +5,21 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.util.concurrent.CompletionStage; - +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; public class ReactiveCteMutationStrategy extends CteMutationStrategy implements ReactiveSqmMultiTableMutationStrategy { @@ -28,22 +32,51 @@ public ReactiveCteMutationStrategy(EntityPersister rootDescriptor, RuntimeModelC } @Override - public CompletionStage reactiveExecuteUpdate( - SqmUpdateStatement sqmUpdateStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - checkMatch( sqmUpdateStatement ); - return new ReactiveCteUpdateHandler( getIdCteTable(), sqmUpdateStatement, domainParameterXref, this, getSessionFactory() ) - .reactiveExecute( context ); + public MultiTableHandlerBuildResult buildHandler(SqmDeleteOrUpdateStatement sqmStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final MultiTableHandler multiTableHandler = sqmStatement instanceof SqmDeleteStatement sqmDelete + ? buildHandler( sqmDelete, domainParameterXref, context, firstJdbcParameterBindings) + : buildHandler( (SqmUpdateStatement) sqmStatement, domainParameterXref, context, firstJdbcParameterBindings ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } - @Override - public CompletionStage reactiveExecuteDelete( - SqmDeleteStatement sqmDeleteStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - checkMatch( sqmDeleteStatement ); - return new ReactiveCteDeleteHandler( getIdCteTable(), sqmDeleteStatement, domainParameterXref, this, getSessionFactory() ) - .reactiveExecute( context ); + public ReactiveHandler buildHandler(SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context, MutableObject firstJdbcParameterBindingsConsumer) { + checkMatch( sqmDelete ); + if ( getRootDescriptor().getSoftDeleteMapping() != null ) { + return new ReactiveCteSoftDeleteHandler( + getIdCteTable(), + sqmDelete, + domainParameterXref, + this, + getSessionFactory(), + context, + firstJdbcParameterBindingsConsumer + ); + } + else { + return new ReactiveCteDeleteHandler( + getIdCteTable(), + sqmDelete, + domainParameterXref, + this, + getSessionFactory(), + context, + firstJdbcParameterBindingsConsumer + ); + } } + + public MultiTableHandler buildHandler(SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context, MutableObject firstJdbcParameterBindingsConsumer) { + checkMatch( sqmUpdate ); + return new ReactiveCteUpdateHandler( + getIdCteTable(), + sqmUpdate, + domainParameterXref, + this, + getSessionFactory(), + context, + firstJdbcParameterBindingsConsumer + ); + } + } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteSoftDeleteHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteSoftDeleteHandler.java new file mode 100644 index 000000000..8e0740d14 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteSoftDeleteHandler.java @@ -0,0 +1,43 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.query.sqm.mutation.internal.cte; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; +import org.hibernate.query.sqm.mutation.internal.cte.CteSoftDeleteHandler; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; +import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +public class ReactiveCteSoftDeleteHandler extends CteSoftDeleteHandler implements ReactiveAbstractCteMutationHandler { + protected ReactiveCteSoftDeleteHandler( + CteTable cteTable, + SqmDeleteStatement sqmDeleteStatement, + DomainParameterXref domainParameterXref, + CteMutationStrategy strategy, + SessionFactoryImplementor sessionFactory, + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( + cteTable, + sqmDeleteStatement, + domainParameterXref, + strategy, + sessionFactory, + context, + firstJdbcParameterBindingsConsumer + ); + } + + @Override + public JdbcOperationQuerySelect getSelect() { + return super.getSelect(); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteUpdateHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteUpdateHandler.java index 0730ff33e..c855f35ad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteUpdateHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteUpdateHandler.java @@ -5,59 +5,35 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.cte; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; - import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy; import org.hibernate.query.sqm.mutation.internal.cte.CteUpdateHandler; -import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.sql.ast.tree.cte.CteContainer; -import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteTable; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; /** * @see CteUpdateHandler */ public class ReactiveCteUpdateHandler extends CteUpdateHandler implements ReactiveAbstractCteMutationHandler { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - public ReactiveCteUpdateHandler( CteTable cteTable, SqmUpdateStatement sqmStatement, DomainParameterXref domainParameterXref, CteMutationStrategy strategy, - SessionFactoryImplementor sessionFactory) { - super( cteTable, sqmStatement, domainParameterXref, strategy, sessionFactory ); - } - - @Override - public void addDmlCtes( - CteContainer statement, - CteStatement idSelectCte, - MultiTableSqmMutationConverter sqmConverter, - Map, List> parameterResolutions, - SessionFactoryImplementor factory) { - super.addDmlCtes( statement, idSelectCte, sqmConverter, parameterResolutions, factory ); - } - - @Override - public int execute(DomainQueryExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); + SessionFactoryImplementor sessionFactory, + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( cteTable, sqmStatement, domainParameterXref, strategy, sessionFactory, context, firstJdbcParameterBindingsConsumer ); } @Override - public Expression createCountStar(SessionFactoryImplementor factory, MultiTableSqmMutationConverter sqmConverter) { - return super.createCountStar( factory, sqmConverter ); + public JdbcOperationQuerySelect getSelect() { + return super.getSelect(); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java deleted file mode 100644 index 399e8b30a..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveInsertExecutionDelegate.java +++ /dev/null @@ -1,72 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.mutation.internal.cte; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; - -import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.temptable.InsertExecutionDelegate; -import org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTableBasedInsertHandler; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.insert.ConflictClause; -import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.update.Assignment; -import org.hibernate.sql.exec.spi.ExecutionContext; - -/** - * @see InsertExecutionDelegate - */ -public class ReactiveInsertExecutionDelegate extends InsertExecutionDelegate implements ReactiveTableBasedInsertHandler.ReactiveExecutionDelegate { - - public ReactiveInsertExecutionDelegate( - MultiTableSqmMutationConverter sqmConverter, - TemporaryTable entityTable, - TemporaryTableStrategy temporaryTableStrategy, - boolean forceDropAfterUse, - Function sessionUidAccess, - DomainParameterXref domainParameterXref, - TableGroup insertingTableGroup, - Map tableReferenceByAlias, - List assignments, - boolean assignedId, - InsertSelectStatement insertStatement, - ConflictClause conflictClause, - JdbcParameter sessionUidParameter, - DomainQueryExecutionContext executionContext) { - super( - sqmConverter, - entityTable, - temporaryTableStrategy, - forceDropAfterUse, - sessionUidAccess, - domainParameterXref, - insertingTableGroup, - tableReferenceByAlias, - assignments, - assignedId, - insertStatement, - conflictClause, - sessionUidParameter, - executionContext - ); - } - - @Override - public CompletionStage reactiveExecute(ExecutionContext executionContext) { - // FIXME: Why is this null? - return null; - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java index d936c590f..164403171 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveExecuteWithTemporaryTableHelper.java @@ -7,6 +7,8 @@ import java.lang.invoke.MethodHandles; import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.UUID; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -16,42 +18,34 @@ import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableColumn; +import org.hibernate.dialect.temptable.TemporaryTableSessionUidColumn; import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.query.sqm.ComparisonOperator; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; +import org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper; import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTemporaryTableHelper.TemporaryTableCreationWork; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; import org.hibernate.reactive.util.impl.CompletionStages; -import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.QueryLiteral; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.StandardTableGroup; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.results.internal.SqlSelectionImpl; import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveTemporaryTableHelper.cleanTemporaryTableRows; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; @@ -68,75 +62,6 @@ public final class ReactiveExecuteWithTemporaryTableHelper { private ReactiveExecuteWithTemporaryTableHelper() { } - public static CompletionStage saveMatchingIdsIntoIdTable( - MultiTableSqmMutationConverter sqmConverter, - Predicate suppliedPredicate, - TemporaryTable idTable, - Function sessionUidAccess, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - - final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup(); - - assert mutatingTableGroup.getModelPart() instanceof EntityMappingType; - final EntityMappingType mutatingEntityDescriptor = (EntityMappingType) mutatingTableGroup.getModelPart(); - - final NamedTableReference idTableReference = new NamedTableReference( - idTable.getTableExpression(), - InsertSelectStatement.DEFAULT_ALIAS - ); - final InsertSelectStatement idTableInsert = new InsertSelectStatement( idTableReference ); - - for ( int i = 0; i < idTable.getColumns().size(); i++ ) { - final TemporaryTableColumn column = idTable.getColumns().get( i ); - idTableInsert.addTargetColumnReferences( - new ColumnReference( - idTableReference, - column.getColumnName(), - // id columns cannot be formulas and cannot have custom read and write expressions - false, - null, - column.getJdbcMapping() - ) - ); - } - - final QuerySpec matchingIdSelection = new QuerySpec( true, 1 ); - idTableInsert.setSourceSelectStatement( matchingIdSelection ); - - matchingIdSelection.getFromClause().addRoot( mutatingTableGroup ); - - mutatingEntityDescriptor.getIdentifierMapping().forEachSelectable( - (selectionIndex, selection) -> { - matchingIdSelection.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - selectionIndex + 1, - sqmConverter.getSqlExpressionResolver().resolveSqlExpression( - mutatingTableGroup.resolveTableReference( mutatingTableGroup.getNavigablePath(), selection.getContainingTableExpression() ), - selection - ) - ) - ); - } - ); - - if ( idTable.getSessionUidColumn() != null ) { - final int jdbcPosition = matchingIdSelection.getSelectClause().getSqlSelections().size(); - matchingIdSelection.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - jdbcPosition, - new QueryLiteral<>( - UUID.fromString( sessionUidAccess.apply( executionContext.getSession() ) ), - (BasicValuedMapping) idTable.getSessionUidColumn().getJdbcMapping() - ) - ) - ); - } - - matchingIdSelection.applyPredicate( suppliedPredicate ); - return saveIntoTemporaryTable( idTableInsert, jdbcParameterBindings, executionContext ); - } - public static CompletionStage saveIntoTemporaryTable( InsertSelectStatement temporaryTableInsert, JdbcParameterBindings jdbcParameterBindings, @@ -150,9 +75,10 @@ public static CompletionStage saveIntoTemporaryTable( // Acquire a WRITE lock for the rows that are about to be modified lockOptions.setLockMode( LockMode.WRITE ); // Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported - if ( temporaryTableInsert.getSourceSelectStatement() != null + final QueryPart sourceSelectStatement = temporaryTableInsert.getSourceSelectStatement(); + if ( sourceSelectStatement != null && !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) { - temporaryTableInsert.getSourceSelectStatement().visitQuerySpecs( + sourceSelectStatement.visitQuerySpecs( querySpec -> querySpec.getFromClause().visitTableJoins( tableJoin -> { if ( tableJoin.isInitialized() @@ -167,124 +93,39 @@ public static CompletionStage saveIntoTemporaryTable( .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); lockOptions.setLockMode( lockMode ); + return saveIntoTemporaryTable(jdbcInsert, jdbcParameterBindings, executionContext); + } + + public static CompletionStage saveIntoTemporaryTable( + JdbcOperationQueryMutation jdbcInsert, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { return StandardReactiveJdbcMutationExecutor.INSTANCE .executeReactive( jdbcInsert, jdbcParameterBindings, - executionContext.getSession().getJdbcCoordinator().getStatementPreparer()::prepareStatement, - ReactiveExecuteWithTemporaryTableHelper::doNothing, + sql -> executionContext.getSession().getJdbcCoordinator() + .getStatementPreparer().prepareStatement( sql ), + (integer, preparedStatement) -> {}, executionContext - ); } public static QuerySpec createIdTableSelectQuerySpec( TemporaryTable idTable, - Function sessionUidAccess, + JdbcParameter sessionUidParameter, EntityMappingType entityDescriptor, ExecutionContext executionContext) { - return createIdTableSelectQuerySpec( idTable, null, sessionUidAccess, entityDescriptor, executionContext ); + return createIdTableSelectQuerySpec( idTable, null, sessionUidParameter, entityDescriptor, executionContext ); } public static QuerySpec createIdTableSelectQuerySpec( TemporaryTable idTable, ModelPart fkModelPart, - Function sessionUidAccess, + JdbcParameter sessionUidParameter, EntityMappingType entityDescriptor, ExecutionContext executionContext) { - final QuerySpec querySpec = new QuerySpec( false ); - - final NamedTableReference idTableReference = new NamedTableReference( - idTable.getTableExpression(), - TemporaryTable.DEFAULT_ALIAS, - true - ); - final TableGroup idTableGroup = new StandardTableGroup( - true, - new NavigablePath( idTableReference.getTableExpression() ), - entityDescriptor, - null, - idTableReference, - null, - executionContext.getSession().getFactory() - ); - - querySpec.getFromClause().addRoot( idTableGroup ); - - applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart, entityDescriptor ); - applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidAccess, executionContext ); - - return querySpec; - } - - // TODO: I think we can reuse the method in ExecuteWithTemporaryTableHelper - private static void applyIdTableSelections( - QuerySpec querySpec, - TableReference tableReference, - TemporaryTable idTable, - ModelPart fkModelPart, - EntityMappingType entityDescriptor) { - if ( fkModelPart == null ) { - final int size = entityDescriptor.getIdentifierMapping().getJdbcTypeCount(); - for ( int i = 0; i < size; i++ ) { - final TemporaryTableColumn temporaryTableColumn = idTable.getColumns().get( i ); - if ( temporaryTableColumn != idTable.getSessionUidColumn() ) { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - i, - new ColumnReference( - tableReference, - temporaryTableColumn.getColumnName(), - false, - null, - temporaryTableColumn.getJdbcMapping() - ) - ) - ); - } - } - } - else { - fkModelPart.forEachSelectable( - (i, selectableMapping) -> querySpec.getSelectClause() - .addSqlSelection( new SqlSelectionImpl( - i, - new ColumnReference( - tableReference, - selectableMapping.getSelectionExpression(), - false, - null, - selectableMapping.getJdbcMapping() - ) - ) - ) - ); - } - } - - private static void applyIdTableRestrictions( - QuerySpec querySpec, - TableReference idTableReference, - TemporaryTable idTable, - Function sessionUidAccess, - ExecutionContext executionContext) { - if ( idTable.getSessionUidColumn() != null ) { - querySpec.applyPredicate( - new ComparisonPredicate( - new ColumnReference( - idTableReference, - idTable.getSessionUidColumn().getColumnName(), - false, null, - idTable.getSessionUidColumn().getJdbcMapping() - ), - ComparisonOperator.EQUAL, - new QueryLiteral<>( - UUID.fromString( sessionUidAccess.apply( executionContext.getSession() ) ), - (BasicValuedMapping) idTable.getSessionUidColumn().getJdbcMapping() - ) - ) - ); - } + return ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec( idTable, fkModelPart, sessionUidParameter, entityDescriptor, executionContext ); } @Deprecated(forRemoval = true, since = "3.1") @@ -336,6 +177,46 @@ public static CompletionStage performAfterTemporaryTableUseActions( }; } + public static CompletionStage loadInsertedRowNumbers( + String sqlSelect, + TemporaryTable temporaryTable, + Function sessionUidAccess, + int rows, + ExecutionContext executionContext) { + final TemporaryTableSessionUidColumn sessionUidColumn = temporaryTable.getSessionUidColumn(); + final SharedSessionContractImplementor session = executionContext.getSession(); + final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator(); + PreparedStatement preparedStatement = null; + preparedStatement = jdbcCoordinator.getStatementPreparer().prepareStatement( sqlSelect ); + Object[] parameters = new Object[1]; + if ( sessionUidColumn != null ) { + parameters[0] = UUID.fromString( sessionUidAccess.apply( session ) ); + } + final Integer[] rowNumbers = new Integer[rows]; + return reactiveConnection(session).selectJdbc( sqlSelect, parameters ) + .thenApply( resultSet -> getRowNumbers( rows, resultSet, rowNumbers ) ); + } + + private static Integer[] getRowNumbers(int rows, ResultSet resultSet, Integer[] rowNumbers) { + int rowIndex = 0; + try { + while ( resultSet.next() ) { + rowNumbers[rowIndex++] = resultSet.getInt( 1 ); + } + return rowNumbers; + } + catch ( IndexOutOfBoundsException e ) { + throw new IllegalArgumentException( "Expected " + rows + " to be inserted but found more", e ); + } + catch ( SQLException ex ) { + throw new IllegalStateException( ex ); + } + } + + private static ReactiveConnection reactiveConnection(SharedSessionContractImplementor session) { + return ( (ReactiveConnectionSupplier) session ).getReactiveConnection(); + } + private static CompletionStage dropAction( TemporaryTable temporaryTable, ExecutionContext executionContext, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java index 708a7b072..9ca1dfce8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableInsertStrategy.java @@ -6,16 +6,19 @@ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.internal.InsertHandler; import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; /** * @see org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy @@ -48,11 +51,9 @@ public void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAcce } @Override - public CompletionStage reactiveExecuteInsert( - SqmInsertStatement sqmInsertStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage.thenCompose( v -> new ReactiveTableBasedInsertHandler( + public MultiTableHandlerBuildResult buildHandler(SqmInsertStatement sqmInsertStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final InsertHandler multiTableHandler = new ReactiveTableBasedInsertHandler( sqmInsertStatement, domainParameterXref, getTemporaryTable(), @@ -60,12 +61,13 @@ public CompletionStage reactiveExecuteInsert( false, // generally a global temp table should already track a Connection-specific uid, // but just in case a particular env needs it... - ReactiveGlobalTemporaryTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindings + ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } - @Override public boolean isPrepared() { return prepared; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java index baa4ed9a5..aaad73b10 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java @@ -6,22 +6,28 @@ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; /** * @see org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy */ -public class ReactiveGlobalTemporaryTableMutationStrategy extends GlobalTemporaryTableStrategy +public class ReactiveGlobalTemporaryTableMutationStrategy extends GlobalTemporaryTableMutationStrategy implements ReactiveGlobalTemporaryTableStrategy, ReactiveSqmMultiTableMutationStrategy { private final CompletableFuture tableCreatedStage = new CompletableFuture<>(); @@ -50,37 +56,68 @@ public void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAcce } @Override - public CompletionStage reactiveExecuteUpdate( - SqmUpdateStatement sqmUpdateStatement, + public MultiTableHandlerBuildResult buildHandler(SqmDeleteOrUpdateStatement sqmStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final MultiTableHandler multiTableHandler = sqmStatement instanceof SqmDeleteStatement sqmDelete + ? buildHandler( sqmDelete, domainParameterXref, context, firstJdbcParameterBindings ) + : buildHandler( (SqmUpdateStatement) sqmStatement, domainParameterXref, context, firstJdbcParameterBindings ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); + } + + public MultiTableHandler buildHandler( + SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage - .thenCompose( v -> new ReactiveTableBasedUpdateHandler( - sqmUpdateStatement, - domainParameterXref, - getTemporaryTable(), - getTemporaryTableStrategy(), - false, - ReactivePersistentTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + return new ReactiveTableBasedUpdateHandler( + sqmUpdate, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + // generally a global temp table should already track a Connection-specific uid, + // but just in case a particular env needs it... + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); } - @Override - public CompletionStage reactiveExecuteDelete( - SqmDeleteStatement sqmDeleteStatement, + public MultiTableHandler buildHandler( + SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage - .thenCompose( v -> new ReactiveTableBasedDeleteHandler( - sqmDeleteStatement, - domainParameterXref, - getTemporaryTable(), - getTemporaryTableStrategy(), - false, - ReactiveGlobalTemporaryTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + final EntityPersister rootDescriptor = context.getSession().getFactory().getMappingMetamodel() + .getEntityDescriptor( sqmDelete.getRoot().getEntityName() ); + if ( rootDescriptor.getSoftDeleteMapping() != null ) { + return new ReactiveTableBasedSoftDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + // generally a global temp table should already track a Connection-specific uid, + // but just in case a particular env needs it... + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); + } + else { + return new ReactiveTableBasedDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + // generally a global temp table should already track a Connection-specific uid, + // but just in case a particular env needs it... + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); + } } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java index 322127730..21a9b3ced 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableInsertStrategy.java @@ -5,15 +5,15 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; -import java.util.concurrent.CompletionStage; - -import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; public class ReactiveLocalTemporaryTableInsertStrategy extends LocalTemporaryTableInsertStrategy implements ReactiveSqmMultiTableInsertStrategy { @@ -23,28 +23,21 @@ public ReactiveLocalTemporaryTableInsertStrategy(LocalTemporaryTableInsertStrate } @Override - public CompletionStage reactiveExecuteInsert( - SqmInsertStatement sqmInsertStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return new ReactiveTableBasedInsertHandler( + public MultiTableHandlerBuildResult buildHandler(SqmInsertStatement sqmInsertStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final MultiTableHandler multiTableHandler = new ReactiveTableBasedInsertHandler( sqmInsertStatement, domainParameterXref, getTemporaryTable(), getTemporaryTableStrategy(), isDropIdTables(), - ReactiveLocalTemporaryTableInsertStrategy::throwUnexpectedCallToSessionUIDError, - getSessionFactory() - ).reactiveExecute( context ); - } - - private static String throwUnexpectedCallToSessionUIDError(SharedSessionContractImplementor session) { - throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); + session -> { + throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); + }, + context, + firstJdbcParameterBindings + ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } - private AfterUseAction afterUserAction() { - return isDropIdTables() - ? AfterUseAction.DROP - : getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(); - } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java index d22d50e5b..1073f118f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java @@ -5,16 +5,16 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; -import java.util.concurrent.CompletionStage; - -import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; public class ReactiveLocalTemporaryTableMutationStrategy extends LocalTemporaryTableMutationStrategy implements ReactiveSqmMultiTableMutationStrategy { @@ -23,46 +23,59 @@ public ReactiveLocalTemporaryTableMutationStrategy(LocalTemporaryTableMutationSt super( mutationStrategy.getTemporaryTable(), mutationStrategy.getSessionFactory() ); } - private static String throwUnexpectedAccessToSessionUID(SharedSessionContractImplementor session) { - // Should probably go in the LOG - throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); - } - - @Override - public CompletionStage reactiveExecuteUpdate( + public MultiTableHandler buildHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { return new ReactiveTableBasedUpdateHandler( sqmUpdate, domainParameterXref, getTemporaryTable(), getTemporaryTableStrategy(), isDropIdTables(), - ReactiveLocalTemporaryTableMutationStrategy::throwUnexpectedAccessToSessionUID, - getSessionFactory() - ).reactiveExecute( context ); + session -> { + throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); + }, + context, + firstJdbcParameterBindingsConsumer + ); } - @Override - public CompletionStage reactiveExecuteDelete( + public MultiTableHandler buildHandler( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return new ReactiveTableBasedDeleteHandler( - sqmDelete, - domainParameterXref, - getTemporaryTable(), - getTemporaryTableStrategy(), - isDropIdTables(), - ReactiveLocalTemporaryTableMutationStrategy::throwUnexpectedAccessToSessionUID, - getSessionFactory() - ).reactiveExecute( context ); - } - - private AfterUseAction afterUseAction() { - return isDropIdTables() - ? AfterUseAction.DROP - : getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + final EntityPersister rootDescriptor = context.getSession().getFactory().getMappingMetamodel() + .getEntityDescriptor( sqmDelete.getRoot().getEntityName() ); + if ( rootDescriptor.getSoftDeleteMapping() != null ) { + return new ReactiveTableBasedSoftDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + isDropIdTables(), + session -> { + throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); + }, + context, + firstJdbcParameterBindingsConsumer + ); + } + else { + return new ReactiveTableBasedDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + isDropIdTables(), + session -> { + throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); + }, + context, + firstJdbcParameterBindingsConsumer + ); + } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java index 951cabce1..a5b0c36c3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableInsertStrategy.java @@ -10,12 +10,16 @@ import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; public class ReactivePersistentTableInsertStrategy extends PersistentTableInsertStrategy implements ReactivePersistentTableStrategy, ReactiveSqmMultiTableInsertStrategy { @@ -45,19 +49,19 @@ public void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAcce } @Override - public CompletionStage reactiveExecuteInsert( - SqmInsertStatement sqmInsertStatement, - DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage.thenCompose( v -> new ReactiveTableBasedInsertHandler( + public MultiTableHandlerBuildResult buildHandler(SqmInsertStatement sqmInsertStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context) { + final MutableObject firstJdbcParameterBindings = new MutableObject<>(); + final MultiTableHandler multiTableHandler = new ReactiveTableBasedInsertHandler( sqmInsertStatement, domainParameterXref, getTemporaryTable(), getTemporaryTableStrategy(), false, - ReactivePersistentTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindings + ); + return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java index 8f23ce727..7b7a641ae 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java @@ -5,18 +5,22 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandler; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; public class ReactivePersistentTableMutationStrategy extends PersistentTableMutationStrategy @@ -46,40 +50,55 @@ public void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAcce release( sessionFactory, connectionAccess, tableDroppedStage ); } - @Override - public CompletionStage reactiveExecuteUpdate( - SqmUpdateStatement sqmUpdateStatement, + public MultiTableHandler buildHandler( + SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage - .thenCompose( v -> new ReactiveTableBasedUpdateHandler( - sqmUpdateStatement, - domainParameterXref, - getTemporaryTable(), - getTemporaryTableStrategy(), - false, - ReactivePersistentTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + return new ReactiveTableBasedUpdateHandler( + sqmUpdate, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); } - @Override - public CompletionStage reactiveExecuteDelete( - SqmDeleteStatement sqmDeleteStatement, + public MultiTableHandler buildHandler( + SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context) { - return tableCreatedStage - .thenCompose( v -> new ReactiveTableBasedDeleteHandler( - sqmDeleteStatement, - domainParameterXref, - getTemporaryTable(), - getTemporaryTableStrategy(), - false, - ReactivePersistentTableStrategy::sessionIdentifier, - getSessionFactory() - ).reactiveExecute( context ) ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + final EntityPersister rootDescriptor = context.getSession().getFactory().getMappingMetamodel() + .getEntityDescriptor( sqmDelete.getRoot().getEntityName() ); + if ( rootDescriptor.getSoftDeleteMapping() != null ) { + return new ReactiveTableBasedSoftDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); + } + else { + return new ReactiveTableBasedDeleteHandler( + sqmDelete, + domainParameterXref, + getTemporaryTable(), + getTemporaryTableStrategy(), + false, + session -> session.getSessionIdentifier().toString(), + context, + firstJdbcParameterBindingsConsumer + ); + } } - @Override public CompletionStage getDropTableActionStage() { return tableDroppedStage; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java deleted file mode 100644 index c119e718a..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveRestrictedDeleteExecutionDelegate.java +++ /dev/null @@ -1,605 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.mutation.internal.temptable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.LoadQueryInfluencers; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.MutableInteger; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryOptions; -import org.hibernate.query.spi.QueryParameterBindings; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector; -import org.hibernate.query.sqm.mutation.internal.temptable.AbstractDeleteExecutionDelegate; -import org.hibernate.query.sqm.mutation.internal.temptable.ColumnReferenceCheckingSqlAstWalker; -import org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithoutIdTableHelper; -import org.hibernate.query.sqm.mutation.internal.temptable.RestrictedDeleteExecutionDelegate; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.reactive.util.impl.CompletionStages; -import org.hibernate.reactive.util.impl.CompletionStages.Completable; -import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.spi.SqlExpressionResolver; -import org.hibernate.sql.ast.tree.delete.DeleteStatement; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; -import org.hibernate.sql.ast.tree.expression.SqlTuple; -import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.from.UnionTableReference; -import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.predicate.PredicateCollector; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.spi.JdbcParameterBindings; - -import static java.lang.invoke.MethodHandles.lookup; -import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; -import static org.hibernate.reactive.logging.impl.LoggerFactory.make; -import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; - -/** - * The reactive version of {@link RestrictedDeleteExecutionDelegate} - */ -// Basically a copy of RestrictedDeleteExecutionDelegate, we will probably need to refactor this code to avoid duplication -public class ReactiveRestrictedDeleteExecutionDelegate extends AbstractDeleteExecutionDelegate - implements ReactiveTableBasedDeleteHandler.ReactiveExecutionDelegate { - - private static final Log LOG = make( Log.class, lookup() ); - - public ReactiveRestrictedDeleteExecutionDelegate( - EntityMappingType entityDescriptor, TemporaryTable idTable, TemporaryTableStrategy temporaryTableStrategy, boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, - DomainParameterXref domainParameterXref, QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings, - Function sessionUidAccess, SessionFactoryImplementor sessionFactory - ) { - super( - entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess, - sessionFactory - ); - } - - - @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { - final EntityPersister entityDescriptor = getSessionFactory().getRuntimeMetamodels() - .getMappingMetamodel() - .getEntityDescriptor( getSqmDelete().getTarget().getEntityName() ); - final String hierarchyRootTableName = entityDescriptor.getTableName(); - - final TableGroup deletingTableGroup = getConverter().getMutatingTableGroup(); - - final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference( - deletingTableGroup.getNavigablePath(), - hierarchyRootTableName - ); - assert hierarchyRootTableReference != null; - - // Use the converter to interpret the where-clause. We do this for 2 reasons: - // 1) the resolved Predicate is ultimately the base for applying restriction to the deletes - // 2) we also inspect each ColumnReference that is part of the where-clause to see which - // table it comes from. If all the referenced columns (if any at all) are from the root table - // we can perform all the deletes without using an id-table - final Predicate specifiedRestriction = getConverter().visitWhereClause( getSqmDelete().getWhereClause() ); - - final PredicateCollector predicateCollector = new PredicateCollector( specifiedRestriction ); - entityDescriptor.applyBaseRestrictions( - predicateCollector, - deletingTableGroup, - true, - executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), - false, - null, - getConverter() - ); - - getConverter().pruneTableGroupJoins(); - final ColumnReferenceCheckingSqlAstWalker walker = new ColumnReferenceCheckingSqlAstWalker( - hierarchyRootTableReference.getIdentificationVariable() - ); - if ( predicateCollector.getPredicate() != null ) { - predicateCollector.getPredicate().accept( walker ); - } - - // We need an id table if we want to delete from an intermediate table to avoid FK violations - // The intermediate table has a FK to the root table, so we can't delete from the root table first - // Deleting from the intermediate table first also isn't possible, - // because that is the source for deletion in other tables, hence we need an id table - final boolean needsIdTable = !walker.isAllColumnReferencesFromIdentificationVariable() - || entityDescriptor != entityDescriptor.getRootEntityDescriptor(); - - final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( - executionContext ); - - if ( needsIdTable ) { - return executeWithIdTable( - predicateCollector.getPredicate(), - getConverter().getJdbcParamsBySqmParam(), - getConverter().getSqmParameterMappingModelExpressibleResolutions(), - executionContextAdapter - ); - } - else { - return executeWithoutIdTable( - predicateCollector.getPredicate(), - deletingTableGroup, - getConverter().getJdbcParamsBySqmParam(), - getConverter().getSqmParameterMappingModelExpressibleResolutions(), - getConverter().getSqlExpressionResolver(), - executionContextAdapter - ); - } - } - - private CompletionStage executeWithoutIdTable( - Predicate suppliedPredicate, - TableGroup tableGroup, - Map, List>> restrictionSqmParameterResolutions, - Map, MappingModelExpressible> paramTypeResolutions, - SqlExpressionResolver sqlExpressionResolver, - ExecutionContext executionContext) { - assert getEntityDescriptor() == getEntityDescriptor().getRootEntityDescriptor(); - - final EntityPersister rootEntityPersister = getEntityDescriptor().getEntityPersister(); - final String rootTableName = rootEntityPersister.getTableName(); - final NamedTableReference rootTableReference = (NamedTableReference) tableGroup.resolveTableReference( - tableGroup.getNavigablePath(), - rootTableName - ); - - final QuerySpec matchingIdSubQuerySpec = ExecuteWithoutIdTableHelper.createIdMatchingSubQuerySpec( - tableGroup.getNavigablePath(), - rootTableReference, - suppliedPredicate, - rootEntityPersister, - sqlExpressionResolver, - getSessionFactory() - ); - - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - getDomainParameterXref(), - SqmUtil.generateJdbcParamsXref( - getDomainParameterXref(), - () -> restrictionSqmParameterResolutions - ), - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) paramTypeResolutions.get(parameter); - } - }, - executionContext.getSession() - ); - - - CompletionStage cleanUpCollectionTablesStage = ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - getEntityDescriptor(), - (tableReference, attributeMapping) -> { - // No need for a predicate if there is no supplied predicate i.e. this is a full cleanup - if ( suppliedPredicate == null ) { - return null; - } - final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); - final QuerySpec idSelectFkSubQuery; - // todo (6.0): based on the location of the attribute mapping, we could prune the table group of the subquery - if ( fkDescriptor.getTargetPart().isEntityIdentifierMapping() ) { - idSelectFkSubQuery = matchingIdSubQuerySpec; - } - else { - idSelectFkSubQuery = ExecuteWithoutIdTableHelper.createIdMatchingSubQuerySpec( - tableGroup.getNavigablePath(), - rootTableReference, - suppliedPredicate, - rootEntityPersister, - sqlExpressionResolver, - getSessionFactory() - ); - } - return new InSubQueryPredicate( - MappingModelCreationHelper.buildColumnReferenceExpression( - new MutatingTableReferenceGroupWrapper( - new NavigablePath( attributeMapping.getRootPathName() ), - attributeMapping, - (NamedTableReference) tableReference - ), - fkDescriptor, - null, - getSessionFactory() - ), - idSelectFkSubQuery, - false - ); - - }, - jdbcParameterBindings, - executionContext - ); - - final CompletionStage[] deleteFromNonRootStages = new CompletionStage[] { voidFuture() }; - if ( rootTableReference instanceof UnionTableReference ) { - final MutableInteger rows = new MutableInteger(); - return cleanUpCollectionTablesStage - .thenCompose( v -> visitUnionTableReferences( - suppliedPredicate, - tableGroup, - sqlExpressionResolver, - executionContext, - matchingIdSubQuerySpec, - jdbcParameterBindings, - deleteFromNonRootStages, - rows - ) ) - .thenApply( o -> rows.get() ); - } - else { - getEntityDescriptor().visitConstraintOrderedTables( - (tableExpression, tableKeyColumnVisitationSupplier) -> { - if ( !tableExpression.equals( rootTableName ) ) { - final NamedTableReference tableReference = (NamedTableReference) tableGroup.getTableReference( - tableGroup.getNavigablePath(), - tableExpression, - true - ); - final QuerySpec idMatchingSubQuerySpec; - // No need for a predicate if there is no supplied predicate i.e. this is a full cleanup - idMatchingSubQuerySpec = suppliedPredicate == null ? null : matchingIdSubQuerySpec; - CompletableFuture future = new CompletableFuture<>(); - deleteFromNonRootStages[0] = deleteFromNonRootStages[0] - .thenCompose( v -> future ); - try { - deleteFromNonRootTableWithoutIdTable( - tableReference, - tableKeyColumnVisitationSupplier, - sqlExpressionResolver, - tableGroup, - idMatchingSubQuerySpec, - jdbcParameterBindings, - executionContext - ) - .thenCompose( CompletionStages::voidFuture ) - .whenComplete( (unused, throwable) -> { - if ( throwable == null ) { - future.complete( unused ); - } - else { - future.completeExceptionally( throwable ); - } - } ); - } - catch (Throwable t) { - future.completeExceptionally( t ); - } - } - } - ); - - return deleteFromNonRootStages[0] - .thenCompose( v -> deleteFromRootTableWithoutIdTable( - rootTableReference, - suppliedPredicate, - jdbcParameterBindings, - executionContext - ) ); - } - } - - private CompletionStage visitUnionTableReferences( - Predicate suppliedPredicate, - TableGroup tableGroup, - SqlExpressionResolver sqlExpressionResolver, - ExecutionContext executionContext, - QuerySpec matchingIdSubQuerySpec, - JdbcParameterBindings jdbcParameterBindings, - CompletionStage[] deleteFromNonRootStages, - MutableInteger rows) { - getEntityDescriptor().visitConstraintOrderedTables( - (tableExpression, tableKeyColumnVisitationSupplier) -> { - final NamedTableReference tableReference = new NamedTableReference( - tableExpression, - tableGroup.getPrimaryTableReference().getIdentificationVariable() - ); - final QuerySpec idMatchingSubQuerySpec; - // No need for a predicate if there is no supplied predicate i.e. this is a full cleanup - idMatchingSubQuerySpec = suppliedPredicate == null ? null : matchingIdSubQuerySpec; - CompletableFuture future = new CompletableFuture<>(); - deleteFromNonRootStages[0] = deleteFromNonRootStages[0] - .thenCompose( v -> future ); - deleteFromNonRootTableWithoutIdTable( - tableReference, - tableKeyColumnVisitationSupplier, - sqlExpressionResolver, - tableGroup, - idMatchingSubQuerySpec, - jdbcParameterBindings, - executionContext - ) - .thenAccept( rows::plus ) - .whenComplete( (unused, throwable) -> { - if ( throwable == null ) { - future.complete( unused ); - } - else { - future.completeExceptionally( throwable ); - } - } ); - } - ); - return deleteFromNonRootStages[0]; - } - - private CompletionStage deleteFromRootTableWithoutIdTable( - NamedTableReference rootTableReference, - Predicate predicate, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - return executeSqlDelete( - new DeleteStatement( rootTableReference, predicate ), - jdbcParameterBindings, - executionContext - ); - } - - private CompletionStage deleteFromNonRootTableWithoutIdTable( - NamedTableReference targetTableReference, - Supplier> tableKeyColumnVisitationSupplier, - SqlExpressionResolver sqlExpressionResolver, - TableGroup rootTableGroup, - QuerySpec matchingIdSubQuerySpec, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - assert targetTableReference != null; - LOG.tracef( "deleteFromNonRootTable - %s", targetTableReference.getTableExpression() ); - - final NamedTableReference deleteTableReference = new NamedTableReference( - targetTableReference.getTableExpression(), - DeleteStatement.DEFAULT_ALIAS, - true - ); - final Predicate tableDeletePredicate; - if ( matchingIdSubQuerySpec == null ) { - tableDeletePredicate = null; - } - else { - /* - * delete from sub_table - * where sub_id in ( - * select root_id from root_table - * where {predicate} - * ) - */ - - /* - * Create the `sub_id` reference as the LHS of the in-subquery predicate - */ - final List deletingTableColumnRefs = new ArrayList<>(); - tableKeyColumnVisitationSupplier.get().accept( - (columnIndex, selection) -> { - assert deleteTableReference.getTableReference( selection.getContainingTableExpression() ) != null; - - final Expression expression = sqlExpressionResolver.resolveSqlExpression( - deleteTableReference, - selection - ); - - deletingTableColumnRefs.add( (ColumnReference) expression ); - } - ); - - final Expression deletingTableColumnRefsExpression = deletingTableColumnRefs.size() == 1 - ? deletingTableColumnRefs.get( 0 ) - : new SqlTuple( deletingTableColumnRefs, getEntityDescriptor().getIdentifierMapping() ); - - tableDeletePredicate = new InSubQueryPredicate( - deletingTableColumnRefsExpression, - matchingIdSubQuerySpec, - false - ); - } - - final DeleteStatement sqlAstDelete = new DeleteStatement( deleteTableReference, tableDeletePredicate ); - return executeSqlDelete( - sqlAstDelete, - jdbcParameterBindings, - executionContext - ).thenApply( rows -> { - LOG.debugf( "deleteFromNonRootTable - `%s` : %s rows", targetTableReference, rows ); - return rows; - } ); - } - - private static CompletionStage executeSqlDelete( - DeleteStatement sqlAst, - JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - - final JdbcServices jdbcServices = factory.getJdbcServices(); - - final JdbcOperationQueryMutation jdbcDelete = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, sqlAst ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcDelete, - jdbcParameterBindings, - sql -> executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - .prepareStatement( sql ), - (integer, preparedStatement) -> {}, - executionContext - ); - } - - private CompletionStage executeWithIdTable( - Predicate predicate, - Map, List>> restrictionSqmParameterResolutions, - Map, MappingModelExpressible> paramTypeResolutions, - ExecutionContext executionContext) { - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - getDomainParameterXref(), - SqmUtil.generateJdbcParamsXref( - getDomainParameterXref(), - () -> restrictionSqmParameterResolutions - ), - new SqmParameterMappingModelResolutionAccess() { - @Override @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) paramTypeResolutions.get(parameter); - } - }, - executionContext.getSession() - ); - - return ReactiveExecuteWithTemporaryTableHelper - .performBeforeTemporaryTableUseActions( getIdTable(), getTemporaryTableStrategy(), executionContext ) - .thenCompose( v -> executeUsingIdTable( predicate, executionContext, jdbcParameterBindings ) - .handle( CompletionStages::handle ) - .thenCompose( resultHandler -> ReactiveExecuteWithTemporaryTableHelper - .performAfterTemporaryTableUseActions( - getIdTable(), - getSessionUidAccess(), - getAfterUseAction(), - executionContext - ) - .thenCompose( resultHandler::getResultAsCompletionStage ) - ) - ); - } - - private CompletionStage executeUsingIdTable( - Predicate predicate, - ExecutionContext executionContext, - JdbcParameterBindings jdbcParameterBindings) { - return ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable( - getConverter(), - predicate, - getIdTable(), - getSessionUidAccess(), - jdbcParameterBindings, - executionContext ) - .thenCompose( rows -> { - final QuerySpec idTableIdentifierSubQuery = createIdTableSelectQuerySpec( - getIdTable(), - getSessionUidAccess(), - getEntityDescriptor(), - executionContext - ); - - return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - getEntityDescriptor(), - (tableReference, attributeMapping) -> { - final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); - final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart().isEntityIdentifierMapping() - ? idTableIdentifierSubQuery - : createIdTableSelectQuerySpec( getIdTable(), fkDescriptor.getTargetPart(), getSessionUidAccess(), getEntityDescriptor(), executionContext ); - return new InSubQueryPredicate( - MappingModelCreationHelper.buildColumnReferenceExpression( - new MutatingTableReferenceGroupWrapper( - new NavigablePath( attributeMapping.getRootPathName() ), - attributeMapping, - (NamedTableReference) tableReference - ), - fkDescriptor, - null, - getSessionFactory() - ), - idTableFkSubQuery, - false - ); - - }, - JdbcParameterBindings.NO_BINDINGS, - executionContext - ).thenCompose( unused -> visitConstraintOrderedTables( idTableIdentifierSubQuery, executionContext ) - .thenApply( v -> rows ) ); - } ); - } - - private CompletionStage visitConstraintOrderedTables( - QuerySpec idTableIdentifierSubQuery, - ExecutionContext executionContext) { - final Completable completable = new Completable<>(); - getEntityDescriptor() - .visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> deleteFromTableUsingIdTable( - tableExpression, - tableKeyColumnVisitationSupplier, - idTableIdentifierSubQuery, - executionContext - ) - .handle( completable::complete ) - ); - return completable.getStage().thenCompose( CompletionStages::voidFuture ); - } - - private CompletionStage deleteFromTableUsingIdTable( - String tableExpression, - Supplier> tableKeyColumnVisitationSupplier, - QuerySpec idTableSubQuery, - ExecutionContext executionContext) { - LOG.tracef( "deleteFromTableUsingIdTable - %s", tableExpression ); - - final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( getEntityDescriptor() ); - final NamedTableReference targetTable = new NamedTableReference( tableExpression, DeleteStatement.DEFAULT_ALIAS, true ); - - tableKeyColumnVisitationSupplier.get().accept( - (columnIndex, selection) -> { - assert selection.getContainingTableExpression().equals( tableExpression ); - assert !selection.isFormula(); - assert selection.getCustomReadExpression() == null; - assert selection.getCustomWriteExpression() == null; - - keyColumnCollector - .apply( new ColumnReference( targetTable, selection ) ); - } - ); - - final InSubQueryPredicate predicate = new InSubQueryPredicate( - keyColumnCollector.buildKeyExpression(), - idTableSubQuery, - false - ); - - return executeSqlDelete( - new DeleteStatement( targetTable, predicate ), - JdbcParameterBindings.NO_BINDINGS, - executionContext - ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java deleted file mode 100644 index 607812f93..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveSoftDeleteExecutionDelegate.java +++ /dev/null @@ -1,320 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.mutation.internal.temptable; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; - -import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.LoadQueryInfluencers; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; -import org.hibernate.metamodel.mapping.MappingModelExpressible; -import org.hibernate.metamodel.mapping.TableDetails; -import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryOptions; -import org.hibernate.query.spi.QueryParameterBindings; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.internal.SqmUtil; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.temptable.AbstractDeleteExecutionDelegate; -import org.hibernate.query.sqm.mutation.internal.temptable.ColumnReferenceCheckingSqlAstWalker; -import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; -import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.expression.SqmParameter; -import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveSqmMutationStrategyHelper; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.tree.delete.DeleteStatement; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.SqlTuple; -import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.predicate.PredicateCollector; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.ast.tree.update.Assignment; -import org.hibernate.sql.ast.tree.update.UpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.sql.results.internal.SqlSelectionImpl; - -import static java.util.Collections.singletonList; -import static org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter.omittingLockingAndPaging; -import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; - -public class ReactiveSoftDeleteExecutionDelegate extends AbstractDeleteExecutionDelegate - implements ReactiveTableBasedDeleteHandler.ReactiveExecutionDelegate { - - public ReactiveSoftDeleteExecutionDelegate( - EntityMappingType entityDescriptor, TemporaryTable idTable, TemporaryTableStrategy temporaryTableStrategy, - boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, QueryParameterBindings queryParameterBindings, - Function sessionUidAccess, SessionFactoryImplementor sessionFactory - ) { - super( - entityDescriptor, idTable, temporaryTableStrategy, forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, loadQueryInfluencers, queryParameterBindings, sessionUidAccess, - sessionFactory - ); - } - - @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext domainQueryExecutionContext) { - final String targetEntityName = getSqmDelete().getTarget().getEntityName(); - final EntityPersister targetEntityDescriptor = getSessionFactory().getMappingMetamodel().getEntityDescriptor( targetEntityName ); - - final EntityMappingType rootEntityDescriptor = targetEntityDescriptor.getRootEntityDescriptor(); - - // determine if we need to use a sub-query for matching ids - - // 1. if the target is not the root we will - // 2. if the supplied predicate (if any) refers to columns from a table - // other than the identifier table we will - final SqmJdbcExecutionContextAdapter executionContext = omittingLockingAndPaging( domainQueryExecutionContext ); - - final TableGroup deletingTableGroup = getConverter().getMutatingTableGroup(); - final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails(); - final NamedTableReference rootTableReference = (NamedTableReference) deletingTableGroup.resolveTableReference( - deletingTableGroup.getNavigablePath(), - softDeleteTable.getTableName() - ); - assert rootTableReference != null; - - // NOTE : `converter.visitWhereClause` already applies the soft-delete restriction - final Predicate specifiedRestriction = getConverter().visitWhereClause( getSqmDelete().getWhereClause() ); - - final PredicateCollector predicateCollector = new PredicateCollector( specifiedRestriction ); - targetEntityDescriptor.applyBaseRestrictions( - predicateCollector, - deletingTableGroup, - true, - executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), - false, - null, - getConverter() - ); - - getConverter().pruneTableGroupJoins(); - final ColumnReferenceCheckingSqlAstWalker walker = new ColumnReferenceCheckingSqlAstWalker( - rootTableReference.getIdentificationVariable() - ); - if ( predicateCollector.getPredicate() != null ) { - predicateCollector.getPredicate().accept( walker ); - } - - final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( - executionContext.getQueryParameterBindings(), - getDomainParameterXref(), - SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), getConverter() ), - new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") - public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) getConverter().getSqmParameterMappingModelExpressibleResolutions().get( parameter ); - } - }, - executionContext.getSession() - ); - - final boolean needsSubQuery = !walker.isAllColumnReferencesFromIdentificationVariable() - || targetEntityDescriptor != rootEntityDescriptor; - - if ( needsSubQuery ) { - return getSessionFactory().getJdbcServices().getDialect().supportsSubqueryOnMutatingTable() - ? performDeleteWithSubQuery( rootEntityDescriptor, deletingTableGroup, rootTableReference, predicateCollector, jdbcParameterBindings, getConverter(), executionContext ) - : performDeleteWithIdTable( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext ); - } - return performDirectDelete( rootEntityDescriptor, rootTableReference, predicateCollector, jdbcParameterBindings, executionContext ); - } - - private CompletionStage performDeleteWithIdTable( - EntityMappingType rootEntityDescriptor, - NamedTableReference targetTableReference, - PredicateCollector predicateCollector, - JdbcParameterBindings jdbcParameterBindings, - SqmJdbcExecutionContextAdapter executionContext - ) { - ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( getIdTable(), getTemporaryTableStrategy(), executionContext ); - try { - return deleteUsingIdTable( rootEntityDescriptor, targetTableReference, predicateCollector, jdbcParameterBindings, executionContext ); - } - finally { - ReactiveExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions( getIdTable(), getSessionUidAccess(), getAfterUseAction(), executionContext ); - } - } - - private CompletionStage deleteUsingIdTable( - EntityMappingType rootEntityDescriptor, - NamedTableReference targetTableReference, - PredicateCollector predicateCollector, - JdbcParameterBindings jdbcParameterBindings, - SqmJdbcExecutionContextAdapter executionContext - ) { - return ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable( getConverter(), - predicateCollector.getPredicate(), - getIdTable(), - getSessionUidAccess(), - jdbcParameterBindings, - executionContext - ) - .thenCompose( rows -> { - final QuerySpec idTableIdentifierSubQuery = createIdTableSelectQuerySpec( - getIdTable(), - getSessionUidAccess(), - getEntityDescriptor(), - executionContext - ); - - return ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( - getEntityDescriptor(), - (tableReference, attributeMapping) -> { - final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); - final QuerySpec idTableFkSubQuery = fkDescriptor.getTargetPart().isEntityIdentifierMapping() - ? idTableIdentifierSubQuery - : createIdTableSelectQuerySpec( getIdTable(), fkDescriptor.getTargetPart(), getSessionUidAccess(), getEntityDescriptor(), executionContext ); - return new InSubQueryPredicate( - MappingModelCreationHelper.buildColumnReferenceExpression( - new MutatingTableReferenceGroupWrapper( - new NavigablePath( attributeMapping.getRootPathName() ), - attributeMapping, - (NamedTableReference) tableReference - ), - fkDescriptor, - null, - getSessionFactory() - ), - idTableFkSubQuery, - false - ); - - }, - JdbcParameterBindings.NO_BINDINGS, - executionContext - ).thenCompose( unused -> { - final Assignment softDeleteAssignment = rootEntityDescriptor - .getSoftDeleteMapping() - .createSoftDeleteAssignment( targetTableReference ); - final Expression idExpression = createIdExpression( rootEntityDescriptor, targetTableReference ); - final UpdateStatement updateStatement = new UpdateStatement( - targetTableReference, - singletonList( softDeleteAssignment ), - new InSubQueryPredicate( idExpression, idTableIdentifierSubQuery, false ) - ); - - return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); - } ) - .thenApply( v -> rows ); - } ); - } - - private static Expression createIdExpression(EntityMappingType rootEntityDescriptor, NamedTableReference targetTableReference) { - final TableDetails softDeleteTable = rootEntityDescriptor.getSoftDeleteTableDetails(); - final TableDetails.KeyDetails keyDetails = softDeleteTable.getKeyDetails(); - final List idExpressions = new ArrayList<>( keyDetails.getColumnCount() ); - keyDetails.forEachKeyColumn( (position, column) -> idExpressions.add( new ColumnReference( targetTableReference, column ) ) ); - return idExpressions.size() == 1 - ? idExpressions.get( 0 ) - : new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() ); - } - - private CompletionStage performDeleteWithSubQuery( - EntityMappingType rootEntityDescriptor, - TableGroup deletingTableGroup, - NamedTableReference rootTableReference, - PredicateCollector predicateCollector, - JdbcParameterBindings jdbcParameterBindings, - MultiTableSqmMutationConverter converter, - SqmJdbcExecutionContextAdapter executionContext - ) { - final QuerySpec matchingIdSubQuery = new QuerySpec( false, 1 ); - matchingIdSubQuery.getFromClause().addRoot( deletingTableGroup ); - - final TableDetails identifierTableDetails = rootEntityDescriptor.getIdentifierTableDetails(); - final TableDetails.KeyDetails keyDetails = identifierTableDetails.getKeyDetails(); - - final NamedTableReference targetTable = new NamedTableReference( - identifierTableDetails.getTableName(), - DeleteStatement.DEFAULT_ALIAS, - false - ); - - final List idExpressions = new ArrayList<>( keyDetails.getColumnCount() ); - keyDetails.forEachKeyColumn( (position, column) -> { - final Expression columnReference = converter.getSqlExpressionResolver().resolveSqlExpression( - rootTableReference, - column - ); - matchingIdSubQuery.getSelectClause().addSqlSelection( - new SqlSelectionImpl( position, columnReference ) - ); - idExpressions.add( new ColumnReference( targetTable, column ) ); - } ); - - matchingIdSubQuery.applyPredicate( predicateCollector.getPredicate() ); - final Expression idExpression = idExpressions.size() == 1 - ? idExpressions.get( 0 ) - : new SqlTuple( idExpressions, rootEntityDescriptor.getIdentifierMapping() ); - - final Assignment softDeleteAssignment = rootEntityDescriptor - .getSoftDeleteMapping() - .createSoftDeleteAssignment( targetTable ); - - final UpdateStatement updateStatement = new UpdateStatement( - targetTable, - singletonList( softDeleteAssignment ), - new InSubQueryPredicate( idExpression, matchingIdSubQuery, false ) - ); - - return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); - } - - private CompletionStage performDirectDelete( - EntityMappingType rootEntityDescriptor, - NamedTableReference rootTableReference, - PredicateCollector predicateCollector, - JdbcParameterBindings jdbcParameterBindings, - SqmJdbcExecutionContextAdapter executionContext - ) { - - final Assignment softDeleteAssignment = rootEntityDescriptor.getSoftDeleteMapping().createSoftDeleteAssignment( rootTableReference ); - final UpdateStatement updateStatement = new UpdateStatement( rootTableReference, singletonList( softDeleteAssignment ), predicateCollector.getPredicate() ); - return executeUpdate( updateStatement, jdbcParameterBindings, executionContext ); - } - - private CompletionStage executeUpdate(UpdateStatement updateStatement, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final JdbcServices jdbcServices = factory.getJdbcServices(); - - final JdbcOperationQueryMutation jdbcUpdate = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, updateStatement ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcUpdate, - jdbcParameterBindings, - sql -> executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - .prepareStatement( sql ), - (integer, preparedStatement) -> {}, - executionContext - ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java index d916e4910..c27fbf502 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedDeleteHandler.java @@ -6,34 +6,38 @@ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; import java.lang.invoke.MethodHandles; +import java.util.UUID; import java.util.concurrent.CompletionStage; import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.internal.CacheableSqmInterpretation; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedDeleteHandler; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveAbstractMutationHandler; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.reactive.util.impl.CompletionStages; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; +import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +import static org.hibernate.reactive.util.impl.CompletionStages.loop; public class ReactiveTableBasedDeleteHandler extends TableBasedDeleteHandler implements ReactiveAbstractMutationHandler { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - public interface ReactiveExecutionDelegate extends TableBasedDeleteHandler.ExecutionDelegate { - - @Override - default int execute(DomainQueryExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); - } - - CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext); - } - public ReactiveTableBasedDeleteHandler( SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, @@ -41,7 +45,8 @@ public ReactiveTableBasedDeleteHandler( TemporaryTableStrategy temporaryTableStrategy, boolean forceDropAfterUse, Function sessionUidAccess, - SessionFactoryImplementor sessionFactory) { + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { super( sqmDeleteStatement, domainParameterXref, @@ -49,46 +54,99 @@ public ReactiveTableBasedDeleteHandler( temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, - sessionFactory + context, + firstJdbcParameterBindingsConsumer ); } @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { + public CompletionStage reactiveExecute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecutionContext context) { if ( LOG.isTraceEnabled() ) { - LOG.tracef( "Starting multi-table delete execution - %s", getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() ); + LOG.tracef( + "Starting multi-table delete execution - %s", + getSqmStatement().getTarget().getModel().getName() + ); + } + final SqmJdbcExecutionContextAdapter executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + final CacheableSqmInterpretation idTableInsert = getIdTableInsert(); + final TemporaryTable idTable = getIdTable(); + final StandardReactiveJdbcMutationExecutor jdbcMutationExecutor = StandardReactiveJdbcMutationExecutor.INSTANCE; + if ( idTableInsert != null ) { + return ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( + idTable, + getTemporaryTableStrategy(), + executionContext + ).thenCompose( unused -> + ReactiveExecuteWithTemporaryTableHelper.saveIntoTemporaryTable( idTableInsert.jdbcOperation(), jdbcParameterBindings, executionContext ) + .thenCompose( rows -> executeDelete( jdbcParameterBindings, rows, executionContext, jdbcMutationExecutor ) ) + .handle( CompletionStages::handle ) + .thenCompose( handler -> ReactiveExecuteWithTemporaryTableHelper + .performAfterTemporaryTableUseActions( idTable, getSessionUidAccess(), getAfterUseAction(), executionContext ) + .thenCompose( v -> handler.getResultAsCompletionStage() ) ) + ); + } + else { + return loop(getCollectionTableDeletes() ,delete -> + reactiveExecute( jdbcParameterBindings, delete, jdbcMutationExecutor, executionContext ) + ).thenCompose( v -> { + final int[] rows = { 0 }; + return deleteRows( jdbcParameterBindings, jdbcMutationExecutor, executionContext, rows ) + .thenApply( vv -> rows[0] ); + } ); } - return resolveDelegate( executionContext ).reactiveExecute( executionContext ); } - protected ReactiveExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { - if ( getEntityDescriptor().getSoftDeleteMapping() != null ) { - return new ReactiveSoftDeleteExecutionDelegate( - getEntityDescriptor(), - getIdTable(), - getTemporaryTableStrategy(), - isForceDropAfterUse(), - getSqmDeleteOrUpdateStatement(), - getDomainParameterXref(), - executionContext.getQueryOptions(), - executionContext.getSession().getLoadQueryInfluencers(), - executionContext.getQueryParameterBindings(), - getSessionUidAccess(), - getSessionFactory() + private CompletionStage deleteRows(JdbcParameterBindings jdbcParameterBindings, StandardReactiveJdbcMutationExecutor jdbcMutationExecutor, SqmJdbcExecutionContextAdapter executionContext, int[] rows) { + if ( getEntityDescriptor() instanceof UnionSubclassEntityPersister ) { + return CompletionStages + .loop( getDeletes(), delete -> reactiveExecute( jdbcParameterBindings, delete, jdbcMutationExecutor, executionContext ) + .thenApply( tot -> rows[0] += tot ) + ); + } + else { + return CompletionStages + .loop( getDeletes(), delete -> reactiveExecute( jdbcParameterBindings, delete, jdbcMutationExecutor, executionContext ) + .thenApply( tot -> rows[0] = tot ) + ); + } + } + + private CompletionStage executeDelete( + JdbcParameterBindings jdbcParameterBindings, + Integer rows, + SqmJdbcExecutionContextAdapter executionContext, + StandardReactiveJdbcMutationExecutor jdbcMutationExecutor) { + final JdbcParameterBindings sessionUidBindings = new JdbcParameterBindingsImpl( 1 ); + final JdbcParameter sessionUidParameter = getSessionUidParameter(); + if ( sessionUidParameter != null ) { + sessionUidBindings.addBinding( + sessionUidParameter, + new JdbcParameterBindingImpl( + sessionUidParameter.getExpressionType().getSingleJdbcMapping(), + UUID.fromString( getSessionUidAccess().apply( executionContext.getSession() ) ) + ) ); } - return new ReactiveRestrictedDeleteExecutionDelegate( - getEntityDescriptor(), - getIdTable(), - getTemporaryTableStrategy(), - isForceDropAfterUse(), - getSqmDeleteOrUpdateStatement(), - getDomainParameterXref(), - executionContext.getQueryOptions(), - executionContext.getSession().getLoadQueryInfluencers(), - executionContext.getQueryParameterBindings(), - getSessionUidAccess(), - getSessionFactory() + return loop( getCollectionTableDeletes(), delete -> + reactiveExecute( jdbcParameterBindings, delete, jdbcMutationExecutor, executionContext ) + ).thenApply( v -> rows ); + } + + private static CompletionStage reactiveExecute( + JdbcParameterBindings jdbcParameterBindings, + JdbcOperationQueryMutation delete, + StandardReactiveJdbcMutationExecutor jdbcMutationExecutor, + SqmJdbcExecutionContextAdapter executionContext) { + return jdbcMutationExecutor.executeReactive( + delete, + jdbcParameterBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java index eb10dc8a4..194b8b598 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedInsertHandler.java @@ -5,46 +5,57 @@ */ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; - import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.BeforeExecutionGenerator; +import org.hibernate.generator.Generator; +import org.hibernate.generator.values.GeneratedValuesMutationDelegate; +import org.hibernate.id.insert.Binder; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedInsertHandler; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; +import org.hibernate.reactive.id.insert.ReactiveInsertGeneratedIdentifierDelegate; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; -import org.hibernate.reactive.query.sqm.mutation.internal.cte.ReactiveInsertExecutionDelegate; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; +import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.sql.ast.tree.expression.JdbcParameter; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.insert.ConflictClause; -import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.update.Assignment; +import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; +import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBinder; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.type.descriptor.ValueBinder; -public class ReactiveTableBasedInsertHandler extends TableBasedInsertHandler implements ReactiveHandler { +import java.lang.invoke.MethodHandles; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import java.util.stream.IntStream; - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.reactive.util.impl.CompletionStages.loop; - public interface ReactiveExecutionDelegate extends ExecutionDelegate { - @Override - default int execute(ExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); - } +public class ReactiveTableBasedInsertHandler extends TableBasedInsertHandler implements ReactiveHandler { - CompletionStage reactiveExecute(ExecutionContext executionContext); - } + private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); public ReactiveTableBasedInsertHandler( SqmInsertStatement sqmInsert, @@ -53,62 +64,339 @@ public ReactiveTableBasedInsertHandler( TemporaryTableStrategy temporaryTableStrategy, boolean forceDropAfterUse, Function sessionUidAccess, - SessionFactoryImplementor sessionFactory) { - super( sqmInsert, domainParameterXref, entityTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, sessionFactory ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( sqmInsert, domainParameterXref, entityTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, context, firstJdbcParameterBindingsConsumer ); } @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { + public CompletionStage reactiveExecute( + JdbcParameterBindings jdbcParameterBindings, + DomainQueryExecutionContext context) { if ( LOG.isTraceEnabled() ) { LOG.tracef( "Starting multi-table insert execution - %s", - getSqmInsertStatement().getTarget().getModel().getName() + getSqmStatement().getTarget().getModel().getName() ); } - final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter - .omittingLockingAndPaging( executionContext ); - return resolveDelegate( executionContext ) - .reactiveExecute( executionContextAdapter ); + final SqmJdbcExecutionContextAdapter executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + // NOTE: we could get rid of using a temporary table if the expressions in Values are "stable". + // But that is a non-trivial optimization that requires more effort + // as we need to split out individual inserts if we have a non-bulk capable optimizer + return ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( + getEntityTable(), + getTemporaryTableStrategy(), + executionContext + ).thenCompose( createdTable -> + ReactiveExecuteWithTemporaryTableHelper.saveIntoTemporaryTable( + getTemporaryTableInsert().jdbcOperation(), + jdbcParameterBindings, + executionContext + ).thenCompose( rows -> { + if ( rows != 0 ) { + final JdbcParameterBindings sessionUidBindings = new JdbcParameterBindingsImpl( 1 ); + final JdbcParameter sessionUidParameter = getSessionUidParameter(); + if ( sessionUidParameter != null ) { + sessionUidBindings.addBinding( + sessionUidParameter, + new JdbcParameterBindingImpl( + sessionUidParameter.getExpressionType().getSingleJdbcMapping(), + UUID.fromString( getSessionUidAccess().apply( executionContext.getSession() ) ) + ) + ); + } + return insertRootTable( rows, createdTable, sessionUidBindings, executionContext ) + .thenCompose( insertedRows -> CompletionStages + .loop( + getNonRootTableInserts(), nonRootTableInsert -> + insertTable( nonRootTableInsert, sessionUidBindings, executionContext ) + ).thenApply( v -> insertedRows ) + ); + } + return CompletionStages.completedFuture( rows ); + } ) ) + .handle( CompletionStages::handle ) + .thenCompose( handler -> ReactiveExecuteWithTemporaryTableHelper + .performAfterTemporaryTableUseActions( + getEntityTable(), + getSessionUidAccess(), + getAfterUseAction(), + executionContext + ) + .thenCompose( v -> handler.getResultAsCompletionStage() ) + ); } - @Override - protected ReactiveExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { - return (ReactiveExecutionDelegate) super.resolveDelegate( executionContext ); + private CompletionStage insertTable( + JdbcOperationQueryMutation nonRootTableInsert, + JdbcParameterBindings sessionUidBindings, + ExecutionContext executionContext) { + return StandardReactiveJdbcMutationExecutor.INSTANCE + .executeReactive( + nonRootTableInsert, + sessionUidBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ).thenCompose( unused -> CompletionStages.voidFuture() ); } - @Override - protected ExecutionDelegate buildExecutionDelegate( - SqmInsertStatement sqmInsert, - MultiTableSqmMutationConverter sqmConverter, - TemporaryTable entityTable, - TemporaryTableStrategy temporaryTableStrategy, - boolean forceDropAfterUse, - Function sessionUidAccess, - DomainParameterXref domainParameterXref, - TableGroup insertingTableGroup, - Map tableReferenceByAlias, - List assignments, - boolean assignsId, - InsertSelectStatement insertStatement, - ConflictClause conflictClause, - JdbcParameter sessionUidParameter, - DomainQueryExecutionContext executionContext) { - return new ReactiveInsertExecutionDelegate( - sqmConverter, - entityTable, - temporaryTableStrategy, - forceDropAfterUse, - sessionUidAccess, - domainParameterXref, - insertingTableGroup, - tableReferenceByAlias, - assignments, - assignsId, - insertStatement, - conflictClause, - sessionUidParameter, - executionContext - ); + private CompletionStage insertRootTable( + int rows, + boolean rowNumberStartsAtOne, + JdbcParameterBindings sessionUidBindings, + SqmJdbcExecutionContextAdapter executionContext) { + final EntityPersister entityPersister = getEntityDescriptor().getEntityPersister(); + final Generator generator = entityPersister.getGenerator(); + final EntityIdentifierMapping identifierMapping = entityPersister.getIdentifierMapping(); + + final SharedSessionContractImplementor session = executionContext.getSession(); + final RootTableInserter rootTableInserter = getRootTableInserter(); + + if ( rootTableInserter.temporaryTableIdentitySelect() != null ) { + return StandardReactiveSelectExecutor.INSTANCE.list( + rootTableInserter.temporaryTableIdentitySelect(), + sessionUidBindings, + executionContext, + null, + null, + ReactiveListResultsConsumer.UniqueSemantic.NONE, + rows + ).thenApply( list -> { + Map entityTableToRootIdentity = new LinkedHashMap<>( list.size() ); + for ( Object o : list ) { + entityTableToRootIdentity.put( o, null ); + } + return entityTableToRootIdentity; + } ).thenCompose( entityTableToRootIdentity -> insertRootTable( + sessionUidBindings, + executionContext, + rootTableInserter, + entityPersister, + identifierMapping, + entityTableToRootIdentity, + session + ) ); + } + else { + final Map entityTableToRootIdentity = null; + + if ( rootTableInserter.temporaryTableIdUpdate() != null ) { + final BeforeExecutionGenerator beforeExecutionGenerator = (BeforeExecutionGenerator) generator; + final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 3 ); + final JdbcParameter sessionUidParameter = getSessionUidParameter(); + if ( sessionUidParameter != null ) { + updateBindings.addBinding( + sessionUidParameter, + new JdbcParameterBindingImpl( + sessionUidParameter.getExpressionType().getSingleJdbcMapping(), + UUID.fromString( getSessionUidAccess().apply( session ) ) + ) + ); + } + final List parameterBinders = rootTableInserter.temporaryTableIdUpdate().getParameterBinders(); + final JdbcParameter rootIdentity = (JdbcParameter) parameterBinders.get( 0 ); + final JdbcParameter rowNumber = (JdbcParameter) parameterBinders.get( 1 ); + final BasicEntityIdentifierMapping basicIdentifierMapping = (BasicEntityIdentifierMapping) identifierMapping; + + if ( !rowNumberStartsAtOne ) { + return ReactiveExecuteWithTemporaryTableHelper.loadInsertedRowNumbers( + rootTableInserter.temporaryTableRowNumberSelectSql(), + getEntityTable(), + getSessionUidAccess(), + rows, + executionContext + ).thenCompose( rowNumbers -> + forEachRow( + rowNumbers, + executionContext, + updateBindings, + rowNumber, + rootIdentity, + basicIdentifierMapping, + beforeExecutionGenerator, + session, + rootTableInserter + ).thenCompose( v -> insertRootTable( + sessionUidBindings, + executionContext, + rootTableInserter, + entityPersister, + identifierMapping, + entityTableToRootIdentity, + session ) + ) + ); + } + else { + final Integer[] rowNumbers = IntStream.range( 1, rows + 1 ).boxed() + .toArray(Integer[]::new); + return forEachRow( + rowNumbers, + executionContext, + updateBindings, + rowNumber, + rootIdentity, + basicIdentifierMapping, + beforeExecutionGenerator, + session, + rootTableInserter + ).thenCompose( v -> insertRootTable( + sessionUidBindings, + executionContext, + rootTableInserter, + entityPersister, + identifierMapping, + entityTableToRootIdentity, + session) + ); + } + } + return insertRootTable( + sessionUidBindings, + executionContext, + rootTableInserter, + entityPersister, + identifierMapping, + entityTableToRootIdentity, + session + ); + } } + + private static CompletionStage forEachRow( + Integer[] rowNumbers, + SqmJdbcExecutionContextAdapter executionContext, + JdbcParameterBindings updateBindings, + JdbcParameter rowNumber, + JdbcParameter rootIdentity, + BasicEntityIdentifierMapping basicIdentifierMapping, + BeforeExecutionGenerator beforeExecutionGenerator, + SharedSessionContractImplementor session, + RootTableInserter rootTableInserter) { + return loop( rowNumbers, rowNumberValue -> { + updateBindings.addBinding( + rowNumber, + new JdbcParameterBindingImpl( + rowNumber.getExpressionType().getSingleJdbcMapping(), + rowNumberValue + ) + ); + updateBindings.addBinding( + rootIdentity, + new JdbcParameterBindingImpl( + basicIdentifierMapping.getJdbcMapping(), + beforeExecutionGenerator.generate( session, null, null, INSERT ) + ) + ); + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + rootTableInserter.temporaryTableIdUpdate(), + updateBindings, + sql -> session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ).thenApply( updateCount -> { + assert updateCount == 1; + return updateCount; + } ); + } ); + } + + private CompletionStage insertRootTable( + JdbcParameterBindings sessionUidBindings, + SqmJdbcExecutionContextAdapter executionContext, + RootTableInserter rootTableInserter, + EntityPersister entityPersister, + EntityIdentifierMapping identifierMapping, + Map entityTableToRootIdentity, + SharedSessionContractImplementor session ) { + if ( rootTableInserter.rootTableInsertWithReturningSql() != null ) { + final GeneratedValuesMutationDelegate insertDelegate = entityPersister.getEntityPersister().getInsertDelegate(); + final BasicEntityIdentifierMapping basicIdentifierMapping = (BasicEntityIdentifierMapping) identifierMapping; + // todo 7.0 : InsertGeneratedIdentifierDelegate will be removed once we're going to handle + // generated values within the jdbc insert operaetion itself + final ReactiveInsertGeneratedIdentifierDelegate identifierDelegate = (ReactiveInsertGeneratedIdentifierDelegate) insertDelegate; + final ValueBinder jdbcValueBinder = basicIdentifierMapping.getJdbcMapping().getJdbcValueBinder(); + return loop(entityTableToRootIdentity.entrySet() , entry -> + identifierDelegate.reactivePerformInsertReturning( + rootTableInserter.rootTableInsertWithReturningSql(), + session, + new Binder() { + @Override + public void bindValues(PreparedStatement ps) throws SQLException { + jdbcValueBinder.bind( ps, entry.getKey(), 1, session ); + final JdbcParameter sessionUidParameter = getSessionUidParameter(); + if ( sessionUidParameter != null ) { + sessionUidParameter.getParameterBinder().bindParameterValue( + ps, + 2, + sessionUidBindings, + executionContext + ); + } + } + + @Override + public Object getEntity() { + return null; + } + } + ).thenAccept( generatedValues -> { + entry.setValue( generatedValues.getGeneratedValue( identifierMapping ) ); + } ) + ).thenCompose( unused -> { + final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 ); + + final List parameterBinders = rootTableInserter.temporaryTableIdentityUpdate() + .getParameterBinders(); + final JdbcParameter rootIdentity = (JdbcParameter) parameterBinders.get( 0 ); + final JdbcParameter entityIdentity = (JdbcParameter) parameterBinders.get( 1 ); + return loop(entityTableToRootIdentity.entrySet(), entry -> { + JdbcMapping jdbcMapping = basicIdentifierMapping.getJdbcMapping(); + updateBindings.addBinding( + entityIdentity, + new JdbcParameterBindingImpl( jdbcMapping, entry.getKey() ) + ); + updateBindings.addBinding( + rootIdentity, + new JdbcParameterBindingImpl( jdbcMapping, entry.getValue() ) + ); + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + rootTableInserter.temporaryTableIdentityUpdate(), + updateBindings, + sql -> session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ); + }).thenApply( v -> entityTableToRootIdentity.size() ); + }); + } + else { + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + rootTableInserter.rootTableInsert(), + sessionUidBindings, + sql -> session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ); + } + } + } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedSoftDeleteHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedSoftDeleteHandler.java new file mode 100644 index 000000000..e0edb82e2 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedSoftDeleteHandler.java @@ -0,0 +1,129 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.query.sqm.mutation.internal.temptable; + +import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.sqm.internal.CacheableSqmInterpretation; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; +import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedSoftDeleteHandler; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.reactive.util.impl.CompletionStages; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; +import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +import java.lang.invoke.MethodHandles; +import java.util.UUID; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; + +public class ReactiveTableBasedSoftDeleteHandler extends TableBasedSoftDeleteHandler implements ReactiveHandler { + private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + + public ReactiveTableBasedSoftDeleteHandler( + SqmDeleteStatement sqmDelete, + DomainParameterXref domainParameterXref, + TemporaryTable idTable, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, + Function sessionUidAccess, + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( + sqmDelete, + domainParameterXref, + idTable, + temporaryTableStrategy, + forceDropAfterUse, + sessionUidAccess, + context, + firstJdbcParameterBindingsConsumer + ); + } + + @Override + public CompletionStage reactiveExecute( + JdbcParameterBindings jdbcParameterBindings, + DomainQueryExecutionContext context) { + if ( LOG.isTraceEnabled() ) { + LOG.tracef( + "Starting multi-table delete execution - %s", + getSqmStatement().getTarget().getModel().getName() + ); + } + final SqmJdbcExecutionContextAdapter executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + StandardReactiveJdbcMutationExecutor jdbcMutationExecutor = StandardReactiveJdbcMutationExecutor.INSTANCE; + + final CacheableSqmInterpretation idTableInsert = getIdTableInsert(); + if ( idTableInsert != null ) { + return ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( + getIdTable(), + getTemporaryTableStrategy(), + executionContext + ).thenCompose( unused -> + ReactiveExecuteWithTemporaryTableHelper.saveIntoTemporaryTable( + idTableInsert.jdbcOperation(), + jdbcParameterBindings, + executionContext + ).thenCompose( rows -> { + final JdbcParameterBindings sessionUidBindings = new JdbcParameterBindingsImpl( 1 ); + final JdbcParameter sessionUidParameter = getSessionUidParameter(); + if ( sessionUidParameter != null ) { + sessionUidBindings.addBinding( + sessionUidParameter, + new JdbcParameterBindingImpl( + sessionUidParameter.getExpressionType().getSingleJdbcMapping(), + UUID.fromString( getSessionUidAccess().apply( executionContext.getSession() ) ) + ) + ); + } + return jdbcMutationExecutor.executeReactive( + getSoftDelete(), + sessionUidBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext + ).thenApply( u -> rows ); + } ) + .handle( CompletionStages::handle) + .thenCompose( handler -> ReactiveExecuteWithTemporaryTableHelper + .performAfterTemporaryTableUseActions( + getIdTable(), + getSessionUidAccess(), + getAfterUseAction(), + executionContext) + .thenCompose( v -> handler.getResultAsCompletionStage() ) ) + ); + } + else { + return jdbcMutationExecutor.executeReactive( + getSoftDelete(), + jdbcParameterBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext + ); + } + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java index 05a6d1511..d3ba6f6f3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTableBasedUpdateHandler.java @@ -6,43 +6,35 @@ package org.hibernate.reactive.query.sqm.mutation.internal.temptable; import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; import java.util.concurrent.CompletionStage; import java.util.function.Function; +import org.hibernate.AssertionFailure; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.MutableObject; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.temptable.TableBasedUpdateHandler; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveAbstractMutationHandler; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.update.Assignment; +import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; +import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +import static org.hibernate.reactive.util.impl.CompletionStages.loop; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; public class ReactiveTableBasedUpdateHandler extends TableBasedUpdateHandler implements ReactiveAbstractMutationHandler { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - public interface ReactiveExecutionDelegate extends TableBasedUpdateHandler.ExecutionDelegate { - @Override - default int execute(ExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); - } - - CompletionStage reactiveExecute(ExecutionContext executionContext); - } - public ReactiveTableBasedUpdateHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, @@ -50,58 +42,81 @@ public ReactiveTableBasedUpdateHandler( TemporaryTableStrategy temporaryTableStrategy, boolean forceDropAfterUse, Function sessionUidAccess, - SessionFactoryImplementor sessionFactory) { - super( sqmUpdate, domainParameterXref, idTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, sessionFactory ); + DomainQueryExecutionContext context, + MutableObject firstJdbcParameterBindingsConsumer) { + super( sqmUpdate, domainParameterXref, idTable, temporaryTableStrategy, forceDropAfterUse, sessionUidAccess, context, firstJdbcParameterBindingsConsumer ); } @Override - public int execute(DomainQueryExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); - } - - @Override - public CompletionStage reactiveExecute(DomainQueryExecutionContext executionContext) { + public CompletionStage reactiveExecute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecutionContext context) { if ( LOG.isTraceEnabled() ) { LOG.tracef( "Starting multi-table update execution - %s", - getSqmDeleteOrUpdateStatement().getRoot().getModel().getName() + getSqmStatement().getTarget().getModel().getName() ); } - final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ); - return resolveDelegate( executionContext ).reactiveExecute( executionContextAdapter ); + final SqmJdbcExecutionContextAdapter executionContext = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( context ); + return ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( + getIdTable(), + getTemporaryTableStrategy(), + executionContext + ).thenCompose( unused -> ReactiveExecuteWithTemporaryTableHelper + .saveIntoTemporaryTable( + getMatchingIdsIntoIdTableInsert().jdbcOperation(), + jdbcParameterBindings, + executionContext + ).thenCompose( rows -> loop(getTableUpdaters(), tableUpdater -> + updateTable( tableUpdater, rows, jdbcParameterBindings, executionContext ) ) + .thenApply(v -> rows)) + .handle( CompletionStages::handle) + .thenCompose( handler -> ReactiveExecuteWithTemporaryTableHelper + .performAfterTemporaryTableUseActions( getIdTable(), getSessionUidAccess(), getAfterUseAction(), executionContext ) + .thenCompose( v -> handler.getResultAsCompletionStage() )) + ); } - @Override - protected ReactiveExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { - return (ReactiveExecutionDelegate) super.resolveDelegate( executionContext ); + private CompletionStage updateTable( + TableUpdater tableUpdater, + int expectedUpdateCount, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { + if ( tableUpdater == null ) { + // no assignments for this table - skip it + return voidFuture(); + } + return executeMutation( tableUpdater.jdbcUpdate(), jdbcParameterBindings, executionContext ) + .thenCompose( updateCount -> { + // We are done when the update count matches + if ( updateCount == expectedUpdateCount ) { + return voidFuture(); + } + + // If the table is optional, execute an insert + if ( tableUpdater.jdbcInsert() != null ) { + return executeMutation( tableUpdater.jdbcInsert(), jdbcParameterBindings, executionContext ) + .thenAccept( insertCount -> { + if(insertCount + updateCount != expectedUpdateCount){ + throw new AssertionFailure( "insertCount + updateCount != expectedUpdateCount"); + } + } ); + } + return voidFuture(); + } ); } - @Override - protected ReactiveUpdateExecutionDelegate buildExecutionDelegate( - MultiTableSqmMutationConverter sqmConverter, - TemporaryTable idTable, - TemporaryTableStrategy temporaryTableStrategy, - boolean forceDropAfterUse, - Function sessionUidAccess, - DomainParameterXref domainParameterXref, - TableGroup updatingTableGroup, - Map tableReferenceByAlias, - List assignments, - Predicate suppliedPredicate, - DomainQueryExecutionContext executionContext) { - return new ReactiveUpdateExecutionDelegate( - sqmConverter, - idTable, - temporaryTableStrategy, - forceDropAfterUse, - sessionUidAccess, - domainParameterXref, - updatingTableGroup, - tableReferenceByAlias, - assignments, - suppliedPredicate, + private CompletionStage executeMutation(JdbcOperationQueryMutation jdbcUpdate, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { + return StandardReactiveJdbcMutationExecutor.INSTANCE.executeReactive( + jdbcUpdate, + jdbcParameterBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, executionContext ); } + } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java index 2b3eda5f5..9dde81ab7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveTemporaryTableHelper.java @@ -44,7 +44,6 @@ public interface ReactiveWork { public static class TemporaryTableCreationWork implements ReactiveWork { private final TemporaryTable temporaryTable; private final TemporaryTableExporter exporter; - private final SessionFactoryImplementor sessionFactory; public TemporaryTableCreationWork( TemporaryTable temporaryTable, @@ -62,7 +61,6 @@ public TemporaryTableCreationWork( SessionFactoryImplementor sessionFactory) { this.temporaryTable = temporaryTable; this.exporter = exporter; - this.sessionFactory = sessionFactory; } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java deleted file mode 100644 index 454864b80..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveUpdateExecutionDelegate.java +++ /dev/null @@ -1,295 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.query.sqm.mutation.internal.temptable; - -import java.lang.invoke.MethodHandles; -import java.sql.PreparedStatement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.temptable.UpdateExecutionDelegate; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; -import org.hibernate.reactive.util.impl.CompletionStages; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.SqlTuple; -import org.hibernate.sql.ast.tree.from.NamedTableReference; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; -import org.hibernate.sql.ast.tree.predicate.ExistsPredicate; -import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.ast.tree.update.Assignment; -import org.hibernate.sql.ast.tree.update.UpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.results.internal.SqlSelectionImpl; - -import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; -import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions; -import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions; -import static org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactiveExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable; -import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; - -public class ReactiveUpdateExecutionDelegate extends UpdateExecutionDelegate implements ReactiveTableBasedUpdateHandler.ReactiveExecutionDelegate { - - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - public ReactiveUpdateExecutionDelegate( - MultiTableSqmMutationConverter sqmConverter, - TemporaryTable idTable, - TemporaryTableStrategy temporaryTableStrategy, - boolean forceDropAfterUse, - Function sessionUidAccess, - DomainParameterXref domainParameterXref, - TableGroup updatingTableGroup, - Map tableReferenceByAlias, - List assignments, - Predicate suppliedPredicate, - DomainQueryExecutionContext executionContext) { - super( - sqmConverter, - idTable, - temporaryTableStrategy, - forceDropAfterUse, - sessionUidAccess, - domainParameterXref, - updatingTableGroup, - tableReferenceByAlias, - assignments, - suppliedPredicate, - executionContext - ); - } - - private static void doNothing(Integer integer, PreparedStatement preparedStatement) { - } - - @Override - public int execute(ExecutionContext executionContext) { - throw LOG.nonReactiveMethodCall( "reactiveExecute" ); - } - - @Override - public CompletionStage reactiveExecute(ExecutionContext executionContext) { - return performBeforeTemporaryTableUseActions( - getIdTable(), - getTemporaryTableStrategy(), - executionContext - ) - .thenCompose( v -> saveMatchingIdsIntoIdTable( - getSqmConverter(), - getSuppliedPredicate(), - getIdTable(), - getSessionUidAccess(), - getJdbcParameterBindings(), - executionContext - ) ) - .thenCompose( rows -> { - final QuerySpec idTableSubQuery = createIdTableSelectQuerySpec( - getIdTable(), - getSessionUidAccess(), - getEntityDescriptor(), - executionContext - ); - - final CompletionStage[] resultStage = new CompletionStage[] { voidFuture() }; - getEntityDescriptor().visitConstraintOrderedTables( - (tableExpression, tableKeyColumnVisitationSupplier) -> resultStage[0] = resultStage[0].thenCompose( - v -> reactiveUpdateTable( - tableExpression, - tableKeyColumnVisitationSupplier, - rows, - idTableSubQuery, - executionContext - ) ) - ); - return resultStage[0].thenApply( v -> rows ); - }) - .handle( CompletionStages::handle ) - .thenCompose( handler -> performAfterTemporaryTableUseActions( - getIdTable(), - getSessionUidAccess(), - getAfterUseAction(), - executionContext - ) - .thenCompose( handler::getResultAsCompletionStage ) - ); - } - - private CompletionStage reactiveUpdateTable( - String tableExpression, - Supplier> tableKeyColumnVisitationSupplier, - int expectedUpdateCount, - QuerySpec idTableSubQuery, - ExecutionContext executionContext) { - - // update `updatingTableReference` - // set ... - // where `keyExpression` in ( `idTableSubQuery` ) - - final TableReference updatingTableReference = getUpdatingTableGroup().getTableReference( - getUpdatingTableGroup().getNavigablePath(), - tableExpression, - true - ); - - final List assignments = getAssignmentsByTable().get( updatingTableReference ); - if ( assignments == null || assignments.isEmpty() ) { - // no assignments for this table - skip it - return voidFuture(); - } - - final NamedTableReference dmlTableReference = resolveUnionTableReference( updatingTableReference, tableExpression ); - final JdbcServices jdbcServices = getSessionFactory().getJdbcServices(); - final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcServices.getJdbcEnvironment().getSqlAstTranslatorFactory(); - - final Expression keyExpression = resolveMutatingTableKeyExpression( tableExpression, tableKeyColumnVisitationSupplier ); - - return executeUpdate( idTableSubQuery, executionContext, assignments, dmlTableReference, sqlAstTranslatorFactory, keyExpression ) - .thenCompose( updateCount -> { - // We are done when the update count matches - if ( updateCount == expectedUpdateCount ) { - return voidFuture(); - } - // If the table is optional, execute an insert - if ( isTableOptional( tableExpression ) ) { - return executeInsert( - tableExpression, - dmlTableReference, - keyExpression, - tableKeyColumnVisitationSupplier, - idTableSubQuery, - assignments, - sqlAstTranslatorFactory, - executionContext - ) - .thenAccept( insertCount -> { - assert insertCount + updateCount == expectedUpdateCount; - } ); - } - return voidFuture(); - } ); - } - - - private CompletionStage executeUpdate(QuerySpec idTableSubQuery, ExecutionContext executionContext, List assignments, NamedTableReference dmlTableReference, SqlAstTranslatorFactory sqlAstTranslatorFactory, Expression keyExpression) { - final UpdateStatement sqlAst = new UpdateStatement( - dmlTableReference, - assignments, - new InSubQueryPredicate( keyExpression, idTableSubQuery, false ) - ); - - final JdbcOperationQueryMutation jdbcUpdate = sqlAstTranslatorFactory - .buildMutationTranslator( getSessionFactory(), sqlAst ) - .translate( getJdbcParameterBindings(), executionContext.getQueryOptions() ); - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcUpdate, - getJdbcParameterBindings(), - executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - ::prepareStatement, - ReactiveUpdateExecutionDelegate::doNothing, - executionContext - ); - } - - private CompletionStage executeInsert( - String targetTableExpression, - NamedTableReference targetTableReference, - Expression targetTableKeyExpression, - Supplier> tableKeyColumnVisitationSupplier, - QuerySpec idTableSubQuery, - List assignments, - SqlAstTranslatorFactory sqlAstTranslatorFactory, - ExecutionContext executionContext) { - - // Execute a query in the form - - // - // insert into (...) - // select ... - // from temptable_ - // where not exists ( - // select 1 - // from dml_ - // where dml_. = temptable_. - // ) - - // Create a new QuerySpec for the "insert source" select query. This - // is mostly a copy of the incoming `idTableSubQuery` along with the - // NOT-EXISTS predicate - final QuerySpec insertSourceSelectQuerySpec = makeInsertSourceSelectQuerySpec( idTableSubQuery ); - - // create the `select 1 ...` sub-query and apply the not-exists predicate - final QuerySpec existsSubQuerySpec = createExistsSubQuerySpec( targetTableExpression, tableKeyColumnVisitationSupplier, idTableSubQuery ); - insertSourceSelectQuerySpec.applyPredicate( - new ExistsPredicate( - existsSubQuerySpec, - true, - getSessionFactory().getTypeConfiguration().getBasicTypeForJavaType( Boolean.class ) - ) - ); - - // Collect the target column references from the key expressions - final List targetColumnReferences = new ArrayList<>(); - if ( targetTableKeyExpression instanceof SqlTuple ) { - //noinspection unchecked - targetColumnReferences.addAll( (Collection) ( (SqlTuple) targetTableKeyExpression ).getExpressions() ); - } - else { - targetColumnReferences.add( (ColumnReference) targetTableKeyExpression ); - } - - // And transform assignments to target column references and selections - for ( Assignment assignment : assignments ) { - targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() ); - insertSourceSelectQuerySpec.getSelectClause() - .addSqlSelection( new SqlSelectionImpl( assignment.getAssignedValue() ) ); - } - - final InsertSelectStatement insertSqlAst = new InsertSelectStatement( targetTableReference ); - insertSqlAst.addTargetColumnReferences( targetColumnReferences.toArray( new ColumnReference[0] ) ); - insertSqlAst.setSourceSelectStatement( insertSourceSelectQuerySpec ); - - final JdbcOperationQueryMutation jdbcInsert = sqlAstTranslatorFactory - .buildMutationTranslator( getSessionFactory(), insertSqlAst ) - .translate( getJdbcParameterBindings(), executionContext.getQueryOptions() ); - - return StandardReactiveJdbcMutationExecutor.INSTANCE - .executeReactive( - jdbcInsert, - getJdbcParameterBindings(), - executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - ::prepareStatement, - ReactiveUpdateExecutionDelegate::doNothing, - executionContext - ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveAbstractMutationHandler.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveAbstractMutationHandler.java index a4a622537..f3a3b9a42 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveAbstractMutationHandler.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveAbstractMutationHandler.java @@ -8,7 +8,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; /** @@ -16,8 +15,6 @@ */ public interface ReactiveAbstractMutationHandler extends ReactiveHandler { - SqmDeleteOrUpdateStatement getSqmDeleteOrUpdateStatement(); - EntityMappingType getEntityDescriptor(); SessionFactoryImplementor getSessionFactory(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableInsertStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableInsertStrategy.java index dc52d893e..93399180d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableInsertStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableInsertStrategy.java @@ -10,10 +10,12 @@ import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; public interface ReactiveSqmMultiTableInsertStrategy extends SqmMultiTableInsertStrategy { @@ -27,8 +29,18 @@ default int executeInsert( throw LOG.nonReactiveMethodCall( "reactiveExecuteInsert" ); } - CompletionStage reactiveExecuteInsert( + /** + * Execute the multi-table insert indicated by the passed SqmInsertStatement + * + * @return The number of rows affected + * @deprecated Uses {@link #buildHandler(SqmInsertStatement, DomainParameterXref, DomainQueryExecutionContext)} instead + */ + @Deprecated(forRemoval = true, since = "3.1") + default CompletionStage reactiveExecuteInsert( SqmInsertStatement sqmInsertStatement, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context); + DomainQueryExecutionContext context){ + final MultiTableHandlerBuildResult buildResult = buildHandler( sqmInsertStatement, domainParameterXref, context ); + return ((ReactiveHandler)buildResult.multiTableHandler()).reactiveExecute( buildResult.firstJdbcParameterBindings(), context ); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableMutationStrategy.java index 41b1c6224..b4a4bd077 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/spi/ReactiveSqmMultiTableMutationStrategy.java @@ -10,11 +10,14 @@ import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.MultiTableHandlerBuildResult; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.query.sqm.mutation.internal.ReactiveHandler; public interface ReactiveSqmMultiTableMutationStrategy extends SqmMultiTableMutationStrategy { @@ -28,10 +31,21 @@ default int executeUpdate( throw LOG.nonReactiveMethodCall( "reactiveExecuteUpdate" ); } - CompletionStage reactiveExecuteUpdate( + /** + * Execute the multi-table update indicated by the passed SqmUpdateStatement + * + * @return The number of rows affected + * @deprecated Use {@link #buildHandler(SqmDeleteOrUpdateStatement, DomainParameterXref, DomainQueryExecutionContext)} instead + */ + @Deprecated(forRemoval = true, since = "7.1") + default CompletionStage reactiveExecuteUpdate( SqmUpdateStatement sqmUpdateStatement, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context); + DomainQueryExecutionContext context){ + final MultiTableHandlerBuildResult buildResult = buildHandler( sqmUpdateStatement, domainParameterXref, context ); + return ((ReactiveHandler)buildResult.multiTableHandler()).reactiveExecute( buildResult.firstJdbcParameterBindings(), context ); + + } @Override default int executeDelete( @@ -41,8 +55,18 @@ default int executeDelete( throw LOG.nonReactiveMethodCall( "reactiveExecuteDelete" ); } - CompletionStage reactiveExecuteDelete( + /** + * Execute the multi-table update indicated by the passed SqmUpdateStatement + * + * @return The number of rows affected + * @deprecated Use {@link #buildHandler(SqmDeleteOrUpdateStatement, DomainParameterXref, DomainQueryExecutionContext)} instead + */ + @Deprecated(forRemoval = true, since = "3.1") + default CompletionStage reactiveExecuteDelete( SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, - DomainQueryExecutionContext context); + DomainQueryExecutionContext context){ + final MultiTableHandlerBuildResult buildResult = buildHandler( sqmDeleteStatement, domainParameterXref, context ); + return ((ReactiveHandler)buildResult.multiTableHandler()).reactiveExecute( buildResult.firstJdbcParameterBindings(), context ); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java index 795b565ff..7b0bd112a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionImpl.java @@ -109,7 +109,7 @@ import org.hibernate.reactive.query.ReactiveSelectionQuery; import org.hibernate.reactive.query.sql.internal.ReactiveNativeQueryImpl; import org.hibernate.reactive.query.sql.spi.ReactiveNativeQueryImplementor; -import org.hibernate.reactive.query.sqm.internal.ReactiveQuerySqmImpl; +import org.hibernate.reactive.query.sqm.internal.ReactiveSqmQueryImpl; import org.hibernate.reactive.query.sqm.internal.ReactiveSqmSelectionQueryImpl; import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.util.impl.CompletionStages; @@ -390,7 +390,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } protected ReactiveQueryImplementor createReactiveCriteriaQuery(SqmStatement criteria, Class resultType) { - final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( criteria, resultType, this ); + final ReactiveSqmQueryImpl query = new ReactiveSqmQueryImpl<>( criteria, resultType, this ); applyQuerySettingsAndHints( query ); return query; } @@ -400,11 +400,11 @@ public ReactiveQuery createReactiveQuery(TypedQueryReference typedQuer checksBeforeQueryCreation(); if ( typedQueryReference instanceof SelectionSpecificationImpl specification ) { final CriteriaQuery query = specification.buildCriteria( getCriteriaBuilder() ); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, specification.getResultType(), this ); + return new ReactiveSqmQueryImpl<>( (SqmStatement) query, specification.getResultType(), this ); } else if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); + return new ReactiveSqmQueryImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); } else { @SuppressWarnings("unchecked") @@ -433,8 +433,8 @@ public ReactiveQuery createReactiveQuery(String queryString, Class exp try { final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); - final ReactiveQuerySqmImpl query = - new ReactiveQuerySqmImpl<>( queryString, interpretation, expectedResultType, this ); + final ReactiveSqmQueryImpl query = + new ReactiveSqmQueryImpl<>( queryString, interpretation, expectedResultType, this ); applyQuerySettingsAndHints( query ); query.setComment( queryString ); return query; @@ -611,7 +611,7 @@ protected ReactiveNativeQueryImpl createReactiveNativeQueryImplementor(Cl return (ReactiveNativeQueryImpl) query; } - protected ReactiveQuerySqmImpl createReactiveSqmQueryImplementor(Class resultType, NamedSqmQueryMemento memento) { + protected ReactiveSqmQueryImpl createReactiveSqmQueryImplementor(Class resultType, NamedSqmQueryMemento memento) { final SqmQueryImplementor query = memento.toQuery( this, resultType ); if ( isEmpty( query.getComment() ) ) { query.setComment( "dynamic query" ); @@ -620,7 +620,7 @@ protected ReactiveQuerySqmImpl createReactiveSqmQueryImplementor(Class if ( memento.getLockOptions() != null ) { query.setLockOptions( memento.getLockOptions() ); } - return (ReactiveQuerySqmImpl) query; + return (ReactiveSqmQueryImpl) query; } private RuntimeException convertNamedQueryException(RuntimeException e) { @@ -649,7 +649,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(String hqlString final QueryImplementor query = createQuery( hqlString ); final SqmStatement sqmStatement = ( (SqmQueryImplementor) query ).getSqmStatement(); checkMutationQuery( hqlString, sqmStatement ); - return new ReactiveQuerySqmImpl<>( sqmStatement, null, this ); + return new ReactiveSqmQueryImpl<>( sqmStatement, null, this ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java index 63babefd0..51cf19747 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveStatelessSessionImpl.java @@ -68,7 +68,7 @@ import org.hibernate.reactive.query.ReactiveSelectionQuery; import org.hibernate.reactive.query.sql.internal.ReactiveNativeQueryImpl; import org.hibernate.reactive.query.sql.spi.ReactiveNativeQueryImplementor; -import org.hibernate.reactive.query.sqm.internal.ReactiveQuerySqmImpl; +import org.hibernate.reactive.query.sqm.internal.ReactiveSqmQueryImpl; import org.hibernate.reactive.query.sqm.internal.ReactiveSqmSelectionQueryImpl; import org.hibernate.reactive.session.ReactiveSqmQueryImplementor; import org.hibernate.reactive.session.ReactiveStatelessSession; @@ -951,11 +951,11 @@ public ReactiveQuery createReactiveQuery(TypedQueryReference typedQuer checksBeforeQueryCreation(); if ( typedQueryReference instanceof SelectionSpecificationImpl specification ) { final CriteriaQuery query = specification.buildCriteria( getCriteriaBuilder() ); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, specification.getResultType(), this ); + return new ReactiveSqmQueryImpl<>( (SqmStatement) query, specification.getResultType(), this ); } if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); + return new ReactiveSqmQueryImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); } @SuppressWarnings("unchecked") // this cast is fine because of all our impls of TypedQueryReference return Class @@ -1000,7 +1000,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } private ReactiveQuery createReactiveCriteriaQuery(SqmStatement criteria, Class resultType) { - final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( criteria, resultType, this ); + final ReactiveSqmQueryImpl query = new ReactiveSqmQueryImpl<>( criteria, resultType, this ); applyQuerySettingsAndHints( query ); return query; } @@ -1026,8 +1026,8 @@ public ReactiveSqmQueryImplementor createReactiveQuery(String queryString try { final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); - final ReactiveQuerySqmImpl query = - new ReactiveQuerySqmImpl<>( queryString, interpretation, expectedResultType, this ); + final ReactiveSqmQueryImpl query = + new ReactiveSqmQueryImpl<>( queryString, interpretation, expectedResultType, this ); applyQuerySettingsAndHints( query ); query.setComment( queryString ); @@ -1170,7 +1170,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(String hqlString final QueryImplementor query = createQuery( hqlString ); final SqmStatement sqmStatement = ( (SqmQueryImplementor) query ).getSqmStatement(); checkMutationQuery( hqlString, sqmStatement ); - return new ReactiveQuerySqmImpl<>( sqmStatement, null, this ); + return new ReactiveSqmQueryImpl<>( sqmStatement, null, this ); } @Override From ec8ec2ef0242dbce4a4e32fb294f570f76d029a3 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 6 Aug 2025 16:32:26 +0200 Subject: [PATCH 73/85] [#2404] Remove left-over code --- .../sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java index 81f1addf5..4f499f5cf 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; import org.hibernate.ScrollMode; import org.hibernate.engine.spi.SubselectFetch; @@ -130,9 +129,6 @@ private static CompletionStage executeQueryInterpreter( ReactiveResultsConsumer resultsConsumer) { final ReactiveSharedSessionContractImplementor session = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation(); - // I'm using a supplier so that the whenComplete at the end will catch any errors, like a finally-block - final Supplier fetchHandlerSupplier = () -> SubselectFetch - .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.statement(), JdbcParametersList.empty(), jdbcParameterBindings ); return CompletionStages .supplyStage( () -> { final var subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler( From ccde019d33581705b25a8fdbd8261ddceb32e200 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 8 Aug 2025 14:46:18 +0200 Subject: [PATCH 74/85] [#2408] Fix Hibernate Gradle Plugin setup in the examples --- examples/native-sql-example/build.gradle | 2 +- examples/session-example/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index c1239deeb..36c2494fe 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -47,7 +47,7 @@ dependencies { } // Optional: enable the bytecode enhancements -hibernate { enhancement } +hibernate { enhancement {} } // Create tasks to run the different API available. // diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index 795fc3fb0..621df2bb4 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -48,7 +48,7 @@ dependencies { } // Optional: enable the bytecode enhancements -hibernate { enhancement } +hibernate { enhancement {} } // Create tasks to run the different API available. // From 57a41f4d3790edb388ff6613047c473d73e52aca Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 8 Aug 2025 09:44:48 +0200 Subject: [PATCH 75/85] [#2408] Update Hibernate ORM to 7.1.0.Final And realign the Hibernate Gradle plugin to the same version --- gradle.properties | 4 ++-- gradle/libs.versions.toml | 4 ++-- settings.gradle | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index d081888e0..b23834e1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -39,10 +39,10 @@ org.gradle.java.installations.auto-download=false ### Settings the following properties will override the version defined in gradle/libs.versions.toml # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -#hibernateOrmVersion = 7.1.0.CR1 +#hibernateOrmVersion = 7.1.0.Final # Override default Hibernate ORM Gradle plugin version -#hibernateOrmGradlePluginVersion = 7.1.0.CR1 +#hibernateOrmGradlePluginVersion = 7.1.0.Final # If set to true, skip Hibernate ORM version parsing (default is true, if set to null) # this is required when using intervals or weird versions or the build will fail diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 694d04bcd..30fc4edae 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] assertjVersion = "3.27.3" -hibernateOrmVersion = "7.1.0.CR2" -hibernateOrmGradlePluginVersion = "7.0.8.Final" +hibernateOrmVersion = "7.1.0.Final" +hibernateOrmGradlePluginVersion = "7.1.0.Final" jacksonDatabindVersion = "2.19.2" jbossLoggingAnnotationVersion = "3.0.4.Final" jbossLoggingVersion = "3.6.1.Final" diff --git a/settings.gradle b/settings.gradle index 9e3962b53..f1c542d37 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,13 +24,13 @@ def GRADLE_MAX_SUPPORTED_BYTECODE_VERSION = 23 dependencyResolutionManagement { versionCatalogs { create("libs") { - // ./gradlew build -PhibernateOrmVersion=7.1.0.CR1 + // ./gradlew build -PhibernateOrmVersion=7.1.0.Final def hibernateOrmVersion = settings.ext.find("hibernateOrmVersion") ?: "" if ( hibernateOrmVersion != "" ) { version("hibernateOrmVersion", hibernateOrmVersion) } - // ./gradlew build -PhibernateOrmGradlePluginVersion=7.1.0.CR1 + // ./gradlew build -PhibernateOrmGradlePluginVersion=7.1.0.Final def hibernateOrmGradlePluginVersion = settings.ext.find("hibernateOrmGradlePluginVersion") ?: "" if ( hibernateOrmGradlePluginVersion ) { version("hibernateOrmGradlePluginVersion", hibernateOrmGradlePluginVersion) From 08dadb9deabed5e9919bcbea4701d26df3f00d05 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 8 Aug 2025 16:14:03 +0200 Subject: [PATCH 76/85] [#2408] Add missing Override annotations --- .../sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java | 2 ++ .../temptable/ReactiveGlobalTemporaryTableMutationStrategy.java | 2 ++ .../temptable/ReactiveLocalTemporaryTableMutationStrategy.java | 2 ++ .../temptable/ReactivePersistentTableMutationStrategy.java | 2 ++ 4 files changed, 8 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java index be67bd8ad..445cfe2f6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/cte/ReactiveCteMutationStrategy.java @@ -40,6 +40,7 @@ public MultiTableHandlerBuildResult buildHandler(SqmDeleteOrUpdateStatement s return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } + @Override public ReactiveHandler buildHandler(SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context, MutableObject firstJdbcParameterBindingsConsumer) { checkMatch( sqmDelete ); if ( getRootDescriptor().getSoftDeleteMapping() != null ) { @@ -66,6 +67,7 @@ public ReactiveHandler buildHandler(SqmDeleteStatement sqmDelete, DomainParam } } + @Override public MultiTableHandler buildHandler(SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, DomainQueryExecutionContext context, MutableObject firstJdbcParameterBindingsConsumer) { checkMatch( sqmUpdate ); return new ReactiveCteUpdateHandler( diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java index aaad73b10..0995dc70e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableMutationStrategy.java @@ -64,6 +64,7 @@ public MultiTableHandlerBuildResult buildHandler(SqmDeleteOrUpdateStatement s return new MultiTableHandlerBuildResult( multiTableHandler, firstJdbcParameterBindings.get() ); } + @Override public MultiTableHandler buildHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, @@ -83,6 +84,7 @@ public MultiTableHandler buildHandler( ); } + @Override public MultiTableHandler buildHandler( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java index 1073f118f..3e26378d9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveLocalTemporaryTableMutationStrategy.java @@ -23,6 +23,7 @@ public ReactiveLocalTemporaryTableMutationStrategy(LocalTemporaryTableMutationSt super( mutationStrategy.getTemporaryTable(), mutationStrategy.getSessionFactory() ); } + @Override public MultiTableHandler buildHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, @@ -42,6 +43,7 @@ public MultiTableHandler buildHandler( ); } + @Override public MultiTableHandler buildHandler( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java index 7b7a641ae..76109c78e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableMutationStrategy.java @@ -50,6 +50,7 @@ public void release(SessionFactoryImplementor sessionFactory, JdbcConnectionAcce release( sessionFactory, connectionAccess, tableDroppedStage ); } + @Override public MultiTableHandler buildHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, @@ -67,6 +68,7 @@ public MultiTableHandler buildHandler( ); } + @Override public MultiTableHandler buildHandler( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, From bd7a4aa666cf4412f3cba8ebe5f81c426550ce56 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 11 Aug 2025 10:23:42 +0200 Subject: [PATCH 77/85] Change development version to 4.1.0-SNAPSHOT --- gradle/version.properties | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gradle/version.properties b/gradle/version.properties index 7cc669e01..2310dc846 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1,5 +1 @@ -<<<<<<< HEAD -projectVersion=4.0.0-SNAPSHOT -======= -projectVersion=3.1.0-SNAPSHOT ->>>>>>> 2a11b54c ([#2387] Upgrade Hibernate ORM to 7.1.0.CR1) +projectVersion=4.1.0-SNAPSHOT From 2838a2b7dff59cda8c975cf1a545b2239d6e6d49 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 11 Aug 2025 10:42:54 +0200 Subject: [PATCH 78/85] [#2413] Upgrade Vert.x to 5.0.2 --- README.md | 10 +++++----- gradle.properties | 6 +++--- gradle/libs.versions.toml | 6 +++--- tooling/jbang/CockroachDBReactiveTest.java.qute | 4 ++-- tooling/jbang/Db2ReactiveTest.java.qute | 4 ++-- tooling/jbang/Example.java | 6 +++--- tooling/jbang/MariaDBReactiveTest.java.qute | 4 ++-- tooling/jbang/MySQLReactiveTest.java.qute | 4 ++-- tooling/jbang/PostgreSQLReactiveTest.java.qute | 4 ++-- tooling/jbang/ReactiveTest.java | 8 ++++---- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 7cd62b47f..755b33565 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,11 @@ Hibernate Reactive has been tested with: - MS SQL Server 2025 - Oracle 23 - [Hibernate ORM][] 7.1.0.Final -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0.0 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0.0 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.2 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.2 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.2 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0.2 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0.2 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/gradle.properties b/gradle.properties index b23834e1c..d098a7035 100644 --- a/gradle.properties +++ b/gradle.properties @@ -49,9 +49,9 @@ org.gradle.java.installations.auto-download=false #skipOrmVersionParsing = true # Override default Vert.x Sql client version -#vertxSqlClientVersion = 5.0.0-SNAPSHOT +#vertxSqlClientVersion = 5.0.2-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 5.0.0 -#vertxWebtClientVersion = 5.0.0 +#vertxWebVersion = 5.0.2 +#vertxWebtClientVersion = 5.0.2 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 30fc4edae..5f6355381 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,9 +9,9 @@ junitVersion = "5.13.4" junitPlatformVersion = "1.13.3" log4jVersion = "2.25.1" testcontainersVersion = "1.21.3" -vertxSqlClientVersion = "5.0.1" -vertxWebVersion= "5.0.1" -vertxWebClientVersion = "5.0.1" +vertxSqlClientVersion = "5.0.2" +vertxWebVersion= "5.0.2" +vertxWebClientVersion = "5.0.2" [libraries] com-fasterxml-jackson-core-jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jacksonDatabindVersion" } diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 498892f67..8a002701f 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index d0ee70229..4d39e5ee3 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-db2-client:$\{vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-db2-client:$\{vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 3dde6e2b1..145f1a625 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -6,9 +6,9 @@ */ //DEPS com.ongres.scram:scram-client:3.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.0} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.0} -//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.2} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.2} +//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.slf4j:slf4j-simple:2.0.7 //DESCRIPTION Allow authentication to PostgreSQL using SCRAM: diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index b0129f086..658d93f5d 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 00d82a12b..f4c2c35d1 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index b5c6c8cbd..5762e7e6b 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -5,8 +5,8 @@ * Copyright: Red Hat Inc. and Hibernate Authors */ -//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.0} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:$\{vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 87c8659a3..9e931e52e 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -5,11 +5,11 @@ */ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-pg-client:${vertx.version:5.0.2} //DEPS com.ongres.scram:scram-client:3.1 -//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.0} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.0} -//DEPS io.vertx:vertx-unit:${vertx.version:5.0.0} +//DEPS io.vertx:vertx-db2-client:${vertx.version:5.0.2} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:5.0.2} +//DEPS io.vertx:vertx-unit:${vertx.version:5.0.2} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:4.0.0.Final} //DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 From 280b36debbbc8aa843a296b1913c6f7b92201869 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 11 Aug 2025 11:05:32 +0200 Subject: [PATCH 79/85] [#2420] Keep only major.minor version in README For Hibernate ORM and Vert.x. The current automated release system and dependabot don't update the readme. It would get out of date at every minor release. I've included a link to the exact version in the catalog, though. --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 755b33565..b0cc462fb 100644 --- a/README.md +++ b/README.md @@ -38,14 +38,18 @@ Hibernate Reactive has been tested with: - CockroachDB v25 - MS SQL Server 2025 - Oracle 23 -- [Hibernate ORM][] 7.1.0.Final -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.2 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.2 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.2 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0.2 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0.2 +- [Hibernate ORM][] 7.1 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0 - [Quarkus][Quarkus] via the Hibernate Reactive extension +The exact version of the libraries and images are in the +[catalog](https://github.com/hibernate/hibernate-reactive/blob/main/gradle/libs.versions.toml) +and in the [tooling/docker](https://github.com/hibernate/hibernate-reactive/tree/main/tooling/docker) folder. + [PostgreSQL]: https://www.postgresql.org [MySQL]: https://www.mysql.com [MariaDB]: https://mariadb.com From 6e2a0ae212e9ae95855fcd6f150f39076e5a8c05 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 11 Aug 2025 10:51:55 +0200 Subject: [PATCH 80/85] Include new branches in the GitHub workflows --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scheduler.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0be7d261f..ce053481f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,9 @@ name: "CodeQL" on: push: - branches: [ "main", "4.0", "3.0", "2.4" ] + branches: [ "main", "4.0", "3.1", "3.0", "2.4" ] pull_request: - branches: [ "main" ] + branches: [ "main", "4.0", "3.1", "3.0", "2.4" ] schedule: - cron: "59 17 * * 2" diff --git a/.github/workflows/scheduler.yml b/.github/workflows/scheduler.yml index 476be2550..57a1c58fc 100644 --- a/.github/workflows/scheduler.yml +++ b/.github/workflows/scheduler.yml @@ -11,7 +11,7 @@ jobs: build-snapshots: strategy: matrix: - branch: [ 'wip/2.4', 'wip/3.0', 'wip/3.1', 'wip/4.0' ] + branch: [ 'wip/2.4', 'wip/3.0', 'wip/3.1', 'wip/4.0', 'wip/4.1' ] uses: ./.github/workflows/build.yml with: branch: ${{ matrix.branch }} From b1fbde479c348a00a55275f719922501db7870b9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 8 Aug 2025 12:59:38 +0200 Subject: [PATCH 81/85] [#2398] Add test to show Mutation query with Joined Inheritance for databases supporting Global Temporary Tabels issue has been solved --- .../JoinedSubclassInheritanceTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java index ab64e1925..9c3846ce0 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java @@ -5,6 +5,8 @@ */ package org.hibernate.reactive; +import org.hibernate.reactive.annotations.DisabledFor; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -28,6 +30,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.POSTGRESQL; @Timeout(value = 10, timeUnit = MINUTES) @@ -185,6 +188,31 @@ public void testQueryUpdateWithParameters(VertxTestContext context) { ); } + @Test + @DisabledFor( value = POSTGRESQL, reason = "vertx-sql-client issue: https://github.com/eclipse-vertx/vertx-sql-client/issues/1540" ) + public void testHqlInsertWithTransaction(VertxTestContext context) { + Integer id = 1; + String title = "Spell Book: A Comprehensive Guide to Magic Spells and Incantations"; + test( context, getMutinySessionFactory() + .withTransaction( session -> session.createMutationQuery( "insert into SpellBook (id, title, forbidden) values (:id, :title, :forbidden)" ) + .setParameter( "id", id ) + .setParameter( "title", title ) + .setParameter( "forbidden", true ) + .executeUpdate() + ).call( () -> getMutinySessionFactory() + .withTransaction( session -> session.createSelectionQuery( "from SpellBook g where g.id = :id ", SpellBook.class ) + .setParameter( "id", id ) + .getSingleResult() + .invoke( spellBook -> { + assertThat( spellBook.getTitle() ).isEqualTo( title ); + assertThat( spellBook.forbidden ).isTrue(); + } + ) + ) + ) + ); + } + @Entity(name="SpellBook") @Table(name = "SpellBookJS") @DiscriminatorValue("S") From 9d3675378b2cbff4af70f95bb6ba3796fd77ae58 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 11 Aug 2025 19:24:50 +0200 Subject: [PATCH 82/85] [#2398] Update reason for disabling the test and formattation Better link to the Hibernate Reactive issue, instead of the Vert.x one. --- .../JoinedSubclassInheritanceTest.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java index 9c3846ce0..432535b51 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedSubclassInheritanceTest.java @@ -189,27 +189,26 @@ public void testQueryUpdateWithParameters(VertxTestContext context) { } @Test - @DisabledFor( value = POSTGRESQL, reason = "vertx-sql-client issue: https://github.com/eclipse-vertx/vertx-sql-client/issues/1540" ) + @DisabledFor(value = POSTGRESQL, reason = "https://github.com/hibernate/hibernate-reactive/issues/2412") public void testHqlInsertWithTransaction(VertxTestContext context) { - Integer id = 1; - String title = "Spell Book: A Comprehensive Guide to Magic Spells and Incantations"; - test( context, getMutinySessionFactory() - .withTransaction( session -> session.createMutationQuery( "insert into SpellBook (id, title, forbidden) values (:id, :title, :forbidden)" ) + final Integer id = 1; + final String title = "Spell Book: A Comprehensive Guide to Magic Spells and Incantations"; + test( context, getMutinySessionFactory().withTransaction( session -> session + .createMutationQuery( "insert into SpellBook (id, title, forbidden) values (:id, :title, :forbidden)" ) + .setParameter( "id", id ) + .setParameter( "title", title ) + .setParameter( "forbidden", true ) + .executeUpdate() ) + .call( () -> getMutinySessionFactory().withTransaction( session -> session + .createSelectionQuery( "from SpellBook g where g.id = :id ", SpellBook.class ) .setParameter( "id", id ) - .setParameter( "title", title ) - .setParameter( "forbidden", true ) - .executeUpdate() - ).call( () -> getMutinySessionFactory() - .withTransaction( session -> session.createSelectionQuery( "from SpellBook g where g.id = :id ", SpellBook.class ) - .setParameter( "id", id ) - .getSingleResult() - .invoke( spellBook -> { - assertThat( spellBook.getTitle() ).isEqualTo( title ); - assertThat( spellBook.forbidden ).isTrue(); - } - ) + .getSingleResult() + .invoke( spellBook -> { + assertThat( spellBook.getTitle() ).isEqualTo( title ); + assertThat( spellBook.forbidden ).isTrue(); + } ) - ) + ) ) ); } From 916a6ecec9a131292cf477c03f4bd703d981d54d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 16 Jul 2025 11:08:17 +0200 Subject: [PATCH 83/85] [#2332] SqlException, Reactive doesn't use JDBC when locking previously loaded entity --- .../DefaultReactiveLoadEventListener.java | 23 +-- .../ast/internal/ReactiveLoaderHelper.java | 143 ++++++++++++++++++ .../internal/ReactiveCacheLoadHelper.java | 46 ++++++ 3 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/internal/ReactiveCacheLoadHelper.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java index 9804a02f6..4f3d53e58 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLoadEventListener.java @@ -27,7 +27,6 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LoadEvent; import org.hibernate.event.spi.LoadEventListener; -import org.hibernate.loader.internal.CacheLoadHelper; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; @@ -48,7 +47,7 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.loader.internal.CacheLoadHelper.loadFromSecondLevelCache; -import static org.hibernate.loader.internal.CacheLoadHelper.loadFromSessionCache; +import static org.hibernate.reactive.loader.internal.ReactiveCacheLoadHelper.loadFromSessionCache; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound; @@ -653,15 +652,17 @@ private CompletionStage doLoad( return nullFuture(); } else { - final CacheLoadHelper.PersistenceContextEntry persistenceContextEntry = - loadFromSessionCache( keyToLoad, event.getLockOptions(), options, event.getSession() ); - final Object entity = persistenceContextEntry.entity(); - if ( entity != null ) { - return persistenceContextEntry.isManaged() ? initializeIfNecessary( entity ) : nullFuture(); - } - else { - return loadFromCacheOrDatasource( event, persister, keyToLoad ); - } + return loadFromSessionCache( keyToLoad, event.getLockOptions(), options, event.getSession() ).thenCompose( + persistenceContextEntry -> { + final Object entity = persistenceContextEntry.entity(); + if ( entity != null ) { + return persistenceContextEntry.isManaged() ? initializeIfNecessary( entity ) : nullFuture(); + } + else { + return loadFromCacheOrDatasource( event, persister, keyToLoad ); + } + } + ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveLoaderHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveLoaderHelper.java index 44c90bfdf..e15c42956 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveLoaderHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveLoaderHelper.java @@ -9,10 +9,23 @@ import java.util.List; import java.util.concurrent.CompletionStage; +import org.hibernate.Hibernate; +import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.ObjectDeletedException; +import org.hibernate.cache.spi.access.EntityDataAccess; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; +import org.hibernate.event.monitor.spi.DiagnosticEvent; +import org.hibernate.event.monitor.spi.EventMonitor; +import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.OptimisticLockHelper; +import org.hibernate.loader.LoaderLogging; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.pretty.MessageHelper; +import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.sql.ast.tree.expression.JdbcParameter; @@ -25,6 +38,8 @@ import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import static java.util.Objects.requireNonNull; +import static org.hibernate.reactive.util.impl.CompletionStages.supplyStage; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** * @see org.hibernate.loader.ast.internal.LoaderHelper @@ -92,4 +107,132 @@ public static CompletionStage> loadByArrayParameter( ReactiveListResultsConsumer.UniqueSemantic.FILTER ); } + + /** + * A Reactive implementation of {@link org.hibernate.loader.ast.internal.LoaderHelper#upgradeLock(Object, EntityEntry, LockOptions, SharedSessionContractImplementor)} + */ + public static CompletionStage upgradeLock( + Object object, + EntityEntry entry, + LockOptions lockOptions, + SharedSessionContractImplementor session) { + final LockMode requestedLockMode = lockOptions.getLockMode(); + if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) { + // Request is for a more restrictive lock than the lock already held + final ReactiveEntityPersister persister = (ReactiveEntityPersister) entry.getPersister(); + + if ( entry.getStatus().isDeletedOrGone()) { + throw new ObjectDeletedException( + "attempted to lock a deleted instance", + entry.getId(), + persister.getEntityName() + ); + } + + if ( LoaderLogging.LOADER_LOGGER.isTraceEnabled() ) { + LoaderLogging.LOADER_LOGGER.tracef( + "Locking `%s( %s )` in `%s` lock-mode", + persister.getEntityName(), + entry.getId(), + requestedLockMode + ); + } + + final boolean cachingEnabled = persister.canWriteToCache(); + SoftLock lock = null; + Object ck = null; + try { + if ( cachingEnabled ) { + final EntityDataAccess cache = persister.getCacheAccessStrategy(); + ck = cache.generateCacheKey( entry.getId(), persister, session.getFactory(), session.getTenantIdentifier() ); + lock = cache.lockItem( session, ck, entry.getVersion() ); + } + + if ( persister.isVersioned() && entry.getVersion() == null ) { + // This should be an empty entry created for an uninitialized bytecode proxy + if ( !Hibernate.isPropertyInitialized( object, persister.getVersionMapping().getPartName() ) ) { + Hibernate.initialize( object ); + entry = session.getPersistenceContextInternal().getEntry( object ); + assert entry.getVersion() != null; + } + else { + throw new IllegalStateException( String.format( + "Trying to lock versioned entity %s but found null version", + MessageHelper.infoString( persister.getEntityName(), entry.getId() ) + ) ); + } + } + + if ( persister.isVersioned() && requestedLockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT ) { + // todo : should we check the current isolation mode explicitly? + OptimisticLockHelper.forceVersionIncrement( object, entry, session ); + } + else if ( entry.isExistsInDatabase() ) { + final EventMonitor eventMonitor = session.getEventMonitor(); + final DiagnosticEvent entityLockEvent = eventMonitor.beginEntityLockEvent(); + return reactiveLock( object, entry, lockOptions, session, persister, eventMonitor, entityLockEvent, cachingEnabled, ck, lock ); + } + else { + // should only be possible for a stateful session + if ( session instanceof EventSource eventSource ) { + eventSource.forceFlush( entry ); + } + } + entry.setLockMode(requestedLockMode); + } + finally { + // the database now holds a lock + the object is flushed from the cache, + // so release the soft lock + if ( cachingEnabled ) { + persister.getCacheAccessStrategy().unlockItem( session, ck, lock ); + } + } + } + return voidFuture(); + } + + private static CompletionStage reactiveLock( + Object object, + EntityEntry entry, + LockOptions lockOptions, + SharedSessionContractImplementor session, + ReactiveEntityPersister persister, + EventMonitor eventMonitor, + DiagnosticEvent entityLockEvent, + boolean cachingEnabled, + Object ck, + SoftLock lock) { + return supplyStage( () -> supplyStage( () -> persister.reactiveLock( entry.getId(), entry.getVersion(), object, lockOptions, session ) ) + .whenComplete( (v, e) -> completeLockEvent( entry, lockOptions, session, persister, eventMonitor, entityLockEvent, cachingEnabled, ck, lock, e == null ) ) ) + .whenComplete( (v, e) -> { + if ( cachingEnabled ) { + persister.getCacheAccessStrategy().unlockItem( session, ck, lock ); + } + } ); + } + + private static void completeLockEvent( + EntityEntry entry, + LockOptions lockOptions, + SharedSessionContractImplementor session, + ReactiveEntityPersister persister, + EventMonitor eventMonitor, + DiagnosticEvent entityLockEvent, + boolean cachingEnabled, + Object ck, + SoftLock lock, + boolean succes) { + eventMonitor.completeEntityLockEvent( + entityLockEvent, + entry.getId(), + persister.getEntityName(), + lockOptions.getLockMode(), + succes, + session + ); + if ( cachingEnabled ) { + persister.getCacheAccessStrategy().unlockItem( session, ck, lock ); + } + } + } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/internal/ReactiveCacheLoadHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/internal/ReactiveCacheLoadHelper.java new file mode 100644 index 000000000..1e6f3eddd --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/internal/ReactiveCacheLoadHelper.java @@ -0,0 +1,46 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.loader.internal; + +import org.hibernate.LockOptions; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.spi.LoadEventListener; +import org.hibernate.loader.internal.CacheLoadHelper; +import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry.EntityStatus; + +import java.util.concurrent.CompletionStage; + +import static org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry.EntityStatus.MANAGED; +import static org.hibernate.reactive.loader.ast.internal.ReactiveLoaderHelper.upgradeLock; +import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; + +/** + * A reactive implementation of {@link CacheLoadHelper} + */ +public class ReactiveCacheLoadHelper { + + public static CompletionStage loadFromSessionCache( + EntityKey keyToLoad, + LockOptions lockOptions, + LoadEventListener.LoadType options, + SharedSessionContractImplementor session) { + final Object old = session.getEntityUsingInterceptor( keyToLoad ); + EntityStatus entityStatus = MANAGED; + if ( old != null ) { + // this object was already loaded + final EntityEntry oldEntry = session.getPersistenceContext().getEntry( old ); + entityStatus = CacheLoadHelper.entityStatus( keyToLoad, options, session, oldEntry, old ); + if ( entityStatus == MANAGED ) { + return upgradeLock( old, oldEntry, lockOptions, session ) + .thenApply(v -> new CacheLoadHelper.PersistenceContextEntry( old, MANAGED ) ); + } + } + return completedFuture( new CacheLoadHelper.PersistenceContextEntry( old, entityStatus ) ); + } + +} From fd089587731804d989a5b895dc243b52a37f70f9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 16 Jul 2025 11:10:47 +0200 Subject: [PATCH 84/85] [#2332] Add test for SqlException, Reactive doesn't use JDBC when locking previously loaded entity --- .../hibernate/reactive/LockOnLoadTest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/LockOnLoadTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LockOnLoadTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LockOnLoadTest.java new file mode 100644 index 000000000..fe0c7e20e --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LockOnLoadTest.java @@ -0,0 +1,70 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import java.util.Collection; +import java.util.List; + +import org.hibernate.LockMode; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@Timeout(value = 10, timeUnit = MINUTES) +public class LockOnLoadTest extends BaseReactiveTest{ + @Override + protected Collection> annotatedEntities() { + return List.of( Person.class ); + } + + @Test + public void testLockOnLoad(VertxTestContext context) { + Person person = new Person( 1L, "Davide" ); + + test( context, getMutinySessionFactory() + .withTransaction( session -> session.persist( person ) ) + .call( () -> getMutinySessionFactory().withSession( session -> session + .find( Person.class, person.getId() ) + // the issue occurred when trying to find the same entity but upgrading the lock mode + .chain( p -> session.find( Person.class, person.getId(), LockMode.PESSIMISTIC_WRITE ) ) + .invoke( p -> assertThat( p ).isNotNull() ) + ) ) + ); + } + + @Entity(name = "Person") + @Table(name = "LockOnLoadTest.Person") + public static class Person { + @Id + private Long id; + + private String name; + + public Person() { + } + + public Person(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + } +} From f020c20dbd60a93f6cbfcac2eb581312862c6706 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Tue, 12 Aug 2025 08:50:27 +0000 Subject: [PATCH 85/85] Update project version to : `4.1.0.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 2310dc846..5788ca34e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=4.1.0-SNAPSHOT +projectVersion=4.1.0.Final \ No newline at end of file