From 0cb0281a928544a5f84f65a2c7504ae534749e18 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 12 Dec 2024 19:02:26 +0100 Subject: [PATCH 001/201] [#2032] Upgrade jboss-logging to 3.6.1.Final --- hibernate-reactive-core/build.gradle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index bbcbc9e6c..3153b6640 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -13,13 +13,14 @@ dependencies { api 'io.smallrye.reactive:mutiny:2.7.0' //Logging - implementation 'org.jboss.logging:jboss-logging:3.5.0.Final' - compileOnly 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final' + implementation 'org.jboss.logging:jboss-logging:3.6.1.Final' + annotationProcessor 'org.jboss.logging:jboss-logging:3.6.1.Final' - annotationProcessor 'org.jboss.logging:jboss-logging:3.5.0.Final' + compileOnly 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final' annotationProcessor 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final' annotationProcessor 'org.jboss.logging:jboss-logging-processor:2.2.1.Final' + //Specific implementation details of Hibernate Reactive: implementation "io.vertx:vertx-sql-client:${vertxSqlClientVersion}" From 002fb47ccf6e226ac632dcb03ba174c5d1256118 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 12 Dec 2024 19:03:37 +0100 Subject: [PATCH 002/201] [#2032] Upgrade jboss-logging-annotations to 3.0.3.Final --- hibernate-reactive-core/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 3153b6640..87652c572 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -16,9 +16,9 @@ dependencies { implementation 'org.jboss.logging:jboss-logging:3.6.1.Final' annotationProcessor 'org.jboss.logging:jboss-logging:3.6.1.Final' - compileOnly 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-processor:2.2.1.Final' + compileOnly 'org.jboss.logging:jboss-logging-annotations:3.0.3.Final' + annotationProcessor 'org.jboss.logging:jboss-logging-annotations:3.0.3.Final' + annotationProcessor 'org.jboss.logging:jboss-logging-processor:3.0.3.Final' //Specific implementation details of Hibernate Reactive: From ce758a10aa76bd3a2d1abc2168475e52bff613a5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 12 Dec 2024 19:10:46 +0100 Subject: [PATCH 003/201] [#1895] Upgrade baseline JDK to 17 --- .github/workflows/build.yml | 13 ++++++------- .github/workflows/codeql.yml | 2 +- settings.gradle | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c90f0b9c..a6e33ef46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,11 +94,11 @@ jobs: .gradle/wrapper # refresh cache every month to avoid unlimited growth key: gradle-examples-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2.2.0 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Print the effective ORM version used run: ./gradlew :${{ matrix.example }}:dependencyInsight --dependency org.hibernate.orm:hibernate-core - name: Run examples in '${{ matrix.example }}' on ${{ matrix.db }} @@ -136,11 +136,11 @@ jobs: .gradle/wrapper # refresh cache every month to avoid unlimited growth key: gradle-db-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2.2.0 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Print the effective ORM version used run: ./gradlew :hibernate-reactive-core:dependencyInsight --dependency org.hibernate.orm:hibernate-core - name: Build and Test with ${{ matrix.db }} @@ -165,7 +165,6 @@ jobs: # To see the available versions and download links on jdk.java.net: # https://github.com/oracle-actions/setup-java/blob/main/jdk.java.net-uri.properties java: - - { name: "11", java_version_numeric: 11 } - { name: "17", java_version_numeric: 17 } # We want to enable preview features when testing newer builds of OpenJDK: # even if we don't use these features, just enabling them can cause side effects @@ -246,11 +245,11 @@ jobs: id: testjdk-exportpath run: echo "::set-output name=path::${JAVA_HOME}" # Always use JDK 11 to build the main code: that's what we use for releases. - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2.2.0 with: distribution: 'temurin' - java-version: 11 + java-version: 17 check-latest: true - name: Export path to JDK 11 id: mainjdk-exportpath diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 584de137b..60d7a6ec1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/settings.gradle b/settings.gradle index af4c707eb..e997f3e73 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,7 +13,7 @@ pluginManagement { rootProject.name = 'hibernate-reactive' -gradle.ext.baselineJavaVersion = JavaLanguageVersion.of( 11 ) +gradle.ext.baselineJavaVersion = JavaLanguageVersion.of( 17 ) // Gradle does bytecode transformation on tests. // You can't use bytecode higher than what Gradle supports, even with toolchains. From 97bf4fc8340acb5f8dae93c945f134b95b57d63e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 11 Dec 2024 16:46:19 +0100 Subject: [PATCH 004/201] [#1895] Upgrade JDK to 17 for jenkins pipelines --- ci/release/Jenkinsfile | 2 +- ci/snapshot-publish.Jenkinsfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index bb4315fb8..9407ca050 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -61,7 +61,7 @@ pipeline { label 'Worker&&Containers' } tools { - jdk 'OpenJDK 11 Latest' + jdk 'OpenJDK 17 Latest' } options { buildDiscarder logRotator(daysToKeepStr: '30', numToKeepStr: '10') diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index c95f6663f..9be740a1d 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -15,7 +15,7 @@ pipeline { label 'Fedora' } tools { - jdk 'OpenJDK 11 Latest' + jdk 'OpenJDK 17 Latest' } options { rateLimitBuilds(throttle: [count: 1, durationName: 'hour', userBoost: true]) From e257189f643346799703eab7805de6c4b74a8b33 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 12 Dec 2024 10:10:20 +0100 Subject: [PATCH 005/201] [#1895] Upgrade Hibernate ORM to 7.0.0.Beta3 Notes: * Fix https://github.com/hibernate/hibernate-reactive/issues/1826: ReactiveIdentifierGenerator must extends Generator * The Hibernate Gradle plugin has not been released yet, so we keep it to 7.0.0.Beta2 * XML sql data type is not supported: https://github.com/eclipse-vertx/vertx-sql-client/issues/1475 * Stop using `org.hibernate.dialect.DialectDelegateWrapper` (not required anymore) * Remove `ReactiveOracleSqlAstTranslator` (not required anymore) * Only process parameters for native queries. See https://github.com/hibernate/hibernate-reactive/issues/2012 * Remove `ReactiveIdentifierGeneratorFactory`: the service doesn't exist anymore in ORM * Add `persist(String, Object)` to the session * Remove test for `hibernate.create_empty_composites.enabled`: see [HHH-18222](https://hibernate.atlassian.net/browse/HHH-18222) * Contains some clean ups * There are still some issues with embeddable mapped as JSON and native queries: See https://github.com/hibernate/hibernate-reactive/issues/1999 --- README.md | 2 +- .../main/asciidoc/reference/introduction.adoc | 4 +- gradle.properties | 4 +- .../impl/PreparedStatementAdaptor.java | 20 +- .../adaptor/impl/ResultSetAdaptor.java | 13 +- .../boot/spi/ReactiveMetadataImplementor.java | 5 +- .../ReactiveOracleSqlAstTranslator.java | 43 -- .../reactive/engine/ReactiveActionQueue.java | 116 ++--- .../ReactiveStandardDialectResolver.java | 36 +- .../env/internal/ReactiveJdbcEnvironment.java | 31 -- .../internal/ReactiveMutationExecutor.java | 10 +- .../ReactiveMutationExecutorStandard.java | 4 +- .../AbstractReactiveSaveEventListener.java | 97 ++-- .../DefaultReactiveLockEventListener.java | 27 +- ...ReactiveResolveNaturalIdEventListener.java | 6 +- ...tiveInsertGeneratedIdentifierDelegate.java | 116 ----- .../ReactiveGeneratedValuesHelper.java | 8 +- .../id/ReactiveIdentifierGenerator.java | 3 +- ...veIdentifierGeneratorFactoryInitiator.java | 27 - ...edSequenceReactiveIdentifierGenerator.java | 45 +- .../ReactiveIdentifierGeneratorFactory.java | 143 ------ .../ReactiveSequenceIdentifierGenerator.java | 12 + .../TableReactiveIdentifierGenerator.java | 124 ++++- .../ReactiveAbstractReturningDelegate.java | 14 +- .../ReactiveInsertReturningDelegate.java | 17 +- .../hibernate/reactive/logging/impl/Log.java | 11 + .../ReactiveRuntimeModelCreationContext.java | 128 +++++ .../org/hibernate/reactive/mutiny/Mutiny.java | 20 +- .../mutiny/impl/MutinySessionImpl.java | 5 + .../impl/ReactiveAbstractEntityPersister.java | 94 ++-- .../impl/ReactiveIdentityGenerator.java | 4 +- ...ReactiveJoinedSubclassEntityPersister.java | 29 +- ...eMergeCoordinatorStandardScopeFactory.java | 60 +-- .../ReactiveSingleTableEntityPersister.java | 40 +- .../ReactiveUnionSubclassEntityPersister.java | 22 +- .../ReactiveInsertCoordinatorStandard.java | 7 +- .../mutation/ReactiveMergeCoordinator.java | 4 +- .../ReactiveUpdateCoordinatorStandard.java | 11 +- .../reactive/pool/impl/Parameters.java | 4 - .../pool/impl/SqlClientConnection.java | 3 +- .../provider/ReactivePersistenceProvider.java | 51 +- .../ReactiveEntityManagerFactoryBuilder.java | 22 +- .../impl/ReactiveServiceInitiators.java | 4 - .../impl/ReactiveTypeContributor.java | 61 ++- ...veInformationSchemaBasedExtractorImpl.java | 23 +- .../service/NativeParametersHandling.java | 4 +- .../service/NoJdbcEnvironmentInitiator.java | 7 +- ...leSqlReactiveInformationExtractorImpl.java | 7 +- ...reSqlReactiveInformationExtractorImpl.java | 6 +- .../service/ReactiveGenerationTarget.java | 2 +- ...ReactiveImprovedExtractionContextImpl.java | 4 +- .../service/ReactiveSchemaManagementTool.java | 8 +- ...ReactiveValuesMappingProducerProvider.java | 2 +- ...erverReactiveInformationExtractorImpl.java | 6 + .../query/ReactiveSelectionQuery.java | 5 +- .../ReactiveNamedObjectRepositoryImpl.java | 35 +- .../ReactiveNativeNonSelectQueryPlan.java | 4 +- .../sql/internal/ReactiveNativeQueryImpl.java | 114 ++++- .../ReactiveNativeSelectQueryPlanImpl.java | 47 +- .../spi/ReactiveNamedNativeQueryMemento.java | 23 +- .../sql/spi/ReactiveNamedSqmQueryMemento.java | 19 +- .../spi/ReactiveNativeQueryImplementor.java | 2 +- .../query/sqm/ReactiveSqmSelectionQuery.java | 2 +- .../ConcreteSqmSelectReactiveQueryPlan.java | 22 +- .../sqm/internal/ReactiveQuerySqmImpl.java | 31 +- .../ReactiveSimpleDeleteQueryPlan.java | 8 +- .../ReactiveSimpleInsertQueryPlan.java | 55 +-- .../ReactiveSimpleUpdateQueryPlan.java | 30 +- .../ReactiveSqmSelectionQueryImpl.java | 8 +- .../ReactiveSqmMutationStrategyHelper.java | 120 +---- .../ReactiveAbstractCteMutationHandler.java | 5 +- .../cte/ReactiveCteInsertHandler.java | 38 +- .../cte/ReactiveInsertExecutionDelegate.java | 6 +- ...activeExecuteWithTemporaryTableHelper.java | 42 +- ...tiveLocalTemporaryTableInsertStrategy.java | 2 +- ...veLocalTemporaryTableMutationStrategy.java | 2 +- ...tiveRestrictedDeleteExecutionDelegate.java | 61 +-- .../ReactiveTableBasedDeleteHandler.java | 2 +- .../ReactiveTableBasedInsertHandler.java | 3 +- .../ReactiveTableBasedUpdateHandler.java | 2 +- .../ReactiveTemporaryTableHelper.java | 6 +- .../ReactiveUpdateExecutionDelegate.java | 13 +- .../reactive/session/ReactiveSession.java | 2 + .../impl/ReactiveSessionFactoryImpl.java | 6 - .../session/impl/ReactiveSessionImpl.java | 23 +- .../impl/ReactiveStatelessSessionImpl.java | 101 +++- .../StandardReactiveJdbcMutationExecutor.java | 7 +- .../ReactiveDeleteOrUpsertOperation.java | 17 +- .../ReactiveOptionalTableUpdateOperation.java | 133 ----- .../sql/results/ReactiveResultSetMapping.java | 6 +- .../ReactiveEntityFetchSelectImpl.java | 2 +- .../ReactiveDeferredResultSetAccess.java | 31 +- .../ReactiveEntityDelayedFetchImpl.java | 2 +- .../org/hibernate/reactive/stage/Stage.java | 16 + .../reactive/stage/impl/StageSessionImpl.java | 5 + .../tuple/entity/ReactiveEntityMetamodel.java | 124 +++++ .../jdbc/ReactiveArrayJdbcType.java | 16 +- .../ReactiveArrayJdbcTypeConstructor.java | 8 +- .../jdbc/ReactiveJsonArrayJdbcType.java | 134 +++++ .../ReactiveJsonArrayJdbcTypeConstructor.java | 43 ++ .../jdbc/ReactiveXmlArrayJdbcType.java | 63 +++ .../ReactiveXmlArrayJdbcTypeConstructor.java | 39 ++ .../descriptor/jdbc/ReactiveXmlJdbcType.java | 69 +++ .../reactive/util/impl/CompletionStages.java | 53 ++ .../reactive/BatchQueryOnConnectionTest.java | 25 +- .../reactive/CustomGeneratorTest.java | 67 +-- .../EmptyCompositeCollectionKeyTest.java | 201 -------- .../GeneratedPropertyJoinedTableTest.java | 12 +- .../GeneratedPropertySingleTableTest.java | 12 +- .../GeneratedPropertyUnionSubclassesTest.java | 7 +- .../reactive/IdentityGeneratorTypeTest.java | 1 - .../hibernate/reactive/ManyToManyMapTest.java | 88 ++-- .../MultithreadedIdentityGenerationTest.java | 79 +-- .../reactive/MultithreadedInsertionTest.java | 2 +- .../reactive/ReactiveSessionTest.java | 463 ++++++++++-------- .../reactive/TableGeneratorTest.java | 47 +- .../reactive/containers/DB2Database.java | 10 +- .../containers/MSSQLServerDatabase.java | 12 +- .../reactive/containers/MariaDatabase.java | 22 +- .../reactive/containers/MySQLDatabase.java | 10 +- .../reactive/containers/OracleDatabase.java | 10 +- .../containers/PostgreSQLDatabase.java | 2 +- .../reactive/dynamic/DynamicEntityTest.java | 36 +- .../schema/ColumnTypesMappingTest.java | 34 +- .../reactive/schema/SchemaUpdateTest.java | 146 ++++++ .../reactive/schema/SchemaUpdateTestBase.java | 127 ----- .../reactive/schema/SchemaValidationTest.java | 154 ++++++ .../schema/SchemaValidationTestBase.java | 142 ------ .../reactive/types/JavaTypesArrayTest.java | 124 +++-- .../reactive/types/JsonQueryTest.java | 24 + .../hibernate/reactive/it/lazytoone/Ship.java | 4 - 131 files changed, 2605 insertions(+), 2439 deletions(-) delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcTypeConstructor.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcTypeConstructor.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmptyCompositeCollectionKeyTest.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTest.java delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTestBase.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java diff --git a/README.md b/README.md index a44faa31b..7280fce86 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Hibernate Reactive has been tested with: - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 -- [Hibernate ORM][] 6.6.3.Final +- [Hibernate ORM][] 7.0.0.Beta3 - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.11 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.11 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.11 diff --git a/documentation/src/main/asciidoc/reference/introduction.adoc b/documentation/src/main/asciidoc/reference/introduction.adoc index 40b4847bc..4f2237edb 100644 --- a/documentation/src/main/asciidoc/reference/introduction.adoc +++ b/documentation/src/main/asciidoc/reference/introduction.adoc @@ -578,8 +578,8 @@ custom reactive identifier generator. === JSON Mapping -:orm-json-basic-mapping: https://docs.jboss.org/hibernate/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-json -:orm-json-embeddable-mapping: https://docs.jboss.org/hibernate/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#_jsonxml_aggregate_embeddable_mapping +:orm-json-basic-mapping: https://docs.jboss.org/hibernate/orm/7.0/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-json +:orm-json-embeddable-mapping: https://docs.jboss.org/hibernate/orm/7.0/userguide/html_single/Hibernate_User_Guide.html#_jsonxml_aggregate_embeddable_mapping :string-to-json-converter: https://github.com/hibernate/hibernate-reactive/blob/main/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/StringToJsonConverter.java Like in Hibernate ORM, it's possible to map a JSON field using the {orm-json-basic-mapping}[SqlTypes.JSON] diff --git a/gradle.properties b/gradle.properties index 55ec51fc8..0ee0c1936 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 = 6.6.3.Final +hibernateOrmVersion = 7.0.0.Beta3 # 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 = 6.6.3.Final +hibernateOrmGradlePluginVersion = 7.0.0.Beta2 # 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/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java index 8ff274857..066ec9de6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.adaptor.impl; -import io.vertx.core.buffer.Buffer; - -import org.hibernate.AssertionFailure; -import org.hibernate.HibernateException; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -39,12 +34,23 @@ import java.util.Calendar; import java.util.function.Function; +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; +import org.hibernate.reactive.logging.impl.Log; + +import io.vertx.core.buffer.Buffer; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + /** * Collects parameter bindings from Hibernate core code * that expects a JDBC {@link PreparedStatement}. */ public class PreparedStatementAdaptor implements PreparedStatement { + private static final Log LOG = make( Log.class, lookup() ); + @FunctionalInterface public interface Binder { void bind(PreparedStatement statement) throws SQLException; @@ -315,7 +321,7 @@ public void setNClob(int parameterIndex, Reader reader, long length) { @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override @@ -540,7 +546,7 @@ public int[] executeBatch() { @Override public Connection getConnection() { - throw new UnsupportedOperationException(); + throw LOG.unexpectedConnectionRequest(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java index 0bfcd89d5..b75133699 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java @@ -36,8 +36,9 @@ import java.util.NoSuchElementException; import java.util.function.Function; -import org.hibernate.engine.jdbc.BlobProxy; -import org.hibernate.engine.jdbc.ClobProxy; +import org.hibernate.engine.jdbc.proxy.BlobProxy; +import org.hibernate.engine.jdbc.proxy.ClobProxy; +import org.hibernate.reactive.logging.impl.Log; import org.hibernate.type.descriptor.jdbc.JdbcType; import io.vertx.core.buffer.Buffer; @@ -48,8 +49,10 @@ import io.vertx.sqlclient.desc.ColumnDescriptor; import io.vertx.sqlclient.impl.RowBase; +import static java.lang.invoke.MethodHandles.lookup; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; /** * An adaptor that allows Hibernate core code which expects a JDBC @@ -57,6 +60,8 @@ */ public class ResultSetAdaptor implements ResultSet { + private static final Log LOG = make( Log.class, lookup() ); + private final Iterator iterator; private final List columnDescriptors; @@ -866,12 +871,12 @@ public NClob getNClob(String columnLabel) { @Override public SQLXML getSQLXML(int columnIndex) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override public SQLXML getSQLXML(String columnLabel) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java index fb72b2624..e63d3215d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java @@ -7,7 +7,6 @@ import org.hibernate.boot.spi.AbstractDelegatingMetadata; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.reactive.query.internal.ReactiveNamedObjectRepositoryImpl; @@ -18,7 +17,7 @@ public ReactiveMetadataImplementor(MetadataImplementor delegate) { } @Override - public NamedObjectRepository buildNamedQueryRepository(SessionFactoryImplementor sessionFactory) { - return new ReactiveNamedObjectRepositoryImpl( delegate().buildNamedQueryRepository( sessionFactory ) ); + public NamedObjectRepository buildNamedQueryRepository() { + return new ReactiveNamedObjectRepositoryImpl( super.buildNamedQueryRepository() ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java deleted file mode 100644 index 0773ab033..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java +++ /dev/null @@ -1,43 +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.dialect; - -import org.hibernate.dialect.OracleSqlAstTranslator; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.entity.mutation.EntityTableMapping; -import org.hibernate.reactive.sql.model.ReactiveDeleteOrUpsertOperation; -import org.hibernate.sql.ast.tree.Statement; -import org.hibernate.sql.exec.spi.JdbcOperation; -import org.hibernate.sql.model.MutationOperation; -import org.hibernate.sql.model.internal.OptionalTableUpdate; -import org.hibernate.sql.model.jdbc.UpsertOperation; - -public class ReactiveOracleSqlAstTranslator extends OracleSqlAstTranslator { - public ReactiveOracleSqlAstTranslator( - SessionFactoryImplementor sessionFactory, - Statement statement) { - super( sessionFactory, statement ); - } - - @Override - public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableUpdate) { - renderUpsertStatement( optionalTableUpdate ); - - final UpsertOperation upsertOperation = new UpsertOperation( - optionalTableUpdate.getMutatingTable().getTableMapping(), - optionalTableUpdate.getMutationTarget(), - getSql(), - getParameterBinders() - ); - - return new ReactiveDeleteOrUpsertOperation( - optionalTableUpdate.getMutationTarget(), - (EntityTableMapping) optionalTableUpdate.getMutatingTable().getTableMapping(), - upsertOperation, - optionalTableUpdate - ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java index 034c9f583..3c815b2a8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java @@ -21,6 +21,8 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.PropertyValueException; +import org.hibernate.TransientObjectException; +import org.hibernate.action.internal.AbstractEntityInsertAction; import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.action.internal.EntityDeleteAction; import org.hibernate.action.internal.UnresolvedEntityInsertActions; @@ -28,13 +30,13 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.action.spi.Executable; import org.hibernate.cache.CacheException; +import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.ComparableExecutable; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.ExecutableList; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metadata.ClassMetadata; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; @@ -59,7 +61,6 @@ import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; -import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; @@ -520,11 +521,21 @@ public CompletionStage executeInserts() { */ public CompletionStage executeActions() { if ( hasUnresolvedEntityInsertActions() ) { - return failedFuture( new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." ) ); + final AbstractEntityInsertAction insertAction = unresolvedInsertions + .getDependentEntityInsertActions() + .iterator() + .next(); + final NonNullableTransientDependencies transientEntities = insertAction.findNonNullableTransientEntities(); + final Object transientEntity = transientEntities.getNonNullableTransientEntities().iterator().next(); + final String path = transientEntities.getNonNullableTransientPropertyPaths(transientEntity).iterator().next(); + //TODO: should be TransientPropertyValueException + throw new TransientObjectException( "Persistent instance of '" + insertAction.getEntityName() + + "' with id '" + insertAction.getId() + + "' references an unsaved transient instance via attribute '" + path + + "' (save the transient instance before flushing)" ); } CompletionStage ret = voidFuture(); - for ( OrderedActions action : ORDERED_OPERATIONS ) { ret = ret.thenCompose( v -> executeActions( action.getActions( this ) ) ); } @@ -738,26 +749,6 @@ public int numberOfInsertions() { return insertions.size(); } -// public TransactionCompletionProcesses getTransactionCompletionProcesses() { -// return new TransactionCompletionProcesses( beforeTransactionProcesses(), afterTransactionProcesses() ); -// } -// -// /** -// * Bind transaction completion processes to make them shared between primary and secondary session. -// * Transaction completion processes are always executed by transaction owner (primary session), -// * but can be registered using secondary session too. -// * -// * @param processes Transaction completion processes. -// * @param isTransactionCoordinatorShared Flag indicating shared transaction context. -// */ -// public void setTransactionCompletionProcesses( -// TransactionCompletionProcesses processes, -// boolean isTransactionCoordinatorShared) { -// this.isTransactionCoordinatorShared = isTransactionCoordinatorShared; -// this.beforeTransactionProcesses = processes.beforeTransactionCompletionProcesses; -// this.afterTransactionProcesses = processes.afterTransactionCompletionProcesses; -// } - public void sortCollectionActions() { if ( isOrderUpdatesEnabled() ) { // sort the updates by fk @@ -864,32 +855,6 @@ public void unScheduleDeletion(EntityEntry entry, Object rescuedEntity) { throw new AssertionFailure( "Unable to perform un-delete for instance " + entry.getEntityName() ); } -// /** -// * Used by the owning session to explicitly control serialization of the action queue -// * -// * @param oos The stream to which the action queue should get written -// * -// * @throws IOException Indicates an error writing to the stream -// */ -// public void serialize(ObjectOutputStream oos) throws IOException { -// LOG.trace( "Serializing action-queue" ); -// if ( unresolvedInsertions == null ) { -// unresolvedInsertions = new UnresolvedEntityInsertActions(); -// } -// unresolvedInsertions.serialize( oos ); -// -// for ( ListProvider p : EXECUTABLE_LISTS_MAP.values() ) { -// ExecutableList l = p.get( this ); -// if ( l == null ) { -// oos.writeBoolean( false ); -// } -// else { -// oos.writeBoolean( true ); -// l.writeExternal( oos ); -// } -// } -// } - private abstract static class AbstractTransactionCompletionProcessQueue { final ReactiveSession session; @@ -994,21 +959,6 @@ public CompletionStage afterTransactionCompletion(boolean success) { } } -// /** -// * Wrapper class allowing to bind the same transaction completion process queues in different sessions. -// */ -// public static class TransactionCompletionProcesses { -// private final BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcesses; -// private final AfterTransactionCompletionProcessQueue afterTransactionCompletionProcesses; -// -// private TransactionCompletionProcesses( -// BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcessQueue, -// AfterTransactionCompletionProcessQueue afterTransactionCompletionProcessQueue) { -// this.beforeTransactionCompletionProcesses = beforeTransactionCompletionProcessQueue; -// this.afterTransactionCompletionProcesses = afterTransactionCompletionProcessQueue; -// } -// } - /** * Order the {@link #insertions} queue such that we group inserts against the same entity together (without * violating constraints). The original order is generated by cascade order, which in turn is based on the @@ -1152,26 +1102,23 @@ public void sort(List insertions) { */ private void addParentChildEntityNames(ReactiveEntityInsertAction action, BatchIdentifier batchIdentifier) { Object[] propertyValues = action.getState(); - ClassMetadata classMetadata = action.getPersister().getClassMetadata(); - if ( classMetadata != null ) { - Type[] propertyTypes = classMetadata.getPropertyTypes(); - Type identifierType = classMetadata.getIdentifierType(); - - for ( int i = 0; i < propertyValues.length; i++ ) { - Object value = propertyValues[i]; - if (value!=null) { - Type type = propertyTypes[i]; - addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, value ); - } + Type[] propertyTypes = action.getPersister().getPropertyTypes(); + Type identifierType = action.getPersister().getIdentifierType(); + + for ( int i = 0; i < propertyValues.length; i++ ) { + Object value = propertyValues[i]; + if (value!=null) { + Type type = propertyTypes[i]; + addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, value ); } + } - if ( identifierType.isComponentType() ) { - CompositeType compositeType = (CompositeType) identifierType; - Type[] compositeIdentifierTypes = compositeType.getSubtypes(); + if ( identifierType.isComponentType() ) { + CompositeType compositeType = (CompositeType) identifierType; + Type[] compositeIdentifierTypes = compositeType.getSubtypes(); - for ( Type type : compositeIdentifierTypes ) { - addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, null ); - } + for ( Type type : compositeIdentifierTypes ) { + addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, null ); } } } @@ -1275,10 +1222,9 @@ public boolean equals(Object o) { if ( this == o ) { return true; } - if ( !( o instanceof BatchIdentifier ) ) { + if ( !( o instanceof BatchIdentifier that ) ) { return false; } - BatchIdentifier that = (BatchIdentifier) o; return Objects.equals( entityName, that.entityName ); } @@ -1315,9 +1261,7 @@ boolean hasAnyChildEntityNames(BatchIdentifier batchIdentifier) { /** * Check if this {@link BatchIdentifier} has a parent or grandparent * matching the given {@link BatchIdentifier reference. - * * @param batchIdentifier {@link BatchIdentifier} reference - * * @return this {@link BatchIdentifier} has a parent matching the given {@link BatchIdentifier reference */ boolean hasParent(BatchIdentifier batchIdentifier) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java index 9964ffe93..1b24e567d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java @@ -8,19 +8,8 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Database; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolver; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.entity.mutation.EntityMutationTarget; -import org.hibernate.reactive.dialect.ReactiveOracleSqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.Statement; -import org.hibernate.sql.exec.spi.JdbcOperation; -import org.hibernate.sql.model.MutationOperation; -import org.hibernate.sql.model.internal.OptionalTableUpdate; import static org.hibernate.dialect.CockroachDialect.parseVersion; @@ -36,30 +25,7 @@ public Dialect resolveDialect(DialectResolutionInfo info) { for ( Database database : Database.values() ) { if ( database.matchesResolutionInfo( info ) ) { - Dialect dialect = database.createDialect( info ); - if ( info.getDatabaseName().toUpperCase().startsWith( "ORACLE" ) ) { - return new DialectDelegateWrapper( dialect ) { - @Override - public MutationOperation createOptionalTableUpdateOperation( - EntityMutationTarget mutationTarget, - OptionalTableUpdate optionalTableUpdate, - SessionFactoryImplementor factory) { - return new ReactiveOracleSqlAstTranslator<>( factory, optionalTableUpdate ) - .createMergeOperation( optionalTableUpdate ); - } - - @Override - public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { - return new StandardSqlAstTranslatorFactory() { - @Override - protected SqlAstTranslator buildTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { - return new ReactiveOracleSqlAstTranslator<>( sessionFactory, statement ); - } - }; - } - }; - } - return dialect; + return database.createDialect( info ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java deleted file mode 100644 index a8fd73a6a..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java +++ /dev/null @@ -1,31 +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.engine.jdbc.env.internal; - -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl; -import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; - -public class ReactiveJdbcEnvironment extends JdbcEnvironmentImpl { - - public ReactiveJdbcEnvironment(ServiceRegistryImplementor registry, Dialect dialect) { - super( registry, dialect ); - } - - @Deprecated - public ReactiveJdbcEnvironment(ServiceRegistryImplementor registry, Dialect dialect, DatabaseMetaData metaData) throws SQLException { - super( registry, dialect, metaData ); - } - - @Override - public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { - return super.getSqlAstTranslatorFactory(); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java index fe9873791..3253be9dd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java @@ -10,7 +10,6 @@ import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SQLServerDialect; @@ -104,15 +103,14 @@ default CompletionStage performReactiveBatchedOperations( private static String createInsert(String insertSql, String identifierColumnName, Dialect dialect) { String sql = insertSql; final String sqlEnd = " returning " + identifierColumnName; - Dialect realDialect = DialectDelegateWrapper.extractRealDialect( dialect ); - if ( realDialect instanceof MySQLDialect ) { + if ( dialect instanceof MySQLDialect ) { // For some reason ORM generates a query with an invalid syntax int index = sql.lastIndexOf( sqlEnd ); return index > -1 ? sql.substring( 0, index ) : sql; } - if ( realDialect instanceof SQLServerDialect ) { + if ( dialect instanceof SQLServerDialect ) { int index = sql.lastIndexOf( sqlEnd ); // FIXME: this is a hack for HHH-16365 if ( index > -1 ) { @@ -128,12 +126,12 @@ private static String createInsert(String insertSql, String identifierColumnName } return sql; } - if ( realDialect instanceof DB2Dialect ) { + if ( dialect instanceof DB2Dialect ) { // ORM query: select id from new table ( insert into IntegerTypeEntity values ( )) // Correct : select id from new table ( insert into LongTypeEntity (id) values (default)) return sql.replace( " values ( ))", " (" + identifierColumnName + ") values (default))" ); } - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { final String valuesStr = " values ( )"; int index = sql.lastIndexOf( sqlEnd ); // remove "returning id" since it's added via diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java index 80e24fac7..4f1d62325 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.concurrent.CompletionStage; +import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.OperationResultChecker; import org.hibernate.engine.jdbc.mutation.ParameterUsage; @@ -99,7 +100,8 @@ protected void performSelfExecutingOperations( @Override protected void performBatchedOperations( ValuesAnalysis valuesAnalysis, - TableInclusionChecker inclusionChecker) { + TableInclusionChecker inclusionChecker, + Batch.StaleStateMapper staleStateMapper) { throw LOG.nonReactiveMethodCall( "performReactiveBatchedOperations" ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java index e9d68b00e..0887dfff6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java @@ -25,7 +25,7 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; -import org.hibernate.id.Assigned; +import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; @@ -134,7 +134,7 @@ protected CompletionStage reactiveSaveWithGeneratedId( // and is not yet available generatedId = null; } - else if ( generator instanceof Assigned ) { + else if ( !generator.generatesOnInsert() ) { // get it from the entity later, since we need // the @PrePersist callback to happen first generatedId = null; @@ -144,55 +144,58 @@ else if ( generator instanceof Assigned ) { // the entity instance, so it will be available // to the entity in the @PrePersist callback if ( generator instanceof ReactiveIdentifierGenerator ) { - return ( (ReactiveIdentifierGenerator) generator ) - .generate( ( ReactiveConnectionSupplier ) source, entity ) - .thenApply( id -> castToIdentifierType( id, persister ) ) - .thenCompose( gid -> performSaveWithId( - entity, - context, - source, - persister, - generator, - gid, - requiresImmediateIdAccess, - false - ) ); + return generateId( entity, source, (ReactiveIdentifierGenerator) generator, persister ) + .thenCompose( gid -> { + if ( gid == SHORT_CIRCUIT_INDICATOR ) { + source.getIdentifier( entity ); + return voidFuture(); + } + persister.setIdentifier( entity, gid, source ); + return reactivePerformSave( + entity, + gid, + persister, + generatedOnExecution, + context, + source, + false + ); + } ); } generatedId = ( (BeforeExecutionGenerator) generator ).generate( source, entity, null, INSERT ); + if ( generatedId == SHORT_CIRCUIT_INDICATOR ) { + source.getIdentifier( entity ); + return voidFuture(); + } + persister.setIdentifier( entity, generatedId, source ); } final Object id = castToIdentifierType( generatedId, persister ); - return reactivePerformSave( entity, id, persister, generatedOnExecution, context, source, requiresImmediateIdAccess ); + final boolean delayIdentityInserts = !source.isTransactionInProgress() && !requiresImmediateIdAccess && generatedOnExecution; + return reactivePerformSave( entity, id, persister, generatedOnExecution, context, source, delayIdentityInserts ); } - private CompletionStage performSaveWithId( + private CompletionStage generateId( Object entity, - C context, EventSource source, - EntityPersister persister, - Generator generator, - Object generatedId, - boolean requiresImmediateIdAccess, - boolean generatedOnExecution) { - if ( generatedId == null ) { - throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); - } - if ( generatedId == SHORT_CIRCUIT_INDICATOR ) { - source.getIdentifier( entity ); - return voidFuture(); - } - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Generated identifier: %s, using strategy: %s", - persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), - generator.getClass().getName() - ); - } - final boolean delayIdentityInserts = - !source.isTransactionInProgress() - && !requiresImmediateIdAccess - && generatedOnExecution; - return reactivePerformSave( entity, generatedId, persister, false, context, source, delayIdentityInserts ); + ReactiveIdentifierGenerator generator, + EntityPersister persister) { + return generator + .generate( (ReactiveConnectionSupplier) source, entity ) + .thenApply( id -> castToIdentifierType( id, persister ) ) + .thenCompose( generatedId -> { + if ( generatedId == null ) { + return failedFuture( new IdentifierGenerationException( "null id generated for: " + entity.getClass() ) ); + } + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Generated identifier: %s, using strategy: %s", + persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), + generator.getClass().getName() + ); + } + return completedFuture( generatedId ); + } ); } /** @@ -229,13 +232,11 @@ protected CompletionStage reactivePerformSave( processIfSelfDirtinessTracker( entity, SelfDirtinessTracker::$$_hibernate_clearDirtyAttributes ); processIfManagedEntity( entity, managedEntity -> managedEntity.$$_hibernate_setUseTracker( true ) ); - if ( persister.getGenerator() instanceof Assigned ) { + final Generator generator = persister.getGenerator(); + if ( !generator.generatesOnInsert() || generator instanceof CompositeNestedGeneratedValueGenerator ) { id = persister.getIdentifier( entity, source ); if ( id == null ) { - throw new IdentifierGenerationException( - "Identifier of entity '" + persister.getEntityName() - + "' must be manually assigned before calling 'persist()'" - ); + return failedFuture( new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'persist()'" ) ); } } @@ -420,7 +421,7 @@ private CompletionStage addInsertAction( boolean useIdentityColumn, EventSource source, boolean shouldDelayIdentityInserts) { - final ReactiveActionQueue actionQueue = source.unwrap( ReactiveSession.class ).getReactiveActionQueue(); + final ReactiveActionQueue actionQueue = source.unwrap(ReactiveSession.class).getReactiveActionQueue(); if ( useIdentityColumn ) { final ReactiveEntityIdentityInsertAction insert = new ReactiveEntityIdentityInsertAction( values, entity, persister, false, source, shouldDelayIdentityInserts diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java index 7832298e0..2df247665 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.event.impl; -import java.lang.invoke.MethodHandles; import java.util.concurrent.CompletionStage; import org.hibernate.HibernateException; @@ -20,7 +19,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.internal.AbstractReassociateEventListener; +import org.hibernate.event.internal.DefaultLockEventListener; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LockEvent; import org.hibernate.event.spi.LockEventListener; @@ -33,19 +32,24 @@ import org.hibernate.reactive.engine.impl.ReactiveEntityVerifyVersionProcess; import org.hibernate.reactive.event.ReactiveLockEventListener; import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.reactive.session.ReactiveSession; +import static java.lang.invoke.MethodHandles.lookup; 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.voidFuture; -public class DefaultReactiveLockEventListener extends AbstractReassociateEventListener - implements LockEventListener, ReactiveLockEventListener { +public class DefaultReactiveLockEventListener extends DefaultLockEventListener implements LockEventListener, ReactiveLockEventListener { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + private static final Log LOG = make( Log.class, lookup() ); + + @Override + public void onLock(LockEvent event) throws HibernateException { + throw LOG.nonReactiveMethodCall( "reactiveOnLock" ); + } @Override public CompletionStage reactiveOnLock(LockEvent event) throws HibernateException { @@ -96,11 +100,13 @@ private CompletionStage lockEntry( if ( entry == null ) { final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); final Object id = persister.getIdentifier( entity, source ); - return ForeignKeys.isNotTransient( event.getEntityName(), entity, Boolean.FALSE, source ) + return ForeignKeys + .isNotTransient( event.getEntityName(), entity, Boolean.FALSE, source ) .thenCompose( trans -> { if ( !trans ) { return failedFuture( new TransientObjectException( - "cannot lock an unsaved transient instance: " + persister.getEntityName() ) ); + "Cannot lock unsaved transient instance of entity '" + persister.getEntityName() + "'" + ) ); } final EntityEntry e = reassociate( event, entity, id, persister ); @@ -227,9 +233,4 @@ private CompletionStage doUpgradeLock( throw he; } } - - @Override - public void onLock(LockEvent event) throws HibernateException { - throw new UnsupportedOperationException(); - } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java index 1d80f3634..783c02ec8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java @@ -10,7 +10,6 @@ import org.hibernate.HibernateException; import org.hibernate.engine.spi.NaturalIdResolutions; -import org.hibernate.event.internal.AbstractLockUpgradeEventListener; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.ResolveNaturalIdEvent; import org.hibernate.event.spi.ResolveNaturalIdEventListener; @@ -27,10 +26,9 @@ import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; /** - * A reactific {@link org.hibernate.event.internal.DefaultResolveNaturalIdEventListener}. + * A reactive {@link org.hibernate.event.internal.DefaultResolveNaturalIdEventListener}. */ -public class DefaultReactiveResolveNaturalIdEventListener extends AbstractLockUpgradeEventListener - implements ReactiveResolveNaturalIdEventListener, ResolveNaturalIdEventListener { +public class DefaultReactiveResolveNaturalIdEventListener implements ReactiveResolveNaturalIdEventListener, ResolveNaturalIdEventListener { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java deleted file mode 100644 index 36d85f353..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java +++ /dev/null @@ -1,116 +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.generator.values; - -import java.sql.PreparedStatement; -import java.util.concurrent.CompletionStage; - -import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; -import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.generator.EventType; -import org.hibernate.generator.values.GeneratedValues; -import org.hibernate.id.insert.Binder; -import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; -import org.hibernate.jdbc.Expectation; -import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; -import org.hibernate.sql.model.ast.builder.TableInsertBuilder; -import org.hibernate.sql.model.ast.builder.TableMutationBuilder; -import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; - -public class ReactiveInsertGeneratedIdentifierDelegate implements InsertGeneratedIdentifierDelegate, ReactiveGeneratedValuesMutationDelegate { - private final InsertGeneratedIdentifierDelegate delegate; - - public ReactiveInsertGeneratedIdentifierDelegate(InsertGeneratedIdentifierDelegate delegate) { - this.delegate = delegate; - } - - @Override - public TableInsertBuilder createTableInsertBuilder( - BasicEntityIdentifierMapping identifierMapping, - Expectation expectation, - SessionFactoryImplementor sessionFactory) { - return delegate.createTableInsertBuilder( identifierMapping, expectation, sessionFactory ); - } - - @Override - public PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session) { - return delegate.prepareStatement( insertSql, session ); - } - - @Override - public Object performInsert( - PreparedStatementDetails insertStatementDetails, - JdbcValueBindings valueBindings, - Object entity, - SharedSessionContractImplementor session) { - return delegate.performInsert( insertStatementDetails, valueBindings, entity, session ); - } - - @Override - public String prepareIdentifierGeneratingInsert(String insertSQL) { - return delegate.prepareIdentifierGeneratingInsert( insertSQL ); - } - - @Override - public Object performInsert(String insertSQL, SharedSessionContractImplementor session, Binder binder) { - return delegate.performInsert( insertSQL, session, binder ); - } - - @Override - public GeneratedValues performInsertReturning( - String insertSQL, - SharedSessionContractImplementor session, - Binder binder) { - return delegate.performInsertReturning( insertSQL, session, binder ); - } - - @Override - public TableMutationBuilder createTableMutationBuilder( - Expectation expectation, - SessionFactoryImplementor sessionFactory) { - return delegate.createTableMutationBuilder( expectation, sessionFactory ); - } - - @Override - public GeneratedValues performMutation( - PreparedStatementDetails statementDetails, - JdbcValueBindings valueBindings, - Object entity, - SharedSessionContractImplementor session) { - return delegate.performMutation( statementDetails, valueBindings, entity, session ); - } - - @Override - public EventType getTiming() { - return delegate.getTiming(); - } - - @Override - public boolean supportsArbitraryValues() { - return delegate.supportsArbitraryValues(); - } - - @Override - public boolean supportsRowId() { - return delegate.supportsRowId(); - } - - @Override - public JdbcValuesMappingProducer getGeneratedValuesMappingProducer() { - return delegate.getGeneratedValuesMappingProducer(); - } - - @Override - public CompletionStage reactivePerformMutation( - PreparedStatementDetails singleStatementDetails, - JdbcValueBindings jdbcValueBindings, - Object modelReference, - SharedSessionContractImplementor session) { - return null; - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java index 8854f22d8..70495ccea 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java @@ -63,14 +63,14 @@ public class ReactiveGeneratedValuesHelper { * * @see GeneratedValuesHelper#getGeneratedValuesDelegate(EntityPersister, EventType) */ - public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate( - EntityPersister persister, - EventType timing) { + public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate(EntityPersister persister, EventType timing) { final boolean hasGeneratedProperties = !persister.getGeneratedProperties( timing ).isEmpty(); final boolean hasRowId = timing == EventType.INSERT && persister.getRowIdMapping() != null; final Dialect dialect = persister.getFactory().getJdbcServices().getDialect(); - if ( hasRowId && dialect.supportsInsertReturning() && dialect.supportsInsertReturningRowId() + if ( hasRowId + && dialect.supportsInsertReturning() + && dialect.supportsInsertReturningRowId() && noCustomSql( persister, timing ) ) { // Special case for RowId on INSERT, since GetGeneratedKeysDelegate doesn't support it // make InsertReturningDelegate the preferred method if the dialect supports it 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 4e44eb7d0..7121ea5c4 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.Generator; import org.hibernate.id.IdentifierGenerator; import org.hibernate.reactive.session.ReactiveConnectionSupplier; @@ -24,7 +25,7 @@ * @see IdentifierGenerator */ @Incubating -public interface ReactiveIdentifierGenerator { +public interface ReactiveIdentifierGenerator extends Generator { /** * Returns a generated identifier, via a {@link CompletionStage}. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java deleted file mode 100644 index 78eed431d..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java +++ /dev/null @@ -1,27 +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.id.factory.spi; - -import java.util.Map; - -import org.hibernate.boot.registry.StandardServiceInitiator; -import org.hibernate.id.factory.IdentifierGeneratorFactory; -import org.hibernate.reactive.id.impl.ReactiveIdentifierGeneratorFactory; -import org.hibernate.service.spi.ServiceRegistryImplementor; - -public class ReactiveIdentifierGeneratorFactoryInitiator implements StandardServiceInitiator { - public static final ReactiveIdentifierGeneratorFactoryInitiator INSTANCE = new ReactiveIdentifierGeneratorFactoryInitiator(); - - @Override - public Class getServiceInitiated() { - return IdentifierGeneratorFactory.class; - } - - @Override - public IdentifierGeneratorFactory initiateService(Map configurationValues, ServiceRegistryImplementor registry) { - return new ReactiveIdentifierGeneratorFactory( registry ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java index 889f3d69a..dd9702c69 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java @@ -5,8 +5,15 @@ */ package org.hibernate.reactive.id.impl; +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.id.enhanced.TableStructure; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; @@ -25,6 +32,10 @@ */ public class EmulatedSequenceReactiveIdentifierGenerator extends TableReactiveIdentifierGenerator { + public EmulatedSequenceReactiveIdentifierGenerator(TableStructure structure, RuntimeModelCreationContext runtimeModelCreationContext) { + super( structure, runtimeModelCreationContext ); + } + @Override public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { super.configure( type, params, serviceRegistry ); @@ -86,20 +97,44 @@ protected Object[] selectParameters() { } @Override - protected String buildSelectQuery() { + protected String buildSelectQuery(Dialect dialect) { return "select tbl." + valueColumnName + " from " + renderedTableName + " tbl"; } @Override - protected String buildUpdateQuery() { + protected String buildUpdateQuery(Dialect dialect) { + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=$1" + + " where " + valueColumnName + "=$2"; + + } + if ( dialect instanceof SQLServerDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=@P1" + + " where " + valueColumnName + "=@P2"; + + } + if ( dialect instanceof OracleDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=:1" + + " where " + valueColumnName + "=:2"; + + } return "update " + renderedTableName + " set " + valueColumnName + "=?" + " where " + valueColumnName + "=?"; } @Override - protected String buildInsertQuery() { - return "insert into " + renderedTableName + " (" + valueColumnName + ") " - + " values (?)"; + protected String buildInsertQuery(Dialect dialect) { + final String sql = "insert into " + renderedTableName + " (" + valueColumnName + ") values "; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + "($1)"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + "(@P1)"; + } + if ( dialect instanceof OracleDialect ) { + return sql + "(:1)"; + } + return sql + "(?)"; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java deleted file mode 100644 index 1e3596294..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java +++ /dev/null @@ -1,143 +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.id.impl; - -import java.lang.invoke.MethodHandles; -import java.util.Properties; - -import org.hibernate.MappingException; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; -import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; -import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; -import org.hibernate.generator.BeforeExecutionGenerator; -import org.hibernate.generator.Generator; -import org.hibernate.generator.GeneratorCreationContext; -import org.hibernate.generator.OnExecutionGenerator; -import org.hibernate.id.Configurable; -import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.PersistentIdentifierGenerator; -import org.hibernate.id.SelectGenerator; -import org.hibernate.id.enhanced.DatabaseStructure; -import org.hibernate.id.enhanced.SequenceStructure; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.id.enhanced.TableGenerator; -import org.hibernate.id.enhanced.TableStructure; -import org.hibernate.id.factory.internal.StandardIdentifierGeneratorFactory; -import org.hibernate.reactive.id.ReactiveIdentifierGenerator; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.reactive.provider.Settings; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.Type; - -public class ReactiveIdentifierGeneratorFactory extends StandardIdentifierGeneratorFactory { - - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - private final ServiceRegistry serviceRegistry; - - public ReactiveIdentifierGeneratorFactory(ServiceRegistry serviceRegistry) { - super( serviceRegistry ); - this.serviceRegistry = serviceRegistry; - } - - @Override - public Generator createIdentifierGenerator(String strategy, Type type, GeneratorCreationContext creationContext, Properties config) { - Object generator; - try { - generator = super.createIdentifierGenerator( strategy, type, creationContext, config ); - } - catch ( MappingException ignored ) { - try { - final Class clazz = generatorClassForName( strategy ); - generator = clazz.getConstructor().newInstance(); - if ( generator instanceof Configurable ) { - ( (Configurable) generator ).configure( type, config, serviceRegistry ); - } - } - catch ( Exception e ) { - final String entityName = config.getProperty( IdentifierGenerator.ENTITY_NAME ); - throw new MappingException( String.format( "Could not instantiate id generator [entity-name=%s]", entityName ), e ); - } - } - - //FIXME: Not sure why we need all these instanceof - if ( generator instanceof BeforeExecutionGenerator ) { - return augmentWithReactiveGenerator( (BeforeExecutionGenerator) generator, type, config ); - } - - if ( generator instanceof OnExecutionGenerator ) { - return augmentWithReactiveGenerator( (OnExecutionGenerator) generator, type, config ); - } - - if ( generator instanceof ReactiveIdentifierGenerator ) { - return new ReactiveGeneratorWrapper( (ReactiveIdentifierGenerator) generator ); - } - - final String entityName = config.getProperty( IdentifierGenerator.ENTITY_NAME ); - throw new MappingException( String.format( "Not an id generator [entity-name=%s]", entityName ) ); - } - - protected Class generatorClassForName(String strategy) { - try { - return serviceRegistry.getService( ClassLoaderService.class ).classForName( strategy ); - } - catch ( ClassLoadingException e ) { - throw new MappingException( String.format( "Could not interpret id generator strategy [%s]", strategy ) ); - } - } - - public Generator augmentWithReactiveGenerator(Generator generator, Type type, Properties params) { - return augmentWithReactiveGenerator( serviceRegistry, generator, type, params ); - } - - public static Generator augmentWithReactiveGenerator(ServiceRegistry serviceRegistry, Generator generator, Type type, Properties params) { - final ReactiveIdentifierGenerator reactiveGenerator; - if ( generator instanceof SequenceStyleGenerator ) { - final DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); - if ( structure instanceof TableStructure ) { - reactiveGenerator = new EmulatedSequenceReactiveIdentifierGenerator(); - } - else if ( structure instanceof SequenceStructure ) { - reactiveGenerator = new ReactiveSequenceIdentifierGenerator(); - } - else { - throw LOG.unknownStructureType(); - } - } - else if ( generator instanceof TableGenerator ) { - reactiveGenerator = new TableReactiveIdentifierGenerator(); - } - else if ( generator instanceof SelectGenerator ) { - throw LOG.selectGeneratorIsNotSupportedInHibernateReactive(); - } - else { - //nothing to do - return generator; - } - - //this is not the way ORM does this: instead it passes a - //SqlStringGenerationContext to IdentifierGenerator.initialize() - final ConfigurationService cs = serviceRegistry.getService( ConfigurationService.class ); - if ( !params.containsKey( PersistentIdentifierGenerator.SCHEMA ) ) { - final String schema = cs.getSetting( Settings.DEFAULT_SCHEMA, StandardConverters.STRING ); - if ( schema != null ) { - params.put( PersistentIdentifierGenerator.SCHEMA, schema ); - } - } - if ( !params.containsKey( PersistentIdentifierGenerator.CATALOG ) ) { - final String catalog = cs.getSetting( Settings.DEFAULT_CATALOG, StandardConverters.STRING ); - if ( catalog != null ) { - params.put( PersistentIdentifierGenerator.CATALOG, catalog ); - } - } - - ( (Configurable) reactiveGenerator ).configure( type, params, serviceRegistry ); - return new ReactiveGeneratorWrapper( reactiveGenerator, (IdentifierGenerator) generator ); - } - -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java index 6331d178b..325a5b662 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java @@ -19,11 +19,13 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.enhanced.DatabaseStructure; import org.hibernate.id.enhanced.ImplicitDatabaseObjectNamingStrategy; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.StandardNamingStrategy; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; @@ -51,6 +53,16 @@ public class ReactiveSequenceIdentifierGenerator extends BlockingIdentifierGener private String sql; private int increment; + + public ReactiveSequenceIdentifierGenerator() { + } + + public ReactiveSequenceIdentifierGenerator(DatabaseStructure structure, RuntimeModelCreationContext creationContext) { + qualifiedName = structure.getPhysicalName(); + increment = structure.getIncrementSize(); + dialect = creationContext.getDialect(); + } + @Override protected int getBlockSize() { return increment; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java index a49a04289..79fb63aa6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java @@ -14,17 +14,22 @@ import org.hibernate.LockOptions; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.id.enhanced.TableStructure; import org.hibernate.internal.util.StringHelper; import org.hibernate.jdbc.TooManyRowsAffectedException; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.service.ServiceRegistry; @@ -65,6 +70,60 @@ public class TableReactiveIdentifierGenerator extends BlockingIdentifierGenerato private String insertQuery; private String updateQuery; + public TableReactiveIdentifierGenerator(TableGenerator generator, RuntimeModelCreationContext runtimeModelCreationContext) { + ServiceRegistry serviceRegistry = runtimeModelCreationContext.getServiceRegistry(); + segmentColumnName = generator.getSegmentColumnName(); + valueColumnName = generator.getValueColumnName(); + segmentValue = generator.getSegmentValue(); + initialValue = generator.getInitialValue(); + increment = generator.getIncrementSize(); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = generator.getTableName(); + + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + Dialect dialect = jdbcEnvironment.getDialect(); + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + + public TableReactiveIdentifierGenerator( + TableStructure structure, + RuntimeModelCreationContext runtimeModelCreationContext) { + ServiceRegistry serviceRegistry = runtimeModelCreationContext.getServiceRegistry(); + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + Dialect dialect = jdbcEnvironment.getDialect(); + + valueColumnName = structure.getLogicalValueColumnNameIdentifier().render( dialect ); + initialValue = structure.getInitialValue(); + increment = structure.getIncrementSize(); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = structure.getPhysicalName().render(); + segmentColumnName = null; + segmentValue = null; + + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + + @Override + public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); + valueColumnName = determineValueColumnNameForTable( params, jdbcEnvironment ); + segmentValue = determineSegmentValue( params ); + initialValue = determineInitialValue( params ); + increment = determineIncrement( params ); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = determineTableName( type, params, serviceRegistry ); + + Dialect dialect = jdbcEnvironment.getDialect(); + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + @Override protected int getBlockSize() { return increment; @@ -130,24 +189,6 @@ protected CompletionStage nextHiValue(ReactiveConnectionSupplier session) } ); } - @Override - public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { - JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); - segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); - valueColumnName = determineValueColumnNameForTable( params, jdbcEnvironment ); - segmentValue = determineSegmentValue( params ); - initialValue = determineInitialValue( params ); - increment = determineIncrement( params ); - storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); - renderedTableName = determineTableName( type, params, serviceRegistry ); - - Dialect dialect = jdbcEnvironment.getDialect(); - Parameters parameters = Parameters.instance( dialect ); - selectQuery = parameters.process( applyLocksToSelect( dialect, "tbl", buildSelectQuery() ) ); - updateQuery = parameters.process( buildUpdateQuery() ); - insertQuery = parameters.process( buildInsertQuery() ); - } - @Override public void registerExportables(Database database) { } @@ -224,19 +265,48 @@ protected Object[] selectParameters() { return new Object[]{ segmentValue }; } - protected String buildSelectQuery() { - return "select tbl." + valueColumnName + " from " + renderedTableName + " tbl" - + " where tbl." + segmentColumnName + "=?"; + protected String buildSelectQuery(Dialect dialect) { + final String sql = "select tbl." + valueColumnName + " from " + renderedTableName + " tbl where tbl." + segmentColumnName; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + "=$1"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + "=@P1"; + } + if ( dialect instanceof OracleDialect ) { + return sql + "=:1"; + } + return sql + "=?"; } - protected String buildUpdateQuery() { + protected String buildUpdateQuery(Dialect dialect) { + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=$1" + + " where " + valueColumnName + "=$2 and " + segmentColumnName + "=$3"; + } + if ( dialect instanceof SQLServerDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=@P1" + + " where " + valueColumnName + "=@P2 and " + segmentColumnName + "=@P3"; + } + if ( dialect instanceof OracleDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=:1" + + " where " + valueColumnName + "=:2 and " + segmentColumnName + "=:3"; + } return "update " + renderedTableName + " set " + valueColumnName + "=?" + " where " + valueColumnName + "=? and " + segmentColumnName + "=?"; } - protected String buildInsertQuery() { - return "insert into " + renderedTableName + " (" + segmentColumnName + ", " + valueColumnName + ") " - + " values (?, ?)"; + protected String buildInsertQuery(Dialect dialect) { + final String sql = "insert into " + renderedTableName + " (" + segmentColumnName + ", " + valueColumnName + ") "; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + " values ($1, $2)"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + " values (@P1, @P2)"; + } + if ( dialect instanceof OracleDialect ) { + return sql + " values (:1, :2)"; + } + return sql + " values (?, ?)"; } - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java index 42dfddef0..aed2555fe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java @@ -12,7 +12,6 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SQLServerDialect; @@ -21,8 +20,8 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.generator.values.GeneratedValues; -import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.Binder; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.adaptor.impl.PrepareStatementDetailsAdaptor; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; import org.hibernate.reactive.logging.impl.Log; @@ -35,7 +34,7 @@ public interface ReactiveAbstractReturningDelegate extends ReactiveInsertGenerat @Override PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session); - PostInsertIdentityPersister getPersister(); + EntityPersister getPersister(); @Override default CompletionStage reactivePerformInsertReturning(String sql, SharedSessionContractImplementor session, Binder binder) { @@ -89,15 +88,14 @@ && getPersister().getFactory().getJdbcServices().getDialect() instanceof Cockroa private static String createInsert(String insertSql, String identifierColumnName, Dialect dialect) { String sql = insertSql; final String sqlEnd = " returning " + identifierColumnName; - Dialect realDialect = DialectDelegateWrapper.extractRealDialect( dialect ); - if ( realDialect instanceof MySQLDialect ) { + if ( dialect instanceof MySQLDialect ) { // For some reason ORM generates a query with an invalid syntax int index = sql.lastIndexOf( sqlEnd ); return index > -1 ? sql.substring( 0, index ) : sql; } - if ( realDialect instanceof SQLServerDialect ) { + if ( dialect instanceof SQLServerDialect ) { int index = sql.lastIndexOf( sqlEnd ); // FIXME: this is a hack for HHH-16365 if ( index > -1 ) { @@ -113,12 +111,12 @@ private static String createInsert(String insertSql, String identifierColumnName } return sql; } - if ( realDialect instanceof DB2Dialect ) { + if ( dialect instanceof DB2Dialect ) { // ORM query: select id from new table ( insert into IntegerTypeEntity values ( )) // Correct : select id from new table ( insert into LongTypeEntity (id) values (default)) return sql.replace( " values ( ))", " (" + identifierColumnName + ") values (default))" ); } - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { final String valuesStr = " values ( )"; int index = sql.lastIndexOf( sqlEnd ); // remove "returning id" since it's added via 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 79460c546..2b4063173 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 @@ -21,7 +21,6 @@ import org.hibernate.generator.values.GeneratedValueBasicResultBuilder; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.generator.values.internal.TableUpdateReturningBuilder; -import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.AbstractReturningDelegate; import org.hibernate.id.insert.InsertReturningDelegate; import org.hibernate.id.insert.TableInsertReturningBuilder; @@ -36,6 +35,7 @@ import org.hibernate.sql.model.ast.builder.TableMutationBuilder; import static java.sql.Statement.NO_GENERATED_KEYS; +import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart; import static org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper.getGeneratedValues; @@ -46,27 +46,26 @@ public class ReactiveInsertReturningDelegate extends AbstractReturningDelegate i private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final PostInsertIdentityPersister persister; + private final EntityPersister persister; private final MutatingTableReference tableReference; private final List generatedColumns; public ReactiveInsertReturningDelegate(EntityPersister persister, EventType timing) { - this( (PostInsertIdentityPersister) persister, timing, false ); - + this( persister, timing, false ); } - public ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, Dialect dialect) { + public ReactiveInsertReturningDelegate(EntityPersister persister, Dialect dialect) { // With JDBC it's possible to enabled GetGeneratedKeys for identity generation. // Vert.x doesn't have this option, so we always use the same strategy for all database. // But MySQL requires setting supportsArbitraryValues to false or it's not going to work. - this( persister, EventType.INSERT, supportsArbitraryValues( dialect ) ); + this( persister, INSERT, supportsArbitraryValues( dialect ) ); } private static boolean supportsArbitraryValues( Dialect dialect) { return !( dialect instanceof MySQLDialect ); } - private ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, EventType timing, boolean supportsArbitraryValues) { + private ReactiveInsertReturningDelegate(EntityPersister persister, EventType timing, boolean supportsArbitraryValues) { super( persister, timing, @@ -88,7 +87,7 @@ private ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, E public TableMutationBuilder createTableMutationBuilder( Expectation expectation, SessionFactoryImplementor sessionFactory) { - if ( getTiming() == EventType.INSERT ) { + if ( getTiming() == INSERT ) { return new TableInsertReturningBuilder( persister, tableReference, generatedColumns, sessionFactory ); } else { @@ -110,7 +109,7 @@ public PreparedStatement prepareStatement(String sql, SharedSessionContractImple } @Override - public PostInsertIdentityPersister getPersister() { + public EntityPersister getPersister() { return persister; } 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 72760a26b..9c6c126cc 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 @@ -258,6 +258,12 @@ public interface Log extends BasicLogger { @Message(id = 80, value = "No results were returned by the query (you can try running it with '.executeUpdate()'): %1$s") HibernateException noResultException(String sql); + @Message(id = 81, value = "The Vert.x SQL client doesn't support the SQL XML data type. If it's the mapping of an array, you can also try setting the property `hibernate.type.preferred_array_jdbc_type`") + HibernateException unsupportedXmlType(); + + @Message(id = 83, value = "Unexpected request of a non reactive connection") + HibernateException unexpectedConnectionRequest(); + // Same method that exists in CoreMessageLogger @LogMessage(level = WARN) @Message(id = 104, value = "firstResult/maxResults specified with collection fetch; applying in memory!" ) @@ -278,6 +284,11 @@ public interface Log extends BasicLogger { @Message(id = 245, value = "Manipulation query [%s] resulted in [%s] split queries" ) void splitQueries(String sourceQuery, int length); + // Same method that exists in CoreMessageLogger + @LogMessage(level = INFO) + @Message(value = "Could not find any META-INF/persistence.xml file in the classpath", id = 318) + void unableToFindPersistenceXmlInClasspath(); + // Same method that exists in CoreMessageLogger @LogMessage(level = INFO) @Message(id = 327, value = "Error performing load command") diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java new file mode 100644 index 000000000..876312b47 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java @@ -0,0 +1,128 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.metamodel.mapping.internal; + +import java.util.Map; + +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.cache.spi.CacheImplementor; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.generator.Generator; +import org.hibernate.mapping.GeneratorSettings; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.metamodel.spi.MappingMetamodelImplementor; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.sqm.function.SqmFunctionRegistry; +import org.hibernate.reactive.tuple.entity.ReactiveEntityMetamodel; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +public class ReactiveRuntimeModelCreationContext implements RuntimeModelCreationContext { + + private final RuntimeModelCreationContext delegate; + + public ReactiveRuntimeModelCreationContext(RuntimeModelCreationContext delegate) { + this.delegate = delegate; + } + + @Override + public EntityMetamodel createEntityMetamodel(PersistentClass persistentClass, EntityPersister persister) { + return new ReactiveEntityMetamodel( persistentClass, persister, delegate ); + } + + @Override + public SessionFactoryImplementor getSessionFactory() { + return delegate.getSessionFactory(); + } + + @Override + public BootstrapContext getBootstrapContext() { + return delegate.getBootstrapContext(); + } + + @Override + public MetadataImplementor getBootModel() { + return delegate.getBootModel(); + } + + @Override + public MappingMetamodelImplementor getDomainModel() { + return delegate.getDomainModel(); + } + + @Override + public TypeConfiguration getTypeConfiguration() { + return delegate.getTypeConfiguration(); + } + + @Override + public JavaTypeRegistry getJavaTypeRegistry() { + return delegate.getJavaTypeRegistry(); + } + + @Override + public MetadataImplementor getMetadata() { + return delegate.getMetadata(); + } + + @Override + public SqmFunctionRegistry getFunctionRegistry() { + return delegate.getFunctionRegistry(); + } + + @Override + public Map getSettings() { + return delegate.getSettings(); + } + + @Override + public Dialect getDialect() { + return delegate.getDialect(); + } + + @Override + public CacheImplementor getCache() { + return delegate.getCache(); + } + + @Override + public SessionFactoryOptions getSessionFactoryOptions() { + return delegate.getSessionFactoryOptions(); + } + + @Override + public JdbcServices getJdbcServices() { + return delegate.getJdbcServices(); + } + + @Override + public SqlStringGenerationContext getSqlStringGenerationContext() { + return delegate.getSqlStringGenerationContext(); + } + + @Override + public ServiceRegistry getServiceRegistry() { + return delegate.getServiceRegistry(); + } + + @Override + public Map getGenerators() { + return delegate.getGenerators(); + } + + @Override + public GeneratorSettings getGeneratorSettings() { + return delegate.getGeneratorSettings(); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 65b0ad906..9bdf215f7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -677,11 +677,27 @@ default Uni find(Class entityClass, Object id, LockModeType lockModeTy * {@code session.persist(newBook).map(v -> session.flush());} * * - * @param entity a transient instance of a persistent class + * @param object a transient instance of a persistent class * * @see jakarta.persistence.EntityManager#persist(Object) */ - Uni persist(Object entity); + Uni persist(Object object); + + /** + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

+ * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) + */ + Uni persist(String entityName, Object object); /** * Persist multiple transient entity instances at once. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 2db4b89b4..f36dc31e0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -236,6 +236,11 @@ public Uni persist(Object entity) { return uni( () -> delegate.reactivePersist( entity ) ); } + @Override + public Uni persist(String entityName, Object entity) { + return uni( () -> delegate.reactivePersist( entityName, entity ) ); + } + @Override public Uni persistAll(Object... entity) { return uni( () -> applyToAll( delegate::reactivePersist, entity ) ); 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 30c383331..a29799b23 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 @@ -5,11 +5,14 @@ */ package org.hibernate.reactive.persister.entity.impl; -import java.lang.invoke.MethodHandles; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletionStage; import org.hibernate.AssertionFailure; @@ -20,22 +23,36 @@ import org.hibernate.MappingException; import org.hibernate.StaleObjectStateException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; -import org.hibernate.bytecode.enhance.spi.interceptor.*; +import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ManagedTypeHelper; -import org.hibernate.engine.spi.*; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LoadEvent; import org.hibernate.generator.OnExecutionGenerator; import org.hibernate.generator.values.GeneratedValuesMutationDelegate; import org.hibernate.internal.util.collections.ArrayHelper; -import org.hibernate.jdbc.Expectation; import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.spi.NaturalIdLoader; import org.hibernate.mapping.PersistentClass; -import org.hibernate.metamodel.mapping.*; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.AttributeMappingsList; +import org.hibernate.metamodel.mapping.EntityVersionMapping; +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.NaturalIdMapping; +import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; @@ -43,11 +60,9 @@ import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleIdEntityLoader; import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.metamodel.mapping.internal.ReactiveCompoundNaturalIdMapping; import org.hibernate.reactive.metamodel.mapping.internal.ReactiveSimpleNaturalIdMapping; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; import org.hibernate.sql.SimpleSelect; @@ -58,11 +73,13 @@ import jakarta.persistence.metamodel.Attribute; +import static java.lang.invoke.MethodHandles.lookup; import static java.util.Collections.emptyMap; import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.generator.EventType.UPDATE; import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize; 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; @@ -78,7 +95,7 @@ * three flavors of {@link ReactiveEntityPersister}. Therefore, this * interface is defined as a mixin. This design avoid duplicating * the code in this class in the three different subclasses. - * + *

* Concrete implementations of this interface _must_ also extend * {@code AbstractEntityPersister} or one of its concrete * subclasses. @@ -90,12 +107,6 @@ */ public interface ReactiveAbstractEntityPersister extends ReactiveEntityPersister { - Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - default Parameters parameters() { - return Parameters.instance( getFactory().getJdbcServices().getDialect() ); - } - /** * A self-reference of type {@code AbstractEntityPersister}. * @@ -121,13 +132,6 @@ default ReactiveConnection getReactiveConnection(SharedSessionContractImplemento return ReactiveQueryExecutorLookup.extract( session ).getReactiveConnection(); } - boolean check( - int rows, - Object id, - int tableNumber, - Expectation expectation, - PreparedStatement statement, String sql) throws HibernateException; - default String generateSelectLockString(LockOptions lockOptions) { final SessionFactoryImplementor factory = getFactory(); final SimpleSelect select = new SimpleSelect( factory ) @@ -141,20 +145,20 @@ default String generateSelectLockString(LockOptions lockOptions) { if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) { select.setComment( lockOptions.getLockMode() + " lock " + getEntityName() ); } - return parameters().process( select.toStatementString() ); + return select.toStatementString(); } default String generateUpdateLockString(LockOptions lockOptions) { final SessionFactoryImplementor factory = getFactory(); final Update update = new Update( factory ); - update.setTableName( getEntityMappingType().getMappedTableDetails().getTableName() ); + update.setTableName( getEntityMappingType().getMappedTableDetails().getTableName() ); update.addAssignment( getVersionMapping().getVersionAttribute().getAttributeName() ); update.addRestriction( getIdentifierPropertyName() ); update.addRestriction( getVersionMapping().getVersionAttribute().getAttributeName() ); if ( factory.getSessionFactoryOptions().isCommentsEnabled() ) { update.setComment( lockOptions.getLockMode() + " lock " + getEntityName() ); } - return parameters().process( update.toStatementString() ); + return update.toStatementString(); } @Override @@ -172,7 +176,7 @@ default CompletionStage reactiveLock( String sql; boolean writeLock; - switch (lockMode) { + switch ( lockMode ) { // 0) noop case NONE: return voidFuture(); @@ -201,14 +205,14 @@ default CompletionStage reactiveLock( // locks obtained in the before completion phase case OPTIMISTIC: case OPTIMISTIC_FORCE_INCREMENT: - throw new AssertionFailure("optimistic lock mode is not supported here"); + throw new AssertionFailure( "optimistic lock mode is not supported here" ); // 7) READ and WRITE are obtained implicitly by // other operations case READ: case WRITE: - throw new AssertionFailure("implicit lock mode is not supported here"); + throw new AssertionFailure( "implicit lock mode is not supported here" ); default: - throw new AssertionFailure("illegal lock mode"); + throw new AssertionFailure( "illegal lock mode" ); } Object[] arguments = PreparedStatementAdaptor.bind( statement -> { @@ -259,6 +263,7 @@ default Object nextVersionForLock(LockMode lockMode, Object id, Object currentVe final Object nextVersion = getVersionJavaType() .next( currentVersion, versionMapping.getLength(), versionMapping.getPrecision(), versionMapping.getScale(), session ); + Log LOG = make( Log.class, lookup() ); if ( LOG.isTraceEnabled() ) { LOG.trace( "Forcing version increment [" + infoString( this, id, getFactory() ) + "; " + versionType.toLoggableString( currentVersion, getFactory() ) + " -> " @@ -289,6 +294,7 @@ default ReactiveSingleIdEntityLoader getReactiveSingleIdEntityLoader() { */ @Override default CompletionStage reactiveGetCurrentVersion(Object id, SharedSessionContractImplementor session) { + Log LOG = make( Log.class, lookup() ); if ( LOG.isTraceEnabled() ) { LOG.tracev( "Getting version: {0}", infoString( this, id, getFactory() ) ); } @@ -339,7 +345,7 @@ else if ( result instanceof PersistentCollection ) { // collection. That's inconsistent with what happens // for other lazy fields, so let's set the field here final String[] propertyNames = getPropertyNames(); - for ( int index=0; index collection = (PersistentCollection) result; return collection.wasInitialized() ? completedFuture( (T) collection ) - : ((ReactiveSession) session).reactiveInitializeCollection( collection, false ) + : ( (ReactiveSession) session ).reactiveInitializeCollection( collection, false ) .thenApply( v -> (T) result ); } else { @@ -379,7 +385,7 @@ default CompletionStage reactiveInitializeLazyPropertiesFromDatastore( throw new AssertionFailure( "Expecting bytecode interceptor to be non-null" ); } - LOG.tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName ); + make( Log.class, lookup() ).tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName ); final String fetchGroup = getEntityPersister().getBytecodeEnhancementMetadata() .getLazyAttributesMetadata() @@ -418,7 +424,7 @@ default CompletionStage initLazyProperty( Object[] values) { // Load all the lazy properties that are in the same fetch group CompletionStage resultStage = nullFuture(); int i = 0; - for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor: fetchGroupAttributeDescriptors ) { + for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) { if ( initializedLazyAttributeNames.contains( fetchGroupAttributeDescriptor.getName() ) ) { // Already initialized if ( fetchGroupAttributeDescriptor.getName().equals( fieldName ) ) { @@ -427,7 +433,7 @@ default CompletionStage initLazyProperty( continue; } - final Object selectedValue = values[i++]; + final Object selectedValue = values[i++]; if ( selectedValue instanceof CompletionStage ) { // This happens with a lazy one-to-one (bytecode enhancement enabled) CompletionStage selectedValueStage = (CompletionStage) selectedValue; @@ -459,7 +465,7 @@ default CompletionStage initLazyProperty( } return resultStage.thenApply( result -> { - LOG.trace( "Done initializing lazy properties" ); + make( Log.class, lookup() ).trace( "Done initializing lazy properties" ); return result; } ); } @@ -471,7 +477,7 @@ default CompletionStage reactiveInitializeEnhancedEntityUsedAsProxy( final BytecodeEnhancementMetadata enhancementMetadata = getEntityPersister().getBytecodeEnhancementMetadata(); final BytecodeLazyAttributeInterceptor currentInterceptor = enhancementMetadata.extractLazyInterceptor( entity ); - if ( currentInterceptor instanceof EnhancementAsProxyLazinessInterceptor) { + if ( currentInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) { final EnhancementAsProxyLazinessInterceptor proxyInterceptor = (EnhancementAsProxyLazinessInterceptor) currentInterceptor; @@ -539,12 +545,8 @@ private CompletionStage loadFromDatabaseOrCache( Object initializeLazyProperty(String fieldName, Object entity, SharedSessionContractImplementor session); - String[][] getLazyPropertyColumnAliases(); - ReactiveSingleIdArrayLoadPlan reactiveGetSQLLazySelectLoadPlan(String fetchGroup); - boolean isBatchable(); - /** * @see AbstractEntityPersister#generateNaturalIdMapping(MappingModelCreationProcess, PersistentClass) */ @@ -558,7 +560,9 @@ default NaturalIdMapping generateNaturalIdMapping( assert naturalIdAttributeIndexes.length > 0; if ( naturalIdAttributeIndexes.length == 1 ) { - final String propertyName = getEntityPersister().getAttributeMappings().get(naturalIdAttributeIndexes[0]).getAttributeName(); + final String propertyName = getEntityPersister().getAttributeMappings() + .get( naturalIdAttributeIndexes[0] ) + .getAttributeName(); final AttributeMapping attributeMapping = findAttributeMapping( propertyName ); final SingularAttributeMapping singularAttributeMapping = (SingularAttributeMapping) attributeMapping; return new ReactiveSimpleNaturalIdMapping( singularAttributeMapping, this, creationProcess ); @@ -597,14 +601,14 @@ default NaturalIdLoader getNaturalIdLoader() { /** * @see AbstractEntityPersister#getLazyLoadPlanByFetchGroup() */ - default Map getLazyLoadPlanByFetchGroup(String[] subclassPropertyNameClosure ) { + default Map getLazyLoadPlanByFetchGroup(String[] subclassPropertyNameClosure) { final BytecodeEnhancementMetadata metadata = delegate().getEntityPersister().getBytecodeEnhancementMetadata(); return metadata.isEnhancedForLazyLoading() && metadata.getLazyAttributesMetadata().hasLazyAttributes() ? createLazyLoadPlanByFetchGroup( metadata, subclassPropertyNameClosure ) : emptyMap(); } - default Map createLazyLoadPlanByFetchGroup(BytecodeEnhancementMetadata metadata, String[] subclassPropertyNameClosure ) { + default Map createLazyLoadPlanByFetchGroup(BytecodeEnhancementMetadata metadata, String[] subclassPropertyNameClosure) { final Map result = new HashMap<>(); final LazyAttributesMetadata attributesMetadata = metadata.getLazyAttributesMetadata(); for ( String groupName : attributesMetadata.getFetchGroupNames() ) { @@ -617,7 +621,7 @@ default Map createLazyLoadPlanByFetchGrou return result; } - default ReactiveSingleIdArrayLoadPlan createLazyLoadPlan(List fetchGroupAttributeDescriptors, String[] subclassPropertyNameClosure ) { + default ReactiveSingleIdArrayLoadPlan createLazyLoadPlan(List fetchGroupAttributeDescriptors, String[] subclassPropertyNameClosure) { final List partsToSelect = new ArrayList<>( fetchGroupAttributeDescriptors.size() ); for ( LazyAttributeDescriptor lazyAttributeDescriptor : fetchGroupAttributeDescriptors ) { // all this only really needs to consider properties diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveIdentityGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveIdentityGenerator.java index 20d32814d..d37f07eec 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveIdentityGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveIdentityGenerator.java @@ -9,8 +9,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport; import org.hibernate.id.IdentityGenerator; -import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.id.insert.ReactiveInsertReturningDelegate; /** @@ -22,7 +22,7 @@ public class ReactiveIdentityGenerator extends IdentityGenerator { * @see CockroachDBIdentityColumnSupport#supportsIdentityColumns() for some limitations related to CockroachDB */ @Override - public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) { + public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(EntityPersister persister) { Dialect dialect = persister.getFactory().getJdbcServices().getDialect(); // Hibernate ORM allows the selection of different strategies based on the property `hibernate.jdbc.use_get_generated_keys`. // But that's a specific JDBC property and with Vert.x we only have one viable option for each supported database. 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 8eca0a6f4..0de5391f0 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 @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.persister.entity.impl; -import java.sql.PreparedStatement; import java.util.List; import java.util.concurrent.CompletionStage; @@ -21,7 +20,6 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; -import org.hibernate.jdbc.Expectation; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.ast.spi.SingleIdEntityLoader; @@ -44,6 +42,8 @@ import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.metamodel.mapping.internal.ReactiveRuntimeModelCreationContext; import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator; import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard; import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator; @@ -54,6 +54,9 @@ import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.EntityType; +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + /** * An {@link ReactiveEntityPersister} backed by {@link JoinedSubclassEntityPersister} * and {@link ReactiveAbstractEntityPersister}. @@ -61,6 +64,8 @@ public class ReactiveJoinedSubclassEntityPersister extends JoinedSubclassEntityPersister implements ReactiveAbstractEntityPersister { + private static final Log LOG = make( Log.class, lookup() ); + private final ReactiveAbstractPersisterDelegate reactiveDelegate; public ReactiveJoinedSubclassEntityPersister( @@ -68,8 +73,8 @@ public ReactiveJoinedSubclassEntityPersister( final EntityDataAccess cacheAccessStrategy, final NaturalIdDataAccess naturalIdRegionAccessStrategy, final RuntimeModelCreationContext creationContext) throws HibernateException { - super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext ); - reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, creationContext ); + super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, new ReactiveRuntimeModelCreationContext( creationContext ) ); + reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } @Override @@ -134,12 +139,6 @@ protected AttributeMapping buildPluralAttributeMapping( ); } - @Override - public String generateSelectVersionString() { - String sql = super.generateSelectVersionString(); - return parameters().process( sql ); - } - @Override protected InsertCoordinator buildInsertCoordinator() { return ReactiveCoordinatorFactory.buildInsertCoordinator( this, getFactory() ); @@ -281,21 +280,11 @@ public void merge( throw LOG.nonReactiveMethodCall( "mergeReactive" ); } - @Override - public boolean check(int rows, Object id, int tableNumber, Expectation expectation, PreparedStatement statement, String sql) throws HibernateException { - return super.check(rows, id, tableNumber, expectation, statement, sql); - } - @Override public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue) { return super.initializeLazyProperty( fieldName, entity, entry, lazyIndex, selectedValue ); } - @Override - public String[][] getLazyPropertyColumnAliases() { - return super.getLazyPropertyColumnAliases(); - } - /** * Process properties generated with an insert * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveMergeCoordinatorStandardScopeFactory.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveMergeCoordinatorStandardScopeFactory.java index 73441f382..062edd9d6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveMergeCoordinatorStandardScopeFactory.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveMergeCoordinatorStandardScopeFactory.java @@ -12,15 +12,13 @@ import org.hibernate.reactive.persister.entity.mutation.ReactiveMergeCoordinator; import org.hibernate.reactive.persister.entity.mutation.ReactiveScopedUpdateCoordinator; import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator; +import org.hibernate.reactive.sql.model.ReactiveDeleteOrUpsertOperation; import org.hibernate.reactive.sql.model.ReactiveOptionalTableUpdateOperation; -import org.hibernate.sql.model.ModelMutationLogging; import org.hibernate.sql.model.MutationOperation; -import org.hibernate.sql.model.MutationOperationGroup; import org.hibernate.sql.model.ValuesAnalysis; -import org.hibernate.sql.model.ast.MutationGroup; import org.hibernate.sql.model.ast.TableMutation; -import org.hibernate.sql.model.internal.MutationOperationGroupFactory; import org.hibernate.sql.model.internal.OptionalTableUpdate; +import org.hibernate.sql.model.jdbc.DeleteOrUpsertOperation; import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation; public class ReactiveMergeCoordinatorStandardScopeFactory extends MergeCoordinator @@ -44,61 +42,15 @@ public ReactiveScopedUpdateCoordinator makeScopedCoordinator() { ); } - // We override the whole method but we just need to plug in our custom createOperation(...) method @Override - protected MutationOperationGroup createOperationGroup(ValuesAnalysis valuesAnalysis, MutationGroup mutationGroup) { - final int numberOfTableMutations = mutationGroup.getNumberOfTableMutations(); - switch ( numberOfTableMutations ) { - case 0: - return MutationOperationGroupFactory.noOperations( mutationGroup ); - case 1: { - MutationOperation operation = createOperation( valuesAnalysis, mutationGroup.getSingleTableMutation() ); - return operation == null - ? MutationOperationGroupFactory.noOperations( mutationGroup ) - : MutationOperationGroupFactory.singleOperation( mutationGroup, operation ); - } - default: { - MutationOperation[] operations = new MutationOperation[numberOfTableMutations]; - int outputIndex = 0; - int skipped = 0; - for ( int i = 0; i < mutationGroup.getNumberOfTableMutations(); i++ ) { - final TableMutation tableMutation = mutationGroup.getTableMutation( i ); - MutationOperation operation = createOperation( valuesAnalysis, tableMutation ); - if ( operation != null ) { - operations[outputIndex++] = operation; - } - else { - skipped++; - ModelMutationLogging.MODEL_MUTATION_LOGGER.debugf( - "Skipping table update - %s", - tableMutation.getTableName() - ); - } - } - if ( skipped != 0 ) { - final MutationOperation[] trimmed = new MutationOperation[outputIndex]; - System.arraycopy( operations, 0, trimmed, 0, outputIndex ); - operations = trimmed; - } - return MutationOperationGroupFactory.manyOperations( - mutationGroup.getMutationType(), - entityPersister, - operations - ); - } - } - } - - // FIXME: We could add this method in ORM and override only this code protected MutationOperation createOperation(ValuesAnalysis valuesAnalysis, TableMutation singleTableMutation) { MutationOperation operation = singleTableMutation.createMutationOperation( valuesAnalysis, factory() ); if ( operation instanceof OptionalTableUpdateOperation ) { // We need to plug in our own reactive operation - return new ReactiveOptionalTableUpdateOperation( - operation.getMutationTarget(), - (OptionalTableUpdate) singleTableMutation, - factory() - ); + return new ReactiveOptionalTableUpdateOperation( operation.getMutationTarget(), (OptionalTableUpdate) singleTableMutation, factory() ); + } + if ( operation instanceof DeleteOrUpsertOperation ) { + return new ReactiveDeleteOrUpsertOperation( (DeleteOrUpsertOperation) operation ); } return operation; } 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 e16523523..bc52a7b6e 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 @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.persister.entity.impl; -import java.sql.PreparedStatement; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; @@ -23,8 +22,6 @@ import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.generator.values.GeneratedValuesMutationDelegate; -import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; -import org.hibernate.jdbc.Expectation; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.ast.spi.SingleIdEntityLoader; @@ -47,9 +44,10 @@ import org.hibernate.persister.entity.mutation.UpdateCoordinator; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.reactive.generator.values.GeneratedValuesMutationDelegateAdaptor; -import org.hibernate.reactive.generator.values.ReactiveInsertGeneratedIdentifierDelegate; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.metamodel.mapping.internal.ReactiveRuntimeModelCreationContext; import org.hibernate.reactive.persister.entity.mutation.ReactiveAbstractDeleteCoordinator; import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard; import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator; @@ -60,6 +58,9 @@ import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.EntityType; +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + /** * A {@link ReactiveEntityPersister} backed by {@link SingleTableEntityPersister} @@ -67,6 +68,8 @@ */ public class ReactiveSingleTableEntityPersister extends SingleTableEntityPersister implements ReactiveAbstractEntityPersister { + private static final Log LOG = make( Log.class, lookup() ); + private ReactiveAbstractPersisterDelegate reactiveDelegate; public ReactiveSingleTableEntityPersister( @@ -74,8 +77,8 @@ public ReactiveSingleTableEntityPersister( final EntityDataAccess cacheAccessStrategy, final NaturalIdDataAccess naturalIdRegionAccessStrategy, final RuntimeModelCreationContext creationContext) throws HibernateException { - super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext ); - reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, creationContext ); + super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, new ReactiveRuntimeModelCreationContext( creationContext ) ); + reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } @Override @@ -98,12 +101,6 @@ protected MultiIdEntityLoader buildMultiIdLoader() { return reactiveDelegate.buildMultiIdEntityLoader(); } - @Override - public String generateSelectVersionString() { - String sql = super.generateSelectVersionString(); - return parameters().process( sql ); - } - @Override protected UpdateCoordinator buildUpdateCoordinator() { return ReactiveCoordinatorFactory.buildUpdateCoordinator( this, getFactory() ); @@ -147,15 +144,6 @@ public GeneratedValuesMutationDelegate getUpdateDelegate() { return new GeneratedValuesMutationDelegateAdaptor( updateDelegate ); } - @Override - public InsertGeneratedIdentifierDelegate getIdentityInsertDelegate() { - final GeneratedValuesMutationDelegate insertDelegate = super.getInsertDelegate(); - if ( insertDelegate instanceof InsertGeneratedIdentifierDelegate ) { - return new ReactiveInsertGeneratedIdentifierDelegate( (InsertGeneratedIdentifierDelegate) insertDelegate ); - } - return null; - } - @Override public DomainResult createDomainResult( NavigablePath navigablePath, @@ -217,11 +205,6 @@ protected AttributeMapping buildPluralAttributeMapping( ); } - @Override - public boolean check(int rows, Object id, int tableNumber, Expectation expectation, PreparedStatement statement, String sql) throws HibernateException { - return super.check( rows, id, tableNumber, expectation,statement, sql ); - } - @Override public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue) { return super.initializeLazyProperty(fieldName, entity, entry, lazyIndex, selectedValue); @@ -232,11 +215,6 @@ public Object initializeLazyPropertiesFromDatastore(final Object entity, final O return reactiveInitializeLazyPropertiesFromDatastore( entity, id, entry, fieldName, session ); } - @Override - public String[][] getLazyPropertyColumnAliases() { - return super.getLazyPropertyColumnAliases(); - } - @Override public Object insert(Object[] fields, Object object, SharedSessionContractImplementor session) { throw LOG.nonReactiveMethodCall( "insertReactive" ); 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 51eb33fad..420200027 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 @@ -6,7 +6,6 @@ package org.hibernate.reactive.persister.entity.impl; import java.lang.invoke.MethodHandles; -import java.sql.PreparedStatement; import java.util.List; import java.util.concurrent.CompletionStage; @@ -24,7 +23,6 @@ import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.id.IdentityGenerator; -import org.hibernate.jdbc.Expectation; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.ast.spi.SingleIdEntityLoader; @@ -49,6 +47,7 @@ import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.metamodel.mapping.internal.ReactiveRuntimeModelCreationContext; import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator; import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard; import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator; @@ -74,8 +73,8 @@ public ReactiveUnionSubclassEntityPersister( final EntityDataAccess cacheAccessStrategy, final NaturalIdDataAccess naturalIdRegionAccessStrategy, final RuntimeModelCreationContext creationContext) throws HibernateException { - super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext ); - reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, creationContext ); + super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, new ReactiveRuntimeModelCreationContext( creationContext ) ); + reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } @Override @@ -145,11 +144,6 @@ public NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess cre return ReactiveAbstractEntityPersister.super.generateNaturalIdMapping(creationProcess, bootEntityDescriptor); } - @Override - public String generateSelectVersionString() { - String sql = super.generateSelectVersionString(); - return parameters().process( sql ); - } @Override protected void validateGenerator() { @@ -187,16 +181,6 @@ public Generator getGenerator() throws HibernateException { return reactiveDelegate.reactive( super.getGenerator() ); } - @Override - public String[][] getLazyPropertyColumnAliases() { - return super.getLazyPropertyColumnAliases(); - } - - @Override - public boolean check(int rows, Object id, int tableNumber, Expectation expectation, PreparedStatement statement, String sql) throws HibernateException { - return super.check(rows, id, tableNumber, expectation, statement, sql); - } - @Override public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue) { return super.initializeLazyProperty( fieldName, entity, entry, lazyIndex, selectedValue ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java index 96f5813d4..db2671be9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java @@ -69,10 +69,7 @@ public ReactiveInsertCoordinatorStandard(AbstractEntityPersister entityPersister batchKey = null; } else { - batchKey = new BasicBatchKey( - entityPersister.getEntityName() + "#INSERT", - null - ); + batchKey = new BasicBatchKey( entityPersister.getEntityName() + "#INSERT" ); } if ( entityPersister.getEntityMetamodel().isDynamicInsert() ) { @@ -193,7 +190,7 @@ protected CompletionStage decomposeForReactiveInsert( mutationGroup.forEachOperation( (position, jdbcOperation) -> { if ( id == null ) { - assert entityPersister().getIdentityInsertDelegate() != null; + assert entityPersister().getInsertDelegate() != null; } else { final EntityTableMapping tableDetails = (EntityTableMapping) jdbcOperation.getTableDetails(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveMergeCoordinator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveMergeCoordinator.java index 815842ea8..aa3165b25 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveMergeCoordinator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveMergeCoordinator.java @@ -7,7 +7,7 @@ import org.hibernate.engine.jdbc.batch.spi.BatchKey; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperationGroup; @@ -20,7 +20,7 @@ */ public class ReactiveMergeCoordinator extends ReactiveUpdateCoordinatorStandard { public ReactiveMergeCoordinator( - AbstractEntityPersister entityPersister, + EntityPersister entityPersister, SessionFactoryImplementor factory, MutationOperationGroup staticUpdateGroup, BatchKey batchKey, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java index d3237cf72..92c92bb65 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java @@ -7,7 +7,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; - import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -22,7 +21,7 @@ import org.hibernate.generator.values.GeneratedValues; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping; -import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.mutation.AttributeAnalysis; import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard; @@ -47,7 +46,7 @@ public class ReactiveUpdateCoordinatorStandard extends UpdateCoordinatorStandard private CompletableFuture updateResultStage; public ReactiveUpdateCoordinatorStandard( - AbstractEntityPersister entityPersister, + EntityPersister entityPersister, SessionFactoryImplementor factory, MutationOperationGroup staticUpdateGroup, BatchKey batchKey, @@ -107,8 +106,8 @@ public CompletionStage reactiveUpdate( return updateResultStage; } - CompletionStage s = voidFuture(); - return s.thenCompose( v -> reactivePreUpdateInMemoryValueGeneration(entity, values, session) ) + return voidFuture() + .thenCompose( v -> reactivePreUpdateInMemoryValueGeneration( entity, values, session ) ) .thenCompose( preUpdateGeneratedAttributeIndexes -> { final int[] dirtyAttributeIndexes = dirtyAttributeIndexes( incomingDirtyAttributeIndexes, preUpdateGeneratedAttributeIndexes ); @@ -129,7 +128,7 @@ else if ( !isModifiableEntity( entry ) ) { } else if ( dirtyAttributeIndexes != null && entityPersister().hasUninitializedLazyProperties( entity ) - && entityPersister().hasLazyDirtyFields( dirtyAttributeIndexes ) ) { + && hasLazyDirtyFields( entityPersister(), dirtyAttributeIndexes ) ) { // we have an entity with dirty lazy attributes. we need to use dynamic // delete and add the dirty, lazy attributes plus the non-lazy attributes forceDynamicUpdate = true; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java index bf9ddfccb..d30826846 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/Parameters.java @@ -9,7 +9,6 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; @@ -40,9 +39,6 @@ protected Parameters(String paramPrefix) { } public static Parameters instance(Dialect dialect) { - if ( dialect instanceof DialectDelegateWrapper ) { - dialect = ( (DialectDelegateWrapper) dialect ).getWrappedDialect(); - } if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { return PostgresParameters.INSTANCE; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java index 2b5fd2c3e..89a53eb76 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java @@ -149,8 +149,7 @@ private T convertException(T rows, String sql, Throwable sqlException) { if ( sqlException == null ) { return rows; } - if ( sqlException instanceof DatabaseException ) { - DatabaseException de = (DatabaseException) sqlException; + if ( sqlException instanceof DatabaseException de ) { sqlException = sqlExceptionHelper .convert( new SQLException( de.getMessage(), de.getSqlState(), de.getErrorCode() ), "error executing SQL statement", sql ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/ReactivePersistenceProvider.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/ReactivePersistenceProvider.java index df25197b2..de4f1db37 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/ReactivePersistenceProvider.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/ReactivePersistenceProvider.java @@ -6,15 +6,16 @@ package org.hibernate.reactive.provider; import java.lang.invoke.MethodHandles; +import java.net.URL; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; -import org.hibernate.jpa.boot.internal.PersistenceXmlParser; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.boot.spi.PersistenceXmlParser; import org.hibernate.jpa.internal.util.PersistenceUtilHelper; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; @@ -22,6 +23,8 @@ import org.hibernate.reactive.provider.impl.ReactiveProviderChecker; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceConfiguration; +import jakarta.persistence.PersistenceException; import jakarta.persistence.spi.LoadState; import jakarta.persistence.spi.PersistenceProvider; import jakarta.persistence.spi.PersistenceUnitInfo; @@ -38,6 +41,12 @@ public class ReactivePersistenceProvider implements PersistenceProvider { private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache(); + @Override + public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration persistenceConfiguration) { + // Same as ORM + throw log.notYetImplemented(); + } + /** * {@inheritDoc} *

@@ -55,21 +64,11 @@ public EntityManagerFactory createEntityManagerFactory(String persistenceUnitNam return builder.build(); } - protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull( - String persistenceUnitName, - Map properties) { - log.tracef( - "Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", - persistenceUnitName - ); + protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties) { + log.tracef( "Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", persistenceUnitName ); - final List units; - try { - units = PersistenceXmlParser.locatePersistenceUnits( properties ); - } - catch (Exception e) { - throw log.unableToLocatePersistenceUnits( e ); - } + final Map integration = immutable( properties ); + final Collection units = locatePersistenceUnits( integration ); log.debugf( "Located and parsed %s persistence units; checking each", units.size() ); @@ -78,7 +77,7 @@ protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull( throw log.noNameProvidedAndMultiplePersistenceUnitsFound(); } - for ( ParsedPersistenceXmlDescriptor persistenceUnit : units ) { + for ( PersistenceUnitDescriptor persistenceUnit : units ) { log.debugf( "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", persistenceUnit.getName(), @@ -106,6 +105,24 @@ protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull( return null; } + // Check before changing: may be overridden in Quarkus + // This is basically a copy and paste of the method in HibernatePersistenceProvider + protected Collection locatePersistenceUnits(Map integration) { + try { + var parser = PersistenceXmlParser.create( integration, null, null ); + final List xmlUrls = parser.getClassLoaderService().locateResources( "META-INF/persistence.xml" ); + if ( xmlUrls.isEmpty() ) { + log.unableToFindPersistenceXmlInClasspath(); + return List.of(); + } + return parser.parse( xmlUrls ).values(); + } + catch (Exception e) { + log.debug( "Unable to locate persistence units", e ); + throw new PersistenceException( "Unable to locate persistence units", e ); + } + } + private static Map immutable(Map properties) { return properties == null ? Collections.emptyMap() : Collections.unmodifiableMap( properties ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveEntityManagerFactoryBuilder.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveEntityManagerFactoryBuilder.java index 9752df913..9d243d741 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveEntityManagerFactoryBuilder.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveEntityManagerFactoryBuilder.java @@ -6,7 +6,6 @@ package org.hibernate.reactive.provider.impl; import org.hibernate.boot.internal.MetadataImpl; -import org.hibernate.boot.internal.SessionFactoryBuilderImpl; import org.hibernate.boot.internal.SessionFactoryOptionsBuilder; import org.hibernate.boot.registry.BootstrapServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; @@ -20,6 +19,8 @@ import org.hibernate.reactive.provider.service.ReactiveSessionFactoryBuilder; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceException; + import java.util.Map; /** @@ -44,33 +45,24 @@ protected StandardServiceRegistryBuilder getStandardServiceRegistryBuilder(Boots @Override public EntityManagerFactory build() { final MetadataImplementor metadata = metadata(); - SessionFactoryOptionsBuilder optionsBuilder = new SessionFactoryOptionsBuilder( metadata.getMetadataBuildingOptions().getServiceRegistry(), - ( (MetadataImpl) metadata).getBootstrapContext() + ( (MetadataImpl) metadata ).getBootstrapContext() ); optionsBuilder.enableCollectionInDefaultFetchGroup(true); - // FIXME [ORM-6]: This method does not exists anymore -// optionsBuilder.applyMultiTableBulkIdStrategy( new ReactiveBulkIdStrategy( metadata ) ); int batchSize = ConfigurationHelper.getInt( Settings.STATEMENT_BATCH_SIZE, getConfigurationValues(), 0 ); optionsBuilder.applyJdbcBatchSize(batchSize); - final SessionFactoryBuilderImpl defaultBuilder = new SessionFactoryBuilderImpl( + final SessionFactoryBuilderImplementor reactiveSessionFactoryBuilder = new ReactiveSessionFactoryBuilder( metadata, - optionsBuilder, - metadata.getTypeConfiguration() - .getMetadataBuildingContext() - .getBootstrapContext() - ); - final SessionFactoryBuilderImplementor reactiveSessionFactoryBuilder = new ReactiveSessionFactoryBuilder( metadata, defaultBuilder ); - populateSfBuilder( reactiveSessionFactoryBuilder, getStandardServiceRegistry() ); + (SessionFactoryBuilderImplementor) populateSessionFactoryBuilder() + ); try { return reactiveSessionFactoryBuilder.build(); } catch (Exception e) { - throw persistenceException( "Unable to build Hibernate SessionFactory", e ); + throw new PersistenceException( "Unable to build Hibernate SessionFactory ", e ); } } - } 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 f385b8064..2343a9644 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 @@ -26,7 +26,6 @@ import org.hibernate.property.access.internal.PropertyAccessStrategyResolverInitiator; import org.hibernate.reactive.context.impl.VertxContextInitiator; import org.hibernate.reactive.engine.jdbc.mutation.internal.ReactiveMutationExecutorServiceInitiator; -import org.hibernate.reactive.id.factory.spi.ReactiveIdentifierGeneratorFactoryInitiator; import org.hibernate.reactive.loader.ast.internal.ReactiveBatchLoaderFactoryInitiator; import org.hibernate.reactive.pool.impl.ReactiveConnectionPoolInitiator; import org.hibernate.reactive.pool.impl.SqlClientPoolConfigurationInitiator; @@ -70,9 +69,6 @@ private static List> buildInitialServiceInitiatorLis // Custom for Hibernate Reactive: SessionFactoryBuilderService serviceInitiators.add( ReactiveSessionFactoryBuilderInitiator.INSTANCE ); - // Custom for Hibernate Reactive: IdentifierGeneratorFactory - serviceInitiators.add( ReactiveIdentifierGeneratorFactoryInitiator.INSTANCE); - // [standard] BytecodeProvider serviceInitiators.add( BytecodeProviderInitiator.INSTANCE ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveTypeContributor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveTypeContributor.java index e278bd393..4fcd4a46c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveTypeContributor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveTypeContributor.java @@ -21,14 +21,16 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; +import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; import org.hibernate.reactive.type.descriptor.jdbc.ReactiveArrayJdbcTypeConstructor; +import org.hibernate.reactive.type.descriptor.jdbc.ReactiveJsonArrayJdbcTypeConstructor; import org.hibernate.reactive.type.descriptor.jdbc.ReactiveJsonJdbcType; +import org.hibernate.reactive.type.descriptor.jdbc.ReactiveXmlArrayJdbcTypeConstructor; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.BasicTypeRegistry; @@ -47,6 +49,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.descriptor.jdbc.ObjectJdbcType; +import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; import org.hibernate.type.descriptor.jdbc.TimestampJdbcType; import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; @@ -85,8 +88,17 @@ private void registerReactiveChanges(TypeContributions typeContributions, Servic JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); jdbcTypeRegistry.addTypeConstructor( ReactiveArrayJdbcTypeConstructor.INSTANCE ); + jdbcTypeRegistry.addTypeConstructor( ReactiveXmlArrayJdbcTypeConstructor.INSTANCE ); jdbcTypeRegistry.addDescriptor( SqlTypes.JSON, ReactiveJsonJdbcType.INSTANCE ); + if ( !( dialect instanceof MariaDBDialect ) && dialect instanceof MySQLDialect ) { + jdbcTypeRegistry.addTypeConstructor( ReactiveJsonArrayJdbcTypeConstructor.INSTANCE ); + } + + if ( dialect instanceof MySQLDialect ) { + jdbcTypeRegistry.addDescriptor( ObjectNullAsBinaryTypeJdbcType.INSTANCE ); + } + if ( dialect instanceof MySQLDialect || dialect instanceof DB2Dialect || dialect instanceof OracleDialect ) { jdbcTypeRegistry.addDescriptor( TimestampAsLocalDateTimeJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( TimestampUtcAsLocalDateTimeJdbcType.INSTANCE ); @@ -101,7 +113,7 @@ private void registerReactiveChanges(TypeContributions typeContributions, Servic } private Dialect dialect(ServiceRegistry serviceRegistry) { - return DialectDelegateWrapper.extractRealDialect( serviceRegistry.getService( JdbcEnvironment.class ).getDialect() ); + return serviceRegistry.getService( JdbcEnvironment.class ).getDialect(); } /** @@ -116,7 +128,8 @@ private static class TimestampAsLocalDateTimeJdbcType extends TimestampJdbcType public ValueBinder getBinder(final JavaType javaType) { return new BasicBinder<>( javaType, this ) { @Override - protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { final Timestamp timestamp = javaType.unwrap( value, Timestamp.class, options ); if ( value instanceof Calendar ) { ( (PreparedStatementAdaptor) st ) @@ -124,7 +137,12 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o } else if ( options.getJdbcTimeZone() != null ) { ( (PreparedStatementAdaptor) st ) - .setTimestamp( index, timestamp, Calendar.getInstance( options.getJdbcTimeZone() ), ZonedDateTime::toLocalDateTime ); + .setTimestamp( + index, + timestamp, + Calendar.getInstance( options.getJdbcTimeZone() ), + ZonedDateTime::toLocalDateTime + ); } else { st.setTimestamp( index, timestamp ); @@ -141,7 +159,12 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions } else if ( options.getJdbcTimeZone() != null ) { ( (PreparedStatementAdaptor) st ) - .setTimestamp( name, timestamp, Calendar.getInstance( options.getJdbcTimeZone() ), ZonedDateTime::toLocalDateTime ); + .setTimestamp( + name, + timestamp, + Calendar.getInstance( options.getJdbcTimeZone() ), + ZonedDateTime::toLocalDateTime + ); } else { st.setTimestamp( name, timestamp ); @@ -165,16 +188,25 @@ private static class TimestampUtcAsLocalDateTimeJdbcType extends TimestampUtcAsJ public ValueBinder getBinder(final JavaType javaType) { return new BasicBinder<>( javaType, this ) { @Override - protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) { final Instant instant = javaType.unwrap( value, Instant.class, options ); - ( (PreparedStatementAdaptor) st).setTimestamp( index, Timestamp.from( instant ), UTC_CALENDAR, ZonedDateTime::toLocalDateTime ); + ( (PreparedStatementAdaptor) st ).setTimestamp( + index, + Timestamp.from( instant ), + UTC_CALENDAR, + ZonedDateTime::toLocalDateTime + ); } @Override - protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) - throws SQLException { + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) { final Instant instant = javaType.unwrap( value, Instant.class, options ); - ( (PreparedStatementAdaptor) st).setTimestamp( name, Timestamp.from( instant ), UTC_CALENDAR, ZonedDateTime::toLocalDateTime ); + ( (PreparedStatementAdaptor) st ).setTimestamp( + name, + Timestamp.from( instant ), + UTC_CALENDAR, + ZonedDateTime::toLocalDateTime + ); } }; } @@ -203,7 +235,12 @@ public String getCastTypeName(JdbcType jdbcType, JavaType javaType) { } @Override - public String getCastTypeName(JdbcType jdbcType, JavaType javaType, Long length, Integer precision, Integer scale) { + public String getCastTypeName( + JdbcType jdbcType, + JavaType javaType, + Long length, + Integer precision, + Integer scale) { return "json"; } } @@ -253,7 +290,7 @@ public String getName() { @Override public String[] getRegistrationKeys() { - return new String[] { "json", JsonObject.class.getName() }; + return new String[] {"json", JsonObject.class.getName()}; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java index 8ba013cb5..61a5534b4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java @@ -12,14 +12,12 @@ import java.util.List; import java.util.StringTokenizer; -import org.hibernate.boot.model.TruthValue; import org.hibernate.boot.model.naming.DatabaseIdentifier; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.tool.schema.extract.internal.AbstractInformationExtractorImpl; import org.hibernate.tool.schema.extract.internal.ColumnInformationImpl; -import org.hibernate.tool.schema.extract.spi.ColumnInformation; import org.hibernate.tool.schema.extract.spi.ExtractionContext; import org.hibernate.tool.schema.extract.spi.InformationExtractor; import org.hibernate.tool.schema.extract.spi.TableInformation; @@ -197,12 +195,16 @@ protected boolean appendClauseAndParameterIfNotNullOrEmpty( if ( parameter != null && ( ! String.class.isInstance( parameter ) || ! ( (String) parameter ).isEmpty() ) ) { parameters.add( parameter ); sb.append( clause ); - sb.append( "?"); + sb.append( parameterMarker( parameters.size() ) ); return true; } return false; } + protected String parameterMarker(int pos) { + return "?"; + } + @Override protected T processTableResultSet( String catalog, @@ -364,9 +366,9 @@ protected String getDatabaseSchemaColumnName(String catalogColumnName, String sc } @Override - protected void addExtractedColumnInformation(TableInformation tableInformation, ResultSet resultSet) throws SQLException { + protected ColumnInformationImpl columnInformation(TableInformation tableInformation, ResultSet resultSet) throws SQLException { final String typeName = new StringTokenizer( resultSet.getString( getResultSetTypeNameLabel() ), "() " ).nextToken(); - final ColumnInformation columnInformation = new ColumnInformationImpl( + return new ColumnInformationImpl( tableInformation, DatabaseIdentifier.toIdentifier( resultSet.getString( getResultSetColumnNameLabel() ) ), dataTypeCode( typeName ), @@ -375,7 +377,6 @@ protected void addExtractedColumnInformation(TableInformation tableInformation, resultSet.getInt( getResultSetDecimalDigitsLabel() ), interpretTruthValue( resultSet.getString( getResultSetIsNullableLabel() ) ) ); - tableInformation.addColumn( columnInformation ); } /** @@ -384,14 +385,4 @@ protected void addExtractedColumnInformation(TableInformation tableInformation, protected int dataTypeCode(String typeName) { return 0; } - - private TruthValue interpretTruthValue(String nullable) { - if ( "yes".equalsIgnoreCase( nullable ) ) { - return TruthValue.TRUE; - } - else if ( "no".equalsIgnoreCase( nullable ) ) { - return TruthValue.FALSE; - } - return TruthValue.UNKNOWN; - } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NativeParametersHandling.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NativeParametersHandling.java index 1db528258..fb151a3aa 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NativeParametersHandling.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NativeParametersHandling.java @@ -11,7 +11,6 @@ import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -39,8 +38,7 @@ public class NativeParametersHandling implements StandardServiceInitiator configurationValues, ServiceRegistryImplementor registry) { final Dialect dialect = registry.getService( JdbcServices.class ).getDialect(); - final Dialect realDialect = DialectDelegateWrapper.extractRealDialect( dialect ); - final ParameterMarkerStrategy renderer = recommendRendered( realDialect ); + final ParameterMarkerStrategy renderer = recommendRendered( dialect ); LOG.debugf( "Initializing service JdbcParameterRenderer with implementation: %s", renderer.getClass() ); return renderer; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java index deafed172..5580c99fa 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java @@ -12,9 +12,9 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.dialect.spi.DialectFactory; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; -import org.hibernate.reactive.engine.jdbc.env.internal.ReactiveJdbcEnvironment; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.pool.ReactiveConnectionPool; import org.hibernate.reactive.provider.Settings; @@ -46,11 +46,10 @@ public JdbcEnvironment initiateService(Map configurationValues, boolean explicitDialect = configurationValues.containsKey( Settings.DIALECT ); if ( explicitDialect ) { DialectFactory dialectFactory = registry.getService( DialectFactory.class ); - Dialect dialect = dialectFactory.buildDialect( configurationValues, null ); - return new ReactiveJdbcEnvironment( registry, dialect ); + return new JdbcEnvironmentImpl( registry, dialectFactory.buildDialect( configurationValues, null ) ); } - return new ReactiveJdbcEnvironment( registry, new DialectBuilder( configurationValues, registry ).build() ); + return new JdbcEnvironmentImpl( registry, new DialectBuilder( configurationValues, registry ).build() ); } private static class DialectBuilder { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java index 67852da79..d85b74d1e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java @@ -61,10 +61,15 @@ protected T processIndexInfoResultSet( appendClauseAndParameterIfNotNullOrEmpty( " and ui.table_owner = ", schema, sb, parameters ); appendClauseAndParameterIfNotNullOrEmpty( " and ui.table_name = ", table, sb, parameters ); - return getExtractionContext().getQueryResults( sb.toString(), parameters.toArray(), processor ); } + @Override + protected String parameterMarker(int pos) { + return ":" + pos; + } + + @Override protected T processImportedKeysResultSet( String catalog, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java index d6bcb00a3..ee4a012fe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java @@ -100,6 +100,11 @@ protected T processIndexInfoResultSet( ); } + @Override + protected String parameterMarker(int pos) { + return "$" + pos; + } + @Override protected T processImportedKeysResultSet( String catalog, @@ -135,7 +140,6 @@ protected T processImportedKeysResultSet( // No need to order by catalog since it is always null. sb.append( " order by pkn.nspname, pkc.relname, con.conname, pos.n" ); - return getExtractionContext().getQueryResults( sb.toString(), parameterValues.toArray(), processor ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveGenerationTarget.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveGenerationTarget.java index 0919a3358..1f89a0490 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveGenerationTarget.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveGenerationTarget.java @@ -19,7 +19,7 @@ import org.hibernate.reactive.pool.ReactiveConnectionPool; import org.hibernate.reactive.vertx.VertxInstance; import org.hibernate.service.ServiceRegistry; -import org.hibernate.tool.schema.internal.exec.GenerationTarget; +import org.hibernate.tool.schema.spi.GenerationTarget; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveImprovedExtractionContextImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveImprovedExtractionContextImpl.java index 362444a00..160c61c52 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveImprovedExtractionContextImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveImprovedExtractionContextImpl.java @@ -39,7 +39,6 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.reactive.pool.ReactiveConnectionPool; -import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.resource.transaction.spi.DdlTransactionIsolator; import org.hibernate.service.ServiceRegistry; import org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl; @@ -79,8 +78,7 @@ private ResultSet getQueryResultSet( String queryString, Object[] positionalParameters) { final Object[] parametersToUse = positionalParameters != null ? positionalParameters : new Object[0]; - final Parameters parametersDialectSpecific = Parameters.instance( getJdbcEnvironment().getDialect() ); - final String queryToUse = parametersDialectSpecific.process( queryString, parametersToUse.length ); + final String queryToUse = queryString; return connectionPool // DDL needs to run outside the current transaction. For example: // - increment on a table-based id generator should happen outside the current tx. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSchemaManagementTool.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSchemaManagementTool.java index 347848443..0d7dce19c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSchemaManagementTool.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSchemaManagementTool.java @@ -9,8 +9,6 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; -import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; @@ -60,15 +58,11 @@ public ExtractionContext createExtractionContext( public InformationExtractor createInformationExtractor(ExtractionContext extractionContext) { Dialect dialect = extractionContext.getJdbcEnvironment().getDialect(); - //Allow for wrapped cases: - if ( dialect instanceof DialectDelegateWrapper ) { - dialect = ( (DialectDelegateWrapper) dialect ).getWrappedDialect(); - } //Now detect the kind of Dialect: if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { return new PostgreSqlReactiveInformationExtractorImpl( extractionContext ); } - if ( dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect ) { + if ( dialect instanceof MySQLDialect ) { return new MySqlReactiveInformationExtractorImpl( extractionContext ); } if ( dialect instanceof SQLServerDialect ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveValuesMappingProducerProvider.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveValuesMappingProducerProvider.java index 984271279..0f968bd9a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveValuesMappingProducerProvider.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveValuesMappingProducerProvider.java @@ -7,7 +7,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.results.ResultSetMapping; -import org.hibernate.query.results.ResultSetMappingImpl; +import org.hibernate.query.results.internal.ResultSetMappingImpl; import org.hibernate.reactive.sql.results.ReactiveResultSetMapping; import org.hibernate.reactive.sql.results.internal.ReactiveStandardValuesMappingProducer; import org.hibernate.sql.ast.tree.select.SelectStatement; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java index e2d277e2d..2cec39ce7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java @@ -93,6 +93,12 @@ protected T processTableResultSet( return getExtractionContext().getQueryResults( sb.toString(), parameterValues.toArray(), processor ); } + + @Override + protected String parameterMarker(int pos) { + return "@P" + pos; + } + @Override protected T processColumnsResultSet( String catalog, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java index 2b347b3de..6c35d9260 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java @@ -119,14 +119,11 @@ default CompletionStage> getReactiveResultList() { ReactiveSelectionQuery setLockMode(String alias, LockMode lockMode); - @Deprecated - ReactiveSelectionQuery setAliasSpecificLockMode(String alias, LockMode lockMode); - ReactiveSelectionQuery setFollowOnLocking(boolean enable); void applyGraph(RootGraphImplementor graph, GraphSemantic semantic); - ReactiveSelectionQuery setOrder(List> orderList); + ReactiveSelectionQuery setOrder(List> orderList); ReactiveSelectionQuery setOrder(Order order); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java index 52d778e1a..2ac40168c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java @@ -22,6 +22,8 @@ import org.hibernate.reactive.query.sql.spi.ReactiveNamedNativeQueryMemento; import org.hibernate.reactive.query.sql.spi.ReactiveNamedSqmQueryMemento; +import jakarta.persistence.TypedQueryReference; + public class ReactiveNamedObjectRepositoryImpl implements NamedObjectRepository { private final NamedObjectRepository delegate; @@ -31,12 +33,17 @@ public ReactiveNamedObjectRepositoryImpl(NamedObjectRepository delegate) { } @Override - public NamedSqmQueryMemento getSqmQueryMemento(String queryName) { + public Map> getNamedQueries(Class resultType) { + return delegate.getNamedQueries( resultType ); + } + + @Override + public NamedSqmQueryMemento getSqmQueryMemento(String queryName) { return wrapSqmQueryMemento( delegate.getSqmQueryMemento( queryName ) ); } @Override - public void visitSqmQueryMementos(Consumer action) { + public void visitSqmQueryMementos(Consumer> action) { delegate.visitSqmQueryMementos( action ); } @@ -46,12 +53,12 @@ public void registerSqmQueryMemento(String name, NamedSqmQueryMemento descriptor } @Override - public NamedNativeQueryMemento getNativeQueryMemento(String queryName) { + public NamedNativeQueryMemento getNativeQueryMemento(String queryName) { return wrapNativeQueryMemento( delegate.getNativeQueryMemento( queryName ) ); } @Override - public void visitNativeQueryMementos(Consumer action) { + public void visitNativeQueryMementos(Consumer> action) { delegate.visitNativeQueryMementos( action ); } @@ -101,11 +108,11 @@ public void validateNamedQueries(QueryEngine queryEngine) { } @Override - public NamedQueryMemento resolve( + public NamedQueryMemento resolve( SessionFactoryImplementor sessionFactory, MetadataImplementor bootMetamodel, String registrationName) { - return wrap(delegate.resolve( sessionFactory, bootMetamodel, registrationName )); + return wrap( delegate.resolve( sessionFactory, bootMetamodel, registrationName ) ); } @Override @@ -118,17 +125,19 @@ public void close() { delegate.close(); } - private static NamedQueryMemento wrap(final NamedQueryMemento namedQueryMemento) { + private static NamedQueryMemento wrap(final NamedQueryMemento namedQueryMemento) { if ( namedQueryMemento == null ) { return null; - } else if( namedQueryMemento instanceof NamedSqmQueryMemento ) { - return wrapSqmQueryMemento( (NamedSqmQueryMemento) namedQueryMemento ); - } else { - return wrapNativeQueryMemento( (NamedNativeQueryMemento) namedQueryMemento ); + } + else if ( namedQueryMemento instanceof NamedSqmQueryMemento ) { + return wrapSqmQueryMemento( (NamedSqmQueryMemento) namedQueryMemento ); + } + else { + return wrapNativeQueryMemento( (NamedNativeQueryMemento) namedQueryMemento ); } } - private static NamedSqmQueryMemento wrapSqmQueryMemento(final NamedSqmQueryMemento sqmQueryMemento) { + private static NamedSqmQueryMemento wrapSqmQueryMemento(final NamedSqmQueryMemento sqmQueryMemento) { if ( sqmQueryMemento == null ) { return null; } @@ -141,7 +150,7 @@ else if ( sqmQueryMemento instanceof ReactiveNamedSqmQueryMemento ) { } } - private static NamedNativeQueryMemento wrapNativeQueryMemento(final NamedNativeQueryMemento nativeQueryMemento) { + private static NamedNativeQueryMemento wrapNativeQueryMemento(final NamedNativeQueryMemento nativeQueryMemento) { if ( nativeQueryMemento == null ) { return null; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeNonSelectQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeNonSelectQueryPlan.java index efed93fad..be5541c5d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeNonSelectQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeNonSelectQueryPlan.java @@ -20,6 +20,7 @@ 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.sql.spi.ReactiveNonSelectQueryPlan; import org.hibernate.reactive.sql.exec.internal.StandardReactiveJdbcMutationExecutor; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; @@ -71,8 +72,9 @@ public CompletionStage executeReactiveUpdate(DomainQueryExecutionContex final SQLQueryParser parser = new SQLQueryParser( sql, null, session.getFactory() ); + Parameters parameters = Parameters.instance( session.getDialect() ); final JdbcOperationQueryMutation jdbcMutation = new JdbcOperationQueryMutationNative( - parser.process(), + parameters.process( parser.process() ), jdbcParameterBinders, affectedTableNames ); 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 1c97f847a..bd38d53d4 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 @@ -34,6 +34,7 @@ import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; import org.hibernate.query.named.NamedResultSetMappingMemento; +import org.hibernate.query.results.internal.dynamic.DynamicResultBuilderEntityStandard; import org.hibernate.query.spi.AbstractSelectionQuery; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryInterpretationCache; @@ -57,7 +58,7 @@ import jakarta.persistence.metamodel.SingularAttribute; public class ReactiveNativeQueryImpl extends NativeQueryImpl - implements ReactiveNativeQueryImplementor { + implements ReactiveNativeQueryImplementor { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); @@ -73,17 +74,26 @@ public ReactiveNativeQueryImpl(NamedNativeQueryMemento memento, SharedSessionCon this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveNativeQueryImpl(NamedNativeQueryMemento memento, Class resultJavaType, SharedSessionContractImplementor session) { + public ReactiveNativeQueryImpl( + NamedNativeQueryMemento memento, + Class resultJavaType, + SharedSessionContractImplementor session) { super( memento, resultJavaType, session ); this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveNativeQueryImpl(NamedNativeQueryMemento memento, String resultSetMappingName, SharedSessionContractImplementor session) { + public ReactiveNativeQueryImpl( + NamedNativeQueryMemento memento, + String resultSetMappingName, + SharedSessionContractImplementor session) { super( memento, resultSetMappingName, session ); this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveNativeQueryImpl(String sqlString, NamedResultSetMappingMemento resultSetMappingMemento, AbstractSharedSessionContract session) { + public ReactiveNativeQueryImpl( + String sqlString, + NamedResultSetMappingMemento resultSetMappingMemento, + AbstractSharedSessionContract session) { super( sqlString, resultSetMappingMemento, session ); this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } @@ -119,6 +129,7 @@ private ReactiveAbstractSelectionQuery createSelectionQueryDelegate(SharedSes null ); } + private CompletionStage> doReactiveList() { return reactiveSelectPlan().reactivePerformList( this ); } @@ -138,7 +149,11 @@ private ReactiveNonSelectQueryPlan reactiveNonSelectPlan() { } final String sqlString = expandParameterLists(); - ReactiveNonSelectQueryPlan queryPlan = new ReactiveNativeNonSelectQueryPlan( sqlString, getQuerySpaces(), getParameterOccurrences() ); + ReactiveNonSelectQueryPlan queryPlan = new ReactiveNativeNonSelectQueryPlan( + sqlString, + getQuerySpaces(), + getParameterOccurrences() + ); if ( cacheKey != null ) { getSession().getFactory().getQueryEngine().getInterpretationCache() .cacheNonSelectQueryPlan( cacheKey, queryPlan ); @@ -224,9 +239,19 @@ public ReactiveNativeQueryImpl applyFetchGraph(RootGraph graph) { return this; } + @Override + public DynamicResultBuilderEntityStandard addRoot(String tableAlias, Class entityType) { + return super.addRoot( tableAlias, entityType ); + } + + @Override + public DynamicResultBuilderEntityStandard addRoot(String tableAlias, String entityName) { + return super.addRoot( tableAlias, entityName ); + } + @Override public void addResultTypeClass(Class resultClass) { - super.addResultTypeClass(resultClass); + super.addResultTypeClass( resultClass ); } @Override @@ -260,31 +285,48 @@ public ReactiveNativeQueryImpl addScalar(String columnAlias, BasicTypeReferen } @Override - public ReactiveNativeQueryImpl addScalar(String columnAlias, Class relationalJavaType, AttributeConverter converter) { + public ReactiveNativeQueryImpl addScalar( + String columnAlias, + Class relationalJavaType, + AttributeConverter converter) { super.addScalar( columnAlias, relationalJavaType, converter ); return this; } @Override - public ReactiveNativeQueryImpl addScalar(String columnAlias, Class domainJavaType, Class jdbcJavaType, AttributeConverter converter) { + public ReactiveNativeQueryImpl addScalar( + String columnAlias, + Class domainJavaType, + Class jdbcJavaType, + AttributeConverter converter) { super.addScalar( columnAlias, domainJavaType, jdbcJavaType, converter ); return this; } @Override - public ReactiveNativeQueryImpl addScalar(String columnAlias, Class relationalJavaType, Class> converter) { + public ReactiveNativeQueryImpl addScalar( + String columnAlias, + Class relationalJavaType, + Class> converter) { super.addScalar( columnAlias, relationalJavaType, converter ); return this; } @Override - public ReactiveNativeQueryImpl addScalar(String columnAlias, Class domainJavaType, Class jdbcJavaType, Class> converter) { + public ReactiveNativeQueryImpl addScalar( + String columnAlias, + Class domainJavaType, + Class jdbcJavaType, + Class> converter) { super.addScalar( columnAlias, domainJavaType, jdbcJavaType, converter ); return this; } @Override - public ReactiveNativeQueryImpl addAttributeResult(String columnAlias, Class entityJavaType, String attributePath) { + public ReactiveNativeQueryImpl addAttributeResult( + String columnAlias, + Class entityJavaType, + String attributePath) { super.addAttributeResult( columnAlias, entityJavaType, attributePath ); return this; } @@ -318,6 +360,7 @@ public ReactiveNativeQueryImpl addEntity(String tableAlias, String entityName super.addEntity( tableAlias, entityName, lockMode ); return this; } + @Override public ReactiveNativeQueryImpl addEntity(Class entityType) { super.addEntity( entityType ); @@ -382,12 +425,6 @@ public ReactiveNativeQueryImpl applyLoadGraph(RootGraph graph) { return this; } - @Override @Deprecated - public ReactiveNativeQueryImpl setAliasSpecificLockMode(String alias, LockMode lockMode) { - super.setAliasSpecificLockMode( alias, lockMode ); - return this; - } - @Override public ReactiveNativeQueryImpl setHint(String hintName, Object value) { super.setHint( hintName, value ); @@ -485,7 +522,7 @@ public ReactiveNativeQueryImpl setLockMode(String alias, LockMode lockMode) { } @Override - public ReactiveNativeQueryImpl setOrder(List> orders) { + public ReactiveNativeQueryImpl setOrder(List> orders) { super.setOrder( orders ); return this; } @@ -635,7 +672,10 @@ public ReactiveNativeQueryImpl setParameter(Parameter param, Date value } @Override - public ReactiveNativeQueryImpl setParameter(Parameter param, Calendar value, TemporalType temporalType) { + public ReactiveNativeQueryImpl setParameter( + Parameter param, + Calendar value, + TemporalType temporalType) { super.setParameter( param, value, temporalType ); return this; } @@ -653,7 +693,10 @@ public

ReactiveNativeQueryImpl setParameterList(String name, Collection ReactiveNativeQueryImpl setParameterList(String name, Collection values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + String name, + Collection values, + BindableType

type) { super.setParameterList( name, values, type ); return this; } @@ -683,13 +726,19 @@ public ReactiveNativeQueryImpl setParameterList(int position, Collection valu } @Override - public

ReactiveNativeQueryImpl setParameterList(int position, Collection values, Class

type) { + public

ReactiveNativeQueryImpl setParameterList( + int position, + Collection values, + Class

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

ReactiveNativeQueryImpl setParameterList(int position, Collection values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + int position, + Collection values, + BindableType

type) { super.setParameterList( position, values, type ); return this; } @@ -713,19 +762,27 @@ public

ReactiveNativeQueryImpl setParameterList(int position, P[] values, } @Override - public

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values, Class

javaType) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + Collection values, + Class

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + Collection values, + BindableType

type) { super.setParameterList( parameter, values, type ); return this; } @@ -743,7 +800,10 @@ public

ReactiveNativeQueryImpl setParameterList(QueryParameter

paramet } @Override - public

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, P[] values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + P[] values, + BindableType

type) { super.setParameterList( parameter, values, type ); return this; } @@ -767,6 +827,6 @@ public void applyGraph(RootGraphImplementor graph, GraphSemantic semantic) { @Override public ReactiveNativeQueryImpl enableFetchProfile(String profileName) { - throw new UnsupportedOperationException("A native SQL query cannot use fetch profiles"); + throw new UnsupportedOperationException( "A native SQL query cannot use fetch profiles" ); } } 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 28753683e..f1eca36df 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 @@ -6,12 +6,12 @@ package org.hibernate.reactive.query.sql.internal; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; 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,6 +23,7 @@ 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; @@ -60,7 +61,8 @@ public ReactiveNativeSelectQueryPlanImpl( resultSetMapping.addAffectedTableNames( affectedTableNames, sessionFactory ); } this.affectedTableNames = affectedTableNames; - this.sql = parser.process(); + Dialect dialect = sessionFactory.getJdbcServices().getDialect(); + this.sql = Parameters.instance( dialect ).process( parser.process() ); this.parameterList = parameterList; } @@ -90,29 +92,28 @@ public CompletionStage> reactivePerformList(DomainQueryExecutionContext ); } - final ReactiveSharedSessionContractImplementor reactiveSession = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); - return reactiveSession.reactiveAutoFlushIfRequired( affectedTableNames ) - .thenCompose( aBoolean -> { - final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( - sql, - jdbcParameterBinders, - resultSetMapping, - affectedTableNames, - Collections.emptySet() - ); + return ( (ReactiveSharedSessionContractImplementor) executionContext.getSession() ) + .reactiveAutoFlushIfRequired( affectedTableNames ) + .thenCompose( aBoolean -> { + final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( + sql, + jdbcParameterBinders, + resultSetMapping, + affectedTableNames + ); - return StandardReactiveSelectExecutor.INSTANCE - .list( - jdbcSelect, - jdbcParameterBindings, - SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), - null, - queryOptions.getUniqueSemantic() == null - ? ReactiveListResultsConsumer.UniqueSemantic.NEVER - : reactiveUniqueSemantic( queryOptions ) - ); + return StandardReactiveSelectExecutor.INSTANCE + .list( + jdbcSelect, + jdbcParameterBindings, + SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), + null, + queryOptions.getUniqueSemantic() == null + ? ReactiveListResultsConsumer.UniqueSemantic.NEVER + : reactiveUniqueSemantic( queryOptions ) + ); - } ); + } ); } private static ReactiveListResultsConsumer.UniqueSemantic reactiveUniqueSemantic(QueryOptions queryOptions) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedNativeQueryMemento.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedNativeQueryMemento.java index fc1493d02..7bc480637 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedNativeQueryMemento.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNamedNativeQueryMemento.java @@ -19,14 +19,19 @@ /** * @see NamedNativeQueryMemento */ -public class ReactiveNamedNativeQueryMemento implements NamedNativeQueryMemento { +public class ReactiveNamedNativeQueryMemento implements NamedNativeQueryMemento { - private final NamedNativeQueryMemento delegate; + private final NamedNativeQueryMemento delegate; - public ReactiveNamedNativeQueryMemento(NamedNativeQueryMemento delegate) { + public ReactiveNamedNativeQueryMemento(NamedNativeQueryMemento delegate) { this.delegate = delegate; } + @Override + public Class getResultType() { + return delegate.getResultType(); + } + @Override public String getSqlString() { return delegate.getSqlString(); @@ -63,23 +68,23 @@ public Integer getMaxResults() { } @Override - public NativeQueryImplementor toQuery(SharedSessionContractImplementor session) { - return new ReactiveNativeQueryImpl( this, session ); + public NativeQueryImplementor toQuery(SharedSessionContractImplementor session) { + return new ReactiveNativeQueryImpl<>( this, session ); } @Override public NativeQueryImplementor toQuery(SharedSessionContractImplementor session, Class resultType) { - return new ReactiveNativeQueryImpl( this, resultType, session ); + return new ReactiveNativeQueryImpl<>( this, resultType, session ); } @Override public NativeQueryImplementor toQuery(SharedSessionContractImplementor session, String resultSetMapping) { - return new ReactiveNativeQueryImpl( this, resultSetMapping, session ); + return new ReactiveNativeQueryImpl<>( this, resultSetMapping, session ); } @Override - public NamedNativeQueryMemento makeCopy(String name) { - return new ReactiveNamedNativeQueryMemento( delegate.makeCopy( name ) ); + public NamedNativeQueryMemento makeCopy(String name) { + return new ReactiveNamedNativeQueryMemento<>( delegate.makeCopy( name ) ); } @Override 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 af7a9ba7c..388488b26 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 @@ -26,17 +26,22 @@ /** * @see org.hibernate.query.sql.spi.NamedNativeQueryMemento */ -public class ReactiveNamedSqmQueryMemento implements NamedSqmQueryMemento { +public class ReactiveNamedSqmQueryMemento implements NamedSqmQueryMemento { - private final NamedSqmQueryMemento delegate; + private final NamedSqmQueryMemento delegate; - public ReactiveNamedSqmQueryMemento(NamedSqmQueryMemento delegate) { + public ReactiveNamedSqmQueryMemento(NamedSqmQueryMemento delegate) { Objects.requireNonNull( delegate ); this.delegate = delegate; } @Override - public SqmQueryImplementor toQuery(SharedSessionContractImplementor session) { + public Class getResultType() { + return delegate.getResultType(); + } + + @Override + public SqmQueryImplementor toQuery(SharedSessionContractImplementor session) { return toQuery( session, null ); } @@ -73,7 +78,7 @@ public String getHqlString() { } @Override - public SqmStatement getSqmStatement() { + public SqmStatement getSqmStatement() { return delegate.getSqmStatement(); } @@ -98,8 +103,8 @@ public Map getParameterTypes() { } @Override - public NamedSqmQueryMemento makeCopy(String name) { - return new ReactiveNamedSqmQueryMemento( delegate.makeCopy( name ) ); + public NamedSqmQueryMemento makeCopy(String name) { + return new ReactiveNamedSqmQueryMemento<>( delegate.makeCopy( name ) ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java index 11e943631..3c59afc3e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java @@ -21,7 +21,7 @@ import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; import org.hibernate.query.named.NameableQuery; -import org.hibernate.query.results.dynamic.DynamicResultBuilderEntityStandard; +import org.hibernate.query.results.internal.dynamic.DynamicResultBuilderEntityStandard; import org.hibernate.query.sql.spi.NamedNativeQueryMemento; import org.hibernate.reactive.query.ReactiveNativeQuery; import org.hibernate.reactive.query.ReactiveQueryImplementor; 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 e0c71f7e3..1d897653f 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 @@ -170,7 +170,7 @@

ReactiveSqmSelectionQuery setParameterList( ReactiveSqmSelectionQuery setTimeout(int timeout); @Override - ReactiveSqmSelectionQuery setOrder(List> orders); + ReactiveSqmSelectionQuery setOrder(List> orderList); @Override ReactiveSqmSelectionQuery setOrder(Order order); 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 821f2c7f3..01ee41c90 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 @@ -228,22 +228,20 @@ private CompletionStage withCacheableSqmInterpretation(DomainQueryExec return interpreter.interpret( context, executionContext, localCopy, jdbcParameterBindings ); } + // Copy and paste from ORM private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, DomainQueryExecutionContext executionContext) { - final SharedSessionContractImplementor session = executionContext.getSession(); return SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), domainParameterXref, sqmInterpretation.getJdbcParamsXref(), - session.getFactory().getRuntimeMetamodels().getMappingMetamodel(), - sqmInterpretation.getTableGroupAccess()::findTableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + //this is pretty ugly! + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypes().get( parameter ); + return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypes().get(parameter); } }, - session + executionContext.getSession() ); } @@ -281,19 +279,15 @@ private static CacheableSqmInterpretation buildCacheableSqmInterpretation( executionContext.getQueryParameterBindings(), domainParameterXref, jdbcParamsXref, - session.getFactory().getRuntimeMetamodels().getMappingMetamodel(), - tableGroupAccess::findTableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation - .getSqmParameterMappingModelTypeResolutions() - .get( parameter ); + return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get(parameter); } }, session ); + final JdbcOperationQuerySelect jdbcSelect = selectTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() 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 fbe3295d6..4e9e8f001 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 @@ -23,15 +23,14 @@ import org.hibernate.LockOptions; import org.hibernate.TypeMismatchException; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.Generator; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; -import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.enhanced.Optimizer; import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.BindableType; import org.hibernate.query.IllegalQueryOperationException; @@ -414,22 +413,23 @@ private ReactiveNonSelectQueryPlan buildUpdateQueryPlan() { ? new ReactiveSimpleUpdateQueryPlan( sqmUpdate, getDomainParameterXref() ) : new ReactiveMultiTableUpdateQueryPlan( sqmUpdate, getDomainParameterXref(), multiTableStrategy ); } + private ReactiveNonSelectQueryPlan buildInsertQueryPlan() { //noinspection rawtypes final SqmInsertStatement sqmInsert = (SqmInsertStatement) getSqmStatement(); final String entityNameToInsert = sqmInsert.getTarget().getModel().getHibernateEntityName(); - final AbstractEntityPersister entityDescriptor = (AbstractEntityPersister) getSessionFactory().getRuntimeMetamodels() - .getMappingMetamodel() - .getEntityDescriptor( entityNameToInsert ); + final EntityPersister persister = getSessionFactory() + .getMappingMetamodel().getEntityDescriptor( entityNameToInsert ); + + boolean useMultiTableInsert = persister.hasMultipleTables(); + if ( !useMultiTableInsert && !isSimpleValuesInsert( sqmInsert, persister ) ) { + final Generator identifierGenerator = persister.getGenerator(); - boolean useMultiTableInsert = entityDescriptor.isMultiTable(); - if ( !useMultiTableInsert && !isSimpleValuesInsert( sqmInsert, entityDescriptor ) ) { - final IdentifierGenerator identifierGenerator = entityDescriptor.getIdentifierGenerator(); if ( identifierGenerator instanceof BulkInsertionCapableIdentifierGenerator && identifierGenerator instanceof OptimizableGenerator ) { final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer(); if ( optimizer != null && optimizer.getIncrementSize() > 1 ) { - useMultiTableInsert = !hasIdentifierAssigned( sqmInsert, entityDescriptor ); + useMultiTableInsert = !hasIdentifierAssigned( sqmInsert, persister ); } } } @@ -440,7 +440,7 @@ private ReactiveNonSelectQueryPlan buildInsertQueryPlan() { return new ReactiveMultiTableInsertQueryPlan( sqmInsert, getDomainParameterXref(), - (ReactiveSqmMultiTableInsertStrategy) entityDescriptor.getSqmMultiTableInsertStrategy() + (ReactiveSqmMultiTableInsertStrategy) persister.getSqmMultiTableInsertStrategy() ); } } @@ -473,7 +473,7 @@ public ReactiveQuerySqmImpl setLockMode(String alias, LockMode lockMode) { } @Override - public ReactiveQuerySqmImpl setOrder(List> orders) { + public ReactiveQuerySqmImpl setOrder(List> orders) { super.setOrder( orders ); return this; } @@ -508,7 +508,7 @@ public ReactiveQuerySqmImpl setMaxResults(int maxResult) { @Override public ReactiveQuerySqmImpl setFirstResult(int startPosition) { - applyFirstResult( startPosition ); + super.setFirstResult( startPosition ); return this; } @@ -533,13 +533,6 @@ public ReactiveQuerySqmImpl setLockMode(LockModeType lockMode) { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // covariance - - @Override @Deprecated - public ReactiveQuerySqmImpl setAliasSpecificLockMode(String alias, LockMode lockMode) { - super.setAliasSpecificLockMode( alias, lockMode ); - return this; - } - @Override public ReactiveQuerySqmImpl applyGraph(RootGraph graph, GraphSemantic semantic) { super.applyGraph( graph, semantic ); 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 ca82ff284..43271d29e 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 @@ -134,14 +134,10 @@ public CompletionStage executeReactiveUpdate(DomainQueryExecutionContex executionContext.getQueryParameterBindings(), domainParameterXref, jdbcParamsXref, - factory.getRuntimeMetamodels().getMappingMetamodel(), - sqmInterpretation.getFromClauseAccess()::findTableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions() - .get( parameter ); + return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get(parameter); } }, session 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 index 5e4710f93..9d76895b7 100644 --- 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 @@ -15,23 +15,19 @@ 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.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.spi.FromClauseAccess; -import org.hibernate.sql.ast.tree.insert.InsertStatement; -import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; +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; @@ -46,8 +42,7 @@ public class ReactiveSimpleInsertQueryPlan implements ReactiveNonSelectQueryPlan private Map, MappingModelExpressible> paramTypeResolutions; - private JdbcOperationQueryInsert jdbcInsert; - private FromClauseAccess tableGroupAccess; + private JdbcOperationQueryMutation jdbcInsert; private Map, Map, List>> jdbcParamsXref; public ReactiveSimpleInsertQueryPlan( @@ -61,17 +56,14 @@ public ReactiveSimpleInsertQueryPlan( public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmInsert ); final SharedSessionContractImplementor session = executionContext.getSession(); - SqlAstTranslator insertTranslator = null; - if ( jdbcInsert == null ) { - insertTranslator = createInsertTranslator( executionContext ); - } + SqlAstTranslator insertTranslator = jdbcInsert == null + ? createInsertTranslator( executionContext ) + : null; final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), domainParameterXref, jdbcParamsXref, - session.getFactory().getRuntimeMetamodels().getMappingMetamodel(), - tableGroupAccess::findTableGroup, new SqmParameterMappingModelResolutionAccess() { @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { @@ -81,10 +73,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter MappingModelExpressible getResolvedMappingModelType(SqmParameter createInsertTranslator(DomainQueryExecutionContext executionContext) { + // Copied from Hibernate ORM SimpleInsertQueryPlan#createInsertTranslator + private SqlAstTranslator createInsertTranslator(DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final QueryEngine queryEngine = factory.getQueryEngine(); - - final SqmTranslatorFactory translatorFactory = queryEngine.getSqmTranslatorFactory(); - final SqmTranslator translator = translatorFactory.createInsertTranslator( - sqmInsert, - executionContext.getQueryOptions(), - domainParameterXref, - executionContext.getQueryParameterBindings(), - executionContext.getSession().getLoadQueryInfluencers(), - factory - ); - - final SqmTranslation sqmInterpretation = translator.translate(); - tableGroupAccess = sqmInterpretation.getFromClauseAccess(); + final SqmTranslation sqmInterpretation = factory.getQueryEngine().getSqmTranslatorFactory() + .createMutationTranslator( + sqmInsert, + executionContext.getQueryOptions(), + domainParameterXref, + executionContext.getQueryParameterBindings(), + executionContext.getSession().getLoadQueryInfluencers(), + factory + ) + .translate(); this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref( domainParameterXref, @@ -131,6 +116,6 @@ private SqlAstTranslator createInsertTranslator(Domain return factory.getJdbcServices() .getJdbcEnvironment() .getSqlAstTranslatorFactory() - .buildInsertTranslator( factory, sqmInterpretation.getSqlAst() ); + .buildMutationTranslator( factory, sqmInterpretation.getSqlAst() ); } } 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 index c3bc21dc5..962149b5b 100644 --- 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 @@ -29,8 +29,8 @@ 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.update.UpdateStatement; -import org.hibernate.sql.exec.spi.JdbcOperationQueryUpdate; +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; @@ -43,7 +43,7 @@ public class ReactiveSimpleUpdateQueryPlan implements ReactiveNonSelectQueryPlan private final SqmUpdateStatement sqmUpdate; private final DomainParameterXref domainParameterXref; - private JdbcOperationQueryUpdate jdbcUpdate; + private JdbcOperationQueryMutation jdbcUpdate; private FromClauseAccess tableGroupAccess; private Map, Map, List>> jdbcParamsXref; private Map, MappingModelExpressible> sqmParamMappingTypeResolutions; @@ -57,22 +57,17 @@ public ReactiveSimpleUpdateQueryPlan(SqmUpdateStatement sqmUpdate, DomainPara public CompletionStage executeReactiveUpdate(DomainQueryExecutionContext executionContext) { BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmUpdate ); final SharedSessionContractImplementor session = executionContext.getSession(); - SqlAstTranslator updateTranslator = null; - if ( jdbcUpdate == null ) { - updateTranslator = createUpdateTranslator( executionContext ); - } - + SqlAstTranslator updateTranslator = jdbcUpdate == null + ? createUpdateTranslator( executionContext ) + : null; final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), domainParameterXref, jdbcParamsXref, - session.getFactory().getRuntimeMetamodels().getMappingMetamodel(), - tableGroupAccess::findTableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmParamMappingTypeResolutions.get( parameter ); + return (MappingModelExpressible) sqmParamMappingTypeResolutions.get(parameter); } }, session @@ -97,12 +92,12 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter createUpdateTranslator(DomainQueryExecutionContext executionContext) { + 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.createSimpleUpdateTranslator( + final SqmTranslator translator = translatorFactory.createMutationTranslator( sqmUpdate, executionContext.getQueryOptions(), domainParameterXref, @@ -111,13 +106,12 @@ private SqlAstTranslator createUpdateTranslator(Domain factory ); - final SqmTranslation sqmInterpretation = translator.translate(); - + final SqmTranslation sqmInterpretation = translator.translate(); tableGroupAccess = sqmInterpretation.getFromClauseAccess(); this.jdbcParamsXref = SqmUtil .generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam ); this.sqmParamMappingTypeResolutions = sqmInterpretation.getSqmParameterMappingModelTypeResolutions(); return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() - .buildUpdateTranslator( factory, sqmInterpretation.getSqlAst() ); + .buildMutationTranslator( factory, sqmInterpretation.getSqlAst() ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java index 13e167697..d24ecb2db 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java @@ -240,12 +240,6 @@ public ReactiveSqmSelectionQueryImpl setHibernateLockMode(LockMode lockMode) return this; } - @Override @Deprecated - public ReactiveSqmSelectionQueryImpl setAliasSpecificLockMode(String alias, LockMode lockMode) { - super.setAliasSpecificLockMode( alias, lockMode ); - return this; - } - @Override public ReactiveSqmSelectionQueryImpl setLockMode(String alias, LockMode lockMode) { super.setLockMode( alias, lockMode ); @@ -259,7 +253,7 @@ public ReactiveSqmSelectionQueryImpl setFollowOnLocking(boolean enable) { } @Override - public ReactiveSqmSelectionQueryImpl setOrder(List> orders) { + public ReactiveSqmSelectionQueryImpl setOrder(List> orders) { super.setOrder( orders ); return this; } 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 index 37689a33f..b2e36a296 100644 --- 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 @@ -6,7 +6,6 @@ package org.hibernate.reactive.query.sqm.mutation.internal; import java.sql.PreparedStatement; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; @@ -17,14 +16,16 @@ 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.JdbcOperationQueryDelete; +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; /** @@ -35,74 +36,6 @@ public class ReactiveSqmMutationStrategyHelper { private ReactiveSqmMutationStrategyHelper() { } -// public static CompletionStage visitCollectionTables(EntityMappingType entityDescriptor, Consumer consumer) { -// if ( !entityDescriptor.getEntityPersister().hasCollections() ) { -// // none to clean-up -// return voidFuture(); -// } -// -// final CompletableFuture stage = new CompletableFuture<>(); -// try { -// entityDescriptor.visitSubTypeAttributeMappings( -// attributeMapping -> { -// if ( attributeMapping instanceof PluralAttributeMapping ) { -// try { -// consumer.accept( (PluralAttributeMapping) attributeMapping ); -// complete( stage, null ); -// } -// catch (Throwable throwable) { -// complete( stage, throwable ); -// } -// } -// else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { -// visitCollectionTables( (EmbeddedAttributeMapping) attributeMapping, consumer ) -// .whenComplete( (v, throwable) -> complete( stage, throwable ) ); -// } -// else { -// complete( stage, null ); -// } -// } ); -// return stage; -// } -// catch (Throwable throwable) { -// complete( stage, throwable ); -// return stage; -// } -// } - -// private static CompletionStage visitCollectionTables(EmbeddedAttributeMapping attributeMapping, Consumer consumer) { -// final CompletableFuture stage = new CompletableFuture<>(); -// -// try { -// attributeMapping.visitSubParts( -// modelPart -> { -// if ( modelPart instanceof PluralAttributeMapping ) { -// try { -// consumer.accept( (PluralAttributeMapping) modelPart ); -// complete( stage, null ); -// } -// catch (Throwable throwable) { -// complete( stage, throwable ); -// } -// } -// else if ( modelPart instanceof EmbeddedAttributeMapping ) { -// visitCollectionTables( (EmbeddedAttributeMapping) modelPart, consumer ) -// .whenComplete( (v, throwable) -> complete( stage, throwable ) ); -// } -// else { -// complete( stage, null ); -// } -// }, -// null -// ); -// return stage; -// } -// catch (Throwable t) { -// complete( stage, t ); -// return stage; -// } -// } - public static CompletionStage cleanUpCollectionTables( EntityMappingType entityDescriptor, BiFunction restrictionProducer, @@ -113,83 +46,75 @@ public static CompletionStage cleanUpCollectionTables( return voidFuture(); } - final CompletableFuture stage = new CompletableFuture<>(); try { - entityDescriptor.visitSubTypeAttributeMappings( - attributeMapping -> { + final Completable stage = new Completable<>(); + entityDescriptor + .visitSubTypeAttributeMappings( attributeMapping -> { if ( attributeMapping instanceof PluralAttributeMapping ) { cleanUpCollectionTable( (PluralAttributeMapping) attributeMapping, - entityDescriptor, restrictionProducer, jdbcParameterBindings, executionContext - ).whenComplete( (v, throwable) -> complete( stage, throwable ) ); + ).handle( stage::complete ); } else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { cleanUpCollectionTables( (EmbeddedAttributeMapping) attributeMapping, - entityDescriptor, restrictionProducer, jdbcParameterBindings, executionContext - ).whenComplete( (v, throwable) -> complete( stage, throwable ) ); + ).handle( stage::complete ); } else { - complete( stage, null ); + stage.complete( null, null ); } } ); - return stage; + return stage.getStage(); } catch (Throwable throwable) { - complete( stage, throwable ); - return stage; + return failedFuture( throwable ); } } private static CompletionStage cleanUpCollectionTables( EmbeddedAttributeMapping attributeMapping, - EntityMappingType entityDescriptor, BiFunction restrictionProducer, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { - final CompletableFuture stage = new CompletableFuture<>(); try { + final Completable completable = new Completable<>(); attributeMapping.visitSubParts( modelPart -> { if ( modelPart instanceof PluralAttributeMapping ) { cleanUpCollectionTable( (PluralAttributeMapping) modelPart, - entityDescriptor, restrictionProducer, jdbcParameterBindings, executionContext - ); + ).handle( completable::complete ); } else if ( modelPart instanceof EmbeddedAttributeMapping ) { cleanUpCollectionTables( (EmbeddedAttributeMapping) modelPart, - entityDescriptor, restrictionProducer, jdbcParameterBindings, executionContext - ); + ).handle( completable::complete ); } }, null ); - return stage; + return completable.getStage(); } catch (Throwable throwable) { - complete( stage, throwable ); - return stage; + return failedFuture( throwable ); } } private static CompletionStage cleanUpCollectionTable( PluralAttributeMapping attributeMapping, - EntityMappingType entityDescriptor, BiFunction restrictionProducer, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { @@ -216,9 +141,9 @@ private static CompletionStage cleanUpCollectionTable( restrictionProducer.apply( tableReference, attributeMapping ) ); - JdbcOperationQueryDelete jdbcDelete = jdbcServices.getJdbcEnvironment() + JdbcOperationQueryMutation jdbcDelete = jdbcServices.getJdbcEnvironment() .getSqlAstTranslatorFactory() - .buildDeleteTranslator( sessionFactory, sqlAstDelete ) + .buildMutationTranslator( sessionFactory, sqlAstDelete ) .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); return StandardReactiveJdbcMutationExecutor.INSTANCE .executeReactive( @@ -231,15 +156,6 @@ private static CompletionStage cleanUpCollectionTable( .thenCompose( CompletionStages::voidFuture ); } - private static void complete(CompletableFuture stage, Throwable throwable) { - if ( throwable == null ) { - stage.complete( null ); - } - else { - stage.completeExceptionally( throwable ); - } - } - 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 d1ea9026f..74f278fe7 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 @@ -117,8 +117,7 @@ default CompletionStage reactiveExecute(DomainQueryExecutionContext exe true, restriction, sqmConverter, - executionContext, - factory + executionContext ), // The id-select cte will be reused multiple times CteMaterialization.MATERIALIZED @@ -159,8 +158,6 @@ default CompletionStage reactiveExecute(DomainQueryExecutionContext exe executionContext.getQueryParameterBindings(), getDomainParameterXref(), SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), sqmConverter ), - factory.getRuntimeMetamodels().getMappingMetamodel(), - navigablePath -> sqmConverter.getMutatingTableGroup(), new SqmParameterMappingModelResolutionAccess() { @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { 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 aaf1be3e9..5a4b1c5af 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 @@ -8,8 +8,6 @@ import java.lang.invoke.MethodHandles; import java.util.AbstractMap; 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; @@ -25,7 +23,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.results.TableGroupImpl; +import org.hibernate.query.results.internal.TableGroupImpl; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.BinaryArithmeticOperator; import org.hibernate.query.sqm.ComparisonOperator; @@ -62,7 +60,6 @@ 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.JdbcParameter; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression; import org.hibernate.sql.ast.tree.from.NamedTableReference; @@ -127,14 +124,6 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec ); final TableGroup insertingTableGroup = sqmConverter.getMutatingTableGroup(); - final Map, List>> parameterResolutions; - if ( getDomainParameterXref().getSqmParameterCount() == 0 ) { - parameterResolutions = Collections.emptyMap(); - } - else { - parameterResolutions = new IdentityHashMap<>(); - } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // visit the insertion target using our special converter, collecting // information about the target paths @@ -288,11 +277,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec targetPathCteColumns.add( rowNumberColumn ); } - final CteTable entityCteTable = createCteTable( - getCteTable(), - targetPathCteColumns, - factory - ); + 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 ); @@ -463,11 +448,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec } else { targetPathCteColumns.add( 0, getCteTable().getCteColumns().get( 0 ) ); - finalEntityCteTable = createCteTable( - getCteTable(), - targetPathCteColumns, - factory - ); + finalEntityCteTable = createCteTable( getCteTable(), targetPathCteColumns ); } final List cteColumns = finalEntityCteTable.getCteColumns(); for ( int i = 1; i < cteColumns.size(); i++ ) { @@ -506,11 +487,7 @@ else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) ); statement.addCteStatement( baseEntityCte ); targetPathCteColumns.add( 0, getCteTable().getCteColumns().get( 0 ) ); - final CteTable finalEntityCteTable = createCteTable( - getCteTable(), - targetPathCteColumns, - factory - ); + final CteTable finalEntityCteTable = createCteTable( getCteTable(), targetPathCteColumns ); final QuerySpec finalQuerySpec = new QuerySpec( true ); final SelectStatement finalQueryStatement = new SelectStatement( finalQuerySpec ); entityCte = new CteStatement( @@ -537,7 +514,6 @@ else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) targetPathColumns, assignsId, sqmConverter, - parameterResolutions, factory ); @@ -564,12 +540,12 @@ else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) executionContext.getQueryParameterBindings(), getDomainParameterXref(), SqmUtil.generateJdbcParamsXref( getDomainParameterXref(), sqmConverter ), - factory.getRuntimeMetamodels().getMappingMetamodel(), - navigablePath -> sqmConverter.getMutatingTableGroup(), new SqmParameterMappingModelResolutionAccess() { @Override + @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmConverter.getSqmParameterMappingModelExpressibleResolutions().get( parameter ); + return (MappingModelExpressible) sqmConverter.getSqmParameterMappingModelExpressibleResolutions() + .get( parameter ); } }, executionContext.getSession() 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 3644af942..33acc38f6 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 @@ -15,9 +15,8 @@ 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.AfterUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.InsertExecutionDelegate; -import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; +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; @@ -32,9 +31,7 @@ */ public class ReactiveInsertExecutionDelegate extends InsertExecutionDelegate implements ReactiveTableBasedInsertHandler.ReactiveExecutionDelegate { - public ReactiveInsertExecutionDelegate( - SqmInsertStatement sqmInsert, MultiTableSqmMutationConverter sqmConverter, TemporaryTable entityTable, AfterUseAction afterUseAction, @@ -48,7 +45,6 @@ public ReactiveInsertExecutionDelegate( JdbcParameter sessionUidParameter, DomainQueryExecutionContext executionContext) { super( - sqmInsert, sqmConverter, entityTable, afterUseAction, 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 7f1444496..e8f7bbea6 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 @@ -26,8 +26,8 @@ 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.AfterUseAction; -import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction; +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.query.sqm.mutation.internal.temptable.ReactiveTemporaryTableHelper.TemporaryTableCreationWork; @@ -47,7 +47,7 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.internal.SqlSelectionImpl; @@ -162,7 +162,7 @@ public static CompletionStage saveIntoTemporaryTable( ) ); } - final JdbcOperationQueryInsert jdbcInsert = sqlAstTranslatorFactory.buildInsertTranslator( factory, temporaryTableInsert ) + final JdbcOperationQueryMutation jdbcInsert = sqlAstTranslatorFactory.buildMutationTranslator( factory, temporaryTableInsert ) .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); lockOptions.setLockMode( lockMode ); @@ -210,7 +210,7 @@ public static QuerySpec createIdTableSelectQuerySpec( querySpec.getFromClause().addRoot( idTableGroup ); - applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart, executionContext ); + applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart ); applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidAccess, executionContext ); return querySpec; @@ -221,8 +221,7 @@ private static void applyIdTableSelections( QuerySpec querySpec, TableReference tableReference, TemporaryTable idTable, - ModelPart fkModelPart, - ExecutionContext executionContext) { + ModelPart fkModelPart) { if ( fkModelPart == null ) { final int size = idTable.getEntityDescriptor().getIdentifierMapping().getJdbcTypeCount(); for ( int i = 0; i < size; i++ ) { @@ -245,20 +244,18 @@ private static void applyIdTableSelections( } else { fkModelPart.forEachSelectable( - (i, selectableMapping) -> { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - i, - new ColumnReference( - tableReference, - selectableMapping.getSelectionExpression(), - false, - null, - selectableMapping.getJdbcMapping() - ) - ) - ); - } + (i, selectableMapping) -> querySpec.getSelectClause() + .addSqlSelection( new SqlSelectionImpl( + i, + new ColumnReference( + tableReference, + selectableMapping.getSelectionExpression(), + false, + null, + selectableMapping.getJdbcMapping() + ) + ) + ) ); } } @@ -275,8 +272,7 @@ private static void applyIdTableRestrictions( new ColumnReference( idTableReference, idTable.getSessionUidColumn().getColumnName(), - false, - null, + false, null, idTable.getSessionUidColumn().getJdbcMapping() ), ComparisonOperator.EQUAL, 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 3d6681881..ae2298ddd 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 @@ -10,8 +10,8 @@ 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.AfterUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableInsertStrategy; 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 983a21043..fe07871eb 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 @@ -10,8 +10,8 @@ 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.AfterUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; 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; 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 61a866756..dc2a833c4 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 @@ -27,7 +27,6 @@ import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.Joinable; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; @@ -36,10 +35,10 @@ 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.AfterUseAction; 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; @@ -48,6 +47,7 @@ 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; @@ -65,11 +65,10 @@ 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.JdbcOperationQueryDelete; +import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import static org.hibernate.query.sqm.mutation.internal.temptable.ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec; -import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -127,7 +126,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec final EntityPersister entityDescriptor = sessionFactory.getRuntimeMetamodels() .getMappingMetamodel() .getEntityDescriptor( sqmDelete.getTarget().getEntityName() ); - final String hierarchyRootTableName = ( (Joinable) entityDescriptor ).getTableName(); + final String hierarchyRootTableName = entityDescriptor.getTableName(); final TableGroup deletingTableGroup = converter.getMutatingTableGroup(); @@ -150,6 +149,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec deletingTableGroup, true, executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), + false, null, converter ); @@ -175,7 +175,6 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec if ( needsIdTable ) { return executeWithIdTable( predicateCollector.getPredicate(), - deletingTableGroup, converter.getJdbcParamsBySqmParam(), converter.getSqmParameterMappingModelExpressibleResolutions(), executionContextAdapter @@ -203,7 +202,7 @@ private CompletionStage executeWithoutIdTable( assert entityDescriptor == entityDescriptor.getRootEntityDescriptor(); final EntityPersister rootEntityPersister = entityDescriptor.getEntityPersister(); - final String rootTableName = ( (Joinable) rootEntityPersister ).getTableName(); + final String rootTableName = rootEntityPersister.getTableName(); final NamedTableReference rootTableReference = (NamedTableReference) tableGroup.resolveTableReference( tableGroup.getNavigablePath(), rootTableName @@ -225,18 +224,16 @@ private CompletionStage executeWithoutIdTable( domainParameterXref, () -> restrictionSqmParameterResolutions ), - sessionFactory.getRuntimeMetamodels().getMappingMetamodel(), - navigablePath -> tableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) paramTypeResolutions.get( parameter ); + return (MappingModelExpressible) paramTypeResolutions.get(parameter); } }, executionContext.getSession() ); + CompletionStage cleanUpCollectionTablesStage = ReactiveSqmMutationStrategyHelper.cleanUpCollectionTables( entityDescriptor, (tableReference, attributeMapping) -> { @@ -480,9 +477,9 @@ private static CompletionStage executeSqlDelete( final JdbcServices jdbcServices = factory.getJdbcServices(); - final JdbcOperationQueryDelete jdbcDelete = jdbcServices.getJdbcEnvironment() + final JdbcOperationQueryMutation jdbcDelete = jdbcServices.getJdbcEnvironment() .getSqlAstTranslatorFactory() - .buildDeleteTranslator( factory, sqlAst ) + .buildMutationTranslator( factory, sqlAst ) .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); return StandardReactiveJdbcMutationExecutor.INSTANCE @@ -500,7 +497,6 @@ private static CompletionStage executeSqlDelete( private CompletionStage executeWithIdTable( Predicate predicate, - TableGroup deletingTableGroup, Map, List>> restrictionSqmParameterResolutions, Map, MappingModelExpressible> paramTypeResolutions, ExecutionContext executionContext) { @@ -511,13 +507,10 @@ private CompletionStage executeWithIdTable( domainParameterXref, () -> restrictionSqmParameterResolutions ), - sessionFactory.getRuntimeMetamodels().getMappingMetamodel(), - navigablePath -> deletingTableGroup, new SqmParameterMappingModelResolutionAccess() { - @Override - @SuppressWarnings("unchecked") + @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) paramTypeResolutions.get( parameter ); + return (MappingModelExpressible) paramTypeResolutions.get(parameter); } }, executionContext.getSession() @@ -589,20 +582,20 @@ private CompletionStage executeUsingIdTable( } ); } - private CompletionStage visitConstraintOrderedTables(QuerySpec idTableIdentifierSubQuery, ExecutionContext executionContext) { - final CompletionStage[] resultStage = new CompletionStage[]{ completedFuture( -1 ) }; - entityDescriptor.visitConstraintOrderedTables( - (tableExpression, tableKeyColumnVisitationSupplier) -> { - resultStage[0] = resultStage[0].thenCompose( ignore -> deleteFromTableUsingIdTable( - tableExpression, - tableKeyColumnVisitationSupplier, - idTableIdentifierSubQuery, - executionContext - ) ); - } - ); - return resultStage[0] - .thenCompose( CompletionStages::voidFuture ); + private CompletionStage visitConstraintOrderedTables( + QuerySpec idTableIdentifierSubQuery, + ExecutionContext executionContext) { + final Completable completable = new Completable<>(); + entityDescriptor + .visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> deleteFromTableUsingIdTable( + tableExpression, + tableKeyColumnVisitationSupplier, + idTableIdentifierSubQuery, + executionContext + ) + .handle( completable::complete ) + ); + return completable.getStage().thenCompose( CompletionStages::voidFuture ); } private CompletionStage deleteFromTableUsingIdTable( 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 c7af71107..bb9ceff9c 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 @@ -14,8 +14,8 @@ 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.AfterUseAction; 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; 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 b27340418..d697758ed 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 @@ -18,8 +18,8 @@ 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.AfterUseAction; 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; @@ -92,7 +92,6 @@ protected ExecutionDelegate buildExecutionDelegate( JdbcParameter sessionUidParameter, DomainQueryExecutionContext executionContext) { return new ReactiveInsertExecutionDelegate( - sqmInsert, sqmConverter, entityTable, afterUseAction, 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 a247c8d7d..7379f962e 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 @@ -18,8 +18,8 @@ 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.AfterUseAction; 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; 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 d027235a2..cc05cef3d 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 @@ -20,7 +20,6 @@ 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.util.impl.CompletionStages; @@ -144,10 +143,7 @@ public static CompletionStage cleanTemporaryTableRows( TemporaryTableExporter exporter, Function sessionUidAccess, SharedSessionContractImplementor session) { - // Workaround for https://hibernate.atlassian.net/browse/HHH-16486 - final String sql = Parameters.instance( temporaryTable.getDialect() ) - .process( exporter.getSqlTruncateCommand( temporaryTable, sessionUidAccess, session ) ); - + final String sql = exporter.getSqlTruncateCommand( temporaryTable, sessionUidAccess, session ); Object[] params = PreparedStatementAdaptor.bind( ps -> { if ( temporaryTable.getSessionUidColumn() != null ) { final String sessionUid = sessionUidAccess.apply( session ); 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 f70e5c794..927105dfa 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 @@ -23,8 +23,8 @@ 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.AfterUseAction; 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; @@ -44,8 +44,7 @@ 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.JdbcOperationQueryInsert; -import org.hibernate.sql.exec.spi.JdbcOperationQueryUpdate; +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; @@ -200,8 +199,8 @@ private CompletionStage executeUpdate(QuerySpec idTableSubQuery, Execut new InSubQueryPredicate( keyExpression, idTableSubQuery, false ) ); - final JdbcOperationQueryUpdate jdbcUpdate = sqlAstTranslatorFactory - .buildUpdateTranslator( getSessionFactory(), sqlAst ) + final JdbcOperationQueryMutation jdbcUpdate = sqlAstTranslatorFactory + .buildMutationTranslator( getSessionFactory(), sqlAst ) .translate( getJdbcParameterBindings(), executionContext.getQueryOptions() ); return StandardReactiveJdbcMutationExecutor.INSTANCE @@ -274,8 +273,8 @@ private CompletionStage executeInsert( insertSqlAst.addTargetColumnReferences( targetColumnReferences.toArray( new ColumnReference[0] ) ); insertSqlAst.setSourceSelectStatement( insertSourceSelectQuerySpec ); - final JdbcOperationQueryInsert jdbcInsert = sqlAstTranslatorFactory - .buildInsertTranslator( getSessionFactory(), insertSqlAst ) + final JdbcOperationQueryMutation jdbcInsert = sqlAstTranslatorFactory + .buildMutationTranslator( getSessionFactory(), insertSqlAst ) .translate( getJdbcParameterBindings(), executionContext.getQueryOptions() ); return StandardReactiveJdbcMutationExecutor.INSTANCE diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index d521f2e8c..94b95318f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -50,6 +50,8 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactivePersist(Object entity); + CompletionStage reactivePersist(String entityName, Object object); + CompletionStage reactivePersist(Object object, PersistContext copiedAlready); CompletionStage reactivePersistOnFlush(Object entity, PersistContext copiedAlready); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionFactoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionFactoryImpl.java index 85c921c7c..f8029e297 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionFactoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/impl/ReactiveSessionFactoryImpl.java @@ -9,7 +9,6 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.internal.SessionFactoryImpl; -import org.hibernate.metamodel.spi.RuntimeMetamodelsImplementor; import org.hibernate.query.spi.QueryEngine; import org.hibernate.reactive.boot.spi.ReactiveMetadataImplementor; import org.hibernate.reactive.mutiny.Mutiny; @@ -28,11 +27,6 @@ public ReactiveSessionFactoryImpl(MetadataImplementor bootMetamodel, SessionFact super( new ReactiveMetadataImplementor( bootMetamodel ), options, bootstrapContext ); } - @Override - public RuntimeMetamodelsImplementor getRuntimeMetamodels() { - return super.getRuntimeMetamodels(); - } - @Override public QueryEngine getQueryEngine() { return super.getQueryEngine(); 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 f3a24808d..c746c9547 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 @@ -339,7 +339,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } } - return createCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); + return createReactiveCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); } catch (RuntimeException e) { if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { @@ -349,7 +349,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } } - private ReactiveQueryImplementor createCriteriaQuery(SqmStatement criteria, Class resultType) { + protected ReactiveQueryImplementor createReactiveCriteriaQuery(SqmStatement criteria, Class resultType) { final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( criteria, resultType, this ); applyQuerySettingsAndHints( query ); return query; @@ -524,7 +524,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(String hqlString public ReactiveMutationQuery createReactiveMutationQuery(CriteriaUpdate updateQuery) { checkOpen(); try { - return createCriteriaQuery( (SqmUpdateStatement) updateQuery, null ); + return createReactiveCriteriaQuery( (SqmUpdateStatement) updateQuery, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); @@ -535,7 +535,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaUpdate ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete deleteQuery) { checkOpen(); try { - return createCriteriaQuery( (SqmDeleteStatement) deleteQuery, null ); + return createReactiveCriteriaQuery( (SqmDeleteStatement) deleteQuery, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); @@ -546,7 +546,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsertSelect insertSelect) { checkOpen(); try { - return createCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); + return createReactiveCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); @@ -715,6 +715,12 @@ public CompletionStage reactivePersist(Object entity) { return firePersist( new PersistEvent( null, entity, this ) ); } + @Override + public CompletionStage reactivePersist(String entityName, Object entity) { + checkOpen(); + return firePersist( new PersistEvent( entityName, entity, this ) ); + } + @Override public CompletionStage reactivePersist(Object object, PersistContext copiedAlready) { checkOpenOrWaitingForAutoClose(); @@ -1376,11 +1382,17 @@ private class ReactiveMultiIdentifierLoadAccessImpl implements MultiIdLoadOpt private boolean sessionCheckingEnabled; private boolean returnOfDeletedEntitiesEnabled; private boolean orderedReturnEnabled = true; + private boolean readOnly; public ReactiveMultiIdentifierLoadAccessImpl(EntityPersister entityPersister) { this.entityPersister = entityPersister; } + @Override + public Boolean getReadOnly(SessionImplementor session) { + return session.getLoadQueryInfluencers().getReadOnly(); + } + public ReactiveMultiIdentifierLoadAccessImpl(Class entityClass) { this( getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ) ); } @@ -1456,7 +1468,6 @@ public ReactiveMultiIdentifierLoadAccessImpl enableOrderedReturn(boolean enab return this; } - @SuppressWarnings("unchecked") public CompletionStage> multiLoad(Object... ids) { Object[] sids = new Object[ids.length]; System.arraycopy( ids, 0, sids, 0, ids.length ); 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 06faba07f..5e02b9389 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 @@ -26,11 +26,16 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.spi.PostInsertEvent; +import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.event.spi.PreInsertEvent; +import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.internal.RootGraphImpl; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.id.IdentifierGenerationException; import org.hibernate.internal.SessionCreationOptions; import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.StatelessSessionImpl; @@ -76,6 +81,7 @@ import org.hibernate.reactive.session.ReactiveSqmQueryImplementor; import org.hibernate.reactive.session.ReactiveStatelessSession; import org.hibernate.reactive.util.impl.CompletionStages; +import org.hibernate.stat.spi.StatisticsImplementor; import jakarta.persistence.EntityGraph; import jakarta.persistence.Tuple; @@ -253,10 +259,20 @@ public ReactiveEntityPersister getEntityPersister(String entityName, Object obje public CompletionStage reactiveInsert(Object entity) { checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( null, entity ); + return reactiveInsert( entity, persister ) + .thenAccept( v -> { + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.insertEntity( persister.getEntityName() ); + } + } ); + } + + private CompletionStage reactiveInsert(Object entity, ReactiveEntityPersister persister) { final Object[] state = persister.getValues( entity ); final Generator generator = persister.getGenerator(); if ( !generator.generatedOnExecution() ) { - return generateId( entity, generator ) + return generateId( persister, entity, generator ) .thenCompose( generatedId -> { final Object id = castToIdentifierType( generatedId, persister ); if ( persister.isVersioned() ) { @@ -264,21 +280,76 @@ public CompletionStage reactiveInsert(Object entity) { persister.setValues( entity, state ); } } - return persister.insertReactive( id, state, entity, this ) - .thenAccept( ignore -> persister.setIdentifier( entity, id, this ) ); + if ( firePreInsert( entity, id, state, persister ) ) { + return voidFuture(); + } + getInterceptor() + .onInsert( + entity, + id, + state, + persister.getPropertyNames(), + persister.getPropertyTypes() + ); + return persister + .insertReactive( id, state, entity, this ) + .thenAccept( ignore -> { + persister.setIdentifier( entity, id, this ); + firePostInsert( entity, id, state, persister ); + } ); } ); } else { - return persister.insertReactive( state, entity, this ) - .thenAccept( id -> persister.setIdentifier( entity, id, this ) ); + if ( firePreInsert( entity, null, state, persister ) ) { + return voidFuture(); + } + getInterceptor() + .onInsert( entity, null, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + return persister + .insertReactive( state, entity, this ) + .thenAccept( id -> { + persister.setIdentifier( entity, id, this ); + firePostInsert( entity, id, state, persister ); + } ); } } - private CompletionStage generateId(Object entity, Generator generator) { - return generator instanceof ReactiveIdentifierGenerator - ? ( (ReactiveIdentifierGenerator) generator ).generate( this, this ) - : completedFuture( ( (BeforeExecutionGenerator) generator ) - .generate( this, entity, null, INSERT ) ); + private boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) { + if ( fastSessionServices.eventListenerGroup_PRE_INSERT.isEmpty() ) { + return false; + } + else { + boolean veto = false; + final PreInsertEvent event = new PreInsertEvent( entity, id, state, persister, null ); + for ( PreInsertEventListener listener : fastSessionServices.eventListenerGroup_PRE_INSERT.listeners() ) { + veto |= listener.onPreInsert( event ); + } + return veto; + } + } + + private void firePostInsert(Object entity, Object id, Object[] state, EntityPersister persister) { + if ( !fastSessionServices.eventListenerGroup_POST_INSERT.isEmpty() ) { + final PostInsertEvent event = new PostInsertEvent( entity, id, state, persister, null ); + for ( PostInsertEventListener listener : fastSessionServices.eventListenerGroup_POST_INSERT.listeners() ) { + listener.onPostInsert( event ); + } + } + } + + private CompletionStage generateId(EntityPersister persister, Object entity, Generator generator) { + if ( generator.generatesOnInsert() ) { + return generator instanceof ReactiveIdentifierGenerator + ? ( (ReactiveIdentifierGenerator) generator ).generate( this, this ) + : completedFuture( ( (BeforeExecutionGenerator) generator ).generate( this, entity, null, INSERT ) ); + } + else { + Object id = persister.getIdentifier( entity, this ); + if ( id == null ) { + throw new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ); + } + return completedFuture( id ); + } } @Override @@ -720,7 +791,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } } - return createCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); + return createReactiveCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); } catch (RuntimeException e) { if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { @@ -730,7 +801,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) } } - private ReactiveQuery createCriteriaQuery(SqmStatement criteria, Class resultType) { + private ReactiveQuery createReactiveCriteriaQuery(SqmStatement criteria, Class resultType) { final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( criteria, resultType, this ); applyQuerySettingsAndHints( query ); return query; @@ -908,7 +979,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(String hqlString public ReactiveMutationQuery createReactiveMutationQuery(CriteriaUpdate updateQuery) { checkOpen(); try { - return createCriteriaQuery( (SqmUpdateStatement) updateQuery, null ); + return createReactiveCriteriaQuery( (SqmUpdateStatement) updateQuery, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); @@ -919,7 +990,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaUpdate ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete deleteQuery) { checkOpen(); try { - return createCriteriaQuery( (SqmDeleteStatement) deleteQuery, null ); + return createReactiveCriteriaQuery( (SqmDeleteStatement) deleteQuery, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); @@ -930,7 +1001,7 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsertSelect insertSelect) { checkOpen(); try { - return createCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); + return createReactiveCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveJdbcMutationExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveJdbcMutationExecutor.java index ea272765a..9182d4939 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveJdbcMutationExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveJdbcMutationExecutor.java @@ -12,8 +12,6 @@ import java.util.function.BiConsumer; import java.util.function.Function; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.QueryOptions; @@ -22,7 +20,6 @@ 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.sql.exec.spi.ReactiveJdbcMutationExecutor; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; @@ -117,7 +114,7 @@ private static String finalSql( ExecutionContext executionContext, JdbcServices jdbcServices, QueryOptions queryOptions) { - String sql = queryOptions == null + return queryOptions == null ? jdbcMutation.getSqlString() : jdbcServices.getDialect() .addSqlHintOrComment( @@ -128,7 +125,5 @@ private static String finalSql( .getSessionFactoryOptions() .isCommentsEnabled() ); - final Dialect dialect = DialectDelegateWrapper.extractRealDialect( executionContext.getSession().getJdbcServices().getDialect() ); - return Parameters.instance( dialect ).process( sql ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveDeleteOrUpsertOperation.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveDeleteOrUpsertOperation.java index d837af865..10d3cd3bc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveDeleteOrUpsertOperation.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveDeleteOrUpsertOperation.java @@ -38,8 +38,6 @@ public class ReactiveDeleteOrUpsertOperation extends DeleteOrUpsertOperation implements ReactiveSelfExecutingUpdateOperation { private static final Log LOG = make( Log.class, lookup() ); - private final OptionalTableUpdate upsert; - private final UpsertOperation upsertOperation; public ReactiveDeleteOrUpsertOperation( EntityMutationTarget mutationTarget, @@ -47,8 +45,10 @@ public ReactiveDeleteOrUpsertOperation( UpsertOperation upsertOperation, OptionalTableUpdate optionalTableUpdate) { super( mutationTarget, tableMapping, upsertOperation, optionalTableUpdate ); - this.upsert = optionalTableUpdate; - this.upsertOperation = upsertOperation; + } + + public ReactiveDeleteOrUpsertOperation(DeleteOrUpsertOperation original) { + super( original ); } @Override @@ -92,10 +92,7 @@ private CompletionStage performReactiveUpsert( final String tableName = getTableDetails().getTableName(); MODEL_MUTATION_LOGGER.tracef( "#performReactiveUpsert(%s)", tableName ); - final PreparedStatementGroupSingleTable statementGroup = new PreparedStatementGroupSingleTable( - upsertOperation, - session - ); + final PreparedStatementGroupSingleTable statementGroup = new PreparedStatementGroupSingleTable( getUpsertOperation(), session ); final PreparedStatementDetails statementDetails = statementGroup.resolvePreparedStatementDetails( tableName ); session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() ); @@ -123,10 +120,10 @@ private CompletionStage performReactiveDelete( MODEL_MUTATION_LOGGER.tracef( "#performReactiveDelete(%s)", tableName ); final TableDeleteStandard upsertDeleteAst = new TableDeleteStandard( - upsert.getMutatingTable(), + getOptionalTableUpdate().getMutatingTable(), getMutationTarget(), "upsert delete", - upsert.getKeyBindings(), + getOptionalTableUpdate().getKeyBindings(), emptyList(), emptyList() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveOptionalTableUpdateOperation.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveOptionalTableUpdateOperation.java index 6fea783a3..366d09d48 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveOptionalTableUpdateOperation.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/model/ReactiveOptionalTableUpdateOperation.java @@ -6,16 +6,13 @@ package org.hibernate.reactive.sql.model; import java.sql.SQLException; -import java.util.Collections; import java.util.concurrent.CompletionStage; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; -import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions; import org.hibernate.engine.jdbc.mutation.internal.PreparedStatementGroupSingleTable; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.persister.entity.mutation.UpdateValuesAnalysis; import org.hibernate.reactive.adaptor.impl.PrepareStatementDetailsAdaptor; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; @@ -23,22 +20,10 @@ import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.util.impl.CompletionStages; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.model.MutationTarget; import org.hibernate.sql.model.TableMapping; import org.hibernate.sql.model.ValuesAnalysis; -import org.hibernate.sql.model.ast.MutatingTableReference; -import org.hibernate.sql.model.ast.TableDelete; -import org.hibernate.sql.model.ast.TableInsert; -import org.hibernate.sql.model.ast.TableUpdate; import org.hibernate.sql.model.internal.OptionalTableUpdate; -import org.hibernate.sql.model.internal.TableDeleteCustomSql; -import org.hibernate.sql.model.internal.TableDeleteStandard; -import org.hibernate.sql.model.internal.TableInsertCustomSql; -import org.hibernate.sql.model.internal.TableInsertStandard; -import org.hibernate.sql.model.internal.TableUpdateCustomSql; -import org.hibernate.sql.model.internal.TableUpdateStandard; import org.hibernate.sql.model.jdbc.JdbcDeleteMutation; import org.hibernate.sql.model.jdbc.JdbcInsertMutation; import org.hibernate.sql.model.jdbc.JdbcMutationOperation; @@ -196,43 +181,6 @@ private CompletionStage performReactiveUpdate( } ); } - // FIXME: Adding this to ORM will save us some duplicated code (similar to createJdbcInsert and createJdbcDelete) - private JdbcMutationOperation createJdbcUpdate(SharedSessionContractImplementor session) { - MutationTarget mutationTarget = super.getMutationTarget(); - TableUpdate tableUpdate; - if ( getTableDetails().getUpdateDetails() != null - && getTableDetails().getUpdateDetails().getCustomSql() != null ) { - tableUpdate = new TableUpdateCustomSql( - new MutatingTableReference( getTableDetails() ), - mutationTarget, - "upsert update for " + mutationTarget.getRolePath(), - upsert.getValueBindings(), - upsert.getKeyBindings(), - upsert.getOptimisticLockBindings(), - upsert.getParameters() - ); - } - else { - tableUpdate = new TableUpdateStandard( - new MutatingTableReference( getTableDetails() ), - mutationTarget, - "upsert update for " + mutationTarget.getRolePath(), - upsert.getValueBindings(), - upsert.getKeyBindings(), - upsert.getOptimisticLockBindings(), - upsert.getParameters() - ); - } - - final SqlAstTranslator translator = session - .getJdbcServices() - .getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildModelMutationTranslator( tableUpdate, session.getFactory() ); - - return translator.translate( null, MutationQueryOptions.INSTANCE ); - } - private CompletionStage performReactiveInsert( JdbcValueBindings jdbcValueBindings, SharedSessionContractImplementor session) { @@ -249,85 +197,4 @@ private CompletionStage performReactiveInsert( ReactiveConnection reactiveConnection = ( (ReactiveConnectionSupplier) session ).getReactiveConnection(); return reactiveConnection.update( statementDetails.getSqlString(), params ).thenCompose(CompletionStages::voidFuture); } - - /** - * @see org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation#createJdbcInsert(SharedSessionContractImplementor) - */ - // FIXME: change visibility to protected in ORM and remove this method - private JdbcInsertMutation createJdbcInsert(SharedSessionContractImplementor session) { - final TableInsert tableInsert; - if ( getTableDetails().getInsertDetails() != null - && getTableDetails().getInsertDetails().getCustomSql() != null ) { - tableInsert = new TableInsertCustomSql( - new MutatingTableReference( getTableDetails() ), - getMutationTarget(), - CollectionHelper.combine( upsert.getValueBindings(), upsert.getKeyBindings() ), - upsert.getParameters() - ); - } - else { - tableInsert = new TableInsertStandard( - new MutatingTableReference( getTableDetails() ), - getMutationTarget(), - CollectionHelper.combine( upsert.getValueBindings(), upsert.getKeyBindings() ), - Collections.emptyList(), - upsert.getParameters() - ); - } - - final SessionFactoryImplementor factory = session.getSessionFactory(); - final SqlAstTranslatorFactory sqlAstTranslatorFactory = factory - .getJdbcServices() - .getJdbcEnvironment() - .getSqlAstTranslatorFactory(); - - final SqlAstTranslator translator = sqlAstTranslatorFactory.buildModelMutationTranslator( - tableInsert, - factory - ); - - return translator.translate( null, MutationQueryOptions.INSTANCE ); - } - - /** - * @see org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation#createJdbcDelete(SharedSessionContractImplementor) - */ - // FIXME: change visibility to protected in ORM and remove this method - private JdbcDeleteMutation createJdbcDelete(SharedSessionContractImplementor session) { - final TableDelete tableDelete; - if ( getTableDetails().getDeleteDetails() != null - && getTableDetails().getDeleteDetails().getCustomSql() != null ) { - tableDelete = new TableDeleteCustomSql( - new MutatingTableReference( getTableDetails() ), - getMutationTarget(), - "upsert delete for " + upsert.getMutationTarget().getRolePath(), - upsert.getKeyBindings(), - upsert.getOptimisticLockBindings(), - upsert.getParameters() - ); - } - else { - tableDelete = new TableDeleteStandard( - new MutatingTableReference( getTableDetails() ), - getMutationTarget(), - "upsert delete for " + getMutationTarget().getRolePath(), - upsert.getKeyBindings(), - upsert.getOptimisticLockBindings(), - upsert.getParameters() - ); - } - - final SessionFactoryImplementor factory = session.getSessionFactory(); - final SqlAstTranslatorFactory sqlAstTranslatorFactory = factory - .getJdbcServices() - .getJdbcEnvironment() - .getSqlAstTranslatorFactory(); - - final SqlAstTranslator translator = sqlAstTranslatorFactory.buildModelMutationTranslator( - tableDelete, - factory - ); - - return translator.translate( null, MutationQueryOptions.INSTANCE ); - } } 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 ea440fb80..a4b45101f 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 @@ -16,9 +16,9 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; +import org.hibernate.query.results.LegacyFetchBuilder; import org.hibernate.query.results.ResultBuilder; import org.hibernate.query.results.ResultSetMapping; -import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.sql.results.internal.ReactiveResultSetAccess; @@ -88,7 +88,7 @@ public void visitResultBuilders(BiConsumer resultBuilder } @Override - public void visitLegacyFetchBuilders(Consumer resultBuilderConsumer) { + public void visitLegacyFetchBuilders(Consumer resultBuilderConsumer) { delegate.visitLegacyFetchBuilders( resultBuilderConsumer ); } @@ -98,7 +98,7 @@ public void addResultBuilder(ResultBuilder resultBuilder) { } @Override - public void addLegacyFetchBuilder(DynamicFetchBuilderLegacy fetchBuilder) { + public void addLegacyFetchBuilder(LegacyFetchBuilder fetchBuilder) { delegate.addLegacyFetchBuilder( fetchBuilder ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityFetchSelectImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityFetchSelectImpl.java index 0cae0a79d..ce38df6e1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityFetchSelectImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityFetchSelectImpl.java @@ -32,7 +32,7 @@ public EntityInitializer createInitializer(InitializerParent parent, Assem } @Override - protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) { + protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) { return new ReactiveEntityAssembler( getFetchedMapping().getJavaType(), entityInitializer ); } } 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 da94cc329..a6f7ed675 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 @@ -13,8 +13,6 @@ import java.util.concurrent.CompletionStage; import org.hibernate.HibernateException; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -22,7 +20,6 @@ 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.util.impl.CompletionStages; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; @@ -100,12 +97,18 @@ private Integer saveColumnCount(Integer columnCount) { } @Override - public BasicType resolveType(int position, JavaType explicitJavaType, SessionFactoryImplementor sessionFactory) { + public BasicType resolveType( + int position, + JavaType explicitJavaType, + SessionFactoryImplementor sessionFactory) { return super.resolveType( position, explicitJavaType, sessionFactory ); } @Override - public BasicType resolveType(int position, JavaType explicitJavaType, TypeConfiguration typeConfiguration) { + public BasicType resolveType( + int position, + JavaType explicitJavaType, + TypeConfiguration typeConfiguration) { return super.resolveType( position, explicitJavaType, typeConfiguration ); } @@ -145,14 +148,11 @@ private JdbcSessionContext context() { } private CompletionStage executeQuery() { - final LogicalConnectionImplementor logicalConnection = getPersistenceContext().getJdbcCoordinator().getLogicalConnection(); + final LogicalConnectionImplementor logicalConnection = getPersistenceContext() + .getJdbcCoordinator().getLogicalConnection(); return completedFuture( logicalConnection ) .thenCompose( lg -> { LOG.tracef( "Executing query to retrieve ResultSet : %s", getFinalSql() ); - - Dialect dialect = DialectDelegateWrapper.extractRealDialect( executionContext.getSession().getJdbcServices().getDialect() ); - // I'm not sure calling Parameters here is necessary, the query should already have the right parameters - final String sql = Parameters.instance( dialect ).process( getFinalSql() ); Object[] parameters = PreparedStatementAdaptor.bind( super::bindParameters ); final SessionEventListenerManager eventListenerManager = executionContext @@ -162,7 +162,7 @@ private CompletionStage executeQuery() { eventListenerManager.jdbcExecuteStatementStart(); return connection() - .selectJdbc( sql, parameters ) + .selectJdbc( getFinalSql(), parameters ) .thenCompose( this::validateResultSet ) .whenComplete( (resultSet, throwable) -> { // FIXME: I don't know if this event makes sense for Vert.x @@ -172,10 +172,11 @@ private CompletionStage executeQuery() { .thenCompose( this::reactiveSkipRows ) .handle( CompletionStages::handle ) .thenCompose( handler -> handler.hasFailed() - ? convertException( resultSet, handler.getThrowable() ) - : handler.getResultAsCompletionStage() + ? convertException( resultSet, handler.getThrowable() ) + : handler.getResultAsCompletionStage() ); } ) + // same as a finally block .whenComplete( (o, throwable) -> logicalConnection.afterStatement() ); } @@ -225,6 +226,10 @@ private CompletionStage convertException(T object, Throwable throwable) { if ( cause instanceof HibernateException ) { return failedFuture( cause ); } + // SQL server throws an exception as soon as we run the query + if ( cause instanceof UnsupportedOperationException && cause.getMessage().contains( "Unable to decode typeInfo for XML" ) ) { + return failedFuture( LOG.unsupportedXmlType() ); + } return failedFuture( new HibernateException( cause ) ); } return completedFuture( object ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveEntityDelayedFetchImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveEntityDelayedFetchImpl.java index 7adeab775..45312ed3f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveEntityDelayedFetchImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveEntityDelayedFetchImpl.java @@ -43,7 +43,7 @@ public EntityInitializer createInitializer(InitializerParent parent, Assem } @Override - protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) { + protected EntityAssembler buildEntityAssembler(EntityInitializer entityInitializer) { return new ReactiveEntityAssembler( getFetchedMapping().getJavaType(), entityInitializer ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 47da9f6cf..44c34325c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -695,6 +695,22 @@ default CompletionStage find(Class entityClass, Object id, LockModeTyp */ CompletionStage persist(Object entity); + /** + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

+ * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) + */ + CompletionStage persist(String entityName, Object object); + /** * Persist multiple transient entity instances at once. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index bc66b714e..fc6914c6b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -159,6 +159,11 @@ public CompletionStage persist(Object entity) { return delegate.reactivePersist( entity ); } + @Override + public CompletionStage persist(String entityName, Object entity) { + return delegate.reactivePersist( entityName, entity ); + } + @Override public CompletionStage persist(Object... entity) { return applyToAll( delegate::reactivePersist, entity ); 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 new file mode 100644 index 000000000..2e20e7781 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/tuple/entity/ReactiveEntityMetamodel.java @@ -0,0 +1,124 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.tuple.entity; + +import java.util.function.Function; + + +import org.hibernate.generator.Generator; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.Configurable; +import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.SelectGenerator; +import org.hibernate.id.enhanced.DatabaseStructure; +import org.hibernate.id.enhanced.SequenceStructure; +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.PersistentClass; +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.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.tuple.entity.EntityMetamodel; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + +public class ReactiveEntityMetamodel extends EntityMetamodel { + + private static final Log LOG = make( Log.class, lookup() ); + + public ReactiveEntityMetamodel( + PersistentClass persistentClass, + EntityPersister persister, + RuntimeModelCreationContext creationContext) { + this( + persistentClass, + persister, + creationContext, + s -> buildIdGenerator( s, persistentClass, creationContext ) + ); + } + + public ReactiveEntityMetamodel( + PersistentClass persistentClass, + EntityPersister persister, + RuntimeModelCreationContext creationContext, + Function generatorSupplier) { + super( persistentClass, persister, creationContext, generatorSupplier ); + } + + private static Generator buildIdGenerator( + String rootName, + PersistentClass persistentClass, + RuntimeModelCreationContext creationContext) { + final Generator existing = creationContext.getGenerators().get( rootName ); + if ( existing != null ) { + 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( + creationContext.getDialect(), + persistentClass.getRootClass(), + persistentClass.getIdentifierProperty(), + creationContext.getGeneratorSettings() + ); + creationContext.getGenerators().put( rootName, idgenerator ); + return idgenerator; + } + } + + public static Generator augmentWithReactiveGenerator( + Generator generator, + GeneratorCreationContext creationContext, + RuntimeModelCreationContext runtimeModelCreationContext) { + if ( generator instanceof SequenceStyleGenerator ) { + final DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); + if ( structure instanceof TableStructure ) { + return initialize( (IdentifierGenerator) generator, new EmulatedSequenceReactiveIdentifierGenerator( (TableStructure) structure, runtimeModelCreationContext ), creationContext ); + } + if ( structure instanceof SequenceStructure ) { + return initialize( (IdentifierGenerator) generator, new ReactiveSequenceIdentifierGenerator( structure, runtimeModelCreationContext ), creationContext ); + } + throw LOG.unknownStructureType(); + } + if ( generator instanceof TableGenerator ) { + return initialize( + (IdentifierGenerator) generator, + new TableReactiveIdentifierGenerator( (TableGenerator) generator, runtimeModelCreationContext ), + creationContext + ); + } + if ( generator instanceof SelectGenerator ) { + throw LOG.selectGeneratorIsNotSupportedInHibernateReactive(); + } + //nothing to do + return generator; + } + + private static Generator initialize( + IdentifierGenerator idGenerator, + ReactiveIdentifierGenerator reactiveIdGenerator, + GeneratorCreationContext creationContext) { + ( (Configurable) reactiveIdGenerator ).initialize( creationContext.getSqlStringGenerationContext() ); + return new ReactiveGeneratorWrapper( reactiveIdGenerator, idGenerator ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcType.java index f9a295007..99d7efaf7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcType.java @@ -45,7 +45,7 @@ * * @see org.hibernate.type.descriptor.jdbc.ArrayJdbcType */ -public class ReactiveArrayJdbcType implements JdbcType { +public class ReactiveArrayJdbcType implements JdbcType { private final JdbcType elementJdbcType; @@ -98,7 +98,7 @@ public ValueBinder getBinder(final JavaType javaTypeDescriptor) { protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { - ArrayAdaptor arrayObject = getArrayObject( value, options ); + ArrayAdaptor arrayObject = getArrayObject( value, options, getJdbcType(), getJavaType() ); st.setArray( index, arrayObject ); } @@ -107,10 +107,10 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions throw new UnsupportedOperationException(); } - private ArrayAdaptor getArrayObject(X value, WrapperOptions options) { + private static ArrayAdaptor getArrayObject(X value, WrapperOptions options, JdbcType jdbcType, JavaType javaType) { final TypeConfiguration typeConfiguration = options.getSessionFactory().getTypeConfiguration(); - ReactiveArrayJdbcType jdbcType = (ReactiveArrayJdbcType) getJdbcType(); - final JdbcType elementJdbcType = jdbcType.getElementJdbcType(); + ReactiveArrayJdbcType arrayJdbcType = (ReactiveArrayJdbcType) jdbcType; + final JdbcType elementJdbcType = arrayJdbcType.getElementJdbcType(); final JdbcType underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry() .getDescriptor( elementJdbcType.getDefaultSqlTypeCode() ); final Class elementJdbcJavaTypeClass = elementJdbcJavaTypeClass( @@ -122,11 +122,11 @@ private ArrayAdaptor getArrayObject(X value, WrapperOptions options) { //noinspection unchecked final Class arrayClass = (Class) Array.newInstance( elementJdbcJavaTypeClass, 0 ) .getClass(); - final Object[] objects = getJavaType().unwrap( value, arrayClass, options ); + final Object[] objects = javaType.unwrap( value, arrayClass, options ); return new ArrayAdaptor( elementJdbcType, objects ); } - private Class elementJdbcJavaTypeClass( + private static Class elementJdbcJavaTypeClass( WrapperOptions options, JdbcType elementJdbcType, JdbcType underlyingJdbcType, @@ -144,7 +144,7 @@ private Class elementJdbcJavaTypeClass( return convertTypes( elementJdbcJavaTypeClass ); } - private Class convertTypes(Class elementJdbcJavaTypeClass) { + private static Class convertTypes(Class elementJdbcJavaTypeClass) { if ( Timestamp.class.equals( elementJdbcJavaTypeClass ) ) { return LocalDateTime.class; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcTypeConstructor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcTypeConstructor.java index f0a26dccc..4c749d7e3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcTypeConstructor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveArrayJdbcTypeConstructor.java @@ -15,8 +15,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.spi.TypeConfiguration; -import static org.hibernate.dialect.DialectDelegateWrapper.extractRealDialect; - /** * Factory for {@link ReactiveArrayJdbcType}. */ @@ -29,8 +27,7 @@ public JdbcType resolveType( Dialect dialect, BasicType elementType, ColumnTypeInformation columnTypeInformation) { - Dialect realDialect = extractRealDialect( dialect ); - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { String typeName = columnTypeInformation == null ? null : columnTypeInformation.getTypeName(); if ( typeName == null || typeName.isBlank() ) { typeName = ReactiveOracleArrayJdbcType.getTypeName( elementType, dialect ); @@ -46,8 +43,7 @@ public JdbcType resolveType( Dialect dialect, JdbcType elementType, ColumnTypeInformation columnTypeInformation) { - Dialect realDialect = extractRealDialect( dialect ); - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { // a bit wrong, since columnTypeInformation.getTypeName() is typically null! return new ReactiveOracleArrayJdbcType( elementType, 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 new file mode 100644 index 000000000..adc00ff79 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcType.java @@ -0,0 +1,134 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.type.descriptor.jdbc; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.dialect.JsonHelper; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +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.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.JsonJdbcType; + +import io.vertx.core.json.JsonArray; + +/** + * @see org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType + */ +public class ReactiveJsonArrayJdbcType extends ReactiveArrayJdbcType { + + public ReactiveJsonArrayJdbcType(JdbcType elementJdbcType) { + super( elementJdbcType ); + } + + @Override + public int getJdbcTypeCode() { + return SqlTypes.VARCHAR; + } + + @Override + public int getDdlTypeCode() { + return SqlTypes.JSON; + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.JSON_ARRAY; + } + + @Override + public String toString() { + return "JsonArrayJdbcType"; + } + + @Override + public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { + // No literal support for now + return null; + } + + protected X fromString(String string, JavaType javaType, WrapperOptions options) throws SQLException { + if ( string == null ) { + return null; + } + + return JsonHelper.arrayFromString( javaType, 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 ); + } + else { + assert !( elementJdbcType instanceof AggregateJdbcType ); + final JavaType elementJavaType = ( (BasicPluralJavaType) javaType ).getElementJavaType(); + return JsonHelper.arrayToString( elementJavaType, elementJdbcType, domainObjects, options ); + } + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + final String json = ( (ReactiveJsonArrayJdbcType) getJdbcType() ).toString( value, getJavaType(), options ); + st.setString( index, json ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + final String json = ( (ReactiveJsonArrayJdbcType) getJdbcType() ).toString( value, getJavaType(), options ); + st.setString( name, json ); + } + }; + } + + @Override + public ValueExtractor getExtractor(JavaType javaType) { + return new BasicExtractor<>( javaType, this ) { + @Override + protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { + return getObject( rs.getObject( paramIndex ), options ); + } + + @Override + protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { + return getObject( statement.getObject( index ), options ); + } + @Override + protected X doExtract(CallableStatement statement, String name, WrapperOptions options) + throws SQLException { + return getObject( statement.getObject( name ), options ); + } + + private X getObject(Object array, WrapperOptions options) throws SQLException { + if ( array == null ) { + return null; + } + + return ( (ReactiveJsonArrayJdbcType) getJdbcType() ) + .fromString( ( (JsonArray) array ).encode(), getJavaType(), options ); + } + }; + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcTypeConstructor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcTypeConstructor.java new file mode 100644 index 000000000..8971a2ae4 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveJsonArrayJdbcTypeConstructor.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.type.descriptor.jdbc; + +import org.hibernate.dialect.Dialect; +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.type.BasicType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * @see org.hibernate.type.descriptor.jdbc.JsonArrayJdbcType + * @see ReactiveJsonArrayJdbcType + */ +public class ReactiveJsonArrayJdbcTypeConstructor implements JdbcTypeConstructor { + public static final ReactiveJsonArrayJdbcTypeConstructor INSTANCE = new ReactiveJsonArrayJdbcTypeConstructor(); + + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + BasicType elementType, + ColumnTypeInformation columnTypeInformation) { + return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation ); + } + + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + JdbcType elementType, + ColumnTypeInformation columnTypeInformation) { + return new ReactiveJsonArrayJdbcType( elementType ); + } + + @Override + public int getDefaultSqlTypeCode() { + return SqlTypes.JSON_ARRAY; + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java new file mode 100644 index 000000000..306e619b8 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java @@ -0,0 +1,63 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.type.descriptor.jdbc; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLXML; + +import org.hibernate.dialect.XmlHelper; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcType; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + +/** + * @see org.hibernate.type.descriptor.jdbc.XmlArrayJdbcType + */ +public class ReactiveXmlArrayJdbcType extends XmlArrayJdbcType { + + public static final ReactiveXmlArrayJdbcType INSTANCE = new ReactiveXmlArrayJdbcType( null ); + + private static final Log LOG = make( Log.class, lookup() ); + + public ReactiveXmlArrayJdbcType(JdbcType elementJdbcType) { + super( elementJdbcType ); + } + + @Override + protected X fromString(String string, JavaType javaType, WrapperOptions options) throws SQLException { + if ( string == null ) { + return null; + } + if ( javaType.getJavaType() == SQLXML.class ) { + throw LOG.unsupportedXmlType(); + } + return XmlHelper.arrayFromString( javaType, this, string, options ); + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) { + throw LOG.unsupportedXmlType(); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) { + throw LOG.unsupportedXmlType(); + } + }; + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcTypeConstructor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcTypeConstructor.java new file mode 100644 index 000000000..1fa8999cb --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcTypeConstructor.java @@ -0,0 +1,39 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.type.descriptor.jdbc; + +import org.hibernate.dialect.Dialect; +import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; +import org.hibernate.type.BasicType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcTypeConstructor; +import org.hibernate.type.spi.TypeConfiguration; + +/** + * @see XmlArrayJdbcTypeConstructor + * @see ReactiveArrayJdbcType + */ +public class ReactiveXmlArrayJdbcTypeConstructor extends XmlArrayJdbcTypeConstructor { + public static final ReactiveXmlArrayJdbcTypeConstructor INSTANCE = new ReactiveXmlArrayJdbcTypeConstructor(); + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + BasicType elementType, + ColumnTypeInformation columnTypeInformation) { + return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation ); + } + + @Override + public JdbcType resolveType( + TypeConfiguration typeConfiguration, + Dialect dialect, + JdbcType elementType, + ColumnTypeInformation columnTypeInformation) { + return new ReactiveXmlArrayJdbcType( elementType ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java new file mode 100644 index 000000000..1362e3a20 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java @@ -0,0 +1,69 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.type.descriptor.jdbc; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLXML; + +import org.hibernate.dialect.XmlHelper; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.XmlJdbcType; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + +/** + * @see XmlJdbcType + */ +public class ReactiveXmlJdbcType extends XmlJdbcType { + + private static final Log LOG = make( Log.class, lookup() ); + + public static final ReactiveXmlJdbcType INSTANCE = new ReactiveXmlJdbcType( null ); + + protected ReactiveXmlJdbcType(EmbeddableMappingType embeddableMappingType) { + super( embeddableMappingType ); + } + + @Override + protected X fromString(String string, JavaType javaType, WrapperOptions options) throws SQLException { + if ( getEmbeddableMappingType() != null ) { + return XmlHelper.fromString( + getEmbeddableMappingType(), + string, + javaType.getJavaTypeClass() != Object[].class, + options + ); + } + if ( javaType.getJavaType() == SQLXML.class ) { + throw LOG.unsupportedXmlType(); + } + return options.getSessionFactory().getFastSessionServices().getXmlFormatMapper() + .fromString( string, javaType, options ); + } + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) { + throw LOG.unsupportedXmlType(); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) { + throw LOG.unsupportedXmlType(); + } + }; + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java index ca7df6d6e..bb168d8f7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java @@ -226,6 +226,59 @@ public static CompletionStageHandler handle(R res return new CompletionStageHandler<>( result, throwable ); } + /** + * Makes the code simpler when dealing with ORM visitor pattern. + *

+ * For example: + *

{@code
+	 * 	private CompletionStage visitConstraintOrderedTables(QuerySpec idTableIdentifierSubQuery, ExecutionContext executionContext) {
+	 * 		final Completable completable = new Completable<>();
+	 * 		entityDescriptor
+	 * 				.visitConstraintOrderedTables( (tableExpression, tableKeyColumnVisitationSupplier) -> deleteFromTableUsingIdTable(
+	 * 								tableExpression,
+	 * 								tableKeyColumnVisitationSupplier,
+	 * 								idTableIdentifierSubQuery,
+	 * 								executionContext
+	 * 						)
+	 * 						.handle( completable::complete )
+	 * 				);
+	 * 		return completable.getStage().thenCompose( CompletionStages::voidFuture );
+	 *  }
+	 * }
+ *

+ */ + public static class Completable { + + private final CompletableFuture stage; + + public Completable() { + this.stage = new CompletableFuture<>(); + } + + /** + * It will complete the underlying {@link CompletionStage} based on the parameters. + * + * @return {@code null} + * @see #getStage() + */ + public Object complete(T result, Throwable throwable) { + if ( throwable != null ) { + stage.completeExceptionally( throwable ); + } + else { + stage.complete( result ); + } + return null; + } + + /** + * @return a {@link CompletionStage} that will complete (successfully or exceptionally) after {@link #complete(Object, Throwable)} gets called + */ + public CompletionStage getStage() { + return stage; + } + } + public static class CompletionStageHandler { private final R result; diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchQueryOnConnectionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchQueryOnConnectionTest.java index 675cbc4c3..0c6c03d85 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchQueryOnConnectionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchQueryOnConnectionTest.java @@ -13,9 +13,6 @@ import java.util.concurrent.CompletionStage; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.pool.impl.OracleParameters; -import org.hibernate.reactive.pool.impl.PostgresParameters; -import org.hibernate.reactive.pool.impl.SQLServerParameters; import org.junit.jupiter.api.Test; @@ -41,7 +38,7 @@ protected Set> annotatedEntities() { @Test public void testBatchInsertSizeEqMultiple(VertxTestContext context) { - test( context, doBatchInserts( context, 50, BATCH_SIZE ) + test( context, doBatchInserts( 50, BATCH_SIZE ) .thenAccept( paramsBatches -> { assertEquals( 3, paramsBatches.size() ); assertEquals( 20, paramsBatches.get( 0 ).size() ); @@ -53,7 +50,7 @@ public void testBatchInsertSizeEqMultiple(VertxTestContext context) { @Test public void testBatchInsertUpdateSizeLtMultiple(VertxTestContext context) { - test( context, doBatchInserts( context, 50, BATCH_SIZE - 1 ) + test( context, doBatchInserts( 50, BATCH_SIZE - 1 ) .thenAccept( paramsBatches -> { assertEquals( 3, paramsBatches.size() ); assertEquals( 19, paramsBatches.get( 0 ).size() ); @@ -65,7 +62,7 @@ public void testBatchInsertUpdateSizeLtMultiple(VertxTestContext context) { @Test public void testBatchInsertUpdateSizeGtMultiple(VertxTestContext context) { - test( context, doBatchInserts( context, 50, BATCH_SIZE + 1 ) + test( context, doBatchInserts( 50, BATCH_SIZE + 1 ) .thenAccept( paramsBatches -> { assertEquals( 5, paramsBatches.size() ); assertEquals( 20, paramsBatches.get( 0 ).size() ); @@ -77,8 +74,8 @@ public void testBatchInsertUpdateSizeGtMultiple(VertxTestContext context) { ); } - public CompletionStage>> doBatchInserts(VertxTestContext context, int nEntities, int nEntitiesMultiple) { - final String insertSql = process( "insert into DataPoint (description, x, y, id) values (?, ?, ?, ?)" ); + public CompletionStage>> doBatchInserts(int nEntities, int nEntitiesMultiple) { + final String insertSql = createInsertSql(); List> paramsBatches = new ArrayList<>(); List paramsBatch = new ArrayList<>( BATCH_SIZE ); @@ -99,7 +96,7 @@ public CompletionStage>> doBatchInserts(VertxTestContext con } } - if ( paramsBatch.size() > 0 ) { + if ( !paramsBatch.isEmpty() ) { paramsBatches.add( paramsBatch ); } @@ -124,17 +121,17 @@ public CompletionStage>> doBatchInserts(VertxTestContext con .thenApply( v -> paramsBatches ); } - private String process(String sql) { + private String createInsertSql() { switch ( dbType() ) { case POSTGRESQL: case COCKROACHDB: - return PostgresParameters.INSTANCE.process( sql ); + return "insert into DataPoint (description, x, y, id) values ($1, $2, $3, $4)"; case SQLSERVER: - return SQLServerParameters.INSTANCE.process( sql ); + return "insert into DataPoint (description, x, y, id) values (@P1, @P2, @P3, @P4)"; case ORACLE: - return OracleParameters.INSTANCE.process( sql ); + return "insert into DataPoint (description, x, y, id) values (:1, :2, :3, :4)"; default: - return sql; + return "insert into DataPoint (description, x, y, id) values (?,?,?,?)"; } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CustomGeneratorTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CustomGeneratorTest.java index 44aea3d2e..cb2f3ae0b 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CustomGeneratorTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CustomGeneratorTest.java @@ -6,18 +6,20 @@ package org.hibernate.reactive; import java.util.Collection; +import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Properties; import java.util.concurrent.CompletionStage; +import org.hibernate.MappingException; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; +import org.hibernate.generator.EventType; +import org.hibernate.generator.GeneratorCreationContext; import org.hibernate.id.Configurable; import org.hibernate.reactive.id.ReactiveIdentifierGenerator; import org.hibernate.reactive.session.ReactiveConnectionSupplier; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.Type; import org.junit.jupiter.api.Test; @@ -44,36 +46,31 @@ protected Collection> annotatedEntities() { @Test public void testSequenceGenerator(VertxTestContext context) { - CustomId b = new CustomId(); b.string = "Hello World"; - test( - context, - openSession() - .thenCompose( s -> s.persist( b ).thenCompose( v -> s.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( s2 -> s2 - .find( CustomId.class, b.getId() ) - .thenAccept( bb -> { - assertNotNull( bb ); - assertEquals( bb.id, 1100 ); - assertEquals( bb.string, b.string ); - assertEquals( bb.version, 0 ); - - bb.string = "Goodbye"; - } ) - .thenCompose( vv -> s2.flush() ) - .thenCompose( vv -> s2.find( CustomId.class, b.getId() ) ) - .thenAccept( bt -> { - assertEquals( bt.version, 1 ); - } ) ) - .thenCompose( v -> openSession() ) - .thenCompose( s3 -> s3.find( CustomId.class, b.getId() ) ) + test( context, openSession() + .thenCompose( s -> s.persist( b ).thenCompose( v -> s.flush() ) ) + .thenCompose( v -> openSession() ) + .thenCompose( s2 -> s2 + .find( CustomId.class, b.getId() ) .thenAccept( bb -> { - assertEquals( bb.version, 1 ); - assertEquals( bb.string, "Goodbye" ); + assertNotNull( bb ); + assertEquals( 1100, bb.id ); + assertEquals( bb.string, b.string ); + assertEquals( 0, bb.version ); + + bb.string = "Goodbye"; } ) + .thenCompose( vv -> s2.flush() ) + .thenCompose( vv -> s2.find( CustomId.class, b.getId() ) ) + .thenAccept( bt -> assertEquals( 1, bt.version ) ) ) + .thenCompose( v -> openSession() ) + .thenCompose( s3 -> s3.find( CustomId.class, b.getId() ) ) + .thenAccept( bb -> { + assertEquals( 1, bb.version ); + assertEquals( "Goodbye", bb.string ); + } ) ); } @@ -87,15 +84,25 @@ public CompletionStage generate(ReactiveConnectionSupplier session, Obj } @Override - public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { - current = Integer.parseInt( params.getProperty( "offset", "0" ) ); + public void configure(GeneratorCreationContext creationContext, Properties parameters) throws MappingException { + current = Integer.parseInt( parameters.getProperty( "offset", "0" ) ); + } + + @Override + public boolean generatedOnExecution() { + return false; + } + + @Override + public EnumSet getEventTypes() { + return EnumSet.of( EventType.INSERT ); } } @Entity @GenericGenerator( name = "thousands", - strategy = "org.hibernate.reactive.CustomGeneratorTest$Thousands", + type = Thousands.class, parameters = @Parameter(name = "offset", value = "100") ) public static class CustomId { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmptyCompositeCollectionKeyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmptyCompositeCollectionKeyTest.java deleted file mode 100644 index c879235da..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmptyCompositeCollectionKeyTest.java +++ /dev/null @@ -1,201 +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; - -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.hibernate.Hibernate; -import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; -import org.hibernate.reactive.annotations.DisabledFor; - -import org.junit.jupiter.api.Test; - -import io.vertx.junit5.Timeout; -import io.vertx.junit5.VertxTestContext; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Embeddable; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Timeout(value = 10, timeUnit = MINUTES) -@DisabledFor( value = DB2, reason = "IllegalStateException: Needed to have 6 in buffer but only had 0" ) -public class EmptyCompositeCollectionKeyTest extends BaseReactiveTest { - - @Override - protected Collection> annotatedEntities() { - return List.of( Family.class ); - } - - @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); - configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" ); - return configuration; - } - - @Test - public void testGetEntityWithEmptyChildrenCollection(VertxTestContext context) { - /* CASE 1: Family has Parent + child with null names + NULL relatives */ - Family family = new Family( 1, new Parent( new Child( null, null ) ) ); - - test( context, openSession() - .thenCompose( session -> session - .persist( family ) - .thenCompose( v -> session.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.find( Family.class, family.id ) ) - .thenAccept( foundFamily -> { - assertTrue( Hibernate.isInitialized( foundFamily.parent.children ) ); - assertNull( foundFamily.parent.nickname ); - assertTrue( foundFamily.parent.children.isEmpty() ); - assertNull( foundFamily.parent.child.name ); - assertNull( foundFamily.parent.child.petname ); - assertTrue( Hibernate.isInitialized( foundFamily.relatives ) ); - assertTrue( foundFamily.relatives.isEmpty() ); - } ) - ); - } - - @Test - public void testGetEntityWithParentNullChild(VertxTestContext context) { - /* CASE 2: Family has Parent + child with null names + NULL relatives */ - Family family = new Family( 2, new Parent() ); - - test( context, openSession() - .thenCompose( session -> session - .persist( family ) - .thenCompose( v -> session.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.find( Family.class, family.id ) ) - .thenAccept( foundFamily -> { - assertTrue( Hibernate.isInitialized( foundFamily.parent.children ) ); - assertNull( foundFamily.parent.nickname ); - assertTrue( foundFamily.parent.children.isEmpty() ); - assertNotNull( foundFamily.parent.child ); - assertNull( foundFamily.parent.child.petname ); - assertTrue( foundFamily.relatives.isEmpty() ); - } ) - ); - } - - @Test - public void testGetEntityWithNullParentNullChild(VertxTestContext context) { - /* CASE 3: Parent and children are all null */ - Family family = new Family( 3, null ); - - test( context, openSession() - .thenCompose( session -> session - .persist( family ) - .thenCompose( v -> session.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.find( Family.class, family.id ) ) - .thenAccept( foundFamily -> { - assertTrue( Hibernate.isInitialized( foundFamily.parent.children ) ); - assertNull( foundFamily.parent.nickname ); - assertTrue( foundFamily.parent.children.isEmpty() ); - assertNotNull( foundFamily.parent.child ); - assertNull( foundFamily.parent.child.name ); - assertTrue( foundFamily.relatives.isEmpty() ); - } ) - ); - } - - - @Test - public void testGetEntityWithNullParentNullChildAndRelatives(VertxTestContext context) { - /* CASE 4: Parent and children are all null and relatives set exists */ - Set relatives = new HashSet<>(); - relatives.add( new Child() ); - Family family = new Family( 4, null, relatives ); - - test( context, openSession() - .thenCompose( session -> session - .persist( family ) - .thenCompose( v -> session.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.find( Family.class, family.id ) ) - .thenAccept( foundFamily -> { - assertTrue( Hibernate.isInitialized( foundFamily.parent.children ) ); - assertNull( foundFamily.parent.nickname ); - assertTrue( foundFamily.parent.children.isEmpty() ); - assertNotNull( foundFamily.parent.child ); - assertNull( foundFamily.parent.child.petname ); - assertFalse( foundFamily.relatives.isEmpty() ); - assertNull( foundFamily.relatives.iterator().next().name ); - } ) - ); - } - - @Entity(name = "Family") - public static class Family { - @Id - private Integer id; - - private Parent parent; - - @ElementCollection(fetch = FetchType.EAGER) - private Set relatives; - - public Family() { - super(); - } - - public Family(Integer id, Parent parent) { - this.id = id; - this.parent = parent; - } - - public Family(Integer id, Parent parent, Set relatives) { - this.id = id; - this.parent = parent; - this.relatives = relatives; - } - } - - @Embeddable - public static class Parent { - private Child child; - private String nickname; - - @ElementCollection(fetch = FetchType.EAGER) - List children; - - public Parent() { - super(); - } - - public Parent(Child child) { - this.child = child; - } - } - - @Embeddable - public static class Child { - private String name; - private String petname; - - public Child() { - super(); - } - - public Child(String name, String petname) { - this.name = name; - this.petname = petname; - } - } -} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyJoinedTableTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyJoinedTableTest.java index 018936ad5..879574b18 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyJoinedTableTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyJoinedTableTest.java @@ -11,9 +11,9 @@ import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.Generated; -import org.hibernate.annotations.GenerationTime; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.generator.EventType; import org.hibernate.reactive.annotations.DisabledFor; import org.junit.jupiter.api.Test; @@ -152,7 +152,7 @@ static class GeneratedRegularParent { public String lastname; - @Generated(GenerationTime.ALWAYS) + @Generated( event = {EventType.INSERT, EventType.UPDATE} ) @Column(columnDefinition = "varchar(600) generated always as (firstname || ' ' || lastname) stored") public String fullName; @@ -171,7 +171,7 @@ public GeneratedRegularParent(String firstname, String lastname) { @Entity(name = "GeneratedRegular") static class GeneratedRegular extends GeneratedRegularParent { @Temporal(value = TemporalType.TIMESTAMP) - @Generated(GenerationTime.INSERT) + @Generated( event = {EventType.INSERT} ) @Column(columnDefinition = "timestamp") @ColumnDefault("current_timestamp") public Date createdAt; @@ -179,7 +179,6 @@ static class GeneratedRegular extends GeneratedRegularParent { @CurrentUser.LoggedUserMutinyAlways public String updatedBy; - @Generated(GenerationTime.NEVER) public String never; public GeneratedRegular() { @@ -201,7 +200,7 @@ static class GeneratedWithIdentityParent { public String lastname; - @Generated(GenerationTime.ALWAYS) + @Generated( event = {EventType.INSERT, EventType.UPDATE} ) @Column(columnDefinition = "varchar(600) generated always as (firstname || ' ' || lastname) stored") public String fullName; @@ -220,7 +219,7 @@ public GeneratedWithIdentityParent(String firstname, String lastname) { @Entity(name = "GeneratedWithIdentity") static class GeneratedWithIdentity extends GeneratedWithIdentityParent { @Temporal(value = TemporalType.TIMESTAMP) - @Generated(GenerationTime.INSERT) + @Generated( event = {EventType.INSERT} ) @Column(columnDefinition = "timestamp") @ColumnDefault("current_timestamp") public Date createdAt; @@ -228,7 +227,6 @@ static class GeneratedWithIdentity extends GeneratedWithIdentityParent { @CurrentUser.LoggedUserStageAlways public String updatedBy; - @Generated(GenerationTime.NEVER) public String never; public GeneratedWithIdentity() { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertySingleTableTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertySingleTableTest.java index 60b23a563..deac05216 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertySingleTableTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertySingleTableTest.java @@ -11,9 +11,9 @@ import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.Generated; -import org.hibernate.annotations.GenerationTime; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.generator.EventType; import org.hibernate.reactive.annotations.DisabledFor; import org.junit.jupiter.api.Test; @@ -150,12 +150,12 @@ static class GeneratedRegular { public String lastname; - @Generated(GenerationTime.ALWAYS) + @Generated( event = { EventType.INSERT, EventType.UPDATE} ) @Column(columnDefinition = "varchar(600) generated always as (firstname || ' ' || lastname) stored") private String fullName; @Temporal(value = TemporalType.TIMESTAMP) - @Generated(GenerationTime.INSERT) + @Generated( event = {EventType.INSERT} ) @Column(columnDefinition = "timestamp") @ColumnDefault("current_timestamp") public Date createdAt; @@ -166,7 +166,6 @@ static class GeneratedRegular { @CurrentUser.LoggedUserStageAlways public String updatedBy; - @Generated(GenerationTime.NEVER) public String never; public GeneratedRegular() { @@ -189,12 +188,12 @@ static class GeneratedWithIdentity { public String lastname; - @Generated(GenerationTime.ALWAYS) + @Generated( event = {EventType.INSERT, EventType.UPDATE} ) @Column(columnDefinition = "varchar(600) generated always as (firstname || ' ' || lastname) stored") private String fullName; @Temporal(value = TemporalType.TIMESTAMP) - @Generated(GenerationTime.INSERT) + @Generated( event = {EventType.INSERT} ) @Column(columnDefinition = "timestamp") @ColumnDefault("current_timestamp") public Date createdAt; @@ -205,7 +204,6 @@ static class GeneratedWithIdentity { @CurrentUser.LoggedUserStageAlways public String updatedBy; - @Generated(GenerationTime.NEVER) public String never; public GeneratedWithIdentity() { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyUnionSubclassesTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyUnionSubclassesTest.java index b024372a8..f2b4ecdfe 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyUnionSubclassesTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/GeneratedPropertyUnionSubclassesTest.java @@ -11,9 +11,9 @@ import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.Generated; -import org.hibernate.annotations.GenerationTime; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.generator.EventType; import org.hibernate.reactive.annotations.DisabledFor; import org.junit.jupiter.api.Test; @@ -111,7 +111,7 @@ static class GeneratedRegularParent { public String lastname; - @Generated(GenerationTime.ALWAYS) + @Generated( event = {EventType.INSERT, EventType.UPDATE} ) @Column(columnDefinition = "varchar(600) generated always as (firstname || ' ' || lastname) stored") public String fullName; @@ -130,7 +130,7 @@ public GeneratedRegularParent(String firstname, String lastname) { @Entity(name = "GeneratedRegular") static class GeneratedRegular extends GeneratedRegularParent { @Temporal(value = TemporalType.TIMESTAMP) - @Generated(GenerationTime.INSERT) + @Generated( event = EventType.INSERT ) @Column(columnDefinition = "timestamp") @ColumnDefault("current_timestamp") public Date createdAt; @@ -138,7 +138,6 @@ static class GeneratedRegular extends GeneratedRegularParent { @CurrentUser.LoggedUserStageAlways public String updatedBy; - @Generated(GenerationTime.NEVER) public String never; public GeneratedRegular() { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTypeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTypeTest.java index a6dfe601a..606f0bf8b 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTypeTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTypeTest.java @@ -97,7 +97,6 @@ public void integerIdentityType(VertxTestContext context) { assertType( context, IntegerTypeEntity.class, new IntegerTypeEntity(), 1 ); } - interface TypeIdentity { T getId(); } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyMapTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyMapTest.java index 4c94668d5..9b7ddd627 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyMapTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyMapTest.java @@ -7,8 +7,11 @@ import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import org.hibernate.Hibernate; @@ -25,6 +28,7 @@ import jakarta.persistence.Table; 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -45,61 +49,49 @@ public void test(VertxTestContext context) { Author author = new Author( "Iain M Banks" ); book1.authors.put( "a", author ); book2.authors.put( "b", author ); - author.books.put( "a", book1 ); - author.books.put( "b", book2 ); + author.books.add( book1 ); + author.books.add( book2 ); test( context, getMutinySessionFactory() - .withTransaction( (session, transaction) -> session.persistAll( book1, book2, author ) ) + .withTransaction( session -> session.persistAll( book1, book2, author ) ) .chain( () -> getMutinySessionFactory() - .withTransaction( (session, transaction) -> session.find( Book.class, book1.id ) + .withTransaction( session -> session.find( Book.class, book1.id ) .invoke( b -> assertFalse( Hibernate.isInitialized( b.authors ) ) ) .chain( b -> session.fetch( b.authors ) ) .invoke( authors -> assertEquals( 1, authors.size() ) ) ) ) .chain( () -> getMutinySessionFactory() - .withTransaction( (session, transaction) -> session.find( Author.class, author.id ) + .withTransaction( session -> session.find( Author.class, author.id ) .invoke( a -> assertFalse( Hibernate.isInitialized( a.books ) ) ) .chain( a -> session.fetch( a.books ) ) - .invoke( books -> { - assertEquals( 2, books.size() ); - assertEquals( book1.title, books.get( "a" ).title ); - assertEquals( book2.title, books.get( "b" ).title ); - - } ) - ) - ) - .chain( () -> getMutinySessionFactory() - .withTransaction( (session, transaction) -> session.createSelectionQuery( - "select distinct a from Author a left join fetch a.books", - Author.class - ) - .getSingleResult() - .invoke( a -> assertTrue( Hibernate.isInitialized( a.books ) ) ) - .invoke( a -> { - assertEquals( 2, a.books.size() ); - assertEquals( book1.title, a.books.get( "a" ).title ); - assertEquals( book2.title, a.books.get( "b" ).title ); - } ) + .invoke( books -> assertThat( books ).containsExactlyInAnyOrder( book1, book2 ) ) ) ) + .chain( () -> getMutinySessionFactory().withTransaction( session -> session + .createSelectionQuery( "select distinct a from Author a left join fetch a.books", Author.class ) + .getSingleResult() + .invoke( a -> assertTrue( Hibernate.isInitialized( a.books ) ) ) + .invoke( a -> assertThat( a.books ).containsExactlyInAnyOrder( book1, book2 ) ) + ) ) ); } @Entity(name = "Book") @Table(name = "MTMMBook") static class Book { - Book(String title) { - this.title = title; - } Book() { } - @GeneratedValue + Book(String title) { + this.title = title; + } + @Id + @GeneratedValue long id; @Basic(optional = false) @@ -108,6 +100,25 @@ static class Book { @ManyToMany @MapKeyColumn(name = "mapkey") Map authors = new HashMap<>(); + + @Override + public boolean equals(Object object) { + if ( object == null || getClass() != object.getClass() ) { + return false; + } + Book book = (Book) object; + return Objects.equals( title, book.title ); + } + + @Override + public int hashCode() { + return Objects.hash( title ); + } + + @Override + public String toString() { + return id + ":" + title; + } } @Entity(name = "Author") @@ -120,15 +131,28 @@ static class Author { public Author() { } - @GeneratedValue @Id + @GeneratedValue long id; @Basic(optional = false) String name; @ManyToMany(mappedBy = "authors") - @MapKeyColumn(name = "mapkey") - Map books = new HashMap<>(); + Set books = new HashSet<>(); + + @Override + public boolean equals(Object object) { + if ( object == null || getClass() != object.getClass() ) { + return false; + } + Author author = (Author) object; + return Objects.equals( name, author.name ); + } + + @Override + public int hashCode() { + return Objects.hashCode( name ); + } } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedIdentityGenerationTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedIdentityGenerationTest.java index 073ab6f92..a41cefd72 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedIdentityGenerationTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedIdentityGenerationTest.java @@ -14,6 +14,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import org.hibernate.AssertionFailure; import org.hibernate.SessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; @@ -45,6 +46,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; +import org.jetbrains.annotations.NotNull; import static java.util.concurrent.TimeUnit.MINUTES; import static org.hibernate.cfg.AvailableSettings.SHOW_SQL; @@ -67,7 +69,8 @@ @Timeout(value = MultithreadedIdentityGenerationTest.TIMEOUT_MINUTES, timeUnit = MINUTES) public class MultithreadedIdentityGenerationTest { - /* The number of threads should be higher than the default size of the connection pool so that + /* + * The number of threads should be higher than the default size of the connection pool so that * this test is also effective in detecting problems with resource starvation. */ private static final int N_THREADS = 48; @@ -93,14 +96,7 @@ public class MultithreadedIdentityGenerationTest { @BeforeAll public static void setupSessionFactory() { - final VertxOptions vertxOptions = new VertxOptions(); - vertxOptions.setEventLoopPoolSize( N_THREADS ); - //We relax the blocked thread checks as we'll actually use latches to block them - //intentionally for the purpose of the test; functionally this isn't required - //but it's useful as self-test in the design of this, to ensure that the way - //things are setup are indeed being run in multiple, separate threads. - vertxOptions.setBlockedThreadCheckInterval( TIMEOUT_MINUTES ); - vertxOptions.setBlockedThreadCheckIntervalUnit( TimeUnit.MINUTES ); + final VertxOptions vertxOptions = createVertxOptions(); vertx = Vertx.vertx( vertxOptions ); Configuration configuration = new Configuration(); setDefaultProperties( configuration ); @@ -116,6 +112,18 @@ public static void setupSessionFactory() { stageSessionFactory = sessionFactory.unwrap( Stage.SessionFactory.class ); } + private static @NotNull VertxOptions createVertxOptions() { + final VertxOptions vertxOptions = new VertxOptions(); + vertxOptions.setEventLoopPoolSize( N_THREADS ); + //We relax the blocked thread checks as we'll actually use latches to block them + //intentionally for the purpose of the test; functionally this isn't required, + //but it's useful as self-test in the design of this, to ensure that the way + //things are set up are indeed being run in multiple, separate threads. + vertxOptions.setBlockedThreadCheckInterval( TIMEOUT_MINUTES ); + vertxOptions.setBlockedThreadCheckIntervalUnit( TimeUnit.MINUTES ); + return vertxOptions; + } + @AfterAll public static void closeSessionFactory() { stageSessionFactory.close(); @@ -123,9 +131,11 @@ public static void closeSessionFactory() { private ReactiveGeneratorWrapper getIdGenerator() { final ReactiveSessionFactoryImpl hibernateSessionFactory = (ReactiveSessionFactoryImpl) sessionFactory; - final ReactiveGeneratorWrapper identifierGenerator = (ReactiveGeneratorWrapper) hibernateSessionFactory.getIdentifierGenerator( - "org.hibernate.reactive.MultithreadedIdentityGenerationTest$EntityWithGeneratedId" ); - return identifierGenerator; + return (ReactiveGeneratorWrapper) hibernateSessionFactory + .getRuntimeMetamodels() + .getMappingMetamodel() + .getEntityDescriptor( "org.hibernate.reactive.MultithreadedIdentityGenerationTest$EntityWithGeneratedId" ) + .getGenerator(); } @Test @@ -150,7 +160,7 @@ public void testIdentityGenerator(VertxTestContext context) { } } ) .onFailure( context::failNow ) - .eventually( unused -> vertx.close() ); + .eventually( () -> vertx.close() ); } private boolean allResultsAreUnique(ResultsCollector allResults) { @@ -190,24 +200,23 @@ public void start(Promise startPromise) { startLatch.reached(); startLatch.waitForEveryone();//Not essential, but to ensure a good level of parallelism final String initialThreadName = Thread.currentThread().getName(); - stageSessionFactory.withSession( - s -> generateMultipleIds( idGenerator, s, generatedIds ) - ) - .whenComplete( (o, throwable) -> { - endLatch.reached(); - if ( throwable != null ) { - startPromise.fail( throwable ); - } - else { - if ( !initialThreadName.equals( Thread.currentThread().getName() ) ) { - startPromise.fail( "Thread switch detected!" ); + stageSessionFactory + .withSession( s -> generateMultipleIds( idGenerator, s, generatedIds ) ) + .whenComplete( (o, throwable) -> { + endLatch.reached(); + if ( throwable != null ) { + startPromise.fail( throwable ); } else { - allResults.deliverResulst( generatedIds ); - startPromise.complete(); + if ( !initialThreadName.equals( Thread.currentThread().getName() ) ) { + startPromise.fail( "Thread switch detected!" ); + } + else { + allResults.deliverResulst( generatedIds ); + startPromise.complete(); + } } - } - } ); + } ); } catch (RuntimeException e) { startPromise.fail( e ); @@ -222,7 +231,7 @@ public void stop() { private static class ResultsCollector { - private final ConcurrentMap> resultsByThread = new ConcurrentHashMap<>(); + private final ConcurrentMap> resultsByThread = new ConcurrentHashMap<>(); public void deliverResulst(List generatedIds) { final String threadName = Thread.currentThread().getName(); @@ -242,8 +251,8 @@ private static CompletionStage generateIds( Stage.Session s, ArrayList collector) { final Thread beforeOperationThread = Thread.currentThread(); - return idGenerator.generate( ( (StageSessionImpl) s ) - .unwrap( ReactiveConnectionSupplier.class ), new EntityWithGeneratedId() ) + return idGenerator + .generate( ( (StageSessionImpl) s ).unwrap( ReactiveConnectionSupplier.class ), new EntityWithGeneratedId() ) .thenAccept( o -> { if ( beforeOperationThread != Thread.currentThread() ) { throw new IllegalStateException( "Detected an unexpected switch of carrier threads!" ); @@ -256,7 +265,7 @@ private static CompletionStage generateIds( * Trivial entity using a Sequence for Id generation */ @Entity - @Table(name="Entity") + @Table(name = "Entity") private static class EntityWithGeneratedId { @Id @GeneratedValue @@ -288,10 +297,12 @@ public void reached() { public void waitForEveryone() { try { - countDownLatch.await( TIMEOUT_MINUTES, TimeUnit.MINUTES ); + if ( !countDownLatch.await( TIMEOUT_MINUTES, MINUTES ) ) { + throw new AssertionFailure( "Time out reached!" ); + } prettyOut( "Everyone has now breached '" + label + "'" ); } - catch ( InterruptedException e ) { + catch (InterruptedException e) { e.printStackTrace(); } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java index f82bc77eb..9e31c20be 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java @@ -137,7 +137,7 @@ public void testIdentityGenerator(VertxTestContext context) { context.completeNow(); } ) .onFailure( context::failNow ) - .eventually( unused -> vertx.close() ); + .eventually( () -> vertx.close() ); } private static class InsertEntitiesVerticle extends AbstractVerticle { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java index 73de9fd30..78494bd1c 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java @@ -15,7 +15,9 @@ import org.hibernate.reactive.stage.Stage; import org.hibernate.reactive.util.impl.CompletionStages; + import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import io.vertx.junit5.Timeout; @@ -32,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -83,14 +86,15 @@ public void reactiveFind(VertxTestContext context) { public void reactiveFindMultipleIds(VertxTestContext context) { final GuineaPig rump = new GuineaPig( 55, "Rumpelstiltskin" ); final GuineaPig emma = new GuineaPig( 77, "Emma" ); - test( context, populateDB() - .thenCompose( v -> getSessionFactory().withTransaction( s -> s.persist( emma, rump ) ) ) - .thenCompose( v -> getSessionFactory().withTransaction( s -> s - .find( GuineaPig.class, emma.getId(), rump.getId() ) ) - ) - .thenAccept( pigs -> { - org.assertj.core.api.Assertions.assertThat( pigs ).containsExactlyInAnyOrder( emma, rump ); - } ) + test( + context, populateDB() + .thenCompose( v -> getSessionFactory().withTransaction( s -> s.persist( emma, rump ) ) ) + .thenCompose( v -> getSessionFactory().withTransaction( s -> s + .find( GuineaPig.class, emma.getId(), rump.getId() ) ) + ) + .thenAccept( pigs -> { + org.assertj.core.api.Assertions.assertThat( pigs ).containsExactlyInAnyOrder( emma, rump ); + } ) ); } @@ -116,22 +120,24 @@ public void sessionClear(VertxTestContext context) { @Test public void reactiveWithTransactionSession(VertxTestContext context) { final GuineaPig guineaPig = new GuineaPig( 61, "Mr. Peanutbutter" ); - test( context, getSessionFactory() - .withTransaction( session -> session.persist( guineaPig ) ) - .thenCompose( v -> getSessionFactory() - .withSession( session -> session.find( GuineaPig.class, guineaPig.getId() ) ) ) - .thenAccept( result -> assertThatPigsAreEqual( guineaPig, result ) ) + test( + context, getSessionFactory() + .withTransaction( session -> session.persist( guineaPig ) ) + .thenCompose( v -> getSessionFactory() + .withSession( session -> session.find( GuineaPig.class, guineaPig.getId() ) ) ) + .thenAccept( result -> assertThatPigsAreEqual( guineaPig, result ) ) ); } @Test public void reactiveWithTransactionStatelessSession(VertxTestContext context) { final GuineaPig guineaPig = new GuineaPig( 61, "Mr. Peanutbutter" ); - test( context, getSessionFactory() - .withStatelessTransaction( session -> session.insert( guineaPig ) ) - .thenCompose( v -> getSessionFactory() - .withSession( session -> session.find( GuineaPig.class, guineaPig.getId() ) ) ) - .thenAccept( result -> assertThatPigsAreEqual( guineaPig, result ) ) + test( + context, getSessionFactory() + .withStatelessTransaction( session -> session.insert( guineaPig ) ) + .thenCompose( v -> getSessionFactory() + .withSession( session -> session.find( GuineaPig.class, guineaPig.getId() ) ) ) + .thenAccept( result -> assertThatPigsAreEqual( guineaPig, result ) ) ); } @@ -164,32 +170,37 @@ public void reactivePersistFindDelete(VertxTestContext context) { public void reactiveFindWithLock(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 5, "Aloi" ); - test( context, populateDB().thenCompose( v -> getSessionFactory() - .withTransaction( (session, tx) -> session - .find( GuineaPig.class, expectedPig.getId(), LockMode.PESSIMISTIC_WRITE ) - .thenAccept( actualPig -> { - assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( session.getLockMode( actualPig ), LockMode.PESSIMISTIC_WRITE ); - } ) - ) ) + test( + context, populateDB().thenCompose( v -> getSessionFactory() + .withTransaction( (session, tx) -> session + .find( GuineaPig.class, expectedPig.getId(), LockMode.PESSIMISTIC_WRITE ) + .thenAccept( actualPig -> { + assertThatPigsAreEqual( expectedPig, actualPig ); + assertEquals( session.getLockMode( actualPig ), LockMode.PESSIMISTIC_WRITE ); + } ) + ) ) ); } @Test public void reactiveFindRefreshWithLock(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 5, "Aloi" ); - test( context, populateDB() - .thenCompose( v -> getSessionFactory() - .withTransaction( (session, tx) -> session - .find( GuineaPig.class, expectedPig.getId() ) - .thenCompose( pig -> session - .refresh( pig, LockMode.PESSIMISTIC_WRITE ) - .thenAccept( vv -> { - assertThatPigsAreEqual( expectedPig, pig ); - assertEquals( session.getLockMode( pig ), LockMode.PESSIMISTIC_WRITE ); - } ) - ) - ) ) + test( + context, populateDB() + .thenCompose( v -> getSessionFactory() + .withTransaction( (session, tx) -> session + .find( GuineaPig.class, expectedPig.getId() ) + .thenCompose( pig -> session + .refresh( pig, LockMode.PESSIMISTIC_WRITE ) + .thenAccept( vv -> { + assertThatPigsAreEqual( expectedPig, pig ); + assertEquals( + session.getLockMode( pig ), + LockMode.PESSIMISTIC_WRITE + ); + } ) + ) + ) ) ); } @@ -231,40 +242,42 @@ public void reactiveFindReadOnlyRefreshWithLock(VertxTestContext context) { @Test public void reactiveFindThenUpgradeLock(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 5, "Aloi" ); - test( context, populateDB() - .thenCompose( unused -> getSessionFactory() - .withTransaction( (session, tx) -> session - .find( GuineaPig.class, expectedPig.getId() ) - .thenCompose( pig -> session - .lock( pig, LockMode.PESSIMISTIC_READ ) - .thenAccept( v -> { - assertThatPigsAreEqual( expectedPig, pig ); - assertEquals( - session.getLockMode( pig ), - LockMode.PESSIMISTIC_READ - ); - } ) + test( + context, populateDB() + .thenCompose( unused -> getSessionFactory() + .withTransaction( (session, tx) -> session + .find( GuineaPig.class, expectedPig.getId() ) + .thenCompose( pig -> session + .lock( pig, LockMode.PESSIMISTIC_READ ) + .thenAccept( v -> { + assertThatPigsAreEqual( expectedPig, pig ); + assertEquals( + session.getLockMode( pig ), + LockMode.PESSIMISTIC_READ + ); + } ) + ) ) ) - ) ); } @Test public void reactiveFindThenWriteLock(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 5, "Aloi" ); - test( context, populateDB().thenCompose( v -> getSessionFactory() - .withTransaction( (session, tx) -> session - .find( GuineaPig.class, expectedPig.getId() ) - .thenCompose( pig -> session - .lock( pig, LockMode.PESSIMISTIC_WRITE ) - .thenAccept( vv -> { - assertThatPigsAreEqual( expectedPig, pig ); - assertEquals( session.getLockMode( pig ), LockMode.PESSIMISTIC_WRITE ); - assertEquals( pig.version, 0 ); - } ) - ) - ) ) + test( + context, populateDB().thenCompose( v -> getSessionFactory() + .withTransaction( (session, tx) -> session + .find( GuineaPig.class, expectedPig.getId() ) + .thenCompose( pig -> session + .lock( pig, LockMode.PESSIMISTIC_WRITE ) + .thenAccept( vv -> { + assertThatPigsAreEqual( expectedPig, pig ); + assertEquals( session.getLockMode( pig ), LockMode.PESSIMISTIC_WRITE ); + assertEquals( pig.version, 0 ); + } ) + ) + ) ) ); } @@ -302,7 +315,8 @@ public void reactiveFindThenForceLock(VertxTestContext context) { ); assertEquals( actualPig.version, 2 ); } ) - .thenCompose( v -> session.createSelectionQuery( "select version from GuineaPig", Integer.class ) + .thenCompose( v -> session + .createSelectionQuery( "select version from GuineaPig", Integer.class ) .getSingleResult() ) .thenAccept( version -> assertEquals( 2, version ) ) ) @@ -315,16 +329,11 @@ public void reactiveFindWithPessimisticIncrementLock(VertxTestContext context) { test( context, populateDB() - .thenCompose( v -> getSessionFactory().withTransaction( - (session, transaction) -> session.find( - GuineaPig.class, - expectedPig.getId(), - LockMode.PESSIMISTIC_FORCE_INCREMENT ) + .thenCompose( v -> getSessionFactory() + .withTransaction( session -> session.find( GuineaPig.class, expectedPig.getId(), LockMode.PESSIMISTIC_FORCE_INCREMENT ) .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( - session.getLockMode( actualPig ), - LockMode.PESSIMISTIC_FORCE_INCREMENT ); // grrr, lame + assertEquals( LockMode.PESSIMISTIC_FORCE_INCREMENT, session.getLockMode( actualPig ) ); // grrr, lame assertEquals( 1, actualPig.version ); } ) ) ) @@ -349,8 +358,8 @@ public void reactiveFindWithOptimisticIncrementLock(VertxTestContext context) { .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); assertEquals( - session.getLockMode( actualPig ), - LockMode.OPTIMISTIC_FORCE_INCREMENT + LockMode.OPTIMISTIC_FORCE_INCREMENT, + session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) @@ -431,12 +440,13 @@ public void reactiveFindWithOptimisticVerifyLock(VertxTestContext context) { .find( GuineaPig.class, expectedPig.getId(), LockMode.OPTIMISTIC ) .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( session.getLockMode( actualPig ), LockMode.OPTIMISTIC ); + assertEquals( LockMode.OPTIMISTIC, session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) ) ) .thenCompose( v -> openSession() ) .thenCompose( session -> session.find( GuineaPig.class, expectedPig.getId() ) ) - .thenAccept( actualPig -> assertEquals( 0, actualPig.version ) ) ); + .thenAccept( actualPig -> assertEquals( 0, actualPig.version ) ) + ); } @Test @@ -450,7 +460,7 @@ public void reactiveLockWithOptimisticVerify(VertxTestContext context) { .thenCompose( actualPig -> session.lock( actualPig, LockMode.OPTIMISTIC ) .thenAccept( vv -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( session.getLockMode( actualPig ), LockMode.OPTIMISTIC ); + assertEquals( LockMode.OPTIMISTIC, session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) ) ) ) .thenCompose( v -> openSession() ) @@ -493,7 +503,7 @@ public void reactiveLockWithPessimisticRead(VertxTestContext context) { .thenCompose( actualPig -> session.lock( actualPig, LockMode.PESSIMISTIC_READ ) .thenAccept( vv -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( session.getLockMode( actualPig ), LockMode.PESSIMISTIC_READ ); + assertEquals( LockMode.PESSIMISTIC_READ, session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) ) ) ) .thenCompose( v -> openSession() ) @@ -508,13 +518,13 @@ public void reactiveFindWithPessimisticWrite(VertxTestContext context) { test( context, populateDB() - .thenCompose( v -> getSessionFactory () + .thenCompose( v -> getSessionFactory() .withTransaction( (session, transaction) -> session // does a select ... for update .find( GuineaPig.class, expectedPig.getId(), LockMode.PESSIMISTIC_WRITE ) .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( session.getLockMode( actualPig ), LockMode.PESSIMISTIC_WRITE ); + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) ) ) .thenCompose( v -> openSession() ) @@ -536,9 +546,7 @@ public void reactiveLockWithPessimisticWrite(VertxTestContext context) { .thenCompose( actualPig -> session.lock( actualPig, LockMode.PESSIMISTIC_WRITE ) .thenAccept( vv -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( - session.getLockMode( actualPig ), - LockMode.PESSIMISTIC_WRITE ); + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getLockMode( actualPig ) ); assertEquals( 0, actualPig.version ); } ) ) ) ) .thenCompose( v -> openSession() ) @@ -560,9 +568,7 @@ public void reactiveQueryWithLock(VertxTestContext context) { .getSingleResult() .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); - assertEquals( - session.getLockMode( actualPig ), - LockMode.PESSIMISTIC_WRITE ); + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getLockMode( actualPig ) ); } ) ) ) ); } @@ -570,21 +576,22 @@ public void reactiveQueryWithLock(VertxTestContext context) { @Test public void reactiveQueryWithAliasedLock(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 5, "Aloi" ); - test( context, populateDB() - .thenCompose( - v -> getSessionFactory().withTransaction( - (session, tx) -> session.createSelectionQuery( "from GuineaPig pig", GuineaPig.class ) + test( + context, populateDB() + .thenCompose( v -> getSessionFactory() + .withTransaction( session -> session + .createSelectionQuery( "from GuineaPig pig", GuineaPig.class ) .setLockMode( "pig", LockMode.PESSIMISTIC_WRITE ) .getSingleResult() .thenAccept( actualPig -> { assertThatPigsAreEqual( expectedPig, actualPig ); assertEquals( - session.getLockMode( actualPig ), - LockMode.PESSIMISTIC_WRITE + LockMode.PESSIMISTIC_WRITE, + session.getLockMode( actualPig ) ); } ) + ) ) - ) ); } @@ -608,7 +615,7 @@ public void reactivePersistInTx(VertxTestContext context) { context, openSession() .thenCompose( s -> s - .withTransaction( t -> s.persist( new GuineaPig( 10, "Tulip" ) )) + .withTransaction( t -> s.persist( new GuineaPig( 10, "Tulip" ) ) ) .thenCompose( v -> s.close() ) ) .thenCompose( vv -> selectNameFromId( 10 ) ) .thenAccept( selectRes -> assertEquals( "Tulip", selectRes ) ) @@ -623,32 +630,33 @@ public void reactiveRollbackTx(VertxTestContext context) { .thenCompose( s -> s .withTransaction( t -> s .persist( new GuineaPig( 10, "Tulip" ) ) - .thenCompose( v -> s.flush() ) - .thenAccept( v -> { - throw new RuntimeException( "No Panic: This is just a test" ); - } ) - ) - .thenCompose( v -> s.close() ) + .thenCompose( v -> s.flush() ) + .thenAccept( v -> { + throw new RuntimeException( "No Panic: This is just a test" ); + } ) + ) + .thenCompose( v -> s.close() ) ) .handle( (v, e) -> null ) .thenCompose( vv -> selectNameFromId( 10 ) ) - .thenAccept( Assertions::assertNull) + .thenAccept( Assertions::assertNull ) ); } @Test public void reactiveMarkedRollbackTx(VertxTestContext context) { - test( context, openSession() - .thenCompose( s -> s - .withTransaction( t -> s - .persist( new GuineaPig( 10, "Tulip" ) ) - .thenCompose( vv -> s.flush() ) - .thenAccept( vv -> t.markForRollback() ) + test( + context, openSession() + .thenCompose( s -> s + .withTransaction( t -> s + .persist( new GuineaPig( 10, "Tulip" ) ) + .thenCompose( vv -> s.flush() ) + .thenAccept( vv -> t.markForRollback() ) + ) + .thenCompose( v -> s.close() ) ) - .thenCompose( v -> s.close() ) - ) - .thenCompose( vv -> selectNameFromId( 10 ) ) - .thenAccept( Assertions::assertNull ) + .thenCompose( vv -> selectNameFromId( 10 ) ) + .thenAccept( Assertions::assertNull ) ); } @@ -698,7 +706,7 @@ public void reactiveUpdate(VertxTestContext context) { .thenAccept( pig -> { assertNotNull( pig ); // Checking we are actually changing the name - assertNotEquals( pig.getName(), NEW_NAME ); + assertNotEquals( NEW_NAME, pig.getName() ); pig.setName( NEW_NAME ); } ) .thenCompose( v -> session.flush() ) @@ -720,8 +728,8 @@ public void reactiveUpdateVersion(VertxTestContext context) { .thenAccept( pig -> { assertNotNull( pig ); // Checking we are actually changing the name - assertNotEquals( pig.getName(), NEW_NAME ); - assertEquals( pig.version, 0 ); + assertNotEquals( NEW_NAME, pig.getName() ); + assertEquals( 0, pig.version ); pig.setName( NEW_NAME ); pig.version = 10; //ignored by Hibernate } ) @@ -730,22 +738,24 @@ public void reactiveUpdateVersion(VertxTestContext context) { ) .thenCompose( v -> openSession() ) .thenCompose( s -> s.find( GuineaPig.class, 5 ) - .thenAccept( pig -> assertEquals( pig.version, 1 ) ) ) + .thenAccept( pig -> assertEquals( 1, pig.version ) ) ) ); } @Test public void reactiveClose(VertxTestContext context) { - test( context, openSession() - .thenCompose( session -> { - assertTrue( session.isOpen() ); - return session.close() - .thenAccept( v -> assertFalse( session.isOpen() ) ); - } ) + test( + context, openSession() + .thenCompose( session -> { + assertTrue( session.isOpen() ); + return session.close() + .thenAccept( v -> assertFalse( session.isOpen() ) ); + } ) ); } @Test + @Disabled public void testSessionWithNativeAffectedEntities(VertxTestContext context) { GuineaPig pig = new GuineaPig( 3, "Rorshach" ); AffectedEntities affectsPigs = new AffectedEntities( GuineaPig.class ); @@ -753,7 +763,8 @@ public void testSessionWithNativeAffectedEntities(VertxTestContext context) { context, openSession().thenCompose( s -> s .persist( pig ) - .thenCompose( v -> s.createNativeQuery( "select * from pig where name=:n", GuineaPig.class, affectsPigs ) + .thenCompose( v -> s + .createNativeQuery( "select * from pig where name=:n", GuineaPig.class, affectsPigs ) .setParameter( "n", pig.name ) .getResultList() ) .thenAccept( list -> { @@ -769,7 +780,7 @@ public void testSessionWithNativeAffectedEntities(VertxTestContext context) { .thenCompose( v -> s.createNativeQuery( "update pig set name='Y' where name='X'", affectsPigs ).executeUpdate() ) .thenAccept( rows -> assertEquals( 1, rows ) ) .thenCompose( v -> s.refresh( pig ) ) - .thenAccept( v -> assertEquals( pig.name, "Y" ) ) + .thenAccept( v -> assertEquals( "Y", pig.name ) ) .thenAccept( v -> pig.name = "Z" ) .thenCompose( v -> s.createNativeQuery( "delete from pig where name='Z'", affectsPigs ).executeUpdate() ) .thenAccept( rows -> assertEquals( 1, rows ) ) @@ -786,46 +797,54 @@ public void testMetamodel() { assertEquals( "GuineaPig", pig.getName() ); } - @Test void testFactory(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> { - session.getFactory().getCache().evictAll(); - session.getFactory().getMetamodel().entity(GuineaPig.class); - session.getFactory().getCriteriaBuilder().createQuery(GuineaPig.class); - session.getFactory().getStatistics().isStatisticsEnabled(); - return CompletionStages.voidFuture(); - } ) ); + @Test + void testFactory(VertxTestContext context) { + test( + context, getSessionFactory().withSession( session -> { + session.getFactory().getCache().evictAll(); + session.getFactory().getMetamodel().entity( GuineaPig.class ); + session.getFactory().getCriteriaBuilder().createQuery( GuineaPig.class ); + session.getFactory().getStatistics().isStatisticsEnabled(); + return CompletionStages.voidFuture(); + } ) + ); } @Test public void testTransactionPropagation(VertxTestContext context) { - test( context, getSessionFactory().withTransaction( - (session, transaction) -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList() - .thenCompose( list -> { - assertNotNull( session.currentTransaction() ); - assertFalse( session.currentTransaction().isMarkedForRollback() ); - session.currentTransaction().markForRollback(); - assertTrue( session.currentTransaction().isMarkedForRollback() ); - assertTrue( transaction.isMarkedForRollback() ); - return session.withTransaction( t -> { - assertEquals( t, transaction ); - assertTrue( t.isMarkedForRollback() ); - return session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); - } ); - } ) - ) ); + test( + context, getSessionFactory().withTransaction( + (session, transaction) -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getResultList() + .thenCompose( list -> { + assertNotNull( session.currentTransaction() ); + assertFalse( session.currentTransaction().isMarkedForRollback() ); + session.currentTransaction().markForRollback(); + assertTrue( session.currentTransaction().isMarkedForRollback() ); + assertTrue( transaction.isMarkedForRollback() ); + return session.withTransaction( t -> { + assertEquals( t, transaction ); + assertTrue( t.isMarkedForRollback() ); + return session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); + } ); + } ) + ) + ); } @Test public void testSessionPropagation(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> { - assertFalse( session.isDefaultReadOnly() ); - session.setDefaultReadOnly( true ); - return session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList() - .thenCompose( list -> getSessionFactory().withSession( s -> { - assertTrue( s.isDefaultReadOnly() ); - return s.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); - } ) ); - } ) ); + test( + context, getSessionFactory().withSession( session -> { + assertFalse( session.isDefaultReadOnly() ); + session.setDefaultReadOnly( true ); + return session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList() + .thenCompose( list -> getSessionFactory().withSession( s -> { + assertTrue( s.isDefaultReadOnly() ); + return s.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); + } ) ); + } ) + ); } @Test @@ -838,8 +857,8 @@ public void testDupeException(VertxTestContext context) { .withTransaction( (s, t) -> s.persist( new GuineaPig( 10, "Tulip" ) ) ) ).handle( (i, t) -> { assertNotNull( t ); - assertTrue( t instanceof CompletionException ); - assertTrue( t.getCause() instanceof PersistenceException ); + assertInstanceOf( CompletionException.class, t ); + assertInstanceOf( PersistenceException.class, t.getCause() ); return null; } ) ); @@ -848,43 +867,49 @@ public void testDupeException(VertxTestContext context) { @Test public void testExceptionInWithSession(VertxTestContext context) { final Stage.Session[] savedSession = new Stage.Session[1]; - test( context, getSessionFactory().withSession( session -> { - assertTrue( session.isOpen() ); - savedSession[0] = session; - throw new RuntimeException( "No Panic: This is just a test" ); - } ).handle( (o, t) -> { - assertNotNull( t ); - assertFalse( savedSession[0].isOpen(), "Session should be closed" ); - return null; - } ) ); + test( + context, getSessionFactory().withSession( session -> { + assertTrue( session.isOpen() ); + savedSession[0] = session; + throw new RuntimeException( "No Panic: This is just a test" ); + } ).handle( (o, t) -> { + assertNotNull( t ); + assertFalse( savedSession[0].isOpen(), "Session should be closed" ); + return null; + } ) + ); } @Test public void testExceptionInWithTransaction(VertxTestContext context) { final Stage.Session[] savedSession = new Stage.Session[1]; - test( context, getSessionFactory().withTransaction( (session, tx) -> { - assertTrue( session.isOpen() ); - savedSession[0] = session; - throw new RuntimeException( "No Panic: This is just a test" ); - } ).handle( (o, t) -> { - assertNotNull( t ); - assertFalse( savedSession[0].isOpen(), "Session should be closed" ); - return null; - } ) ); + test( + context, getSessionFactory().withTransaction( (session, tx) -> { + assertTrue( session.isOpen() ); + savedSession[0] = session; + throw new RuntimeException( "No Panic: This is just a test" ); + } ).handle( (o, t) -> { + assertNotNull( t ); + assertFalse( savedSession[0].isOpen(), "Session should be closed" ); + return null; + } ) + ); } @Test public void testExceptionInWithStatelessSession(VertxTestContext context) { final Stage.StatelessSession[] savedSession = new Stage.StatelessSession[1]; - test( context, getSessionFactory().withStatelessSession( session -> { - assertTrue( session.isOpen() ); - savedSession[0] = session; - throw new RuntimeException( "No Panic: This is just a test" ); - } ).handle( (o, t) -> { - assertNotNull( t ); - assertFalse( savedSession[0].isOpen(), "Session should be closed" ); - return null; - } ) ); + test( + context, getSessionFactory().withStatelessSession( session -> { + assertTrue( session.isOpen() ); + savedSession[0] = session; + throw new RuntimeException( "No Panic: This is just a test" ); + } ).handle( (o, t) -> { + assertNotNull( t ); + assertFalse( savedSession[0].isOpen(), "Session should be closed" ); + return null; + } ) + ); } @Test @@ -892,49 +917,55 @@ public void testCreateSelectionQueryMultiple(VertxTestContext context) { final GuineaPig aloiPig = new GuineaPig( 10, "Aloi" ); final GuineaPig bloiPig = new GuineaPig( 11, "Bloi" ); - test( context, openSession() - .thenCompose( s -> s.withTransaction( t -> s.persist( aloiPig, bloiPig ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getResultList() - .thenAccept( resultList -> assertThat( resultList ).containsExactlyInAnyOrder( aloiPig, bloiPig ) ) + test( + context, openSession() + .thenCompose( s -> s.withTransaction( t -> s.persist( aloiPig, bloiPig ) ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session + .createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getResultList() + .thenAccept( resultList -> assertThat( resultList ).containsExactlyInAnyOrder( aloiPig, bloiPig ) ) ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session + .createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getResultList() + .thenAccept( resultList -> assertThat( resultList ).containsExactlyInAnyOrder( aloiPig, bloiPig ) ) ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session - .createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getResultList() - .thenAccept( resultList -> assertThat( resultList ).containsExactlyInAnyOrder( aloiPig, bloiPig ) ) ) ) ); } @Test public void testCreateSelectionQuerySingle(VertxTestContext context) { final GuineaPig expectedPig = new GuineaPig( 10, "Aloi" ); - test( context, openSession() - .thenCompose( s -> s - .withTransaction( t -> s.persist( new GuineaPig( 10, "Aloi" ) ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getSingleResult() - .thenAccept( actualPig -> assertThatPigsAreEqual( expectedPig, actualPig ) ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getSingleResult() - .thenAccept( actualPig -> assertThatPigsAreEqual( expectedPig, actualPig ) ) ) - ) + test( + context, openSession() + .thenCompose( s -> s + .withTransaction( t -> s.persist( new GuineaPig( 10, "Aloi" ) ) ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session + .createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getSingleResult() + .thenAccept( actualPig -> assertThatPigsAreEqual( expectedPig, actualPig ) ) ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session + .createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getSingleResult() + .thenAccept( actualPig -> assertThatPigsAreEqual( expectedPig, actualPig ) ) ) + ) ); } @Test public void testCreateSelectionQueryNull(VertxTestContext context) { - test( context, openSession() - .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getSingleResultOrNull() - .thenAccept( Assertions::assertNull ) ) - .thenCompose( v -> openSession() ) - .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) - .getSingleResultOrNull() - .thenAccept( Assertions::assertNull ) ) + test( + context, openSession() + .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getSingleResultOrNull() + .thenAccept( Assertions::assertNull ) ) + .thenCompose( v -> openSession() ) + .thenCompose( session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getSingleResultOrNull() + .thenAccept( Assertions::assertNull ) ) ); } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TableGeneratorTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TableGeneratorTest.java index cf0f9be7d..19eed09ec 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TableGeneratorTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TableGeneratorTest.java @@ -34,36 +34,33 @@ protected Collection> annotatedEntities() { @Test public void testTableGenerator(VertxTestContext context) { - TableId b = new TableId(); b.string = "Hello World"; - test( - context, - openSession() - .thenCompose( s -> s.persist( b ).thenCompose( v -> s.flush() ) ) - .thenCompose( v -> openSession() ) - .thenCompose( s2 -> - s2.find( TableId.class, b.getId() ) - .thenAccept( bb -> { - assertNotNull( bb ); - assertEquals( bb.id, 6 ); - assertEquals( bb.string, b.string ); - assertEquals( bb.version, 0 ); - - bb.string = "Goodbye"; - } ) - .thenCompose( vv -> s2.flush() ) - .thenCompose( vv -> s2.find( TableId.class, b.getId() ) ) - .thenAccept( bt -> { - assertEquals( bt.version, 1 ); - } ) ) - .thenCompose( v -> openSession() ) - .thenCompose( s3 -> s3.find( TableId.class, b.getId() ) ) + test( context, openSession() + .thenCompose( s -> s.persist( b ).thenCompose( v -> s.flush() ) ) + .thenCompose( v -> openSession() ) + .thenCompose( s2 -> s2 + .find( TableId.class, b.getId() ) .thenAccept( bb -> { - assertEquals( bb.version, 1 ); - assertEquals( bb.string, "Goodbye" ); + assertNotNull( bb ); + assertEquals( 6, bb.id ); + assertEquals( bb.string, b.string ); + assertEquals( 0, bb.version ); + + bb.string = "Goodbye"; } ) + .thenCompose( vv -> s2.flush() ) + .thenCompose( vv -> s2.find( TableId.class, b.getId() ) ) + .thenAccept( bt -> { + assertEquals( 1, bt.version ); + } ) ) + .thenCompose( v -> openSession() ) + .thenCompose( s3 -> s3.find( TableId.class, b.getId() ) ) + .thenAccept( bb -> { + assertEquals( 1, bb.version ); + assertEquals( "Goodbye", bb.string ); + } ) ); } 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 b7ac63e4d..dfeeaf15c 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 @@ -73,11 +73,11 @@ class DB2Database implements TestableDatabase { expectedDBTypeForClass.put( Character.class, "CHARACTER" ); expectedDBTypeForClass.put( char.class, "CHARACTER" ); expectedDBTypeForClass.put( String.class, "VARCHAR" ); - expectedDBTypeForClass.put( String[].class, "VARBINARY" ); - expectedDBTypeForClass.put( Long[].class, "VARBINARY" ); - expectedDBTypeForClass.put( BigDecimal[].class, "VARBINARY" ); - expectedDBTypeForClass.put( BigInteger[].class, "VARBINARY" ); - expectedDBTypeForClass.put( Boolean[].class, "VARBINARY" ); + expectedDBTypeForClass.put( String[].class, "XML" ); + expectedDBTypeForClass.put( Long[].class, "XML" ); + expectedDBTypeForClass.put( BigDecimal[].class, "XML" ); + expectedDBTypeForClass.put( BigInteger[].class, "XML" ); + expectedDBTypeForClass.put( Boolean[].class, "XML" ); }} /** 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 8078d1b7e..aeb1b8fb0 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 @@ -77,16 +77,16 @@ class MSSQLServerDatabase implements TestableDatabase { expectedDBTypeForClass.put( BigDecimal.class, "numeric" ); expectedDBTypeForClass.put( Serializable.class, "varbinary" ); expectedDBTypeForClass.put( UUID.class, "binary" ); - expectedDBTypeForClass.put( Instant.class, "datetime2" ); + expectedDBTypeForClass.put( Instant.class, "datetimeoffset" ); expectedDBTypeForClass.put( Duration.class, "bigint" ); expectedDBTypeForClass.put( Character.class, "char" ); expectedDBTypeForClass.put( char.class, "char" ); expectedDBTypeForClass.put( String.class, "varchar" ); - expectedDBTypeForClass.put( String[].class, "varbinary" ); - expectedDBTypeForClass.put( Long[].class, "varbinary" ); - expectedDBTypeForClass.put( BigDecimal[].class, "varbinary" ); - expectedDBTypeForClass.put( BigInteger[].class, "varbinary" ); - expectedDBTypeForClass.put( Boolean[].class, "varbinary" ); + expectedDBTypeForClass.put( String[].class, "xml" ); + expectedDBTypeForClass.put( Long[].class, "xml" ); + expectedDBTypeForClass.put( BigDecimal[].class, "xml" ); + expectedDBTypeForClass.put( BigInteger[].class, "xml" ); + expectedDBTypeForClass.put( Boolean[].class, "xml" ); }} /** 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 575c5290d..ba8e12f42 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 @@ -6,12 +6,27 @@ package org.hibernate.reactive.containers; -import static org.hibernate.reactive.containers.DockerImage.imageName; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; import org.testcontainers.containers.MariaDBContainer; +import static org.hibernate.reactive.containers.DockerImage.imageName; + class MariaDatabase extends MySQLDatabase { + private static final Map, String> expectedDBTypeForClass = new HashMap<>(); + + static {{ + expectedDBTypeForClass.putAll( MySQLDatabase.expectedDBTypeForClass ); + + // Even if the column is created using `json`, the client will return `longtext` as the type. + expectedDBTypeForClass.put( BigDecimal[].class, "longtext" ); + expectedDBTypeForClass.put( BigInteger[].class, "longtext" ); + }} + static MariaDatabase INSTANCE = new MariaDatabase(); /** @@ -31,6 +46,11 @@ private String getRegularJdbcUrl() { return "jdbc:mariadb://localhost:3306/" + maria.getDatabaseName(); } + @Override + public String getExpectedNativeDatatype(Class dataType) { + return expectedDBTypeForClass.get( dataType ); + } + @Override public String getJdbcUrl() { return buildJdbcUrlWithCredentials( address() ); 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 9b94cf8ed..e753da93d 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 @@ -34,15 +34,15 @@ class MySQLDatabase implements TestableDatabase { static MySQLDatabase INSTANCE = new MySQLDatabase(); - private static Map, String> expectedDBTypeForClass = new HashMap<>(); + protected static Map, String> expectedDBTypeForClass = new HashMap<>(); static {{ expectedDBTypeForClass.put( boolean.class, "bit" ); expectedDBTypeForClass.put( Boolean.class, "bit" ); expectedDBTypeForClass.put( NumericBooleanConverter.class, "int" ); - expectedDBTypeForClass.put( YesNoConverter.class, "char" ); - expectedDBTypeForClass.put( TrueFalseConverter.class, "char" ); + expectedDBTypeForClass.put( YesNoConverter.class, "varchar" ); + expectedDBTypeForClass.put( TrueFalseConverter.class, "varchar" ); expectedDBTypeForClass.put( byte[].class, "varbinary" ); // expectedDBTypeForClass.put( TextType.class, "text" ); @@ -75,9 +75,9 @@ class MySQLDatabase implements TestableDatabase { expectedDBTypeForClass.put( String.class, "varchar" ); expectedDBTypeForClass.put( String[].class, "varchar" ); expectedDBTypeForClass.put( Long[].class, "varbinary" ); - expectedDBTypeForClass.put( BigDecimal[].class, "varbinary" ); - expectedDBTypeForClass.put( BigInteger[].class, "varbinary" ); expectedDBTypeForClass.put( Boolean[].class, "varbinary" ); + expectedDBTypeForClass.put( BigDecimal[].class, "json" ); + expectedDBTypeForClass.put( BigInteger[].class, "json" ); }}; /** 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 9ad2f8136..a636bc5a9 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 @@ -66,16 +66,16 @@ class OracleDatabase implements TestableDatabase { expectedDBTypeForClass.put( URL.class, "VARCHAR2" ); expectedDBTypeForClass.put( TimeZone.class, "VARCHAR2" ); expectedDBTypeForClass.put( Date.class, "DATE" ); - expectedDBTypeForClass.put( Timestamp.class, "TIMESTAMP(6)" ); - expectedDBTypeForClass.put( Time.class, "TIMESTAMP(6)" ); + expectedDBTypeForClass.put( Timestamp.class, "TIMESTAMP(9)" ); + expectedDBTypeForClass.put( Time.class, "TIMESTAMP(0)" ); expectedDBTypeForClass.put( LocalDate.class, "DATE" ); expectedDBTypeForClass.put( LocalTime.class, "DATE" ); - expectedDBTypeForClass.put( LocalDateTime.class, "TIMESTAMP(6)" ); + expectedDBTypeForClass.put( LocalDateTime.class, "TIMESTAMP(9)" ); expectedDBTypeForClass.put( BigInteger.class, "NUMBER" ); expectedDBTypeForClass.put( BigDecimal.class, "NUMBER" ); expectedDBTypeForClass.put( Serializable.class, "RAW" ); expectedDBTypeForClass.put( UUID.class, "RAW" ); - expectedDBTypeForClass.put( Instant.class, "TIMESTAMP(6)" ); + expectedDBTypeForClass.put( Instant.class, "TIMESTAMP(9) WITH TIME ZONE" ); expectedDBTypeForClass.put( Duration.class, "NUMBER" ); expectedDBTypeForClass.put( Character.class, "CHAR" ); expectedDBTypeForClass.put( char.class, "CHAR" ); @@ -83,7 +83,7 @@ class OracleDatabase implements TestableDatabase { expectedDBTypeForClass.put( String[].class, "STRINGARRAY" ); expectedDBTypeForClass.put( Long[].class, "LONGARRAY" ); expectedDBTypeForClass.put( BigDecimal[].class, "BIGDECIMALARRAY" ); - expectedDBTypeForClass.put( BigInteger[].class, "BIGINTEGERARRAY" ); + expectedDBTypeForClass.put( BigInteger[].class, "BIGINTEGERBIGDECIMALARRAY" ); expectedDBTypeForClass.put( Boolean[].class, "BOOLEANARRAY" ); } } 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 038733e7e..c832ae867 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 @@ -68,7 +68,7 @@ class PostgreSQLDatabase implements TestableDatabase { expectedDBTypeForClass.put( BigDecimal.class, "numeric" ); expectedDBTypeForClass.put( Serializable.class, "bytea" ); expectedDBTypeForClass.put( UUID.class, "binary" ); - expectedDBTypeForClass.put( Instant.class, "timestamp without time zone" ); + expectedDBTypeForClass.put( Instant.class, "timestamp with time zone" ); expectedDBTypeForClass.put( Duration.class, "bigint" ); expectedDBTypeForClass.put( Character.class, "character" ); expectedDBTypeForClass.put( char.class, "character" ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/dynamic/DynamicEntityTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/dynamic/DynamicEntityTest.java index bbdad20cc..2b4050657 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/dynamic/DynamicEntityTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/dynamic/DynamicEntityTest.java @@ -10,17 +10,22 @@ import java.util.List; import java.util.Map; +import org.hibernate.cfg.Configuration; import org.hibernate.reactive.BaseReactiveTest; -import org.hibernate.tuple.DynamicMapInstantiator; import org.junit.jupiter.api.Test; import io.vertx.junit5.Timeout; import io.vertx.junit5.VertxTestContext; +import static java.util.Map.entry; import static java.util.concurrent.TimeUnit.MINUTES; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.metamodel.internal.AbstractDynamicMapInstantiator.TYPE_KEY; +/** + * Copied from Hibernate ORM: org.hibernate.orm.test.mapping.dynamic.DynamicEntityTest + */ @Timeout(value = 10, timeUnit = MINUTES) public class DynamicEntityTest extends BaseReactiveTest { @@ -30,22 +35,29 @@ protected Collection mappings() { return List.of( "org/hibernate/reactive/dynamic/Book.hbm.xml" ); } + @Override + protected Configuration constructConfiguration() { + return super.constructConfiguration() + .setProperty( "hibernate.default_entity_mode", "dynamic-map" ); + } + @Test public void test(VertxTestContext context) { Map book = new HashMap<>(); book.put( "ISBN", "9781932394153" ); book.put( "title", "Hibernate in Action" ); book.put( "author", "Christian Bauer and Gavin King" ); - book.put( DynamicMapInstantiator.KEY, "Book" ); - - test( - context, - getMutinySessionFactory() - .withTransaction( session -> session.persist( book ) ) - .chain( v -> getMutinySessionFactory() - .withSession( session -> session.createSelectionQuery( "from Book", Map.class ).getSingleResult() ) - .invoke( map -> assertEquals( "Christian Bauer and Gavin King", map.get( "author" ) ) ) ) + + test( context, getMutinySessionFactory() + .withTransaction( session -> session.persist( "Book", book ) ) + .chain( v -> getMutinySessionFactory() + .withSession( session -> session.createSelectionQuery( "from Book", Map.class ).getSingleResult() ) + .invoke( map -> assertThat( map.entrySet() ).containsExactlyInAnyOrder( + entry( "author", "Christian Bauer and Gavin King" ), + entry( "ISBN", "9781932394153" ), + entry( "title", "Hibernate in Action" ), + entry( TYPE_KEY, "Book" ) + ) ) ) ); } - } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/ColumnTypesMappingTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/ColumnTypesMappingTest.java index c57621608..892db5e1e 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/ColumnTypesMappingTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/ColumnTypesMappingTest.java @@ -13,12 +13,14 @@ import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.TimeZone; +import java.util.concurrent.CompletionStage; import org.hibernate.reactive.BaseReactiveTest; @@ -36,12 +38,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.reactive.containers.DatabaseConfiguration.expectedDatatype; import static org.hibernate.reactive.containers.DatabaseConfiguration.getDatatypeQuery; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** * Check that each property is mapped as the expected type in the database. */ @Timeout(value = 10, timeUnit = MINUTES) - public class ColumnTypesMappingTest extends BaseReactiveTest { @Override @@ -49,12 +51,21 @@ protected Collection> annotatedEntities() { return List.of( BasicTypesTestEntity.class ); } + @Override + public CompletionStage deleteEntities(Class... entities) { + // Skip delete step. + // We don't insert any value, so there's nothing to delete + // Avoid having extra stuff in the log that's not relevant to the test + return voidFuture(); + } + private void testDatatype(VertxTestContext context, String columnName, Class type) { - test( context, openSession() - .thenCompose( s -> s + test( context, getSessionFactory() + .withTransaction( s -> s .createNativeQuery( getDatatypeQuery( BasicTypesTestEntity.TABLE_NAME, columnName ), String.class ) .getSingleResult() - .thenAccept( typeOnTheDb -> assertThat( toString( typeOnTheDb ) ).isEqualTo( expectedDatatype( type ) ) ) ) + .thenAccept( typeOnTheDb -> assertThat( toString( typeOnTheDb ) ).isEqualTo( expectedDatatype( type ) ) ) + ) ); } @@ -75,6 +86,16 @@ public void testBigDecimal(VertxTestContext context) { testDatatype( context, "bigDecimal", BigDecimal.class ); } + @Test + public void testBigDecimalArray(VertxTestContext context) { + testDatatype( context, "bigDecimalArray", BigDecimal[].class ); + } + + @Test + public void testBigIntegerArray(VertxTestContext context) { + testDatatype( context, "bigIntegerArray", BigInteger[].class ); + } + @Test public void testStringType(VertxTestContext context) { testDatatype( context, "aString", String.class ); @@ -215,4 +236,9 @@ public void testLocalDateTimeType(VertxTestContext context) { public void testSerializableType(VertxTestContext context) { testDatatype( context, "serializable", Serializable.class ); } + + @Test + public void testInstantType(VertxTestContext context) { + testDatatype( context, "instant", Instant.class ); + } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTest.java new file mode 100644 index 000000000..febcda93b --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTest.java @@ -0,0 +1,146 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.schema; + +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.annotations.DisabledFor; +import org.hibernate.reactive.provider.Settings; +import org.hibernate.reactive.stage.Stage; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.SQLSERVER; +import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType; +import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +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; + +/** + * Schema update will run different queries when the table already exists or + * when columns are missing. + */ +@DisabledFor(value = DB2, reason = "No InformationExtractor for Dialect [org.hibernate.dialect.DB2Dialect..]") +public class SchemaUpdateTest extends BaseReactiveTest { + + static Stream settings() { + return Stream.of( + arguments( INDIVIDUALLY.toString(), null ), + arguments( GROUPED.toString(), null ), + arguments( INDIVIDUALLY.toString(), "VARBINARY" ), + arguments( GROUPED.toString(), "VARBINARY" ) + ); + } + + @Override + public CompletionStage deleteEntities(Class... entities) { + return voidFuture(); + } + + protected Configuration constructConfiguration(String action, String strategy, String type) { + Configuration configuration = super.constructConfiguration(); + configuration.setProperty( Settings.HBM2DDL_AUTO, action ); + configuration.setProperty( HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, strategy ); + configuration.addAnnotatedClass( BasicTypesTestEntity.class ); + if ( type != null ) { + // The entity we are using for testing has some arrays. The default behaviour is to store them as XML and + // the Vert.x client doesn't support it at the moment. + configuration.setProperty( "hibernate.type.preferred_array_jdbc_type", "VARBINARY" ); + } + return configuration; + } + + @Override + public void before(VertxTestContext context) { + // Do nothing, we prepare everything in the test so that we can use a parameterized test + } + + @AfterEach + @Override + public void after(VertxTestContext context) { + super.after( context ); + closeFactory( context ); + } + + /** + * Test creation of missing columns + */ + @ParameterizedTest + @MethodSource("settings") + @Timeout(value = 10, timeUnit = MINUTES) + public void testMissingColumnsCreation(final String strategy, final String type, VertxTestContext context) { + final Supplier> testSupplier = () -> setupSessionFactory( constructConfiguration( "drop", strategy, type ) ) + .thenCompose( v -> getSessionFactory().withTransaction( SchemaUpdateTest::createTable ) ) + .whenComplete( (u, throwable) -> factoryManager.stop() ) + .thenCompose( vv -> setupSessionFactory( constructConfiguration( "update", strategy, type ) ) ) + .thenCompose( u -> getSessionFactory().withSession( SchemaUpdateTest::checkAllColumnsExist ) ); + if ( dbType() == SQLSERVER && type == null ) { + test( context, assertThrown( HibernateException.class, testSupplier.get() ) + .thenAccept( e -> assertThat( e.getMessage() ).startsWith( "HR000081: " ) ) ); + } + else { + test( context, testSupplier.get() ); + } + } + + /** + * Test creation of missing table + */ + @ParameterizedTest + @MethodSource("settings") + @Timeout(value = 10, timeUnit = MINUTES) + public void testWholeTableCreation(final String strategy, final String type, VertxTestContext context) { + final Supplier> testSupplier = () -> setupSessionFactory( constructConfiguration( "drop", strategy, type ) ) + .whenComplete( (u, throwable) -> factoryManager.stop() ) + .thenCompose( v -> setupSessionFactory( constructConfiguration( "update", strategy, type ) ) + .thenCompose( vv -> getSessionFactory().withSession( SchemaUpdateTest::checkAllColumnsExist ) ) ); + if ( dbType() == SQLSERVER && type == null ) { + test( context, assertThrown( HibernateException.class, testSupplier.get() ) + .thenAccept( e -> assertThat( e.getMessage() ).startsWith( "HR000081: " ) ) ); + } + else { + test( context, testSupplier.get() ); + } + } + + // I don't think it's possible to create a table without columns, so we add + // a column that's not mapped by the entity. + // We expect the other columns to be created during the update schema phase. + private static CompletionStage createTable(Stage.Session session, Stage.Transaction transaction) { + return session + .createNativeQuery( "create table " + BasicTypesTestEntity.TABLE_NAME + " (unmapped_column " + columnType() + ")" ) + .executeUpdate(); + } + + private static String columnType() { + return dbType() == SQLSERVER ? "int" : "integer"; + } + + /** + * The table is empty, we just want to check that a query runs without errors. + * The query throws an exception if one of the columns is missing + */ + private static CompletionStage checkAllColumnsExist(Stage.Session session) { + return session.find( BasicTypesTestEntity.class, 10 ); + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTestBase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTestBase.java deleted file mode 100644 index 7cc63bc4c..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaUpdateTestBase.java +++ /dev/null @@ -1,127 +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.schema; - -import java.util.concurrent.CompletionStage; - -import org.hibernate.cfg.Configuration; -import org.hibernate.reactive.BaseReactiveTest; -import org.hibernate.reactive.provider.Settings; -import org.hibernate.reactive.stage.Stage; -import org.hibernate.reactive.annotations.DisabledFor; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.vertx.junit5.Timeout; -import io.vertx.junit5.VertxTestContext; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; -import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.SQLSERVER; -import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType; -import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.GROUPED; -import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY; - -/** - * Schema update will run different queries when the table already exists or - * when columns are missing. - */ -@DisabledFor(value = DB2, reason = "No InformationExtractor for Dialect [org.hibernate.dialect.DB2Dialect..]") -public abstract class SchemaUpdateTestBase extends BaseReactiveTest { - - @Timeout(value = 10, timeUnit = MINUTES) - public static class IndividuallyStrategyTest extends SchemaUpdateTestBase { - - @Override - protected Configuration constructConfiguration(String hbm2DdlOption) { - final Configuration configuration = super.constructConfiguration( hbm2DdlOption ); - configuration.setProperty( Settings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, INDIVIDUALLY.toString() ); - return configuration; - } - } - - @Timeout(value = 10, timeUnit = MINUTES) - public static class GroupedStrategyTest extends SchemaUpdateTestBase { - - @Override - protected Configuration constructConfiguration(String hbm2DdlOption) { - final Configuration configuration = super.constructConfiguration( hbm2DdlOption ); - configuration.setProperty( Settings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, GROUPED.toString() ); - return configuration; - } - } - - protected Configuration constructConfiguration(String action) { - Configuration configuration = super.constructConfiguration(); - configuration.setProperty( Settings.HBM2DDL_AUTO, action ); - configuration.addAnnotatedClass( BasicTypesTestEntity.class ); - return configuration; - } - - @BeforeEach - @Override - public void before(VertxTestContext context) { - // For these tests we create the factory when we need it - context.completeNow(); - } - - @AfterEach - @Override - public void after(VertxTestContext context) { - super.after( context ); - closeFactory( context ); - } - - /** - * Test missing columns creation during schema update - */ - @Test - public void testMissingColumnsCreation(VertxTestContext context) { - test( context, - setupSessionFactory( constructConfiguration( "drop" ) ) - .thenCompose( v -> getSessionFactory().withTransaction( SchemaUpdateTestBase::createTable ) ) - .whenComplete( (u, throwable) -> factoryManager.stop() ) - .thenCompose( vv -> setupSessionFactory( constructConfiguration( "update" ) ) - .thenCompose( u -> getSessionFactory().withSession( SchemaUpdateTestBase::checkAllColumnsExist ) ) ) - ); - } - - /** - * Test table creation during schema update - */ - @Test - public void testWholeTableCreation(VertxTestContext context) { - test( context, - setupSessionFactory( constructConfiguration( "drop" ) ) - .whenComplete( (u, throwable) -> factoryManager.stop() ) - .thenCompose( v -> setupSessionFactory( constructConfiguration( "update" ) ) - .thenCompose( vv -> getSessionFactory().withSession( SchemaUpdateTestBase::checkAllColumnsExist ) ) ) - ); - } - - // I don't think it's possible to create a table without columns, so we add - // a column that's not mapped by the entity. - // We expect the other columns to be created during the update schema phase. - private static CompletionStage createTable(Stage.Session session, Stage.Transaction transaction) { - return session - .createNativeQuery( "create table " + BasicTypesTestEntity.TABLE_NAME + " (unmapped_column " + columnType() + ")" ) - .executeUpdate(); - } - - private static String columnType() { - return dbType() == SQLSERVER ? "int" : "integer"; - } - - /** - * The table is empty, we just want to check that a query runs without errors. - * The query throws an exception if one of the columns is missing - */ - private static CompletionStage checkAllColumnsExist(Stage.Session session) { - return session.find( BasicTypesTestEntity.class, 10 ); - } -} 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 new file mode 100644 index 000000000..3132b8498 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTest.java @@ -0,0 +1,154 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.schema; + + +import java.util.concurrent.CompletionStage; +import java.util.stream.Stream; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.annotations.DisabledFor; +import org.hibernate.reactive.provider.Settings; +import org.hibernate.tool.schema.spi.SchemaManagementException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +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.Table; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY; +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.tool.schema.JdbcMetadaAccessStrategy.GROUPED; +import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Test schema validation at startup for all the supported types: + * - Missing table validation error + * - No validation error when everything is fine + * - TODO: Test that validation fails when a column is missing + * - TODO: Test that validation fails when a column is the wrong type + */ +@DisabledFor(value = DB2, reason = "We don't have an information extractor. See https://github.com/hibernate/hibernate-reactive/issues/911") +@DisabledFor(value = {MARIA, MYSQL}, reason = "HHH-18869: Schema creation creates an invalid schema") +public class SchemaValidationTest extends BaseReactiveTest { + + static Stream settings() { + return Stream.of( + arguments( INDIVIDUALLY.toString(), null ), + arguments( GROUPED.toString(), null ), + arguments( INDIVIDUALLY.toString(), "VARBINARY" ), + arguments( GROUPED.toString(), "VARBINARY" ) + ); + } + + protected Configuration constructConfiguration(String action, String strategy, String type) { + Configuration configuration = super.constructConfiguration(); + configuration.setProperty( Settings.HBM2DDL_AUTO, action ); + configuration.setProperty( HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, strategy ); + if ( type != null ) { + // ORM 7 stores arrays as json for MariaDB and MySQL. By setting this property, users + // can keep the behaviour backward compatible. We need to test both. + configuration.setProperty( "hibernate.type.preferred_array_jdbc_type", type ); + } + return configuration; + } + + @Override + public void before(VertxTestContext context) { + // Do nothing, we prepare everything in the test so that we can use a parameterized test + } + + public CompletionStage setupFactory(String strategy, String type) { + Configuration createConf = constructConfiguration( "create", strategy, type ); + createConf.addAnnotatedClass( BasicTypesTestEntity.class ); + + // Make sure that the extra table is not in the db + Configuration dropConf = constructConfiguration( "drop", strategy, type ); + dropConf.addAnnotatedClass( Extra.class ); + + return setupSessionFactory( dropConf ) + .thenCompose( v -> factoryManager.stop() ) + .thenCompose( v -> setupSessionFactory( createConf ) ) + .thenCompose( v -> factoryManager.stop() ); + } + + @AfterEach + @Override + public void after(VertxTestContext context) { + super.after( context ); + closeFactory( context ); + } + + // When we have created the table, the validation should pass + @ParameterizedTest + @MethodSource("settings") + @Timeout(value = 10, timeUnit = MINUTES) + public void testValidationSucceeds(final String strategy, final String type, VertxTestContext context) { + test( + context, setupFactory( strategy, type ) + .thenCompose( v -> { + Configuration validateConf = constructConfiguration( "validate", strategy, type ); + validateConf.addAnnotatedClass( BasicTypesTestEntity.class ); + new StandardServiceRegistryBuilder().applySettings( validateConf.getProperties() ); + return setupSessionFactory( validateConf ); + } ) + ); + } + + + // Validation should fail if a table is missing + @ParameterizedTest + @MethodSource("settings") + @Timeout(value = 10, timeUnit = MINUTES) + public void testValidationFails(String strategy, String type, VertxTestContext context) { + final String errorMessage = "Schema-validation: missing table [" + Extra.TABLE_NAME + "]"; + test( + context, setupFactory( strategy, type ) + .thenCompose( v -> { + Configuration validateConf = constructConfiguration( "validate", 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; + } ); + } ) + ); + } + + /** + * An extra entity used for validation, + * it should not be created at start up + */ + @Entity(name = "Extra") + @Table(name = Extra.TABLE_NAME) + public static class Extra { + public static final String TABLE_NAME = "EXTRA_TABLE"; + @Id + @GeneratedValue + private Integer id; + + private String description; + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java deleted file mode 100644 index 4a5117fee..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java +++ /dev/null @@ -1,142 +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.schema; - - -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; -import org.hibernate.cfg.Configuration; -import org.hibernate.reactive.BaseReactiveTest; -import org.hibernate.reactive.provider.Settings; -import org.hibernate.reactive.annotations.DisabledFor; -import org.hibernate.tool.schema.spi.SchemaManagementException; - -import org.junit.jupiter.api.AfterEach; -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.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; -import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.GROUPED; -import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * Test schema validation at startup for all the supported types: - * - Missing table validation error - * - No validation error when everything is fine - * - TODO: Missing column - * - TODO: Wrong column type - */ -@DisabledFor(value = DB2, reason = "We don't have an information extractor. See https://github.com/hibernate/hibernate-reactive/issues/911") -public abstract class SchemaValidationTestBase extends BaseReactiveTest { - - public static class IndividuallyStrategyTest extends SchemaValidationTestBase { - - @Override - protected Configuration constructConfiguration(String hbm2DdlOption) { - final Configuration configuration = super.constructConfiguration( hbm2DdlOption ); - configuration.setProperty( Settings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, INDIVIDUALLY.toString() ); - return configuration; - } - } - - public static class GroupedStrategyTest extends SchemaValidationTestBase { - - @Override - protected Configuration constructConfiguration(String hbm2DdlOption) { - final Configuration configuration = super.constructConfiguration( hbm2DdlOption ); - configuration.setProperty( Settings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, GROUPED.toString() ); - return configuration; - } - } - - protected Configuration constructConfiguration(String action) { - Configuration configuration = super.constructConfiguration(); - configuration.setProperty( Settings.HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY, INDIVIDUALLY.toString() ); - configuration.setProperty( Settings.HBM2DDL_AUTO, action ); - return configuration; - } - - @BeforeEach - @Override - public void before(VertxTestContext context) { - Configuration createConf = constructConfiguration( "create" ); - createConf.addAnnotatedClass( BasicTypesTestEntity.class ); - - // Make sure that the extra table is not in the db - Configuration dropConf = constructConfiguration( "drop" ); - dropConf.addAnnotatedClass( Extra.class ); - - test( context, setupSessionFactory( dropConf ) - .thenCompose( v -> factoryManager.stop() ) - .thenCompose( v -> setupSessionFactory( createConf ) ) - .thenCompose( v -> factoryManager.stop() ) - ); - } - - @AfterEach - @Override - public void after(VertxTestContext context) { - super.after( context ); - closeFactory( context ); - } - - // When we have created the table, the validation should pass - @Test - @Timeout(value = 10, timeUnit = MINUTES) - public void testValidationSucceeds(VertxTestContext context) { - Configuration validateConf = constructConfiguration( "validate" ); - validateConf.addAnnotatedClass( BasicTypesTestEntity.class ); - - StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder() - .applySettings( validateConf.getProperties() ); - test( context, setupSessionFactory( validateConf ) ); - } - - - // Validation should fail if a table is missing - @Test - @Timeout(value = 10, timeUnit = MINUTES) - public void testValidationFails(VertxTestContext context) { - Configuration validateConf = constructConfiguration( "validate" ); - validateConf.addAnnotatedClass( BasicTypesTestEntity.class ); - // The table mapping this entity shouldn't be in the db - validateConf.addAnnotatedClass( Extra.class ); - - final String errorMessage = "Schema-validation: missing table [" + Extra.TABLE_NAME + "]"; - test( context, setupSessionFactory( validateConf ) - .handle( (unused, throwable) -> { - assertNotNull( throwable ); - assertEquals( throwable.getClass(), SchemaManagementException.class ); - assertEquals( throwable.getMessage(), errorMessage ); - return null; - } ) - ); - } - - /** - * An extra entity used for validation, - * it should not be created at start up - */ - @Entity(name = "Extra") - @Table(name = Extra.TABLE_NAME) - public static class Extra { - public static final String TABLE_NAME = "EXTRA_TABLE"; - @Id - @GeneratedValue - private Integer id; - - private String description; - } -} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JavaTypesArrayTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JavaTypesArrayTest.java index 66fb6dbbd..c66efa587 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JavaTypesArrayTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JavaTypesArrayTest.java @@ -13,12 +13,15 @@ import java.time.Month; import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletionStage; import java.util.function.Consumer; import java.util.function.Predicate; import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; import org.hibernate.annotations.Array; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; @@ -40,8 +43,14 @@ import static java.lang.Boolean.TRUE; import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; +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.containers.DatabaseConfiguration.DBType.ORACLE; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.SQLSERVER; import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType; +import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -66,6 +75,13 @@ protected Configuration constructConfiguration() { return configuration; } + @Override + public CompletionStage deleteEntities(Class... entities) { + // Deleting the entities after each test is not really necessary, and sometimes it causes errors in the log + // that make it harder to figure out what's going on + return voidFuture(); + } + @Override protected void addServices(StandardServiceRegistryBuilder builder) { sqlTracker.registerService( builder ); @@ -80,18 +96,25 @@ protected Set> annotatedEntities() { return Set.of( Basic.class ); } - private void testField( - VertxTestContext context, Basic - original, Consumer consumer) { - test( context, getSessionFactory() - .withTransaction( s -> s.persist( original ) ) - .thenCompose( v -> getSessionFactory().withSession( s -> s - .find( Basic.class, original.id ) - .thenAccept( found -> { - assertNotNull( found ); - consumer.accept( found ); - } ) ) ) - ); + private void testField(VertxTestContext context, Basic original, Consumer consumer) { + if ( List.of( DB2, SQLSERVER ).contains( dbType() ) ) { + test( context, assertThrown( HibernateException.class, getSessionFactory() + .withTransaction( s -> s.persist( original ) ) ) + .thenAccept( e -> assertThat( e.getMessage() ).startsWith( "HR000081: " ) ) + ); + } + else { + test( + context, getSessionFactory() + .withTransaction( s -> s.persist( original ) ) + .thenCompose( v -> getSessionFactory().withSession( s -> s + .find( Basic.class, original.id ) + .thenAccept( found -> { + assertNotNull( found ); + consumer.accept( found ); + } ) ) ) + ); + } } @Test @@ -101,8 +124,8 @@ public void testStringArrayType(VertxTestContext context) { basic.stringArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.stringArray ); validateArrayColumn( "stringArray", null, 255 ); + assertArrayEquals( dataArray, found.stringArray ); } ); } @@ -113,8 +136,8 @@ public void testStringArrayTypeWithArrayAnnotation(VertxTestContext context) { basic.stringArrayWithArrayAnnotation = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.stringArrayWithArrayAnnotation ); validateArrayColumn( "stringArrayWithArrayAnnotation", 5, null ); + assertArrayEquals( dataArray, found.stringArrayWithArrayAnnotation ); } ); } @@ -125,8 +148,8 @@ public void testStringArrayTypeWithColumnAnnotation(VertxTestContext context) { basic.stringArrayWithColumnAnnotation = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.stringArrayWithColumnAnnotation ); validateArrayColumn( "stringArrayWithColumnAnnotation", null, 200 ); + assertArrayEquals( dataArray, found.stringArrayWithColumnAnnotation ); } ); } @@ -137,8 +160,8 @@ public void testStringArrayTypeWithBothAnnotations(VertxTestContext context) { basic.stringArrayWithBothAnnotations = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.stringArrayWithBothAnnotations ); validateArrayColumn( "stringArrayWithBothAnnotations", 5, 200 ); + assertArrayEquals( dataArray, found.stringArrayWithBothAnnotations ); } ); } @@ -148,8 +171,10 @@ public void testBooleanArrayType(VertxTestContext context) { Boolean[] dataArray = {TRUE, FALSE, null, TRUE}; basic.booleanArray = dataArray; - testField( context, basic, found -> assertArrayEquals( dataArray, found.booleanArray ) ); - validateArrayColumn( "booleanArray", null, null ); + testField( context, basic, found -> { + validateArrayColumn( "booleanArray", null, null ); + assertArrayEquals( dataArray, found.booleanArray ); + } ); } @Test @@ -159,8 +184,8 @@ public void testPrimitiveBooleanArrayType(VertxTestContext context) { basic.primitiveBooleanArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveBooleanArray ); - validateArrayColumn( "primitiveBooleanArray", null, null ); + validateArrayColumn( "primitiveBooleanArray", null, null ); + assertArrayEquals( dataArray, found.primitiveBooleanArray ); } ); } @@ -171,8 +196,8 @@ public void testIntegerArrayType(VertxTestContext context) { basic.integerArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.integerArray ); validateArrayColumn( "integerArray", null, null ); + assertArrayEquals( dataArray, found.integerArray ); } ); } @@ -183,8 +208,8 @@ public void testPrimitiveIntegerArrayType(VertxTestContext context) { basic.primitiveIntegerArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveIntegerArray ); validateArrayColumn( "primitiveIntegerArray", null, null ); + assertArrayEquals( dataArray, found.primitiveIntegerArray ); } ); } @@ -195,8 +220,8 @@ public void testLongArrayType(VertxTestContext context) { basic.longArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.longArray ); validateArrayColumn( "longArray", null, null ); + assertArrayEquals( dataArray, found.longArray ); } ); } @@ -207,8 +232,8 @@ public void testPrimitiveLongArrayType(VertxTestContext context) { basic.primitiveLongArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveLongArray ); validateArrayColumn( "primitiveLongArray", null, null ); + assertArrayEquals( dataArray, found.primitiveLongArray ); } ); } @@ -231,8 +256,8 @@ public void testPrimitiveFloatArrayType(VertxTestContext context) { basic.primitiveFloatArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveFloatArray ); validateArrayColumn( "primitiveFloatArray", null, null ); + assertArrayEquals( dataArray, found.primitiveFloatArray ); } ); } @@ -243,8 +268,8 @@ public void testDoubleArrayType(VertxTestContext context) { basic.doubleArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.doubleArray ); validateArrayColumn( "doubleArray", null, null ); + assertArrayEquals( dataArray, found.doubleArray ); } ); } @@ -255,8 +280,8 @@ public void testPrimitiveDoubleArrayType(VertxTestContext context) { basic.primitiveDoubleArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveDoubleArray ); validateArrayColumn( "primitiveDoubleArray", null, null ); + assertArrayEquals( dataArray, found.primitiveDoubleArray ); } ); } @@ -271,8 +296,8 @@ public void testUUIDArrayType(VertxTestContext context) { basic.uuidArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.uuidArray ); validateArrayColumn( "uuidArray", null, null ); + assertArrayEquals( dataArray, found.uuidArray ); } ); } @@ -283,8 +308,8 @@ public void testEnumArrayType(VertxTestContext context) { basic.enumArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.enumArray ); validateArrayColumn( "enumArray", null, null ); + assertArrayEquals( dataArray, found.enumArray ); } ); } @@ -295,8 +320,8 @@ public void testShortArrayType(VertxTestContext context) { basic.shortArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.shortArray ); validateArrayColumn( "shortArray", null, null ); + assertArrayEquals( dataArray, found.shortArray ); } ); } @@ -307,8 +332,8 @@ public void testPrimitiveShortArrayType(VertxTestContext context) { basic.primitiveShortArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.primitiveShortArray ); validateArrayColumn( "primitiveShortArray", null, null ); + assertArrayEquals( dataArray, found.primitiveShortArray ); } ); } @@ -324,20 +349,21 @@ public void testLocalDateArrayType(VertxTestContext context) { basic.localDateArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.localDateArray ); validateArrayColumn( "localDateArray", null, null ); + assertArrayEquals( dataArray, found.localDateArray ); } ); } @Test + @DisabledFor(value = {MYSQL, MARIA}, reason = "HHH-18881: Problem with the conversion of dates") public void testDateArrayType(VertxTestContext context) { Basic basic = new Basic(); Date[] dataArray = {Calendar.getInstance().getTime(), Calendar.getInstance().getTime()}; basic.dateArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.dateArray ); validateArrayColumn( "dateArray", null, null ); + assertArrayEquals( dataArray, found.dateArray ); } ); } @@ -353,12 +379,13 @@ public void testLocalTimeArrayType(VertxTestContext context) { basic.localTimeArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.localTimeArray ); validateArrayColumn( "localTimeArray", null, null ); + assertArrayEquals( dataArray, found.localTimeArray ); } ); } @Test + @DisabledFor(value = {MYSQL, MARIA}, reason = "HHH-18881: Problem with the conversion of dates") public void testLocalDateTimeArrayType(VertxTestContext context) { Basic basic = new Basic(); LocalDateTime[] dataArray = { @@ -374,8 +401,8 @@ public void testLocalDateTimeArrayType(VertxTestContext context) { basic.localDateTimeArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.localDateTimeArray ); validateArrayColumn( "localDateTimeArray", null, null ); + assertArrayEquals( dataArray, found.localDateTimeArray ); } ); } @@ -386,8 +413,8 @@ public void testBigIntegerArrayType(VertxTestContext context) { basic.bigIntegerArray = dataArray; testField( context, basic, found -> { - assertArrayEquals( dataArray, found.bigIntegerArray ); validateArrayColumn( "bigIntegerArray", null, 5000 ); + assertArrayEquals( dataArray, found.bigIntegerArray ); } ); } @@ -398,10 +425,10 @@ public void testBigDecimalArrayType(VertxTestContext context) { basic.bigDecimalArray = dataArray; testField( context, basic, found -> { + validateArrayColumn( "bigDecimalArray", null, 5000 ); assertEquals( dataArray.length, found.bigDecimalArray.length ); assertEquals( 0, dataArray[0].compareTo( found.bigDecimalArray[0] ) ); assertEquals( 0, dataArray[1].compareTo( found.bigDecimalArray[1] ) ); - validateArrayColumn( "bigDecimalArray", null, 5000 ); } ); } @@ -412,10 +439,10 @@ public void testBigDecimalArrayTypeWithArrayAnnotation(VertxTestContext context) basic.bigDecimalArrayWithArrayAnnotation = dataArray; testField( context, basic, found -> { + validateArrayColumn( "bigDecimalArrayWithArrayAnnotation", 5, 5000 ); assertEquals( dataArray.length, found.bigDecimalArrayWithArrayAnnotation.length ); assertEquals( 0, dataArray[0].compareTo( found.bigDecimalArrayWithArrayAnnotation[0] ) ); assertEquals( 0, dataArray[1].compareTo( found.bigDecimalArrayWithArrayAnnotation[1] ) ); - validateArrayColumn( "bigDecimalArrayWithArrayAnnotation", 5, 5000 ); } ); } @@ -426,16 +453,20 @@ private void validateArrayColumn(String columnName, Integer arrayLength, Integer } // A predicate that checks we apply the right size to the array when required - private static Predicate arrayColumnPredicate(String columnName, Integer arrayLength, Integer columnLength) { + private static Predicate arrayColumnPredicate( + String columnName, + Integer arrayLength, + Integer columnLength) { switch ( dbType() ) { case POSTGRESQL: case COCKROACHDB: return postgresPredicate( columnName, arrayLength, columnLength ); case MYSQL: case MARIA: + return arrayAsJsonPredicate( columnName ); case SQLSERVER: case DB2: - return arrayAsVarbinaryPredicate( columnName, columnLength ); + return arrayAsXmlPredicate( columnName ); default: throw new AssertionFailure( "Unexpected database: " + dbType() ); } @@ -444,9 +475,9 @@ private static Predicate arrayColumnPredicate(String columnName, Integer /** * For Postgres, we expect arrays to be defined as {@code array}. *

- * For example: {@code varchar(255) array[2]} + * For example: {@code varchar(255) array[2]} *

- */ + */ private static Predicate postgresPredicate(String columnName, Integer arrayLength, Integer columnLength) { StringBuilder regexBuilder = new StringBuilder(); regexBuilder.append( ".*" ); @@ -468,6 +499,15 @@ private static Predicate postgresPredicate(String columnName, Integer ar return s -> s.matches( regexBuilder.toString() ); } + private static Predicate arrayAsJsonPredicate(String columnName) { + return s -> s.contains( columnName + " json" ); + } + + private static Predicate arrayAsXmlPredicate(String columnName) { + // Example of correct query definition: columnName xml + return s -> s.contains( columnName + " xml" ); + } + private static Predicate arrayAsVarbinaryPredicate(String columnName, Integer columnLength) { StringBuilder regexBuilder = new StringBuilder(); // Example of correct query definition: columnName varbinary(255) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java index 7af5d42c7..f97f354fd 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java @@ -150,6 +150,30 @@ public void nativeQueryWithJson(VertxTestContext context) { ); } + @Test + @Disabled("https://github.com/hibernate/hibernate-reactive/issues/1999") + public void nativeQueryWithEscapedQuestionMark(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createNativeQuery( "select * from BookWithJson where author -> 'name' \\? 'Jo'", Book.class ) + .getSingleResult() + ) + .invoke( result -> assertThat( result ).isEqualTo( fakeHistory ) ) + ); + } + + @Test + @Disabled("https://github.com/hibernate/hibernate-reactive/issues/2012") + public void nativeQuerySelectScalarWithEscapedQuestionMark(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createNativeQuery( "select 123 from BookWithJson where author -> 'name' \\? 'Jo'", Object.class ) + .getSingleResult() + ) + .invoke( result -> assertThat( result ).isEqualTo( 123 ) ) + ); + } + @Entity(name = "Book") @Table(name = "BookWithJson") public static class Book { diff --git a/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java b/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java index 60bf2a278..11ed5a200 100644 --- a/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java +++ b/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java @@ -15,9 +15,6 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyToOne; -import org.hibernate.annotations.LazyToOneOption; - @Entity(name = "Ship") @Table(name = "Ship") public class Ship { @@ -30,7 +27,6 @@ public class Ship { @Basic(fetch = FetchType.LAZY) private byte[] picture; - @LazyToOne(LazyToOneOption.NO_PROXY) @OneToOne(fetch = FetchType.LAZY, mappedBy = "ship", cascade = CascadeType.ALL) private Captain captain; From 9dd2618e9eeed1d006cbeae0e6c3bc81a849794d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 12 Dec 2024 19:11:12 +0100 Subject: [PATCH 006/201] [#1895] Add branch wip/3.0 to the CI workflows --- .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 01f827e22..2a25cf60a 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' ] + branch: [ 'wip/2.3', 'wip/2.4', 'wip/3.0' ] uses: ./.github/workflows/build.yml with: branch: ${{ matrix.branch }} From 6592c3cd780d6beaa5f38a39204f0c055c2429c0 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 13 Dec 2024 08:22:19 +0100 Subject: [PATCH 007/201] [#1895] Update Hibernate Reactive in JBang templates to 3.0.0.Beta1 --- ci/release/Jenkinsfile | 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 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 9407ca050..915d9f99a 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -73,7 +73,7 @@ pipeline { string( name: 'RELEASE_VERSION', defaultValue: '', - description: 'The version to be released, e.g. 2.4.0.Final. Mandatory for manual releases, to prevent mistakes.', + description: 'The version to be released, e.g. 3.0.0.Beta1. Mandatory for manual releases, to prevent mistakes.', trim: true ) string( diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 17eecb479..3a97c09ad 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.11} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:cockroachdb:1.20.4 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 2e6ae3623..d953f1b52 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.11} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:db2:1.20.4 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index fe0b312e8..fd57d05c9 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.11} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.11} //DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //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 b7592b05b..6b5b75cac 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.11} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mariadb:1.20.4 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 7cc856c4a..7ba1cb184 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.11} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mysql:1.20.4 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 306c30476..135b165c9 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.11} //DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.20.4 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index c79d14fe5..d5f35c988 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.11} //DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.11} //DEPS io.vertx:vertx-unit:${vertx.version:4.5.11} -//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:2.4.0.Final} +//DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.20.4 From fb0c6959ed7bccb406fbedb2be03ff322d918b6b Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 13 Dec 2024 16:47:01 +0100 Subject: [PATCH 008/201] [#1895] Change development version to 3.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 dd20a5fa4..90bbb0669 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=2.4.3-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT From b5b8e8db411f1af12aafe5fdd2e3e046e579389a Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Fri, 13 Dec 2024 16:14:23 +0000 Subject: [PATCH 009/201] Update project version to : `3.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 90bbb0669..98368bb21 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT +projectVersion=3.0.0.Beta1 \ No newline at end of file From 84a8b135531d3375477ee9b5be7a5c4d326584f9 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Fri, 13 Dec 2024 16:15:20 +0000 Subject: [PATCH 010/201] Update project version to : `3.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 98368bb21..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.Beta1 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From 87361ffc343351090a122455ff67910c44433541 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 18 Dec 2024 17:17:45 +0100 Subject: [PATCH 011/201] add StatelessSession.getIdentifier() because repositories need it this was already added to ORM SS in 6.6 --- .../java/org/hibernate/reactive/mutiny/Mutiny.java | 11 +++++++++++ .../mutiny/impl/MutinyStatelessSessionImpl.java | 5 +++++ .../reactive/session/ReactiveStatelessSession.java | 2 ++ .../main/java/org/hibernate/reactive/stage/Stage.java | 11 +++++++++++ .../stage/impl/StageStatelessSessionImpl.java | 5 +++++ 5 files changed, 34 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 9bdf215f7..493c0ed03 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1877,6 +1877,17 @@ default Uni refresh(Object entity, LockModeType lockModeType) { */ Uni fetch(T association); + /** + * Return the identifier value of the given entity, which may be detached. + * + * @param entity a persistent instance associated with this session + * + * @return the identifier + * + * @since 3.0 + */ + Object getIdentifier(Object entity); + /** * Obtain a native SQL result set mapping defined via the annotation * {@link jakarta.persistence.SqlResultSetMapping}. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index b7dc5c26c..275cd1cef 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -193,6 +193,11 @@ public Uni fetch(T association) { return uni( () -> delegate.reactiveFetch( association, false ) ); } + @Override + public Object getIdentifier(Object entity) { + return delegate.getIdentifier(entity); + } + // @Override // public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { // return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java index bed4725c5..a0b1f81f0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java @@ -71,4 +71,6 @@ public interface ReactiveStatelessSession extends ReactiveQueryProducer, Reactiv boolean isOpen(); void close(CompletableFuture closing); + + Object getIdentifier(Object entity); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 44c34325c..f84034da5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1916,6 +1916,17 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ CompletionStage fetch(T association); + /** + * Return the identifier value of the given entity, which may be detached. + * + * @param entity a persistent instance associated with this session + * + * @return the identifier + * + * @since 3.0 + */ + Object getIdentifier(Object entity); + /** * Obtain a native SQL result set mapping defined via the annotation * {@link jakarta.persistence.SqlResultSetMapping}. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 1bacfa354..48e7d8e9a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -132,6 +132,11 @@ public CompletionStage fetch(T association) { return delegate.reactiveFetch( association, false ); } + @Override + public Object getIdentifier(Object entity) { + return delegate.getIdentifier(entity); + } + @Override public CompletionStage withTransaction(Function> work) { return currentTransaction == null From 7ba99e507952a8cfa8ed9670facb5f74b7bf8ef9 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 19 Dec 2024 11:46:17 +0100 Subject: [PATCH 012/201] [#2044] Enable GitHub workflows on different branches Include 2.* and 3.* branches and tags (before it was only the WIP ones) --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6e33ef46..d542f6767 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,12 +5,17 @@ on: branches: - 'main' - 'wip/**' + - '2.*' + - '3.*' tags: - '2.*' + - '3.*' pull_request: branches: - 'main' - 'wip/**' + - '2.*' + - '3.*' # For building snapshots workflow_call: inputs: From 8d036a2e1dd2f7883e8adaad1b6b5a78c4edfbc1 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 18 Dec 2024 13:39:30 +0100 Subject: [PATCH 013/201] [#2034] Build with JDK 11 when required `workflow_dispatch` uses the workflow defined in the `main` branch. So, we need different JDK depending on the branch we are building using the scheduler or manually: * Hibernate Reactive 2.* (with ORM 6) requires JDK 11 * Hibernate Reactive 3.* (with ORM 7) requires JDK 17 --- .github/workflows/build.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d542f6767..e89d6c85b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,14 @@ jobs: .gradle/wrapper # refresh cache every month to avoid unlimited growth key: gradle-examples-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} + - name: Set up JDK 11 + if: ${{ startsWith( inputs.branch, 'wip/2' ) }} + uses: actions/setup-java@v2.2.0 + with: + distribution: 'temurin' + java-version: 11 - name: Set up JDK 17 + if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} uses: actions/setup-java@v2.2.0 with: distribution: 'temurin' @@ -141,8 +148,16 @@ jobs: .gradle/wrapper # refresh cache every month to avoid unlimited growth key: gradle-db-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} + - name: Set up JDK 11 + if: ${{ startsWith( inputs.branch, 'wip/2' ) }} + uses: actions/setup-java@v2.2.0 + with: + distribution: 'temurin' + java-version: 11 - name: Set up JDK 17 + if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} uses: actions/setup-java@v2.2.0 + with: distribution: 'temurin' java-version: 17 @@ -249,8 +264,15 @@ jobs: - name: Export path to JDK ${{ matrix.java.name }} id: testjdk-exportpath run: echo "::set-output name=path::${JAVA_HOME}" - # Always use JDK 11 to build the main code: that's what we use for releases. + - name: Set up JDK 11 + if: ${{ startsWith( inputs.branch, 'wip/2' ) }} + uses: actions/setup-java@v2.2.0 + with: + distribution: 'temurin' + java-version: 11 + check-latest: true - name: Set up JDK 17 + if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} uses: actions/setup-java@v2.2.0 with: distribution: 'temurin' From 965b517d3546d68c592016680afd394baf66a62e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 20 Dec 2024 10:51:49 +0100 Subject: [PATCH 014/201] [#2049] Update GitHub workflow cache action to v4 v2 has been deprecated for a while: https://github.blog/changelog/2024-09-16-notice-of-upcoming-deprecations-and-changes-in-github-actions-services/ --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e89d6c85b..34914144a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@v2 + uses: actions/cache@v4 id: cache-gradle with: path: | @@ -139,7 +139,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@v2 + uses: actions/cache@v4 id: cache-gradle with: path: | From b7f9585084cb8aed198a00c16ed992425871fcd0 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 19 Dec 2024 13:07:00 +0100 Subject: [PATCH 015/201] #2042 add insertMultiple() and friends, with semantics aligned with ORM 7 see https://github.com/hibernate/hibernate-reactive/issues/2042 --- .../org/hibernate/reactive/mutiny/Mutiny.java | 48 +++++++++++++++++-- .../impl/MutinyStatelessSessionImpl.java | 21 ++++++++ .../org/hibernate/reactive/stage/Stage.java | 40 ++++++++++++++++ .../stage/impl/StageStatelessSessionImpl.java | 21 ++++++++ 4 files changed, 126 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 493c0ed03..e0a0eb4e4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1730,6 +1730,16 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ Uni insertAll(int batchSize, Object... entities); + /** + * Insert multiple rows, using the size of the + * given list as the batch size. + * + * @param entities new transient instances + * + * @see org.hibernate.StatelessSession#insert(Object) + */ + Uni insertMultiple(List entities); + /** * Delete a row. * @@ -1758,6 +1768,16 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ Uni deleteAll(int batchSize, Object... entities); + /** + * Delete multiple rows, using the size of the + * given list as the batch size. + * + * @param entities detached entity instances + * + * @see org.hibernate.StatelessSession#delete(Object) + */ + Uni deleteMultiple(List entities); + /** * Update a row. * @@ -1787,13 +1807,14 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp Uni updateAll(int batchSize, Object... entities); /** - * Refresh the entity instance state from the database. + * Update multiple rows, using the size of the + * given list as the batch size. * - * @param entity The entity to be refreshed. + * @param entities detached entity instances * - * @see org.hibernate.StatelessSession#refresh(Object) + * @see org.hibernate.StatelessSession#update(Object) */ - Uni refresh(Object entity); + Uni updateMultiple(List entities); /** * Use a SQL {@code merge into} statement to perform an upsert. @@ -1817,6 +1838,15 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp @Incubating Uni upsert(String entityName, Object entity); + /** + * Refresh the entity instance state from the database. + * + * @param entity The entity to be refreshed. + * + * @see org.hibernate.StatelessSession#refresh(Object) + */ + Uni refresh(Object entity); + /** * Refresh the entity instance state from the database. * @@ -1837,6 +1867,16 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ Uni refreshAll(int batchSize, Object... entities); + /** + * Refresh the entity instance state from the database + * using the size of the given list as the batch size. + * + * @param entities The entities to be refreshed. + * + * @see org.hibernate.StatelessSession#refresh(Object) + */ + Uni refreshMultiple(List entities); + /** * Refresh the entity instance state from the database. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index 275cd1cef..ba1bee23e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -17,6 +17,7 @@ import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.session.ReactiveStatelessSession; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -128,6 +129,11 @@ public Uni insertAll(int batchSize, Object... entities) { return uni( () -> delegate.reactiveInsertAll( batchSize, entities ) ); } + @Override + public Uni insertMultiple(List entities) { + return insertAll( entities.size(), entities.toArray() ); + } + @Override public Uni delete(Object entity) { return uni( () -> delegate.reactiveDelete( entity ) ); @@ -143,6 +149,11 @@ public Uni deleteAll(int batchSize, Object... entities) { return uni( () -> delegate.reactiveDeleteAll( entities ) ); } + @Override + public Uni deleteMultiple(List entities) { + return deleteAll( entities.size(), entities.toArray() ); + } + @Override public Uni update(Object entity) { return uni( () -> delegate.reactiveUpdate( entity ) ); @@ -158,6 +169,11 @@ public Uni updateAll(int batchSize, Object... entities) { return uni( () -> delegate.reactiveUpdateAll( batchSize, entities ) ); } + @Override + public Uni updateMultiple(List entities) { + return updateAll( entities.size(), entities.toArray() ); + } + @Override public Uni refresh(Object entity) { return uni( () -> delegate.reactiveRefresh( entity ) ); @@ -183,6 +199,11 @@ public Uni refreshAll(int batchSize, Object... entities) { return uni( () -> delegate.reactiveRefreshAll( batchSize, entities ) ); } + @Override + public Uni refreshMultiple(List entities) { + return refreshAll( entities.size(), entities.toArray() ); + } + @Override public Uni refresh(Object entity, LockMode lockMode) { return uni( () -> delegate.reactiveRefresh( entity, lockMode ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index f84034da5..f009b8dca 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1775,6 +1775,16 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage insert(int batchSize, Object... entities); + /** + * Insert multiple rows, using the size of the + * given list as the batch size. + * + * @param entities new transient instances + * + * @see org.hibernate.StatelessSession#insert(Object) + */ + CompletionStage insertMultiple(List entities); + /** * Delete a row. * @@ -1803,6 +1813,16 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage delete(int batchSize, Object... entities); + /** + * Delete multiple rows, using the size of the + * given list as the batch size. + * + * @param entities detached entity instances + * + * @see org.hibernate.StatelessSession#delete(Object) + */ + CompletionStage deleteMultiple(List entities); + /** * Update a row. * @@ -1831,6 +1851,16 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage update(int batchSize, Object... entities); + /** + * Update multiple rows, using the size of the + * given list as the batch size. + * + * @param entities a detached entity instance + * + * @see org.hibernate.StatelessSession#update(Object) + */ + CompletionStage updateMultiple(List entities); + /** * Refresh the entity instance state from the database. * @@ -1859,6 +1889,16 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage refresh(int batchSize, Object... entities); + /** + * Refresh the entity instance state from the database, + * using the size of the given list as the batch size. + * + * @param entities The entities to be refreshed. + * + * @see org.hibernate.StatelessSession#refresh(Object) + */ + CompletionStage refreshMultiple(List entities); + /** * Refresh the entity instance state from the database. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 48e7d8e9a..6e05c747c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -19,6 +19,7 @@ import org.hibernate.reactive.stage.Stage.Query; import org.hibernate.reactive.stage.Stage.SelectionQuery; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -67,6 +68,11 @@ public CompletionStage insert(int batchSize, Object... entities) { return delegate.reactiveInsertAll( batchSize, entities ); } + @Override + public CompletionStage insertMultiple(List entities) { + return delegate.reactiveInsertAll( entities.size(), entities.toArray() ); + } + @Override public CompletionStage delete(Object entity) { return delegate.reactiveDelete( entity ); @@ -82,6 +88,11 @@ public CompletionStage delete(int batchSize, Object... entities) { return delegate.reactiveDeleteAll( batchSize, entities ); } + @Override + public CompletionStage deleteMultiple(List entities) { + return delegate.reactiveDeleteAll( entities.size(), entities.toArray() ); + } + @Override public CompletionStage update(Object entity) { return delegate.reactiveUpdate( entity ); @@ -97,6 +108,11 @@ public CompletionStage update(int batchSize, Object... entities) { return delegate.reactiveUpdateAll( batchSize, entities ); } + @Override + public CompletionStage updateMultiple(List entities) { + return delegate.reactiveUpdateAll( entities.size(), entities.toArray() ); + } + @Override public CompletionStage refresh(Object entity) { return delegate.reactiveRefresh( entity ); @@ -112,6 +128,11 @@ public CompletionStage refresh(int batchSize, Object... entities) { return delegate.reactiveRefreshAll( batchSize, entities ); } + @Override + public CompletionStage refreshMultiple(List entities) { + return delegate.reactiveRefreshAll( entities.size(), entities.toArray() ); + } + @Override public CompletionStage refresh(Object entity, LockMode lockMode) { return delegate.reactiveRefresh( entity, lockMode ); From cb088949930ab7f5824076a7e526c498c9f2af92 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 12 Dec 2024 09:43:53 -0600 Subject: [PATCH 016/201] [#1095] Sign the artifacts for Sonatype --- ci/release/Jenkinsfile | 6 +- ci/snapshot-publish.Jenkinsfile | 4 +- publish.gradle | 97 ++++++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 18 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 915d9f99a..fb506fb6c 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -168,8 +168,8 @@ pipeline { withCredentials([ usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'), usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'), - file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_GPG_PASSPHRASE') + file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), + string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') ]) { sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) { // set release version @@ -202,7 +202,7 @@ pipeline { usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'), usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'), file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_GPG_PASSPHRASE'), + string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_GPG_PASSPHRASE') 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']) { diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index 9be740a1d..36e1f1edf 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -32,8 +32,8 @@ pipeline { steps { withCredentials([ usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'hibernatePublishUsername', passwordVariable: 'hibernatePublishPassword'), - string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_PASS'), - file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_KEYRING') + file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), + string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') ]) { sh '''./gradlew clean publish \ -PhibernatePublishUsername=$hibernatePublishUsername \ diff --git a/publish.gradle b/publish.gradle index 3c48e1298..39e0e16b2 100644 --- a/publish.gradle +++ b/publish.gradle @@ -1,13 +1,14 @@ +apply plugin: 'java' apply plugin: 'maven-publish' +apply plugin: 'signing' -tasks.register( 'sourcesJar', Jar ) { - from sourceSets.main.allJava - archiveClassifier = 'sources' -} +// Java / publishing -tasks.register( 'javadocJar', Jar ) { - from javadoc - archiveClassifier = 'javadoc' +java { + // include javadoc and sources jar in the Java component + // - classes jar included by default + withJavadocJar() + withSourcesJar() } jar { @@ -35,14 +36,9 @@ javadoc { publishing { publications { - logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'" - publishedArtifacts(MavenPublication) { - groupId = project.group - version = project.version from components.java - artifact sourcesJar - artifact javadocJar + pom { name = project.mavenPomName description = project.description @@ -80,3 +76,78 @@ publishing { } } } + + +// signing + +var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension + +// create a `signPublications` "grouping" task which will execute all Sign tasks +def signPublicationsTask = tasks.register('signPublications') +tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) { + dependsOn signPublicationsTask +} + +gradle.taskGraph.whenReady { TaskExecutionGraph graph -> + boolean wasSigningRequested = false + boolean wasPublishingRequested = false + List signingTasks = [] + + graph.allTasks.each {task -> + logger.lifecycle( "Checking task : $task" ) + if ( task instanceof Sign ) { + logger.lifecycle( " - Task is Sign" ) + signingTasks.add( task ) + wasSigningRequested = true + } + else if ( task instanceof PublishToMavenRepository ) { + logger.lifecycle( " - Task is PublishToMavenRepository" ) + wasPublishingRequested = true + } + } + + if ( wasPublishingRequested ) { + logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'" + } + + if ( wasSigningRequested || wasPublishingRequested ) { + // signing was explicitly requested and/or we are publishing to Sonatype OSSRH + // - we need the signing to happen + signingExtension.required = true + + var signingKey = resolveSigningKey() + var signingPassword = resolveSigningPassphrase() + signingExtension.useInMemoryPgpKeys( signingKey, signingPassword ) + signingExtension.sign publishing.publications.publishedArtifacts + + signPublicationsTask.get().dependsOn( signingTasks ) + } + else { + // signing was not explicitly requested and we are not publishing to OSSRH, + // - disable all Sign tasks + signingTasks.each { enabled = false } + } +} + + +static String resolveSigningKey() { + var key = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY" ) + if ( key != null ) { + return key + } + + var keyFile = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY_PATH" ) + if ( keyFile != null ) { + return new File( keyFile ).text + } + + throw new RuntimeException( "Cannot perform signing without GPG details." ) +} + +static String resolveSigningPassphrase() { + var passphrase = System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) + if ( passphrase == null ) { + throw new RuntimeException( "Cannot perform signing without GPG details." ) + } + return passphrase +} From 763421ef9c16c419603f4b7b5a5ded476c01015a Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 12 Dec 2024 11:38:34 -0600 Subject: [PATCH 017/201] [#2027] Use env-vars for passing secrets used during release --- build.gradle | 18 +-------- ci/release/Jenkinsfile | 38 ++++++++----------- ci/snapshot-publish.Jenkinsfile | 9 ++--- publish.gradle | 65 ++++++++++++++++++++++++++------- 4 files changed, 72 insertions(+), 58 deletions(-) diff --git a/build.gradle b/build.gradle index 5aee62970..ed2007312 100644 --- a/build.gradle +++ b/build.gradle @@ -12,15 +12,6 @@ group = "org.hibernate.reactive" // leverage the ProjectVersion which comes from the `local.versions` plugin version = project.projectVersion.fullName -ext { - if ( !project.hasProperty( 'hibernatePublishUsername' ) ) { - hibernatePublishUsername = null - } - if ( !project.hasProperty( 'hibernatePublishPassword' ) ) { - hibernatePublishPassword = null - } -} - // 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: @@ -39,15 +30,10 @@ ext { logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion } -// To release, see task ciRelease in release/build.gradle -// To publish on Sonatype (Maven Central): -// ./gradlew publishToSonatype closeAndReleaseStagingRepository -PhibernatePublishUsername="" -PhibernatePublishPassword="" +// Publishing to Sonatype (Maven Central): nexusPublishing { repositories { - sonatype { - username = project.hibernatePublishUsername - password = project.hibernatePublishPassword - } + sonatype() } } diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index fb506fb6c..cb5ae0410 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -165,24 +165,18 @@ pipeline { configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"), configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { - withCredentials([ - usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'), - usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'), - file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') - ]) { - 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([ - "BRANCH=${env.GIT_BRANCH}", - // 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'" - ]) { - sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}" - } + + 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([ + "BRANCH=${env.GIT_BRANCH}", + // 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'" + ]) { + sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}" } } } @@ -199,10 +193,10 @@ pipeline { configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { withCredentials([ - usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'), - usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'), - file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_GPG_PASSPHRASE') + // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh + usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername'), + file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), + string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') 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']) { diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index 36e1f1edf..de2880bd5 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -31,15 +31,12 @@ pipeline { stage('Publish') { steps { withCredentials([ - usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'hibernatePublishUsername', passwordVariable: 'hibernatePublishPassword'), + // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh + usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword'), file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') ]) { - sh '''./gradlew clean publish \ - -PhibernatePublishUsername=$hibernatePublishUsername \ - -PhibernatePublishPassword=$hibernatePublishPassword \ - --no-scan \ - ''' + sh "./gradlew clean publish --no-scan" } } } diff --git a/publish.gradle b/publish.gradle index 39e0e16b2..0271197d4 100644 --- a/publish.gradle +++ b/publish.gradle @@ -5,8 +5,8 @@ apply plugin: 'signing' // Java / publishing java { - // include javadoc and sources jar in the Java component - // - classes jar included by default + // Configure the Java "software component" to include javadoc and sources jars in addition to the classes jar. + // Ultimately, this component is what makes up the publication for this project. withJavadocJar() withSourcesJar() } @@ -36,7 +36,7 @@ javadoc { publishing { publications { - publishedArtifacts(MavenPublication) { + register( "publishedArtifacts", MavenPublication) { from components.java pom { @@ -78,35 +78,39 @@ publishing { } -// signing +// Signing var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension -// create a `signPublications` "grouping" task which will execute all Sign tasks -def signPublicationsTask = tasks.register('signPublications') +def signPublicationsTask = tasks.register('signPublications') { + description "Grouping task which executes all Sign tasks" + dependsOn tasks.withType( Sign ) +} + tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) { + // publishing depends on signing dependsOn signPublicationsTask } gradle.taskGraph.whenReady { TaskExecutionGraph graph -> boolean wasSigningRequested = false boolean wasPublishingRequested = false - List signingTasks = [] graph.allTasks.each {task -> - logger.lifecycle( "Checking task : $task" ) if ( task instanceof Sign ) { - logger.lifecycle( " - Task is Sign" ) - signingTasks.add( task ) wasSigningRequested = true } else if ( task instanceof PublishToMavenRepository ) { - logger.lifecycle( " - Task is PublishToMavenRepository" ) wasPublishingRequested = true } } if ( wasPublishingRequested ) { + def publishUser = resolvePublishUser() + def publishPass = resolvePublishPass() + if ( publishUser == null || publishPass == null ) { + throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." ) + } logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'" } @@ -119,14 +123,47 @@ gradle.taskGraph.whenReady { TaskExecutionGraph graph -> var signingPassword = resolveSigningPassphrase() signingExtension.useInMemoryPgpKeys( signingKey, signingPassword ) signingExtension.sign publishing.publications.publishedArtifacts - - signPublicationsTask.get().dependsOn( signingTasks ) } else { // signing was not explicitly requested and we are not publishing to OSSRH, // - disable all Sign tasks - signingTasks.each { enabled = false } + tasks.withType( Sign ).each { enabled = false } + } +} + +String resolvePublishUser() { + var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypeUsername" ) + if ( envVar != null ) { + return envVar + } + + def projectProp = projectPropOrNull( "sonatypeUsername" ) + if ( projectProp != null ) { + return projectProp + } + + return null +} + +String resolvePublishPass() { + var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypePassword" ) + if ( envVar != null ) { + return envVar + } + + def projectProp = projectPropOrNull( "sonatypePassword" ) + if ( projectProp != null ) { + return projectProp + } + + return null +} + +String projectPropOrNull(String name) { + if ( project.hasProperty( name ) ) { + return project.findProperty( name ) } + return null; } From b57183c5c54c72a9d8acc5d815cf3d9ee96b48ed Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 18 Dec 2024 12:01:46 -0600 Subject: [PATCH 018/201] [#1095] Sign the artifacts for Sonatype --- publish.gradle | 81 +++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/publish.gradle b/publish.gradle index 0271197d4..977092d9a 100644 --- a/publish.gradle +++ b/publish.gradle @@ -82,25 +82,19 @@ publishing { var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension -def signPublicationsTask = tasks.register('signPublications') { - description "Grouping task which executes all Sign tasks" - dependsOn tasks.withType( Sign ) -} +var publishingExtension = project.getExtensions().getByType(PublishingExtension) as PublishingExtension +signingExtension.sign publishingExtension.publications.publishedArtifacts -tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) { - // publishing depends on signing - dependsOn signPublicationsTask -} +var signingKey = resolveSigningKey() +var signingPassphrase = resolveSigningPassphrase() +signingExtension.useInMemoryPgpKeys(signingKey, signingPassphrase) gradle.taskGraph.whenReady { TaskExecutionGraph graph -> - boolean wasSigningRequested = false boolean wasPublishingRequested = false graph.allTasks.each {task -> - if ( task instanceof Sign ) { - wasSigningRequested = true - } - else if ( task instanceof PublishToMavenRepository ) { + if ( task instanceof PublishToMavenRepository ) { + logger.lifecycle( "Found PublishToMavenRepository task : {}", task.path ) wasPublishingRequested = true } } @@ -111,24 +105,34 @@ gradle.taskGraph.whenReady { TaskExecutionGraph graph -> if ( publishUser == null || publishPass == null ) { throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." ) } - logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'" - } - if ( wasSigningRequested || wasPublishingRequested ) { - // signing was explicitly requested and/or we are publishing to Sonatype OSSRH - // - we need the signing to happen + logger.lifecycle "Publishing {} : {} : {}", project.group, project.name, project.version + + // require signing if publishing to OSSRH signingExtension.required = true + } + else if ( signingKey == null || signingPassphrase == null ) { + tasks.withType( Sign ).each { t-> t.enabled = false } + } +} + - var signingKey = resolveSigningKey() - var signingPassword = resolveSigningPassphrase() - signingExtension.useInMemoryPgpKeys( signingKey, signingPassword ) - signingExtension.sign publishing.publications.publishedArtifacts +static String resolveSigningKey() { + var key = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY" ) + if ( key != null ) { + return key } - else { - // signing was not explicitly requested and we are not publishing to OSSRH, - // - disable all Sign tasks - tasks.withType( Sign ).each { enabled = false } + + var keyFile = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY_PATH" ) + if ( keyFile != null ) { + return new File( keyFile ).text } + + return null +} + +static String resolveSigningPassphrase() { + return System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) } String resolvePublishUser() { @@ -164,27 +168,4 @@ String projectPropOrNull(String name) { return project.findProperty( name ) } return null; -} - - -static String resolveSigningKey() { - var key = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY" ) - if ( key != null ) { - return key - } - - var keyFile = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY_PATH" ) - if ( keyFile != null ) { - return new File( keyFile ).text - } - - throw new RuntimeException( "Cannot perform signing without GPG details." ) -} - -static String resolveSigningPassphrase() { - var passphrase = System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) - if ( passphrase == null ) { - throw new RuntimeException( "Cannot perform signing without GPG details." ) - } - return passphrase -} +} \ No newline at end of file From 99853546183b5cdee352294fea6327ae27d7217c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 20 Dec 2024 17:46:36 +0100 Subject: [PATCH 019/201] [#1095] Add missing comma to JenkinsFile --- ci/release/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index cb5ae0410..5b2f496bb 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -196,7 +196,7 @@ pipeline { // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername'), file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') + string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE'), 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']) { From 9224ce86a9fe90de945a085700374f550fa656a1 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sat, 4 Jan 2025 10:20:34 +0100 Subject: [PATCH 020/201] [#2007] NPE retrieving Entity with composite id mixing ManyToOne with basic attributes --- .../ReactiveNonAggregatedIdentifierMappingInitializer.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/ReactiveNonAggregatedIdentifierMappingInitializer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java index 0096e8d0f..5228e397e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java @@ -62,7 +62,7 @@ public CompletionStage reactiveResolveKey(NonAggregatedIdentifierMappingIn final RowProcessingState rowProcessingState = data.getRowProcessingState(); final boolean[] dataIsMissing = {false}; return loop( getInitializers(), initializer -> { - if ( dataIsMissing[0] ) { + if ( dataIsMissing[0] || initializer == null ) { return voidFuture(); } final InitializerData subData = ( (ReactiveInitializer) initializer ) From dbea9de084d16d6b64ddc2d02b9d422bea8bb731 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sat, 4 Jan 2025 10:20:16 +0100 Subject: [PATCH 021/201] [#2007] Add test for NPE retrieving Entity with composite id mixing ManyToOne with basic attributes --- .../reactive/ManyToOneIdClassTest.java | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java new file mode 100644 index 000000000..a1d1d882e --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java @@ -0,0 +1,141 @@ +/* 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.junit.jupiter.api.AfterEach; +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.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.ManyToOne; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 1, timeUnit = MINUTES) +public class ManyToOneIdClassTest extends BaseReactiveTest { + + private final static String USER_NAME = "user"; + private final static String SUBSYSTEM_ID = "1"; + + @Override + protected Collection> annotatedEntities() { + return List.of( SystemUser.class, Subsystem.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Subsystem subsystem = new Subsystem( SUBSYSTEM_ID, "sub 1" ); + SystemUser systemUser = new SystemUser( subsystem, USER_NAME, "system 1" ); + test( + context, getMutinySessionFactory() + .withTransaction( (s, t) -> s.persistAll( subsystem, systemUser ) ) + ); + } + + @AfterEach + public void after(VertxTestContext context) { + test( context, cleanDb() ); + } + + @Test + public void testQuery(VertxTestContext context) { + test( + context, openSession() + .thenAccept( session -> session.createQuery( "SELECT s FROM SystemUser s", SystemUser.class ) + .getResultList().thenAccept( list -> { + assertThat( list.size() ).isEqualTo( 1 ); + SystemUser systemUser = list.get( 0 ); + assertThat( systemUser.getSubsystem().getId() ).isEqualTo( SUBSYSTEM_ID ); + assertThat( systemUser.getUsername() ).isEqualTo( USER_NAME ); + } ) ) + ); + } + + @Entity(name = "SystemUser") + @IdClass(PK.class) + public static class SystemUser { + + @Id + @ManyToOne(fetch = FetchType.LAZY) + private Subsystem subsystem; + + @Id + private String username; + + private String name; + + public SystemUser() { + } + + public SystemUser(Subsystem subsystem, String username, String name) { + this.subsystem = subsystem; + this.username = username; + this.name = name; + } + + public Subsystem getSubsystem() { + return subsystem; + } + + public String getUsername() { + return username; + } + + public String getName() { + return name; + } + } + + @Entity(name = "Subsystem") + public static class Subsystem { + + @Id + private String id; + + private String description; + + public Subsystem() { + } + + public Subsystem(String id, String description) { + this.id = id; + this.description = description; + } + + public String getId() { + return id; + } + + public String getDescription() { + return description; + } + } + + public static class PK { + + private Subsystem subsystem; + + private String username; + + public PK(Subsystem subsystem, String username) { + this.subsystem = subsystem; + this.username = username; + } + + private PK() { + } + } + +} From 69875aae5bfc777a890a118dd9546d8adba2d1cd Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 7 Jan 2025 14:19:40 +0100 Subject: [PATCH 022/201] [#2007] Remove obsolete code --- .../ReactiveNonAggregatedIdentifierMappingInitializer.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java index 5228e397e..51f7d59b7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/embeddable/internal/ReactiveNonAggregatedIdentifierMappingInitializer.java @@ -60,9 +60,8 @@ public CompletionStage reactiveResolveKey(NonAggregatedIdentifierMappingIn } else { final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final boolean[] dataIsMissing = {false}; return loop( getInitializers(), initializer -> { - if ( dataIsMissing[0] || initializer == null ) { + if ( initializer == null ) { return voidFuture(); } final InitializerData subData = ( (ReactiveInitializer) initializer ) @@ -72,7 +71,6 @@ public CompletionStage reactiveResolveKey(NonAggregatedIdentifierMappingIn .thenAccept( v -> { if ( subData.getState() == State.MISSING ) { data.setState( State.MISSING ); - dataIsMissing[0] = true; } } ); } ); From 37d57f6429fc20c5cc1e30f5fc22c6cde343d714 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 7 Jan 2025 14:17:53 +0100 Subject: [PATCH 023/201] [#2007] Minor test clean up --- .../reactive/ManyToOneIdClassTest.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java index a1d1d882e..73dead49a 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToOneIdClassTest.java @@ -8,7 +8,6 @@ import java.util.Collection; import java.util.List; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,26 +39,21 @@ public void populateDb(VertxTestContext context) { SystemUser systemUser = new SystemUser( subsystem, USER_NAME, "system 1" ); test( context, getMutinySessionFactory() - .withTransaction( (s, t) -> s.persistAll( subsystem, systemUser ) ) + .withTransaction( s -> s.persistAll( subsystem, systemUser ) ) ); } - @AfterEach - public void after(VertxTestContext context) { - test( context, cleanDb() ); - } - @Test public void testQuery(VertxTestContext context) { test( - context, openSession() - .thenAccept( session -> session.createQuery( "SELECT s FROM SystemUser s", SystemUser.class ) - .getResultList().thenAccept( list -> { - assertThat( list.size() ).isEqualTo( 1 ); - SystemUser systemUser = list.get( 0 ); - assertThat( systemUser.getSubsystem().getId() ).isEqualTo( SUBSYSTEM_ID ); - assertThat( systemUser.getUsername() ).isEqualTo( USER_NAME ); - } ) ) + context, openSession().thenAccept( session -> session + .createQuery( "FROM SystemUser s", SystemUser.class ) + .getResultList().thenAccept( list -> { + assertThat( list ).hasSize( 1 ); + SystemUser systemUser = list.get( 0 ); + assertThat( systemUser.getSubsystem().getId() ).isEqualTo( SUBSYSTEM_ID ); + assertThat( systemUser.getUsername() ).isEqualTo( USER_NAME ); + } ) ) ); } From 8178bf82da4063c8766c30f516bd5138240c5e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 14 Jan 2025 09:20:07 +0100 Subject: [PATCH 024/201] [#2063] Use specific Jenkins nodes for releases This should be safer as these nodes are only used once. --- ci/release/Jenkinsfile | 2 +- ci/snapshot-publish.Jenkinsfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 5b2f496bb..d365436ab 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -58,7 +58,7 @@ def checkoutReleaseScripts() { pipeline { agent { - label 'Worker&&Containers' + label 'Release' } tools { jdk 'OpenJDK 17 Latest' diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index de2880bd5..07fa844c6 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -12,7 +12,7 @@ if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause')) { pipeline { agent { - label 'Fedora' + label 'Release' } tools { jdk 'OpenJDK 17 Latest' From 71fc3d3c314dd4c1254b73d014f0d088ccd09e60 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 28 Aug 2024 22:12:52 +0200 Subject: [PATCH 025/201] [#1929] Enable reproducible archives --- ci/compare-build-results.sh | 53 ++++++++++++++++++++++++++++ ci/nightly/Jenkinsfile | 49 +++++++++++++++++++++++++ hibernate-reactive-core/build.gradle | 9 +++++ 3 files changed, 111 insertions(+) create mode 100644 ci/compare-build-results.sh create mode 100644 ci/nightly/Jenkinsfile diff --git a/ci/compare-build-results.sh b/ci/compare-build-results.sh new file mode 100644 index 000000000..6b45022ce --- /dev/null +++ b/ci/compare-build-results.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# This is a simple script to check if builds are reproducible. The steps are: +# 1. Build Hibernate Reactive with `./gradlew --no-daemon clean publishToMavenLocal --no-build-cache -Dmaven.repo.local=some-path/out/build1` +# 2. Build Hibernate Reactive with `./gradlew --no-daemon clean publishToMavenLocal --no-build-cache -Dmaven.repo.local=some-path/out/build2` second time pointing to a different local maven repository to publish +# 3. Compare the build results with sh ./ci/compare-build-results.sh some-path/out/build1 some-path/out/build2 +# 4. The generated .buildcompare file will also contain the diffscope commands to see/compare the problematic build artifacts + +outputDir1=$1 +outputDir2=$2 +outputDir1=${outputDir1%/} +outputDir2=${outputDir2%/} + +ok=() +okFiles=() +ko=() +koFiles=() + +for f in `find ${outputDir1} -type f | grep -v "javadoc.jar$" | grep -v "maven-metadata-local.xml$" | sort` +do + flocal=${f#$outputDir1} + # echo "comparing ${flocal}" + sha1=`shasum -a 512 $f | cut -f 1 -d ' '` + sha2=`shasum -a 512 $outputDir2$flocal | cut -f 1 -d ' '` + # echo "$sha1" + # echo "$sha2" + if [ "$sha1" = "$sha2" ]; then + ok+=($flocal) + okFiles+=(${flocal##*/}) + else + ko+=($flocal) + koFiles+=(${flocal##*/}) + fi +done + +# generate .buildcompare +buildcompare=".buildcompare" +echo "ok=${#ok[@]}" >> ${buildcompare} +echo "ko=${#ko[@]}" >> ${buildcompare} +echo "okFiles=\"${okFiles[@]}\"" >> ${buildcompare} +echo "koFiles=\"${koFiles[@]}\"" >> ${buildcompare} +echo "" >> ${buildcompare} +echo "# see what caused the mismatch in the checksum by executing the following diffscope commands" >> ${buildcompare} +for f in ${ko[@]} +do + echo "# diffoscope $outputDir1$f $outputDir2$f" >> ${buildcompare} +done + +if [ ${#ko[@]} -eq 0 ]; then + exit 0 +else + exit 1 +fi diff --git a/ci/nightly/Jenkinsfile b/ci/nightly/Jenkinsfile new file mode 100644 index 000000000..945f02606 --- /dev/null +++ b/ci/nightly/Jenkinsfile @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ + +@Library('hibernate-jenkins-pipeline-helpers') _ + +pipeline { + agent none + triggers { + cron '@midnight' + } + tools { + jdk 'OpenJDK 17 Latest' + } + options { + buildDiscarder logRotator(daysToKeepStr: '10', numToKeepStr: '3') + disableConcurrentBuilds(abortPrevious: true) + overrideIndexTriggers(false) + } + stages { + stage('Build reproducibility check') { + agent { + label 'Worker&&Containers' + } + steps { + timeout(time: 30, unit: 'MINUTES') { + script { + def tempDir = pwd(tmp: true) + def repo1 = tempDir + '/repo1' + def repo2 = tempDir + '/repo2' + // build Hibernate Reactive two times without any cache and "publish" the resulting artifacts + // to different maven repositories, so that we can compare them afterwards: + sh "./gradlew --no-daemon clean publishToMavenLocal --no-build-cache -Dmaven.repo.local=${repo1}" + sh "./gradlew --no-daemon clean publishToMavenLocal --no-build-cache -Dmaven.repo.local=${repo2}" + + sh "sh ci/compare-build-results.sh ${repo1} ${repo2}" + sh "cat .buildcompare" + } + } + } + } + } + post { + always { + notifyBuildResult maintainers: 'davide@hibernate.org' + } + } +} diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 87652c572..fca12935f 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -81,6 +81,15 @@ dependencies { testImplementation "org.testcontainers:oracle-xe:${testcontainersVersion}" } +// Reproducible Builds + +// https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives +// Configure archive tasks to produce reproducible archives: +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 From c03c9ff9747dd117dd1d9c48c39f598b79418b9d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 17 Jan 2025 10:31:55 +0100 Subject: [PATCH 026/201] [#2006] UnexpectedAccessToTheDatabase error when merging a detached entity with a ToMany association --- .../reactive/engine/impl/CollectionTypes.java | 507 ++++++++++++++++++ .../reactive/engine/impl/EntityTypes.java | 215 +++++--- .../hibernate/reactive/logging/impl/Log.java | 4 + 3 files changed, 652 insertions(+), 74 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java new file mode 100644 index 000000000..99e227cbc --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java @@ -0,0 +1,507 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.engine.impl; + +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.CompletionStage; + +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.collection.spi.AbstractPersistentCollection; +import org.hibernate.collection.spi.PersistentArrayHolder; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.type.ArrayType; +import org.hibernate.type.CollectionType; +import org.hibernate.type.CustomCollectionType; +import org.hibernate.type.EntityType; +import org.hibernate.type.ForeignKeyDirection; +import org.hibernate.type.MapType; +import org.hibernate.type.Type; + +import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY; +import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize; +import static org.hibernate.pretty.MessageHelper.collectionInfoString; +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; + +/** + * Reactive operations that really belong to {@link CollectionType} + * + */ +public class CollectionTypes { + private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + + /** + * @see org.hibernate.type.AbstractType#replace(Object, Object, SharedSessionContractImplementor, Object, Map, ForeignKeyDirection) + */ + public static CompletionStage replace( + CollectionType type, + Object original, + Object target, + SessionImplementor session, + Object owner, + Map copyCache, + ForeignKeyDirection foreignKeyDirection) + throws HibernateException { + // Collection and OneToOne are the only associations that could be TO_PARENT + return type.getForeignKeyDirection() == foreignKeyDirection + ? replace( type, original, target, session, owner, copyCache ) + : completedFuture( target ); + } + + /** + * @see CollectionType#replace(Object, Object, SharedSessionContractImplementor, Object, Map) + */ + public static CompletionStage replace( + CollectionType type, + Object original, + Object target, + SessionImplementor session, + Object owner, + Map copyCache) throws HibernateException { + if ( original == null ) { + return replaceNullOriginal( target, session ); + } + else if ( !Hibernate.isInitialized( original ) ) { + return replaceUninitializedOriginal( type, original, target, session, copyCache ); + } + else { + return replaceOriginal( type, original, target, session, owner, copyCache ); + } + } + + // todo: make org.hibernate.type.CollectionType#replaceNullOriginal public ? + + /** + * @see CollectionType#replaceNullOriginal(Object, SharedSessionContractImplementor) + */ + private static CompletionStage replaceNullOriginal( + Object target, + SessionImplementor session) { + if ( target == null ) { + return nullFuture(); + } + else if ( target instanceof Collection collection ) { + collection.clear(); + return completedFuture( collection ); + } + else if ( target instanceof Map map ) { + map.clear(); + return completedFuture( map ); + } + else { + final PersistenceContext persistenceContext = session.getPersistenceContext(); + final PersistentCollection collectionHolder = persistenceContext.getCollectionHolder( target ); + if ( collectionHolder != null ) { + if ( collectionHolder instanceof PersistentArrayHolder arrayHolder ) { + persistenceContext.removeCollectionHolder( target ); + arrayHolder.beginRead(); + final PluralAttributeMapping attributeMapping = + persistenceContext.getCollectionEntry( collectionHolder ) + .getLoadedPersister().getAttributeMapping(); + arrayHolder.injectLoadedState( attributeMapping, null ); + arrayHolder.endRead(); + arrayHolder.dirty(); + persistenceContext.addCollectionHolder( collectionHolder ); + return completedFuture( arrayHolder.getArray() ); + } + } + } + return nullFuture(); + } + + // todo: make org.hibernate.type.CollectionType#replaceUninitializedOriginal public + /** + * @see CollectionType#replaceNullOriginal(Object, SharedSessionContractImplementor) + */ + private static CompletionStage replaceUninitializedOriginal( + CollectionType type, + Object original, + Object target, + SessionImplementor session, + Map copyCache) { + final PersistentCollection persistentCollection = (PersistentCollection) original; + if ( persistentCollection.hasQueuedOperations() ) { + if ( original == target ) { + // A managed entity with an uninitialized collection is being merged, + // We need to replace any detached entities in the queued operations + // with managed copies. + final AbstractPersistentCollection pc = (AbstractPersistentCollection) original; + pc.replaceQueuedOperationValues( + session.getFactory() + .getMappingMetamodel() + .getCollectionDescriptor( type.getRole() ), copyCache + ); + } + else { + // original is a detached copy of the collection; + // it contains queued operations, which will be ignored + LOG.ignoreQueuedOperationsOnMerge( + collectionInfoString( type.getRole(), persistentCollection.getKey() ) ); + } + } + return completedFuture( target ); + } + + /** + * @see CollectionType#replaceOriginal(Object, Object, SharedSessionContractImplementor, Object, Map) + */ + private static CompletionStage replaceOriginal( + CollectionType type, + Object original, + Object target, + SessionImplementor session, + Object owner, + Map copyCache) { + + //for arrays, replaceElements() may return a different reference, since + //the array length might not match + return replaceElements( + type, + original, + instantiateResultIfNecessary( type, original, target ), + owner, + copyCache, + session + ).thenCompose( result -> { + if ( original == target ) { + // get the elements back into the target making sure to handle dirty flag + final boolean wasClean = + target instanceof PersistentCollection collection + && !collection.isDirty(); + //TODO: this is a little inefficient, don't need to do a whole + // deep replaceElements() call + return replaceElements( type, result, target, owner, copyCache, session ) + .thenCompose( unused -> { + if ( wasClean ) { + ( (PersistentCollection) target ).clearDirty(); + } + return completedFuture( target ); + } ); + } + else { + return completedFuture( result ); + } + } ); + } + + /** + * @see CollectionType#replaceElements(Object, Object, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage replaceElements( + CollectionType type, + Object original, + Object target, + Object owner, + Map copyCache, + SessionImplementor session) { + if ( type instanceof ArrayType ) { + return replaceArrayTypeElements( type, original, target, owner, copyCache, session ); + } + else if ( type instanceof CustomCollectionType ) { + return completedFuture( type.replaceElements( original, target, owner, copyCache, session ) ); + } + else if ( type instanceof MapType ) { + return replaceMapTypeElements( + type, + (Map) original, + (Map) target, + owner, + copyCache, + session + ); + } + else { + return replaceCollectionTypeElements( + type, + original, + (Collection) target, + owner, + copyCache, + session + ); + } + } + + private static CompletionStage replaceCollectionTypeElements( + CollectionType type, + Object original, + final Collection result, + Object owner, + Map copyCache, + SessionImplementor session) { + result.clear(); + + // copy elements into newly empty target collection + final Type elemType = type.getElementType( session.getFactory() ); + return loop( + (Collection) original, o -> getReplace( elemType, o, owner, session, copyCache ) + .thenAccept( result::add ) + ).thenCompose( unused -> { + // if the original is a PersistentCollection, and that original + // was not flagged as dirty, then reset the target's dirty flag + // here after the copy operation. + //

+ // One thing to be careful of here is a "bare" original collection + // in which case we should never ever ever reset the dirty flag + // on the target because we simply do not know... + if ( original instanceof PersistentCollection originalPersistentCollection + && result instanceof PersistentCollection resultPersistentCollection ) { + return preserveSnapshot( + originalPersistentCollection, resultPersistentCollection, + elemType, owner, copyCache, session + ).thenApply( v -> { + if ( !originalPersistentCollection.isDirty() ) { + resultPersistentCollection.clearDirty(); + } + return result; + } ); + } + else { + return completedFuture( result ); + } + } ); + } + + private static CompletionStage replaceMapTypeElements( + CollectionType type, + Map original, + Map target, + Object owner, + Map copyCache, + SessionImplementor session) { + final CollectionPersister persister = + session.getFactory().getRuntimeMetamodels().getMappingMetamodel() + .getCollectionDescriptor( type.getRole() ); + + final Map result = target; + result.clear(); + + return loop( + original.entrySet(), entry -> { + final Map.Entry me = entry; + return getReplace( persister.getIndexType(), me.getKey(), owner, session, copyCache ) + .thenCompose( key -> + getReplace( + persister.getElementType(), + me.getValue(), + owner, + session, + copyCache + ).thenAccept( value -> + result.put( key, value ) ) + ); + } + ).thenApply( unused -> result ); + } + + private static CompletionStage replaceArrayTypeElements( + CollectionType type, + Object original, + Object target, + Object owner, + Map copyCache, + SessionImplementor session) { + final Object result; + final int length = Array.getLength( original ); + if ( length != Array.getLength( target ) ) { + //note: this affects the return value! + result = ( (ArrayType) type ).instantiateResult( original ); + } + else { + result = target; + } + + final Type elemType = type.getElementType( session.getFactory() ); + return loop( + 0, length, i -> { + return getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) + .thenApply( o -> { + Array.set( result, i, o ); + return result; + } + ); + } + ).thenApply( unused -> result ); + } + + private static CompletionStage getReplace( + Type elemType, + Object o, + Object owner, + SessionImplementor session, + Map copyCache) { + return getReplace( elemType, o, null, owner, session, copyCache ); + } + + private static CompletionStage getReplace( + Type elemType, + Object o, + Object target, + Object owner, + SessionImplementor session, + Map copyCache) { + if ( elemType instanceof EntityType ) { + return EntityTypes.replace( (EntityType) elemType, o, target, session, owner, copyCache ); + } + else { + final Object replace = elemType.replace( o, target, session, owner, copyCache ); + return completedFuture( replace ); + } + } + + /** + * @see CollectionType#preserveSnapshot(PersistentCollection, PersistentCollection, Type, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage preserveSnapshot( + PersistentCollection original, + PersistentCollection result, + Type elemType, + Object owner, + Map copyCache, + SessionImplementor session) { + final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( result ); + if ( ce != null ) { + return createSnapshot( original, result, elemType, owner, copyCache, session ) + .thenAccept( serializable -> + ce.resetStoredSnapshot( result, serializable ) ); + } + return voidFuture(); + } + + /** + * @see CollectionType#createSnapshot(PersistentCollection, PersistentCollection, Type, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage createSnapshot( + PersistentCollection original, + PersistentCollection result, + Type elemType, + Object owner, + Map copyCache, + SessionImplementor session) { + final Serializable originalSnapshot = original.getStoredSnapshot(); + if ( originalSnapshot instanceof List list ) { + return createListSnapshot( list, elemType, owner, copyCache, session ); + } + else if ( originalSnapshot instanceof Map map ) { + return createMapSnapshot( map, result, elemType, owner, copyCache, session ); + } + else if ( originalSnapshot instanceof Object[] array ) { + return createArraySnapshot( array, elemType, owner, copyCache, session ); + } + else { + // retain the same snapshot + return completedFuture( result.getStoredSnapshot() ); + } + } + + /** + * @see CollectionType#createArraySnapshot(Object[], Type, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage createArraySnapshot( + Object[] array, + Type elemType, + Object owner, + Map copyCache, + SessionImplementor session) { + return loop( + 0, array.length, + i -> + getReplace( elemType, array[i], owner, session, copyCache ) + .thenCompose( o -> { + array[i] = o; + return voidFuture(); + } + ) + ).thenApply( unused -> array ); + } + + /** + * @see CollectionType#createMapSnapshot(Map, PersistentCollection, Type, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage createMapSnapshot( + Map map, + PersistentCollection result, + Type elemType, + Object owner, + Map copyCache, + SessionImplementor session) { + final Map resultSnapshot = (Map) result.getStoredSnapshot(); + final Map targetMap; + if ( map instanceof SortedMap sortedMap ) { + //noinspection unchecked, rawtypes + targetMap = new TreeMap( sortedMap.comparator() ); + } + else { + targetMap = mapOfSize( map.size() ); + } + return loop( + map.entrySet(), entry -> + getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) + .thenCompose( newValue -> { + final Object key = entry.getKey(); + targetMap.put( key == entry.getValue() ? newValue : key, newValue ); + return voidFuture(); + } ) + ).thenApply( v -> (Serializable) targetMap ); + } + + /** + * @see CollectionType#createListSnapshot(List, Type, Object, Map, SharedSessionContractImplementor) + */ + private static CompletionStage createListSnapshot( + List list, + Type elemType, + Object owner, + Map copyCache, + SessionImplementor session) { + final ArrayList targetList = new ArrayList<>( list.size() ); + return loop( + list, obj -> + getReplace( elemType, obj, owner, session, copyCache ) + .thenCompose( o -> { + targetList.add( o ); + return voidFuture(); + } ) + ).thenApply( unused -> targetList ); + } + + /** + * @see CollectionType#instantiateResultIfNecessary(Object, Object) + */ + private static Object instantiateResultIfNecessary(CollectionType type, Object original, Object target) { + // for a null target, or a target which is the same as the original, + // we need to put the merged elements in a new collection + // by default just use an unanticipated capacity since we don't + // know how to extract the capacity to use from original here... + return target == null + || target == original + || target == UNFETCHED_PROPERTY + || target instanceof PersistentCollection collection && collection.isWrapper( original ) ? + type.instantiate( -1 ) : + target; + } + + +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index 879f7ddc8..722ba4a78 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -25,6 +25,7 @@ import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; import org.hibernate.reactive.session.impl.ReactiveSessionImpl; +import org.hibernate.type.CollectionType; import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.OneToOneType; @@ -158,42 +159,11 @@ public static CompletionStage replace( final Object owner, final Map copyCache) { Object[] copied = new Object[original.length]; - for ( int i = 0; i < types.length; i++ ) { - if ( original[i] == UNFETCHED_PROPERTY || original[i] == UNKNOWN ) { - copied[i] = target[i]; - } - else { - if ( !( types[i] instanceof EntityType ) ) { - copied[i] = types[i].replace( - original[i], - target[i] == UNFETCHED_PROPERTY ? null : target[i], - session, - owner, - copyCache - ); - } - } - } - return loop( 0, types.length, - i -> original[i] != UNFETCHED_PROPERTY && original[i] != UNKNOWN - && types[i] instanceof EntityType, - i -> replace( - (EntityType) types[i], - original[i], - target[i] == UNFETCHED_PROPERTY ? null : target[i], - session, - owner, - copyCache - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ) - .thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ) + return loop( + 0, types.length, + i -> + replace( original, target, types, session, owner, copyCache, i, copied ) + ).thenApply( v -> copied ); } @@ -209,43 +179,11 @@ public static CompletionStage replace( final Map copyCache, final ForeignKeyDirection foreignKeyDirection) { Object[] copied = new Object[original.length]; - for ( int i = 0; i < types.length; i++ ) { - if ( original[i] == UNFETCHED_PROPERTY || original[i] == UNKNOWN ) { - copied[i] = target[i]; - } - else { - if ( !( types[i] instanceof EntityType ) ) { - copied[i] = types[i].replace( - original[i], - target[i] == UNFETCHED_PROPERTY ? null : target[i], - session, - owner, - copyCache, - foreignKeyDirection - ); - } - } - } - return loop( 0, types.length, - i -> original[i] != UNFETCHED_PROPERTY && original[i] != UNKNOWN - && types[i] instanceof EntityType, - i -> replace( - (EntityType) types[i], - original[i], - target[i] == UNFETCHED_PROPERTY ? null : target[i], - session, - owner, - copyCache, - foreignKeyDirection - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ) + return loop( + 0, types.length, + i -> + replace( original, target, types, session, owner, copyCache, foreignKeyDirection, i, copied ) + ).thenApply( v -> copied ); } @@ -272,7 +210,7 @@ private static CompletionStage replace( /** * @see EntityType#replace(Object, Object, SharedSessionContractImplementor, Object, Map) */ - private static CompletionStage replace( + protected static CompletionStage replace( EntityType entityType, Object original, Object target, @@ -450,4 +388,133 @@ private static CompletionStage loadHibernateProxyEntity( } } + private static CompletionStage replace( + Object[] original, + Object[] target, + Type[] types, + SessionImplementor session, + Object owner, + Map copyCache, + int i, + Object[] copied) { + if ( original[i] == UNFETCHED_PROPERTY || original[i] == UNKNOWN ) { + copied[i] = target[i]; + return voidFuture(); + } + else if ( types[i] instanceof CollectionType ) { + return CollectionTypes.replace( + (CollectionType) types[i], + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache + ).thenCompose( copy -> { + if ( copy instanceof CompletionStage ) { + return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); + } + else { + copied[i] = copy; + return voidFuture(); + } + } ); + } + else if ( types[i] instanceof EntityType ) { + return replace( + (EntityType) types[i], + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache + ).thenCompose( copy -> { + if ( copy instanceof CompletionStage ) { + return ( (CompletionStage) copy ) + .thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); + } + else { + copied[i] = copy; + return voidFuture(); + } + } ); + } + else { + final Type type = types[i]; + copied[i] = type.replace( + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache + ); + return voidFuture(); + } + } + + private static CompletionStage replace( + Object[] original, + Object[] target, + Type[] types, + SessionImplementor session, + Object owner, + Map copyCache, + ForeignKeyDirection foreignKeyDirection, + int i, + Object[] copied) { + if ( original[i] == UNFETCHED_PROPERTY || original[i] == UNKNOWN ) { + copied[i] = target[i]; + return voidFuture(); + } + else if ( types[i] instanceof CollectionType ) { + return CollectionTypes.replace( + (CollectionType) types[i], + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache, + foreignKeyDirection + ).thenCompose( copy -> { + if ( copy instanceof CompletionStage ) { + return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); + } + else { + copied[i] = copy; + return voidFuture(); + } + } ); + } + else if ( types[i] instanceof EntityType ) { + return replace( + (EntityType) types[i], + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache, + foreignKeyDirection + ).thenCompose( copy -> { + if ( copy instanceof CompletionStage ) { + return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); + } + else { + copied[i] = copy; + return voidFuture(); + } + } ); + } + else { + copied[i] = types[i].replace( + original[i], + target[i] == UNFETCHED_PROPERTY ? null : target[i], + session, + owner, + copyCache, + foreignKeyDirection + ); + return voidFuture(); + } + } + + } 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 9c6c126cc..75287e5be 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 @@ -311,4 +311,8 @@ public interface Log extends BasicLogger { @LogMessage(level = WARN) @Message(id = 448, value = "Warnings creating temp table : %s") void warningsCreatingTempTable(SQLWarning warning); + + @LogMessage(level = WARN) + @Message( id= 494, value = "Attempt to merge an uninitialized collection with queued operations; queued operations will be ignored: %s") + void ignoreQueuedOperationsOnMerge(String collectionInfoString); } From d3f939cd3908255094329377cce17af5ab122425 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 20 Jan 2025 10:48:36 +0100 Subject: [PATCH 027/201] [#2006] Add test for UnexpectedAccessToTheDatabase error when merging a detached entity with a ToMany association --- .../reactive/OneToManyArrayMergeTest.java | 190 +++++++++++++++ .../reactive/OneToManyMapMergeTest.java | 199 +++++++++++++++ .../reactive/OneToManyMergeTest.java | 227 ++++++++++++++++++ 3 files changed, 616 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMapMergeTest.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMergeTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java new file mode 100644 index 000000000..e2eb826d9 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java @@ -0,0 +1,190 @@ +/* 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 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.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 2, timeUnit = MINUTES) +public class OneToManyArrayMergeTest extends BaseReactiveTest { + + private final static Long USER_ID = 1L; + private final static Long ADMIN_ROLE_ID = 2L; + private final static Long USER_ROLE_ID = 3L; + private final static String UPDATED_FIRSTNAME = "UPDATED FIRSTNAME"; + private final static String UPDATED_LASTNAME = "UPDATED LASTNAME"; + + @Override + protected Collection> annotatedEntities() { + return List.of( User.class, Role.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + User user = new User( USER_ID, "first", "last", adminRole ); + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.persistAll( user, adminRole, userRole ) ) + ); + } + + @Test + public void testMerge(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + .chain( user -> getMutinySessionFactory() + .withTransaction( s -> s + .createQuery( "FROM Role", Role.class ) + .getResultList() ) + .map( roles -> { + user.addAll( roles ); + user.setFirstname( UPDATED_FIRSTNAME ); + user.setLastname( UPDATED_LASTNAME ); + return user; + } ) + ) + .chain( user -> { + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).hasSize( 2 ); + return getMutinySessionFactory() + .withTransaction( s -> s.merge( user ) ); + } + ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + ) + .invoke( user -> { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).containsExactlyInAnyOrder( + adminRole, + userRole + ); + } + ) + ); + } + + @Entity(name = "User") + @Table(name = "USER_TABLE") + public static class User { + + @Id + private Long id; + + private String firstname; + + private String lastname; + + @OneToMany(fetch = FetchType.EAGER) + private Role[] roles; + + public User() { + } + + public User(Long id, String firstname, String lastname, Role... roles) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + this.roles = new Role[roles.length]; + for ( int i = 0; i < roles.length; i++ ) { + this.roles[i] = roles[i]; + } + } + + public Long getId() { + return id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Role[] getRoles() { + return roles; + } + + public void addAll(List roles) { + this.roles = new Role[roles.size()]; + for ( int i = 0; i < roles.size(); i++ ) { + this.roles[i] = roles.get( i ); + } + } + } + + @Entity(name = "Role") + @Table(name = "ROLE_TABLE") + public static class Role { + + @Id + private Long id; + private String code; + + public Role() { + } + + public Role(Long id, String code) { + this.id = id; + this.code = code; + } + + public Object getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Role role = (Role) o; + return Objects.equals( id, role.id ) && Objects.equals( code, role.code ); + } + + @Override + public int hashCode() { + return Objects.hash( id, code ); + } + + @Override + public String toString() { + return "Role{" + code + '}'; + } + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMapMergeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMapMergeTest.java new file mode 100644 index 000000000..7a1096d3d --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMapMergeTest.java @@ -0,0 +1,199 @@ +/* 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +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.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 2, timeUnit = MINUTES) +public class OneToManyMapMergeTest extends BaseReactiveTest { + + private final static Long USER_ID = 1L; + private final static Long ADMIN_ROLE_ID = 2L; + private final static Long USER_ROLE_ID = 3L; + private final static String UPDATED_FIRSTNAME = "UPDATED FIRSTNAME"; + private final static String UPDATED_LASTNAME = "UPDATED LASTNAME"; + + @Override + protected Collection> annotatedEntities() { + return List.of( User.class, Role.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + User user = new User( USER_ID, "first", "last", adminRole ); + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.persistAll( user, adminRole, userRole ) ) + ); + } + + @Test + public void testMerge(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + .chain( user -> getMutinySessionFactory() + .withTransaction( s -> s + .createQuery( "FROM Role", Role.class ) + .getResultList() ) + .map( roles -> { + user.addAll( roles ); + user.setFirstname( UPDATED_FIRSTNAME ); + user.setLastname( UPDATED_LASTNAME ); + return user; + } ) + ) + .chain( user -> { + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).hasSize( 2 ); + return getMutinySessionFactory() + .withTransaction( s -> s.merge( user ) ); + } + ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + ) + .invoke( user -> { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).containsEntry( + adminRole.getCode(), + adminRole + ); + assertThat( user.getRoles() ).containsEntry( + userRole.getCode(), + userRole + ); + } + ) + ); + } + + @Entity(name = "User") + @Table(name = "USER_TABLE") + public static class User { + + @Id + private Long id; + + private String firstname; + + private String lastname; + + @OneToMany(fetch = FetchType.EAGER) + private Map roles = new HashMap(); + + public User() { + } + + public User(Long id, String firstname, String lastname, Role... roles) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + for ( Role role : roles ) { + this.roles.put( role.getCode(), role ); + } + } + + public Long getId() { + return id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Map getRoles() { + return roles; + } + + public void addAll(List roles) { + this.roles.clear(); + for ( Role role : roles ) { + this.roles.put( role.getCode(), role ); + } + } + } + + @Entity(name = "Role") + @Table(name = "ROLE_TABLE") + public static class Role { + + @Id + private Long id; + private String code; + + public Role() { + } + + public Role(Long id, String code) { + this.id = id; + this.code = code; + } + + public Object getId() { + return id; + } + + public String getCode() { + return code; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Role role = (Role) o; + return Objects.equals( id, role.id ) && Objects.equals( code, role.code ); + } + + @Override + public int hashCode() { + return Objects.hash( id, code ); + } + + @Override + public String toString() { + return "Role{" + code + '}'; + } + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMergeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMergeTest.java new file mode 100644 index 000000000..76d7f7732 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyMergeTest.java @@ -0,0 +1,227 @@ +/* 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 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.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 2, timeUnit = MINUTES) +public class OneToManyMergeTest extends BaseReactiveTest { + + private final static Long USER_ID = 1L; + private final static Long ADMIN_ROLE_ID = 2L; + private final static Long USER_ROLE_ID = 3L; + private final static String UPDATED_FIRSTNAME = "UPDATED FIRSTNAME"; + private final static String UPDATED_LASTNAME = "UPDATED LASTNAME"; + + @Override + protected Collection> annotatedEntities() { + return List.of( User.class, Role.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + User user = new User( USER_ID, "first", "last", adminRole ); + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.persistAll( user, adminRole, userRole ) ) + ); + } + + @Test + public void testMerge(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + .chain( user -> getMutinySessionFactory() + .withTransaction( s -> s + .createQuery( "FROM Role", Role.class ) + .getResultList() ) + .map( roles -> { + user.getRoles().clear(); + user.getRoles().addAll( roles ); + user.setFirstname( UPDATED_FIRSTNAME ); + user.setLastname( UPDATED_LASTNAME ); + return user; + } ) + ) + .chain( user -> { + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).hasSize( 2 ); + return getMutinySessionFactory() + .withTransaction( s -> s.merge( user ) ); + } + ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + ) + .invoke( user -> { + Role adminRole = new Role( ADMIN_ROLE_ID, "admin" ); + Role userRole = new Role( USER_ROLE_ID, "user" ); + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).containsExactlyInAnyOrder( + adminRole, + userRole + ); + } + ) + ); + } + + @Test + public void testMergeRemovingCollectionElements(VertxTestContext context) { + test( + context, getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + .chain( user -> getMutinySessionFactory() + .withTransaction( s -> s + .createQuery( "FROM Role", Role.class ) + .getResultList() ) + .map( roles -> { + user.clearRoles(); + user.setFirstname( UPDATED_FIRSTNAME ); + user.setLastname( UPDATED_LASTNAME ); + return user; + } ) + ) + .chain( user -> { + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).isNull(); + return getMutinySessionFactory() + .withTransaction( s -> s.merge( user ) ); + } + ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( User.class, USER_ID ) ) + ) + .invoke( user -> { + assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME ); + assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME ); + assertThat( user.getRoles() ).isNullOrEmpty(); + } + ) + ); + } + + @Entity(name = "User") + @Table(name = "USER_TABLE") + public static class User { + + @Id + private Long id; + + private String firstname; + + private String lastname; + + @OneToMany(fetch = FetchType.EAGER) + private List roles; + + public User() { + } + + public User(Long id, String firstname, String lastname, Role... roles) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + this.roles = List.of( roles ); + } + + public User(Long id, String firstname, String lastname) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + } + + public Long getId() { + return id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public List getRoles() { + return roles; + } + + public void clearRoles() { + this.roles = null; + } + } + + @Entity(name = "Role") + @Table(name = "ROLE_TABLE") + public static class Role { + + @Id + private Long id; + private String code; + + public Role() { + } + + public Role(Long id, String code) { + this.id = id; + this.code = code; + } + + public Object getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Role role = (Role) o; + return Objects.equals( id, role.id ) && Objects.equals( code, role.code ); + } + + @Override + public int hashCode() { + return Objects.hash( id, code ); + } + + @Override + public String toString() { + return "Role{" + code + '}'; + } + } +} From 6d8519287aeeffbd923caf912c3497af7d0f68fa Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 20 Jan 2025 11:57:06 +0100 Subject: [PATCH 028/201] [#2006] Fix EntityTypes#loadByUniqueKey It's using a `.thenApply` instead of a `.thenCompose`. --- .../reactive/engine/impl/EntityTypes.java | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index 722ba4a78..d5f37dba3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -138,7 +138,7 @@ static CompletionStage loadByUniqueKey( else { return persister .reactiveLoadByUniqueKey( uniqueKeyPropertyName, key, session ) - .thenApply( ukResult -> loadHibernateProxyEntity( ukResult, session ) + .thenCompose( ukResult -> loadHibernateProxyEntity( ukResult, session ) .thenApply( targetUK -> { persistenceContext.addEntity( euk, targetUK ); return targetUK; @@ -364,9 +364,9 @@ private static CompletionStage getIdentifierFromHibernateProxy( if ( type.isEntityIdentifierMapping() ) { propertyValue = getIdentifier( (EntityType) type, propertyValue, (SessionImplementor) session ); } - return completedFuture( propertyValue ); + return propertyValue; } - return nullFuture(); + return null; } ); } @@ -409,15 +409,7 @@ else if ( types[i] instanceof CollectionType ) { session, owner, copyCache - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ); + ).thenAccept( copy -> copied[i] = copy ); } else if ( types[i] instanceof EntityType ) { return replace( @@ -427,16 +419,7 @@ else if ( types[i] instanceof EntityType ) { session, owner, copyCache - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ) - .thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ); + ).thenAccept( copy -> copied[i] = copy ); } else { final Type type = types[i]; @@ -474,15 +457,7 @@ else if ( types[i] instanceof CollectionType ) { owner, copyCache, foreignKeyDirection - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ); + ).thenAccept( copy -> copied[i] = copy ); } else if ( types[i] instanceof EntityType ) { return replace( @@ -493,15 +468,7 @@ else if ( types[i] instanceof EntityType ) { owner, copyCache, foreignKeyDirection - ).thenCompose( copy -> { - if ( copy instanceof CompletionStage ) { - return ( (CompletionStage) copy ).thenAccept( nonStageCopy -> copied[i] = nonStageCopy ); - } - else { - copied[i] = copy; - return voidFuture(); - } - } ); + ).thenAccept( copy -> copied[i] = copy ); } else { copied[i] = types[i].replace( From bba2f99a16c51d0ea7874616227c447539b7668f Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 20 Jan 2025 12:03:22 +0100 Subject: [PATCH 029/201] [#2006] Small refactoring and clean ups --- .../reactive/engine/impl/CollectionTypes.java | 96 +++++++------------ .../reactive/engine/impl/EntityTypes.java | 16 ++-- 2 files changed, 41 insertions(+), 71 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java index 99e227cbc..4bc488bb6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java @@ -42,7 +42,6 @@ import static org.hibernate.pretty.MessageHelper.collectionInfoString; 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; /** @@ -81,10 +80,10 @@ public static CompletionStage replace( Object owner, Map copyCache) throws HibernateException { if ( original == null ) { - return replaceNullOriginal( target, session ); + return completedFuture( replaceNullOriginal( target, session ) ); } else if ( !Hibernate.isInitialized( original ) ) { - return replaceUninitializedOriginal( type, original, target, session, copyCache ); + return completedFuture( replaceUninitializedOriginal( type, original, target, session, copyCache ) ); } else { return replaceOriginal( type, original, target, session, owner, copyCache ); @@ -92,23 +91,22 @@ else if ( !Hibernate.isInitialized( original ) ) { } // todo: make org.hibernate.type.CollectionType#replaceNullOriginal public ? - /** * @see CollectionType#replaceNullOriginal(Object, SharedSessionContractImplementor) */ - private static CompletionStage replaceNullOriginal( + private static Object replaceNullOriginal( Object target, SessionImplementor session) { if ( target == null ) { - return nullFuture(); + return null; } else if ( target instanceof Collection collection ) { collection.clear(); - return completedFuture( collection ); + return collection; } else if ( target instanceof Map map ) { map.clear(); - return completedFuture( map ); + return map; } else { final PersistenceContext persistenceContext = session.getPersistenceContext(); @@ -124,18 +122,15 @@ else if ( target instanceof Map map ) { arrayHolder.endRead(); arrayHolder.dirty(); persistenceContext.addCollectionHolder( collectionHolder ); - return completedFuture( arrayHolder.getArray() ); + return arrayHolder.getArray(); } } } - return nullFuture(); + return null; } // todo: make org.hibernate.type.CollectionType#replaceUninitializedOriginal public - /** - * @see CollectionType#replaceNullOriginal(Object, SharedSessionContractImplementor) - */ - private static CompletionStage replaceUninitializedOriginal( + private static Object replaceUninitializedOriginal( CollectionType type, Object original, Object target, @@ -161,7 +156,7 @@ private static CompletionStage replaceUninitializedOriginal( collectionInfoString( type.getRole(), persistentCollection.getKey() ) ); } } - return completedFuture( target ); + return target; } /** @@ -193,11 +188,11 @@ private static CompletionStage replaceOriginal( //TODO: this is a little inefficient, don't need to do a whole // deep replaceElements() call return replaceElements( type, result, target, owner, copyCache, session ) - .thenCompose( unused -> { + .thenApply( unused -> { if ( wasClean ) { ( (PersistentCollection) target ).clearDirty(); } - return completedFuture( target ); + return target; } ); } else { @@ -291,10 +286,8 @@ private static CompletionStage replaceMapTypeElements( Object owner, Map copyCache, SessionImplementor session) { - final CollectionPersister persister = - session.getFactory().getRuntimeMetamodels().getMappingMetamodel() - .getCollectionDescriptor( type.getRole() ); - + final CollectionPersister persister = session.getFactory().getRuntimeMetamodels() + .getMappingMetamodel().getCollectionDescriptor( type.getRole() ); final Map result = target; result.clear(); @@ -302,15 +295,13 @@ private static CompletionStage replaceMapTypeElements( original.entrySet(), entry -> { final Map.Entry me = entry; return getReplace( persister.getIndexType(), me.getKey(), owner, session, copyCache ) - .thenCompose( key -> - getReplace( - persister.getElementType(), - me.getValue(), - owner, - session, - copyCache - ).thenAccept( value -> - result.put( key, value ) ) + .thenCompose( key -> getReplace( + persister.getElementType(), + me.getValue(), + owner, + session, + copyCache + ).thenAccept( value -> result.put( key, value ) ) ); } ).thenApply( unused -> result ); @@ -335,14 +326,11 @@ private static CompletionStage replaceArrayTypeElements( final Type elemType = type.getElementType( session.getFactory() ); return loop( - 0, length, i -> { - return getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) - .thenApply( o -> { - Array.set( result, i, o ); - return result; - } - ); - } + 0, length, i -> getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) + .thenApply( o -> { + Array.set( result, i, o ); + return result; + } ) ).thenApply( unused -> result ); } @@ -384,8 +372,7 @@ private static CompletionStage preserveSnapshot( final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( result ); if ( ce != null ) { return createSnapshot( original, result, elemType, owner, copyCache, session ) - .thenAccept( serializable -> - ce.resetStoredSnapshot( result, serializable ) ); + .thenAccept( serializable -> ce.resetStoredSnapshot( result, serializable ) ); } return voidFuture(); } @@ -426,14 +413,8 @@ private static CompletionStage createArraySnapshot( Map copyCache, SessionImplementor session) { return loop( - 0, array.length, - i -> - getReplace( elemType, array[i], owner, session, copyCache ) - .thenCompose( o -> { - array[i] = o; - return voidFuture(); - } - ) + 0, array.length, i -> getReplace( elemType, array[i], owner, session, copyCache ) + .thenAccept( o -> array[i] = o ) ).thenApply( unused -> array ); } @@ -459,10 +440,9 @@ private static CompletionStage createMapSnapshot( return loop( map.entrySet(), entry -> getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) - .thenCompose( newValue -> { + .thenAccept( newValue -> { final Object key = entry.getKey(); targetMap.put( key == entry.getValue() ? newValue : key, newValue ); - return voidFuture(); } ) ).thenApply( v -> (Serializable) targetMap ); } @@ -478,12 +458,8 @@ private static CompletionStage createListSnapshot( SessionImplementor session) { final ArrayList targetList = new ArrayList<>( list.size() ); return loop( - list, obj -> - getReplace( elemType, obj, owner, session, copyCache ) - .thenCompose( o -> { - targetList.add( o ); - return voidFuture(); - } ) + list, obj -> getReplace( elemType, obj, owner, session, copyCache ) + .thenAccept( targetList::add ) ).thenApply( unused -> targetList ); } @@ -498,10 +474,8 @@ private static Object instantiateResultIfNecessary(CollectionType type, Object o return target == null || target == original || target == UNFETCHED_PROPERTY - || target instanceof PersistentCollection collection && collection.isWrapper( original ) ? - type.instantiate( -1 ) : - target; + || target instanceof PersistentCollection collection && collection.isWrapper( original ) + ? type.instantiate( -1 ) + : target; } - - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index d5f37dba3..fb1c29fe4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -160,10 +160,7 @@ public static CompletionStage replace( final Map copyCache) { Object[] copied = new Object[original.length]; return loop( - 0, types.length, - i -> - replace( original, target, types, session, owner, copyCache, i, copied ) - + 0, types.length, i -> replace( original, target, types, session, owner, copyCache, i, copied ) ).thenApply( v -> copied ); } @@ -181,9 +178,7 @@ public static CompletionStage replace( Object[] copied = new Object[original.length]; return loop( 0, types.length, - i -> - replace( original, target, types, session, owner, copyCache, foreignKeyDirection, i, copied ) - + i -> replace( original, target, types, session, owner, copyCache, foreignKeyDirection, i, copied ) ).thenApply( v -> copied ); } @@ -274,15 +269,16 @@ private static CompletionStage resolveIdOrUniqueKey( // as a ComponentType. In the case that the entity is unfetched, we need to // explicitly fetch it here before calling replace(). (Note that in Hibernate // ORM this is unnecessary due to transparent lazy fetching.) - return ( (ReactiveSessionImpl) session ).reactiveFetch( id, true ) + return ( (ReactiveSessionImpl) session ) + .reactiveFetch( id, true ) .thenCompose( fetched -> { - Object idOrUniqueKey = entityType.getIdentifierOrUniqueKeyType( session.getFactory() ) + Object idOrUniqueKey = entityType + .getIdentifierOrUniqueKeyType( session.getFactory() ) .replace( fetched, null, session, owner, copyCache ); if ( idOrUniqueKey instanceof CompletionStage ) { return ( (CompletionStage) idOrUniqueKey ) .thenCompose( key -> resolve( entityType, key, owner, session ) ); } - return resolve( entityType, idOrUniqueKey, owner, session ); } ); } ); From 797cc65d9192ee5012479e1831ce669a65c22f2e Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 20 Jan 2025 14:08:20 +0100 Subject: [PATCH 030/201] [#2060] ClassCastException when using embeddable ids --- .../internal/ReactiveEmbeddableForeignKeyResultImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 a708481cf..1e3433320 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 @@ -10,7 +10,6 @@ import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableForeignKeyResultImpl; -import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableInitializerImpl; public class ReactiveEmbeddableForeignKeyResultImpl extends EmbeddableForeignKeyResultImpl { @@ -22,6 +21,6 @@ public ReactiveEmbeddableForeignKeyResultImpl(EmbeddableForeignKeyResultImpl public EmbeddableInitializer createInitializer(InitializerParent parent, AssemblerCreationState creationState) { return getReferencedModePart() instanceof NonAggregatedIdentifierMapping ? new ReactiveNonAggregatedIdentifierMappingInitializer( this, null, creationState, true ) - : new EmbeddableInitializerImpl( this, null, null, creationState, true ); + : new ReactiveEmbeddableInitializerImpl( this, null, null, creationState, true ); } } From 10f3f074ab708d17a65fb8f61ba0499220db41ef Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 8 Jan 2025 11:54:41 +0100 Subject: [PATCH 031/201] [#2060] Add test for ClassCastException when using embeddable ids --- .../reactive/EmbeddedIdWithManyTest.java | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java new file mode 100644 index 000000000..d77de6a4c --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java @@ -0,0 +1,180 @@ +/* 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.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +public class EmbeddedIdWithManyTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( Flower.class, Fruit.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Seed seed1 = new Seed( 1 ); + Flower rose = new Flower( seed1, "Rose" ); + + Fruit cherry = new Fruit( seed1, "Cherry" ); + cherry.addFriend( rose ); + + Seed seed2 = new Seed( 2 ); + Flower sunflower = new Flower( seed2, "Sunflower" ); + + Fruit apple = new Fruit( seed2, "Apple" ); + apple.addFriend( sunflower ); + + Seed seed3 = new Seed( 3 ); + Flower chrysanthemum = new Flower( seed3, "Chrysanthemum" ); + + Fruit banana = new Fruit( seed3, "Banana" ); + banana.addFriend( chrysanthemum ); + + test( + context, + getMutinySessionFactory().withTransaction( s -> s + .persistAll( cherry, rose, sunflower, apple, chrysanthemum, banana ) + ) + ); + } + + @Test + public void test(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> s + .createSelectionQuery( "from Flower", Flower.class ) + .getResultList() + ) + ); + } + + @Embeddable + public static class Seed { + + @Column(nullable = false, updatable = false) + private Integer id; + + public Seed() { + } + + public Seed(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + + @MappedSuperclass + public static abstract class Plant { + + @EmbeddedId + private Seed seed; + + @Column(length = 40, unique = true) + private String name; + + protected Plant() { + } + + protected Plant(Seed seed, String name) { + this.seed = seed; + this.name = name; + } + + public Seed getSeed() { + return seed; + } + + public void setSeed(Seed seed) { + this.seed = seed; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "Fruit") + @Table(name = "known_fruits") + public static class Fruit extends Plant { + + @OneToMany(mappedBy = "friend", fetch = FetchType.LAZY) + private List friends = new ArrayList<>(); + + public Fruit() { + } + + public Fruit(Seed seed, String name) { + super( seed, name ); + } + + public void addFriend(Flower flower) { + this.friends.add( flower ); + flower.friend = this; + } + + public List getFriends() { + return friends; + } + + @Override + public String toString() { + return "Fruit{" + getSeed().getId() + "," + getName() + '}'; + } + + } + + @Entity(name = "Flower") + @Table(name = "known_flowers") + public static class Flower extends Plant { + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "friend", referencedColumnName = "id", nullable = false) + private Fruit friend; + + public Flower() { + } + + public Flower(Seed seed, String name) { + super( seed, name ); + } + + @Override + public String toString() { + return "Flower{" + getSeed().getId() + "," + getName() + '}'; + } + + } + +} From ce59900857455c0567017b30f0c42d13fc11e3f5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 21 Jan 2025 07:51:13 +0100 Subject: [PATCH 032/201] [#2060] Add assertions to the test --- .../reactive/EmbeddedIdWithManyTest.java | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java index d77de6a4c..eed1e5846 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyTest.java @@ -24,8 +24,18 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import static org.assertj.core.api.Assertions.assertThat; + public class EmbeddedIdWithManyTest extends BaseReactiveTest { + Fruit cherry; + Fruit apple; + Fruit banana; + + Flower sunflower; + Flower chrysanthemum; + Flower rose; + @Override protected Collection> annotatedEntities() { return List.of( Flower.class, Fruit.class ); @@ -34,21 +44,21 @@ protected Collection> annotatedEntities() { @BeforeEach public void populateDb(VertxTestContext context) { Seed seed1 = new Seed( 1 ); - Flower rose = new Flower( seed1, "Rose" ); + rose = new Flower( seed1, "Rose" ); - Fruit cherry = new Fruit( seed1, "Cherry" ); + cherry = new Fruit( seed1, "Cherry" ); cherry.addFriend( rose ); Seed seed2 = new Seed( 2 ); - Flower sunflower = new Flower( seed2, "Sunflower" ); + sunflower = new Flower( seed2, "Sunflower" ); - Fruit apple = new Fruit( seed2, "Apple" ); + apple = new Fruit( seed2, "Apple" ); apple.addFriend( sunflower ); Seed seed3 = new Seed( 3 ); - Flower chrysanthemum = new Flower( seed3, "Chrysanthemum" ); + chrysanthemum = new Flower( seed3, "Chrysanthemum" ); - Fruit banana = new Fruit( seed3, "Banana" ); + banana = new Fruit( seed3, "Banana" ); banana.addFriend( chrysanthemum ); test( @@ -60,11 +70,28 @@ public void populateDb(VertxTestContext context) { } @Test - public void test(VertxTestContext context) { + public void testFindWithEmbeddedId(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> s + .find( Flower.class, chrysanthemum.getSeed() ) + .invoke( flower -> assertThat( flower.getName() ).isEqualTo( chrysanthemum.getName() ) ) + ) + ); + } + + @Test + public void testSelectQueryWithEmbeddedId(VertxTestContext context) { test( context, getMutinySessionFactory().withTransaction( s -> s .createSelectionQuery( "from Flower", Flower.class ) .getResultList() + .invoke( list -> assertThat( list.stream().map( Flower::getName ) ) + .containsExactlyInAnyOrder( + sunflower.getName(), + chrysanthemum.getName(), + rose.getName() + ) + ) ) ); } From e035151b5de4cd382d0a72fced27f39ef71fd33b Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 21 Jan 2025 12:17:06 +0100 Subject: [PATCH 033/201] [#2074] Upgrade Hibernate Validator to 8.0.2.Final --- examples/native-sql-example/build.gradle | 2 +- examples/session-example/build.gradle | 2 +- integration-tests/hibernate-validator-postgres-it/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/native-sql-example/build.gradle b/examples/native-sql-example/build.gradle index fabdbc7fa..c79ec8984 100644 --- a/examples/native-sql-example/build.gradle +++ b/examples/native-sql-example/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation project( ':hibernate-reactive-core' ) // Hibernate Validator (optional) - implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' + implementation 'org.hibernate.validator:hibernate-validator:8.0.2.Final' runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' // JPA metamodel generation for criteria queries (optional) diff --git a/examples/session-example/build.gradle b/examples/session-example/build.gradle index 7cf7fb5f6..4da40ba69 100644 --- a/examples/session-example/build.gradle +++ b/examples/session-example/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation project( ':hibernate-reactive-core' ) // Hibernate Validator (optional) - implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' + implementation 'org.hibernate.validator:hibernate-validator:8.0.2.Final' runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' // JPA metamodel generation for criteria queries (optional) diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index 017a52919..8236f6509 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -22,7 +22,7 @@ ext { dependencies { implementation project(':hibernate-reactive-core') - implementation "org.hibernate.validator:hibernate-validator:8.0.1.Final" + implementation "org.hibernate.validator:hibernate-validator:8.0.2.Final" runtimeOnly 'org.glassfish.expressly:expressly:5.0.0' // JPA metamodel generation for criteria queries (optional) From cd79ac303bffa897d053fda969310512be3b5ecd Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 21 Jan 2025 11:13:06 +0100 Subject: [PATCH 034/201] [#1724] Attempt to fix JDBCTimeZoneZonedTest sporadic failures probably due lack of database Timestamp precision --- .../timezones/JDBCTimeZoneZonedTest.java | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java index 5b928c217..c86ee18bb 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java @@ -10,10 +10,15 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseDialect; import org.hibernate.reactive.BaseReactiveTest; import org.hibernate.reactive.annotations.DisabledFor; @@ -31,7 +36,7 @@ import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: IllegalStateException: Needed to have 6 in buffer but only had 0") @@ -51,8 +56,24 @@ protected void setProperties(Configuration configuration) { @Test public void test(VertxTestContext context) { - ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of( "CET" ) ); - OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3 ) ); + final ZonedDateTime nowZoned; + final OffsetDateTime nowOffset; + final Dialect dialect = getDialect(); + if ( dialect instanceof SybaseDialect || dialect instanceof MySQLDialect ) { + // Sybase has 1/300th sec precision + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ) + .with( ChronoField.NANO_OF_SECOND, 0L ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ) + .with( ChronoField.NANO_OF_SECOND, 0L ); + } + else if ( dialect.getDefaultTimestampPrecision() == 6 ) { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); + } + else { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ); + } test( context, getSessionFactory() .withTransaction( s -> { Zoned z = new Zoned(); @@ -63,11 +84,11 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); ZoneId systemZone = ZoneId.systemDefault(); ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() ); From 85a30caabba85847abed1ce8934df518b57254be Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 21 Jan 2025 13:47:59 +0100 Subject: [PATCH 035/201] [#1724] Align timezones tests to changes applied to ORM corresponding tests --- .../reactive/timezones/AutoZonedTest.java | 26 ++++++++++++++----- .../reactive/timezones/ColumnZonedTest.java | 26 ++++++++++++++----- .../reactive/timezones/DefaultZonedTest.java | 26 ++++++++++++++----- .../timezones/JDBCTimeZoneZonedTest.java | 17 +++--------- .../reactive/timezones/PassThruZonedTest.java | 26 ++++++++++++++----- 5 files changed, 80 insertions(+), 41 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/AutoZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/AutoZonedTest.java index a799f5d6b..eb502bdb1 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/AutoZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/AutoZonedTest.java @@ -19,6 +19,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; @@ -29,8 +30,11 @@ import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; +/** + * Test adapted from {@link org.hibernate.orm.test.timezones.AutoZonedTest} + */ @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: IllegalStateException: Needed to have 6 in buffer but only had 0") public class AutoZonedTest extends BaseReactiveTest { @@ -48,8 +52,16 @@ protected void setProperties(Configuration configuration) { @Test public void test(VertxTestContext context) { - ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of( "CET" ) ); - OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3 ) ); + final ZonedDateTime nowZoned; + final OffsetDateTime nowOffset; + if ( getDialect().getDefaultTimestampPrecision() == 6 ) { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); + } + else { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ); + } test( context, getSessionFactory() .withTransaction( s -> { Zoned z = new Zoned(); @@ -60,10 +72,10 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); assertThat( z.zonedDateTime.toOffsetDateTime().getOffset() ) .isEqualTo( nowZoned.toOffsetDateTime().getOffset() ); assertThat( z.offsetDateTime.getOffset() ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/ColumnZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/ColumnZonedTest.java index f10e394e1..6e33da4f4 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/ColumnZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/ColumnZonedTest.java @@ -9,6 +9,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; @@ -29,8 +30,11 @@ import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; +/** + * Test adapted from {@link org.hibernate.orm.test.timezones.ColumnZonedTest} + */ @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "java.sql.SQLException: An error occurred with a DB2 operation, SQLCODE=-180 SQLSTATE=22007") public class ColumnZonedTest extends BaseReactiveTest { @@ -48,8 +52,16 @@ protected void setProperties(Configuration configuration) { @Test public void test(VertxTestContext context) { - ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of( "CET" ) ); - OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3 ) ); + final ZonedDateTime nowZoned; + final OffsetDateTime nowOffset; + if ( getDialect().getDefaultTimestampPrecision() == 6 ) { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); + } + else { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ); + } test( context, getSessionFactory() .withTransaction( s -> { Zoned z = new Zoned(); @@ -60,10 +72,10 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); assertThat( z.zonedDateTime.toOffsetDateTime().getOffset() ) .isEqualTo( nowZoned.toOffsetDateTime().getOffset() ); assertThat( z.offsetDateTime.getOffset() ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/DefaultZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/DefaultZonedTest.java index 6752b7219..4ce4dbea8 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/DefaultZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/DefaultZonedTest.java @@ -9,6 +9,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; @@ -28,8 +29,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; +/** + * Test adapted from {@link org.hibernate.orm.test.timezones.DefaultZonedTest} + */ @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: IllegalStateException: Needed to have 6 in buffer but only had 0") public class DefaultZonedTest extends BaseReactiveTest { @@ -41,8 +45,16 @@ protected Collection> annotatedEntities() { @Test public void test(VertxTestContext context) { - ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of( "CET" ) ); - OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3 ) ); + final ZonedDateTime nowZoned; + final OffsetDateTime nowOffset; + if ( getDialect().getDefaultTimestampPrecision() == 6 ) { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); + } + else { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ); + } test( context, getSessionFactory() .withTransaction( s -> { Zoned z = new Zoned(); @@ -53,10 +65,10 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); if ( getDialect().getTimeZoneSupport() == TimeZoneSupport.NATIVE ) { assertThat( z.zonedDateTime.toOffsetDateTime().getOffset() ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java index c86ee18bb..71966bfc1 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/JDBCTimeZoneZonedTest.java @@ -10,15 +10,11 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; import org.hibernate.cfg.Configuration; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.MySQLDialect; -import org.hibernate.dialect.SybaseDialect; import org.hibernate.reactive.BaseReactiveTest; import org.hibernate.reactive.annotations.DisabledFor; @@ -38,6 +34,9 @@ import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; +/** + * Test adapted from {@link org.hibernate.orm.test.timezones.JDBCTimeZoneZonedTest} + */ @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: IllegalStateException: Needed to have 6 in buffer but only had 0") public class JDBCTimeZoneZonedTest extends BaseReactiveTest { @@ -58,15 +57,7 @@ protected void setProperties(Configuration configuration) { public void test(VertxTestContext context) { final ZonedDateTime nowZoned; final OffsetDateTime nowOffset; - final Dialect dialect = getDialect(); - if ( dialect instanceof SybaseDialect || dialect instanceof MySQLDialect ) { - // Sybase has 1/300th sec precision - nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ) - .with( ChronoField.NANO_OF_SECOND, 0L ); - nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ) - .with( ChronoField.NANO_OF_SECOND, 0L ); - } - else if ( dialect.getDefaultTimestampPrecision() == 6 ) { + if ( getDialect().getDefaultTimestampPrecision() == 6 ) { nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/PassThruZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/PassThruZonedTest.java index 988db4a58..3ce9f4256 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/PassThruZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/PassThruZonedTest.java @@ -10,6 +10,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; @@ -30,8 +31,11 @@ import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; +/** + * Test adapted from {@link org.hibernate.orm.test.timezones.PassThruZonedTest} + */ @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: SQLException: An error occurred with a DB2 operation, SQLCODE=-180 SQLSTATE=22007") public class PassThruZonedTest extends BaseReactiveTest { @@ -49,8 +53,16 @@ protected void setProperties(Configuration configuration) { @Test public void test(VertxTestContext context) { - ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of( "CET" ) ); - OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3 ) ); + final ZonedDateTime nowZoned; + final OffsetDateTime nowOffset; + if ( getDialect().getDefaultTimestampPrecision() == 6 ) { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ).truncatedTo( ChronoUnit.MICROS ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ).truncatedTo( ChronoUnit.MICROS ); + } + else { + nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") ); + nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) ); + } test( context, getSessionFactory() .withTransaction( s -> { Zoned z = new Zoned(); @@ -61,10 +73,10 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); ZoneId systemZone = ZoneId.systemDefault(); ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() ); From f44e2e1a9caa60359f3a2f714aabb04add26d4d2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 22 Jan 2025 10:43:07 +0100 Subject: [PATCH 036/201] remove unused/obsolete LOG --- .../reactive/stage/impl/StageSelectionQueryImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java index 6a1be5d08..eaf1c9a53 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.stage.impl; -import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; @@ -16,8 +15,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.query.Order; import org.hibernate.query.Page; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.ReactiveSelectionQuery; import org.hibernate.reactive.stage.Stage.SelectionQuery; @@ -29,7 +26,6 @@ import jakarta.persistence.Parameter; public class StageSelectionQueryImpl implements SelectionQuery { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); private final ReactiveSelectionQuery delegate; public StageSelectionQueryImpl(ReactiveSelectionQuery delegate) { From c63b6a8220c4a1e1e742f8abe26053322d949a0d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 22 Jan 2025 10:41:57 +0100 Subject: [PATCH 037/201] fix whitespace in example persistence.xml files --- .../src/main/resources/META-INF/persistence.xml | 2 +- .../session-example/src/main/resources/META-INF/persistence.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/native-sql-example/src/main/resources/META-INF/persistence.xml b/examples/native-sql-example/src/main/resources/META-INF/persistence.xml index aaa9d3687..686442cb2 100644 --- a/examples/native-sql-example/src/main/resources/META-INF/persistence.xml +++ b/examples/native-sql-example/src/main/resources/META-INF/persistence.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd" version="3.0"> - + org.hibernate.reactive.provider.ReactivePersistenceProvider org.hibernate.reactive.example.nativesql.Author diff --git a/examples/session-example/src/main/resources/META-INF/persistence.xml b/examples/session-example/src/main/resources/META-INF/persistence.xml index 7c2c13900..f3271f251 100644 --- a/examples/session-example/src/main/resources/META-INF/persistence.xml +++ b/examples/session-example/src/main/resources/META-INF/persistence.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd" version="3.0"> - + org.hibernate.reactive.provider.ReactivePersistenceProvider org.hibernate.reactive.example.session.Author From 8f00411a6a34e51dd1b06a030278000fcd9fa90b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 21 Jan 2025 18:27:18 +0100 Subject: [PATCH 038/201] [#1904] Add test for PropertyAccessException when creating a new object with a one-to-one association --- .../OneToOneGeneratedIdWithMapsIdTest.java | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToOneGeneratedIdWithMapsIdTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToOneGeneratedIdWithMapsIdTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToOneGeneratedIdWithMapsIdTest.java new file mode 100644 index 000000000..4da8823a9 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToOneGeneratedIdWithMapsIdTest.java @@ -0,0 +1,153 @@ +/* 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 org.junit.jupiter.api.Test; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 10, timeUnit = MINUTES) +public class OneToOneGeneratedIdWithMapsIdTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( Person.class, NaturalPerson.class ); + } + + @Test + public void testPersist(VertxTestContext context) { + NaturalPerson naturalPerson = new NaturalPerson( "natual" ); + Person person = new Person( "person", naturalPerson ); + + test( + context, getMutinySessionFactory() + .withTransaction( session -> session.persist( person ) ) + .chain( () -> getMutinySessionFactory() + .withTransaction( session -> session + .find( Person.class, person.getId() ) + .invoke( result -> { + assertThat( result ).isNotNull(); + assertThat( result.getNaturalPerson() ).isNotNull(); + assertThat( result.getNaturalPerson().getId() ).isEqualTo( result.getId() ); + } ) ) + ) + ); + } + + @Entity + public static class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @OneToOne(mappedBy = "person", cascade = CascadeType.ALL) + private NaturalPerson naturalPerson; + + public Person() { + } + + public Person(String name, NaturalPerson naturalPerson) { + this.name = name; + this.naturalPerson = naturalPerson; + naturalPerson.person = this; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public NaturalPerson getNaturalPerson() { + return naturalPerson; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Person person = (Person) o; + return Objects.equals( name, person.name ); + } + + @Override + public int hashCode() { + return Objects.hashCode( name ); + } + } + + @Entity + public static class NaturalPerson { + + @Id + private Long id; + + @Column + private String name; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + private Person person; + + public NaturalPerson() { + } + + public NaturalPerson(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Person getPerson() { + return person; + } + + @Override + public boolean equals(Object o) { + if ( o == null || getClass() != o.getClass() ) { + return false; + } + NaturalPerson that = (NaturalPerson) o; + return Objects.equals( name, that.name ); + } + + @Override + public int hashCode() { + return Objects.hashCode( name ); + } + } + + +} From 89f7cf20df9778fc4f1ced298ebead22d7c4ae00 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 22 Jan 2025 16:18:23 +0100 Subject: [PATCH 039/201] [#1904] Small refactoring With this change we avoid the creation of a couple of CompletionStage objects --- .../AbstractReactiveSaveEventListener.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java index 0887dfff6..2bed60008 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java @@ -182,20 +182,21 @@ private CompletionStage generateId( EntityPersister persister) { return generator .generate( (ReactiveConnectionSupplier) source, entity ) - .thenApply( id -> castToIdentifierType( id, persister ) ) - .thenCompose( generatedId -> { - if ( generatedId == null ) { - return failedFuture( new IdentifierGenerationException( "null id generated for: " + entity.getClass() ) ); - } - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Generated identifier: %s, using strategy: %s", - persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), - generator.getClass().getName() - ); - } - return completedFuture( generatedId ); - } ); + .thenApply( id -> { + final Object generatedId = castToIdentifierType( id, persister ); + if ( generatedId == null ) { + throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); + } + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Generated identifier: %s, using strategy: %s", + persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), + generator.getClass().getName() + ); + } + return generatedId; + } + ); } /** From 7d2d57bcadfc3e048bc8ad5e3990780d72e0cd07 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 24 Jan 2025 09:29:30 +0100 Subject: [PATCH 040/201] [#2086] Upgrade Gradle Wrapper to 8.12 --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 +++++-- gradlew.bat | 22 ++++++++++++---------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 34592 zcmY(qRX`kF)3u#IAjsf0xCD212@LM;?(PINyAue(f;$XO2=4Cg1P$=#e%|lo zKk1`B>Q#GH)wNd-&cJog!qw7YfYndTeo)CyX{fOHsQjGa<{e=jamMNwjdatD={CN3>GNchOE9OGPIqr)3v>RcKWR3Z zF-guIMjE2UF0Wqk1)21791y#}ciBI*bAenY*BMW_)AeSuM5}vz_~`+1i!Lo?XAEq{TlK5-efNFgHr6o zD>^vB&%3ZGEWMS>`?tu!@66|uiDvS5`?bF=gIq3rkK(j<_TybyoaDHg8;Y#`;>tXI z=tXo~e9{U!*hqTe#nZjW4z0mP8A9UUv1}C#R*@yu9G3k;`Me0-BA2&Aw6f`{Ozan2 z8c8Cs#dA-7V)ZwcGKH}jW!Ja&VaUc@mu5a@CObzNot?b{f+~+212lwF;!QKI16FDS zodx>XN$sk9;t;)maB^s6sr^L32EbMV(uvW%or=|0@U6cUkE`_!<=LHLlRGJx@gQI=B(nn z-GEjDE}*8>3U$n(t^(b^C$qSTI;}6q&ypp?-2rGpqg7b}pyT zOARu2x>0HB{&D(d3sp`+}ka+Pca5glh|c=M)Ujn_$ly^X6&u z%Q4Y*LtB_>i6(YR!?{Os-(^J`(70lZ&Hp1I^?t@~SFL1!m0x6j|NM!-JTDk)%Q^R< z@e?23FD&9_W{Bgtr&CG&*Oer3Z(Bu2EbV3T9FeQ|-vo5pwzwQ%g&=zFS7b{n6T2ZQ z*!H(=z<{D9@c`KmHO&DbUIzpg`+r5207}4D=_P$ONIc5lsFgn)UB-oUE#{r+|uHc^hzv_df zV`n8&qry%jXQ33}Bjqcim~BY1?KZ}x453Oh7G@fA(}+m(f$)TY%7n=MeLi{jJ7LMB zt(mE*vFnep?YpkT_&WPV9*f>uSi#n#@STJmV&SLZnlLsWYI@y+Bs=gzcqche=&cBH2WL)dkR!a95*Ri)JH_4c*- zl4pPLl^as5_y&6RDE@@7342DNyF&GLJez#eMJjI}#pZN{Y8io{l*D+|f_Y&RQPia@ zNDL;SBERA|B#cjlNC@VU{2csOvB8$HzU$01Q?y)KEfos>W46VMh>P~oQC8k=26-Ku)@C|n^zDP!hO}Y z_tF}0@*Ds!JMt>?4y|l3?`v#5*oV-=vL7}zehMON^=s1%q+n=^^Z{^mTs7}*->#YL z)x-~SWE{e?YCarwU$=cS>VzmUh?Q&7?#Xrcce+jeZ|%0!l|H_=D_`77hBfd4Zqk&! zq-Dnt_?5*$Wsw8zGd@?woEtfYZ2|9L8b>TO6>oMh%`B7iBb)-aCefM~q|S2Cc0t9T zlu-ZXmM0wd$!gd-dTtik{bqyx32%f;`XUvbUWWJmpHfk8^PQIEsByJm+@+-aj4J#D z4#Br3pO6z1eIC>X^yKk|PeVwX_4B+IYJyJyc3B`4 zPrM#raacGIzVOexcVB;fcsxS=s1e&V;Xe$tw&KQ`YaCkHTKe*Al#velxV{3wxx}`7@isG zp6{+s)CG%HF#JBAQ_jM%zCX5X;J%-*%&jVI?6KpYyzGbq7qf;&hFprh?E5Wyo=bZ) z8YNycvMNGp1836!-?nihm6jI`^C`EeGryoNZO1AFTQhzFJOA%Q{X(sMYlzABt!&f{ zoDENSuoJQIg5Q#@BUsNJX2h>jkdx4<+ipUymWKFr;w+s>$laIIkfP6nU}r+?J9bZg zUIxz>RX$kX=C4m(zh-Eg$BsJ4OL&_J38PbHW&7JmR27%efAkqqdvf)Am)VF$+U3WR z-E#I9H6^)zHLKCs7|Zs<7Bo9VCS3@CDQ;{UTczoEprCKL3ZZW!ffmZFkcWU-V|_M2 zUA9~8tE9<5`59W-UgUmDFp11YlORl3mS3*2#ZHjv{*-1#uMV_oVTy{PY(}AqZv#wF zJVks)%N6LaHF$$<6p8S8Lqn+5&t}DmLKiC~lE{jPZ39oj{wR&fe*LX-z0m}9ZnZ{U z>3-5Bh{KKN^n5i!M79Aw5eY=`6fG#aW1_ZG;fw7JM69qk^*(rmO{|Z6rXy?l=K=#_ zE-zd*P|(sskasO(cZ5L~_{Mz&Y@@@Q)5_8l<6vB$@226O+pDvkFaK8b>%2 zfMtgJ@+cN@w>3)(_uR;s8$sGONbYvoEZ3-)zZk4!`tNzd<0lwt{RAgplo*f@Z)uO` zzd`ljSqKfHJOLxya4_}T`k5Ok1Mpo#MSqf~&ia3uIy{zyuaF}pV6 z)@$ZG5LYh8Gge*LqM_|GiT1*J*uKes=Oku_gMj&;FS`*sfpM+ygN&yOla-^WtIU#$ zuw(_-?DS?6DY7IbON7J)p^IM?N>7x^3)(7wR4PZJu(teex%l>zKAUSNL@~{czc}bR z)I{XzXqZBU3a;7UQ~PvAx8g-3q-9AEd}1JrlfS8NdPc+!=HJ6Bs( zCG!0;e0z-22(Uzw>hkEmC&xj?{0p|kc zM}MMXCF%RLLa#5jG`+}{pDL3M&|%3BlwOi?dq!)KUdv5__zR>u^o|QkYiqr(m3HxF z6J*DyN#Jpooc$ok=b7{UAVM@nwGsr6kozSddwulf5g1{B=0#2)zv!zLXQup^BZ4sv*sEsn)+MA?t zEL)}3*R?4(J~CpeSJPM!oZ~8;8s_=@6o`IA%{aEA9!GELRvOuncE`s7sH91 zmF=+T!Q6%){?lJn3`5}oW31(^Of|$r%`~gT{eimT7R~*Mg@x+tWM3KE>=Q>nkMG$U za7r>Yz2LEaA|PsMafvJ(Y>Xzha?=>#B!sYfVob4k5Orb$INFdL@U0(J8Hj&kgWUlO zPm+R07E+oq^4f4#HvEPANGWLL_!uF{nkHYE&BCH%l1FL_r(Nj@M)*VOD5S42Gk-yT z^23oAMvpA57H(fkDGMx86Z}rtQhR^L!T2iS!788E z+^${W1V}J_NwdwdxpXAW8}#6o1(Uu|vhJvubFvQIH1bDl4J4iDJ+181KuDuHwvM?` z%1@Tnq+7>p{O&p=@QT}4wT;HCb@i)&7int<0#bj8j0sfN3s6|a(l7Bj#7$hxX@~iP z1HF8RFH}irky&eCN4T94VyKqGywEGY{Gt0Xl-`|dOU&{Q;Ao;sL>C6N zXx1y^RZSaL-pG|JN;j9ADjo^XR}gce#seM4QB1?S`L*aB&QlbBIRegMnTkTCks7JU z<0(b+^Q?HN1&$M1l&I@>HMS;!&bb()a}hhJzsmB?I`poqTrSoO>m_JE5U4=?o;OV6 zBZjt;*%1P>%2{UL=;a4(aI>PRk|mr&F^=v6Fr&xMj8fRCXE5Z2qdre&;$_RNid5!S zm^XiLK25G6_j4dWkFqjtU7#s;b8h?BYFxV?OE?c~&ME`n`$ix_`mb^AWr+{M9{^^Rl;~KREplwy2q;&xe zUR0SjHzKVYzuqQ84w$NKVPGVHL_4I)Uw<$uL2-Ml#+5r2X{LLqc*p13{;w#E*Kwb*1D|v?e;(<>vl@VjnFB^^Y;;b3 z=R@(uRj6D}-h6CCOxAdqn~_SG=bN%^9(Ac?zfRkO5x2VM0+@_qk?MDXvf=@q_* z3IM@)er6-OXyE1Z4sU3{8$Y$>8NcnU-nkyWD&2ZaqX1JF_JYL8y}>@V8A5%lX#U3E zet5PJM`z79q9u5v(OE~{by|Jzlw2<0h`hKpOefhw=fgLTY9M8h+?37k@TWpzAb2Fc zQMf^aVf!yXlK?@5d-re}!fuAWu0t57ZKSSacwRGJ$0uC}ZgxCTw>cjRk*xCt%w&hh zoeiIgdz__&u~8s|_TZsGvJ7sjvBW<(C@}Y%#l_ID2&C`0;Eg2Z+pk;IK}4T@W6X5H z`s?ayU-iF+aNr5--T-^~K~p;}D(*GWOAYDV9JEw!w8ZYzS3;W6*_`#aZw&9J ziXhBKU3~zd$kKzCAP-=t&cFDeQR*_e*(excIUxKuD@;-twSlP6>wWQU)$|H3Cy+`= z-#7OW!ZlYzZxkdQpfqVDFU3V2B_-eJS)Fi{fLtRz!K{~7TR~XilNCu=Z;{GIf9KYz zf3h=Jo+1#_s>z$lc~e)l93h&RqW1VHYN;Yjwg#Qi0yzjN^M4cuL>Ew`_-_wRhi*!f zLK6vTpgo^Bz?8AsU%#n}^EGigkG3FXen3M;hm#C38P@Zs4{!QZPAU=m7ZV&xKI_HWNt90Ef zxClm)ZY?S|n**2cNYy-xBlLAVZ=~+!|7y`(fh+M$#4zl&T^gV8ZaG(RBD!`3?9xcK zp2+aD(T%QIgrLx5au&TjG1AazI;`8m{K7^!@m>uGCSR;Ut{&?t%3AsF{>0Cm(Kf)2 z?4?|J+!BUg*P~C{?mwPQ#)gDMmro20YVNsVx5oWQMkzQ? zsQ%Y>%7_wkJqnSMuZjB9lBM(o zWut|B7w48cn}4buUBbdPBW_J@H7g=szrKEpb|aE>!4rLm+sO9K%iI75y~2HkUo^iw zJ3se$8$|W>3}?JU@3h@M^HEFNmvCp|+$-0M?RQ8SMoZ@38%!tz8f8-Ptb@106heiJ z^Bx!`0=Im z1!NUhO=9ICM*+||b3a7w*Y#5*Q}K^ar+oMMtekF0JnO>hzHqZKH0&PZ^^M(j;vwf_ z@^|VMBpcw8;4E-9J{(u7sHSyZpQbS&N{VQ%ZCh{c1UA5;?R} z+52*X_tkDQ(s~#-6`z4|Y}3N#a&dgP4S_^tsV=oZr4A1 zaSoPN1czE(UIBrC_r$0HM?RyBGe#lTBL4~JW#A`P^#0wuK)C-2$B6TvMi@@%K@JAT_IB^T7Zfqc8?{wHcSVG_?{(wUG%zhCm=%qP~EqeqKI$9UivF zv+5IUOs|%@ypo6b+i=xsZ=^G1yeWe)z6IX-EC`F=(|_GCNbHbNp(CZ*lpSu5n`FRA zhnrc4w+Vh?r>her@Ba_jv0Omp#-H7avZb=j_A~B%V0&FNi#!S8cwn0(Gg-Gi_LMI{ zCg=g@m{W@u?GQ|yp^yENd;M=W2s-k7Gw2Z(tsD5fTGF{iZ%Ccgjy6O!AB4x z%&=6jB7^}pyftW2YQpOY1w@%wZy%}-l0qJlOSKZXnN2wo3|hujU+-U~blRF!^;Tan z0w;Srh0|Q~6*tXf!5-rCD)OYE(%S|^WTpa1KHtpHZ{!;KdcM^#g8Z^+LkbiBHt85m z;2xv#83lWB(kplfgqv@ZNDcHizwi4-8+WHA$U-HBNqsZ`hKcUI3zV3d1ngJP-AMRET*A{> zb2A>Fk|L|WYV;Eu4>{a6ESi2r3aZL7x}eRc?cf|~bP)6b7%BnsR{Sa>K^0obn?yiJ zCVvaZ&;d_6WEk${F1SN0{_`(#TuOOH1as&#&xN~+JDzX(D-WU_nLEI}T_VaeLA=bc zl_UZS$nu#C1yH}YV>N2^9^zye{rDrn(rS99>Fh&jtNY7PP15q%g=RGnxACdCov47= zwf^9zfJaL{y`R#~tvVL#*<`=`Qe zj_@Me$6sIK=LMFbBrJps7vdaf_HeX?eC+P^{AgSvbEn?n<}NDWiQGQG4^ZOc|GskK z$Ve2_n8gQ-KZ=s(f`_X!+vM5)4+QmOP()2Fe#IL2toZBf+)8gTVgDSTN1CkP<}!j7 z0SEl>PBg{MnPHkj4wj$mZ?m5x!1ePVEYI(L_sb0OZ*=M%yQb?L{UL(2_*CTVbRxBe z@{)COwTK1}!*CK0Vi4~AB;HF(MmQf|dsoy(eiQ>WTKcEQlnKOri5xYsqi61Y=I4kzAjn5~{IWrz_l))|Ls zvq7xgQs?Xx@`N?f7+3XKLyD~6DRJw*uj*j?yvT3}a;(j_?YOe%hUFcPGWRVBXzpMJ zM43g6DLFqS9tcTLSg=^&N-y0dXL816v&-nqC0iXdg7kV|PY+js`F8dm z2PuHw&k+8*&9SPQ6f!^5q0&AH(i+z3I7a?8O+S5`g)>}fG|BM&ZnmL;rk)|u{1!aZ zEZHpAMmK_v$GbrrWNP|^2^s*!0waLW=-h5PZa-4jWYUt(Hr@EA(m3Mc3^uDxwt-me^55FMA9^>hpp26MhqjLg#^Y7OIJ5%ZLdNx&uDgIIqc zZRZl|n6TyV)0^DDyVtw*jlWkDY&Gw4q;k!UwqSL6&sW$B*5Rc?&)dt29bDB*b6IBY z6SY6Unsf6AOQdEf=P1inu6(6hVZ0~v-<>;LAlcQ2u?wRWj5VczBT$Op#8IhppP-1t zfz5H59Aa~yh7EN;BXJsLyjkjqARS5iIhDVPj<=4AJb}m6M@n{xYj3qsR*Q8;hVxDyC4vLI;;?^eENOb5QARj#nII5l$MtBCI@5u~(ylFi$ zw6-+$$XQ}Ca>FWT>q{k)g{Ml(Yv=6aDfe?m|5|kbGtWS}fKWI+})F6`x@||0oJ^(g|+xi zqlPdy5;`g*i*C=Q(aGeDw!eQg&w>UUj^{o?PrlFI=34qAU2u@BgwrBiaM8zoDTFJ< zh7nWpv>dr?q;4ZA?}V}|7qWz4W?6#S&m>hs4IwvCBe@-C>+oohsQZ^JC*RfDRm!?y zS4$7oxcI|##ga*y5hV>J4a%HHl^t$pjY%caL%-FlRb<$A$E!ws?8hf0@(4HdgQ!@> zds{&g$ocr9W4I84TMa9-(&^_B*&R%^=@?Ntxi|Ejnh;z=!|uVj&3fiTngDPg=0=P2 zB)3#%HetD84ayj??qrxsd9nqrBem(8^_u_UY{1@R_vK-0H9N7lBX5K(^O2=0#TtUUGSz{ z%g>qU8#a$DyZ~EMa|8*@`GOhCW3%DN%xuS91T7~iXRr)SG`%=Lfu%U~Z_`1b=lSi?qpD4$vLh$?HU6t0MydaowUpb zQr{>_${AMesCEffZo`}K0^~x>RY_ZIG{(r39MP>@=aiM@C;K)jUcfQV8#?SDvq>9D zI{XeKM%$$XP5`7p3K0T}x;qn)VMo>2t}Ib(6zui;k}<<~KibAb%p)**e>ln<=qyWU zrRDy|UXFi9y~PdEFIAXejLA{K)6<)Q`?;Q5!KsuEw({!#Rl8*5_F{TP?u|5(Hijv( ztAA^I5+$A*+*e0V0R~fc{ET-RAS3suZ}TRk3r)xqj~g_hxB`qIK5z(5wxYboz%46G zq{izIz^5xW1Vq#%lhXaZL&)FJWp0VZNO%2&ADd?+J%K$fM#T_Eke1{dQsx48dUPUY zLS+DWMJeUSjYL453f@HpRGU6Dv)rw+-c6xB>(=p4U%}_p>z^I@Ow9`nkUG21?cMIh9}hN?R-d)*6%pr6d@mcb*ixr7 z)>Lo<&2F}~>WT1ybm^9UO{6P9;m+fU^06_$o9gBWL9_}EMZFD=rLJ~&e?fhDnJNBI zKM=-WR6g7HY5tHf=V~6~QIQ~rakNvcsamU8m28YE=z8+G7K=h%)l6k zmCpiDInKL6*e#)#Pt;ANmjf`8h-nEt&d}(SBZMI_A{BI#ck-_V7nx)K9_D9K-p@?Zh81#b@{wS?wCcJ%og)8RF*-0z+~)6f#T` zWqF7_CBcnn=S-1QykC*F0YTsKMVG49BuKQBH%WuDkEy%E?*x&tt%0m>>5^HCOq|ux zuvFB)JPR-W|%$24eEC^AtG3Gp4qdK%pjRijF5Sg3X}uaKEE z-L5p5aVR!NTM8T`4|2QA@hXiLXRcJveWZ%YeFfV%mO5q#($TJ`*U>hicS+CMj%Ip# zivoL;dd*araeJK9EA<(tihD50FHWbITBgF9E<33A+eMr2;cgI3Gg6<-2o|_g9|> zv5}i932( zYfTE9?4#nQhP@a|zm#9FST2 z!y+p3B;p>KkUzH!K;GkBW}bWssz)9b>Ulg^)EDca;jDl+q=243BddS$hY^fC6lbpM z(q_bo4V8~eVeA?0LFD6ZtKcmOH^75#q$Eo%a&qvE8Zsqg=$p}u^|>DSWUP5i{6)LAYF4E2DfGZuMJ zMwxxmkxQf}Q$V3&2w|$`9_SQS^2NVbTHh;atB>=A%!}k-f4*i$X8m}Ni^ppZXk5_oYF>Gq(& z0wy{LjJOu}69}~#UFPc;$7ka+=gl(FZCy4xEsk);+he>Nnl>hb5Ud-lj!CNicgd^2 z_Qgr_-&S7*#nLAI7r()P$`x~fy)+y=W~6aNh_humoZr7MWGSWJPLk}$#w_1n%(@? z3FnHf1lbxKJbQ9c&i<$(wd{tUTX6DAKs@cXIOBv~!9i{wD@*|kwfX~sjKASrNFGvN zrFc=!0Bb^OhR2f`%hrp2ibv#KUxl)Np1aixD9{^o=)*U%n%rTHX?FSWL^UGpHpY@7 z74U}KoIRwxI#>)Pn4($A`nw1%-D}`sGRZD8Z#lF$6 zOeA5)+W2qvA%m^|$WluUU-O+KtMqd;Pd58?qZj})MbxYGO<{z9U&t4D{S2G>e+J9K ztFZ?}ya>SVOLp9hpW)}G%kTrg*KXXXsLkGdgHb+R-ZXqdkdQC0_)`?6mqo8(EU#d( zy;u&aVPe6C=YgCRPV!mJ6R6kdY*`e+VGM~`VtC>{k27!9vAZT)x2~AiX5|m1Rq}_= z;A9LX^nd$l-9&2%4s~p5r6ad-siV`HtxKF}l&xGSYJmP=z!?Mlwmwef$EQq~7;#OE z)U5eS6dB~~1pkj#9(}T3j!((8Uf%!W49FfUAozijoxInUE7z`~U3Y^}xc3xp){#9D z<^Tz2xw}@o@fdUZ@hnW#dX6gDOj4R8dV}Dw`u!h@*K)-NrxT8%2`T}EvOImNF_N1S zy?uo6_ZS>Qga4Xme3j#aX+1qdFFE{NT0Wfusa$^;eL5xGE_66!5_N8!Z~jCAH2=${ z*goHjl|z|kbmIE{cl-PloSTtD+2=CDm~ZHRgXJ8~1(g4W=1c3=2eF#3tah7ho`zm4 z05P&?nyqq$nC?iJ-nK_iBo=u5l#|Ka3H7{UZ&O`~t-=triw=SE7ynzMAE{Mv-{7E_ zViZtA(0^wD{iCCcg@c{54Ro@U5p1QZq_XlEGtdBAQ9@nT?(zLO0#)q55G8_Ug~Xnu zR-^1~hp|cy&52iogG@o?-^AD8Jb^;@&Ea5jEicDlze6%>?u$-eE};bQ`T6@(bED0J zKYtdc?%9*<<$2LCBzVx9CA4YV|q-qg*-{yQ;|0=KIgI6~z0DKTtajw2Oms3L zn{C%{P`duw!(F@*P)lFy11|Z&x`E2<=$Ln38>UR~z6~za(3r;45kQK_^QTX%!s zNzoIFFH8|Y>YVrUL5#mgA-Jh>j7)n)5}iVM4%_@^GSwEIBA2g-;43* z*)i7u*xc8jo2z8&=8t7qo|B-rsGw)b8UXnu`RgE4u!(J8yIJi(5m3~aYsADcfZ!GG zzqa7p=sg`V_KjiqI*LA-=T;uiNRB;BZZ)~88 z`C%p8%hIev2rxS12@doqsrjgMg3{A&N8A?%Ui5vSHh7!iC^ltF&HqG~;=16=h0{ygy^@HxixUb1XYcR36SB}}o3nxu z_IpEmGh_CK<+sUh@2zbK9MqO!S5cao=8LSQg0Zv4?ju%ww^mvc0WU$q@!oo#2bv24 z+?c}14L2vlDn%Y0!t*z=$*a!`*|uAVu&NO!z_arim$=btpUPR5XGCG0U3YU`v>yMr z^zmTdcEa!APX zYF>^Q-TP11;{VgtMqC}7>B^2gN-3KYl33gS-p%f!X<_Hr?`rG8{jb9jmuQA9U;BeG zHj6Pk(UB5c6zwX%SNi*Py*)gk^?+729$bAN-EUd*RKN7{CM4`Q65a1qF*-QWACA&m zrT)B(M}yih{2r!Tiv5Y&O&=H_OtaHUz96Npo_k0eN|!*s2mLe!Zkuv>^E8Xa43ZwH zOI058AZznYGrRJ+`*GmZzMi6yliFmGMge6^j?|PN%ARns!Eg$ufpcLc#1Ns!1@1 zvC7N8M$mRgnixwEtX{ypBS^n`k@t2cCh#_6L6WtQb8E~*Vu+Rr)YsKZRX~hzLG*BE zaeU#LPo?RLm(Wzltk79Jd1Y$|6aWz1)wf1K1RtqS;qyQMy@H@B805vQ%wfSJB?m&&=^m4i* zYVH`zTTFbFtNFkAI`Khe4e^CdGZw;O0 zqkQe2|NG_y6D%h(|EZNf&77_!NU%0y={^E=*gKGQ=)LdKPM3zUlM@otH2X07Awv8o zY8Y7a1^&Yy%b%m{mNQ5sWNMTIq96Wtr>a(hL>Qi&F(ckgKkyvM0IH<_}v~Fv-GqDapig=3*ZMOx!%cYY)SKzo7ECyem z9Mj3C)tCYM?C9YIlt1?zTJXNOo&oVxu&uXKJs7i+j8p*Qvu2PAnY}b`KStdpi`trk ztAO}T8eOC%x)mu+4ps8sYZ=vYJp16SVWEEgQyFKSfWQ@O5id6GfL`|2<}hMXLPszS zgK>NWOoR zBRyKeUPevpqKKShD|MZ`R;~#PdNMB3LWjqFKNvH9k+;(`;-pyXM55?qaji#nl~K8m z_MifoM*W*X9CQiXAOH{cZcP0;Bn10E1)T@62Um>et2ci!J2$5-_HPy(AGif+BJpJ^ ziHWynC_%-NlrFY+(f7HyVvbDIM$5ci_i3?22ZkF>Y8RPBhgx-7k3M2>6m5R24C|~I z&RPh9xpMGzhN4bii*ryWaN^d(`0 zTOADlU)g`1p+SVMNLztd)c+;XjXox(VHQwqzu>FROvf0`s&|NEv26}(TAe;@=FpZq zaVs6mp>W0rM3Qg*6x5f_bPJd!6dQGmh?&v0rpBNfS$DW-{4L7#_~-eA@7<2BsZV=X zow){3aATmLZOQrs>uzDkXOD=IiX;Ue*B(^4RF%H zeaZ^*MWn4tBDj(wj114r(`)P96EHq4th-;tWiHhkp2rDlrklX}I@ib-nel0slFoQO zOeTc;Rh7sMIebO`1%u)=GlEj+7HU;c|Nj>2j)J-kpR)s3#+9AiB zd$hAk6;3pu9(GCR#)#>aCGPYq%r&i02$0L9=7AlIGYdlUO5%eH&M!ZWD&6^NBAj0Y9ZDcPg@r@8Y&-}e!aq0S(`}NuQ({;aigCPnq75U9cBH&Y7 ze)W0aD>muAepOKgm7uPg3Dz7G%)nEqTUm_&^^3(>+eEI;$ia`m>m0QHEkTt^=cx^JsBC68#H(3zc~Z$E9I)oSrF$3 zUClHXhMBZ|^1ikm3nL$Z@v|JRhud*IhOvx!6X<(YSX(9LG#yYuZeB{=7-MyPF;?_8 zy2i3iVKG2q!=JHN>~!#Bl{cwa6-yB@b<;8LSj}`f9pw7#x3yTD>C=>1S@H)~(n_K4 z2-yr{2?|1b#lS`qG@+823j;&UE5|2+EdU4nVw5=m>o_gj#K>>(*t=xI7{R)lJhLU{ z4IO6!x@1f$aDVIE@1a0lraN9!(j~_uGlks)!&davUFRNYHflp<|ENwAxsp~4Hun$Q z$w>@YzXp#VX~)ZP8`_b_sTg(Gt7?oXJW%^Pf0UW%YM+OGjKS}X`yO~{7WH6nX8S6Z ztl!5AnM2Lo*_}ZLvo%?iV;D2z>#qdpMx*xY2*GGlRzmHCom`VedAoR=(A1nO)Y>;5 zCK-~a;#g5yDgf7_phlkM@)C8s!xOu)N2UnQhif-v5kL$*t=X}L9EyBRq$V(sI{90> z=ghTPGswRVbTW@dS2H|)QYTY&I$ljbpNPTc_T|FEJkSW7MV!JM4I(ksRqQ8)V5>}v z2Sf^Z9_v;dKSp_orZm09jb8;C(vzFFJgoYuWRc|Tt_&3k({wPKiD|*m!+za$(l*!gNRo{xtmqjy1=kGzFkTH=Nc>EL@1Um0BiN1)wBO$i z6rG={bRcT|%A3s3xh!Bw?=L&_-X+6}L9i~xRj2}-)7fsoq0|;;PS%mcn%_#oV#kAp zGw^23c8_0~ ze}v9(p};6HM0+qF5^^>BBEI3d=2DW&O#|(;wg}?3?uO=w+{*)+^l_-gE zSw8GV=4_%U4*OU^hibDV38{Qb7P#Y8zh@BM9pEM_o2FuFc2LWrW2jRRB<+IE)G=Vx zuu?cp2-`hgqlsn|$nx@I%TC!`>bX^G00_oKboOGGXLgyLKXoo$^@L7v;GWqfUFw3< zekKMWo0LR;TaFY}Tt4!O$3MU@pqcw!0w0 zA}SnJ6Lb597|P5W8$OsEHTku2Kw9y4V=hx*K%iSn!#LW9W#~OiWf^dXEP$^2 zaok=UyGwy3GRp)bm6Gqr>8-4h@3=2`Eto2|JE6Sufh?%U6;ut1v1d@#EfcQP2chCt z+mB{Bk5~()7G>wM3KYf7Xh?LGbwg1uWLotmc_}Z_o;XOUDyfU?{9atAT$={v82^w9 z(MW$gINHt4xB3{bdbhRR%T}L?McK?!zkLK3(e>zKyei(yq%Nsijm~LV|9mll-XHavFcc$teX7v);H>=oN-+E_Q{c|! zp
    JV~-9AH}jxf6IF!PxrB9is{_9s@PYth^`pb%DkwghLdAyDREz(csf9)HcVRq z+2Vn~>{(S&_;bq_qA{v7XbU?yR7;~JrLfo;g$Lkm#ufO1P`QW_`zWW+4+7xzQZnO$ z5&GyJs4-VGb5MEDBc5=zxZh9xEVoY(|2yRv&!T7LAlIs@tw+4n?v1T8M>;hBv}2n) zcqi+>M*U@uY>4N3eDSAH2Rg@dsl!1py>kO39GMP#qOHipL~*cCac2_vH^6x@xmO|E zkWeyvl@P$2Iy*mCgVF+b{&|FY*5Ygi8237i)9YW#Fp& z?TJTQW+7U)xCE*`Nsx^yaiJ0KSW}}jc-ub)8Z8x(|K7G>`&l{Y&~W=q#^4Gf{}aJ%6kLXsmv6cr=Hi*uB`V26;dr4C$WrPnHO>g zg1@A%DvIWPDtXzll39kY6#%j;aN7grYJP9AlJgs3FnC?crv$wC7S4_Z?<_s0j;MmE z75yQGul2=bY%`l__1X3jxju2$Ws%hNv75ywfAqjgFO7wFsFDOW^)q2%VIF~WhwEW0 z45z^+r+}sJ{q+>X-w(}OiD(!*&cy4X&yM`!L0Fe+_RUfs@=J{AH#K~gArqT=#DcGE z!FwY(h&+&811rVCVoOuK)Z<-$EX zp`TzcUQC256@YWZ*GkE@P_et4D@qpM92fWA6c$MV=^qTu7&g)U?O~-fUR&xFqNiY1 zRd=|zUs_rmFZhKI|H}dcKhy%Okl(#y#QuMi81zsY56Y@757xBQqDNkd+XhLQhp2BB zBF^aJ__D676wLu|yYo6jNJNw^B+Ce;DYK!f$!dNs1*?D^97u^jKS++7S z5qE%zG#HY-SMUn^_yru=T6v`)CM%K<>_Z>tPe|js`c<|y7?qol&)C=>uLWkg5 zmzNcSAG_sL)E9or;i+O}tY^70@h7+=bG1;YDlX{<4zF_?{)K5B&?^tKZ6<$SD%@>F zY0cl2H7)%zKeDX%Eo7`ky^mzS)s;842cP{_;dzFuyd~Npb4u!bwkkhf8-^C2e3`q8>MuPhgiv0VxHxvrN9_`rJv&GX0fWz-L-Jg^B zrTsm>)-~j0F1sV=^V?UUi{L2cp%YwpvHwwLaSsCIrGI#({{QfbgDxMqR1Z0TcrO*~ z;`z(A$}o+TN+QHHSvsC2`@?YICZ>s8&hY;SmOyF0PKaZIauCMS*cOpAMn@6@g@rZ+ z+GT--(uT6#mL8^*mMf7BE`(AVj?zLY-2$aI%TjtREu}5AWdGlcWLvfz(%wn72tGczwUOgGD3RXpWs%onuMxs9!*D^698AupW z9qTDQu4`!>n|)e35b4t+d(+uOx+>VC#nXCiRex_Fq4fu1f`;C`>g;IuS%6KgEa3NK z<8dsc`?SDP0g~*EC3QU&OZH-QpPowNEUd4rJF9MGAgb@H`mjRGq;?wFRDVQY7mMpm z3yoB7eQ!#O#`XIBDXqU>Pt~tCe{Q#awQI4YOm?Q3muUO6`nZ4^zi5|(wb9R)oyarG?mI|I@A0U!+**&lW7_bYKF2biJ4BDbi~*$h?kQ`rCC(LG-oO(nPxMU zfo#Z#n8t)+3Ph87roL-y2!!U4SEWNCIM16i~-&+f55;kxC2bL$FE@jH{5p$Z8gxOiP%Y`hTTa_!v{AKQz&- ztE+dosg?pN)leO5WpNTS>IKdEEn21zMm&?r28Q52{$e2tGL44^Ys=^?m6p=kOy!gJ zWm*oFGKS@mqj~{|SONA*T2)3XC|J--en+NrnPlNhAmXMqmiXs^*154{EVE{Uc%xqF zrbcQ~sezg;wQkW;dVezGrdC0qf!0|>JG6xErVZ8_?B(25cZrr-sL&=jKwW>zKyYMY zdRn1&@Rid0oIhoRl)+X4)b&e?HUVlOtk^(xldhvgf^7r+@TXa!2`LC9AsB@wEO&eU2mN) z(2^JsyA6qfeOf%LSJx?Y8BU1m=}0P;*H3vVXSjksEcm>#5Xa`}jj5D2fEfH2Xje-M zUYHgYX}1u_p<|fIC+pI5g6KGn%JeZPZ-0!!1})tOab>y=S>3W~x@o{- z6^;@rhHTgRaoor06T(UUbrK4+@5bO?r=!vckDD+nwK+>2{{|{u4N@g}r(r z#3beB`G2`XrO(iR6q2H8yS9v;(z-=*`%fk%CVpj%l#pt?g4*)yP|xS-&NBKOeW5_5 zXkVr;A)BGS=+F;j%O|69F0Lne?{U*t=^g?1HKy7R)R*<>%xD>K zelPqrp$&BF_?^mZ&U<*tWDIuhrw3HJj~--_0)GL8jxYs2@VLev2$;`DG7X6UI9Z)P zq|z`w46OtLJ1=V3U8B%9@FSsRP+Ze)dQ@;zLq|~>(%J5G-n}dRZ6&kyH|cQ!{Vil( zBUvQvj*~0_A1JCtaGZW|?6>KdP}!4A%l>(MnVv>A%d;!|qA>*t&-9-JFU4GZhn`jG z8GrgNsQJ%JSLgNFP`5;(=b+M9GO8cg+ygIz^4i?=eR@IY>IcG?+on?I4+Y47p-DB8 zjrlar)KtoI{#kBcqL&4?ub@Df+zMt*USCD_T8O$J$~oMrC6*TP7j@H5trGV$r0P6I zV7EZ{MWH`5`DrX*wx&`d;C`jjYoc_PMSqNB290QXlRn_4*F{5hBmEE4DHBC$%EsbR zQGb7p;)4MAjY@Bd*2F3L?<8typrrUykb$JXr#}c1|BL*QF|18D{ZTYBZ_=M&Ec6IS ziv{(%>CbeR(9Aog)}hA!xSm1p@K?*ce*-6R%odqGGk?I4@6q3dmHq)4jbw+B?|%#2 zbX;ioJ_tcGO*#d0v?il&mPAi+AKQvsQnPf*?8tX6qfOPsf-ttT+RZX6Dm&RF6beP3 zdotcJDI1Kn7wkq=;Au=BIyoGfXCNVjCKTj+fxU@mxp*d*7aHec0GTUPt`xbN8x%fe zikv87g)u~0cpQaf zd<7Mi9GR0B@*S&l&9pCl-HEaNX?ZY8MoXaYHGDf}733;(88<{E%)< z^k)X#To3=_O2$lKPsc9P-MkDAhJ~{x<=xTJw2aRY5SSZIA6Gij5cFzsGk@S)4@C65 zwN^6CwOI9`5c(3?cqRrH_gSq+ox(wtSBZc-Jr5N%^t3N&WB|TT_i4!i3lxwI=*p)Y zn7fb%HlXhf8OGjhzswj!=Crh~YwQYb+p~UaV@s%YPgiH_);$|Gx3{{v5v?7s<)+cb zxlT0Bb!OwtE!K>gx6c4v^M9mL0F=It*NfQL0J0O$RCpt746=H1pPNG#AZC|Y`SZt( zG`yKMBPV_0I|S?}?$t7GU%;*_39bCGO*x3+R|<=9WNe!8jH- zw5ZJS(k@wws?6w1rejjyZ>08aizReJBo%IRb3b3|VuR6Uo&sL?L5j(isqs%CYe@@b zIID7kF*hyqmy+7D(SPa^xNVm54hVF3{;4I9+mh)F22+_YFP>ux`{F)8l;uRX>1-cH zXqPnGsFRr|UZwJtjG=1x2^l_tF-mS0@sdC38kMi$kDw8W#zceJowZuV=@agQ_#l5w znB`g+sb1mhkrXh$X4y(<-CntwmVwah5#oA_p-U<_5$ zGDc%(b6Z=!QQ%w6YZS&HWovIaN8wMw1B-9N+Vyl=>(yIgy}BrAhpc2}8YL-i*_KY7 ztV+`WKcC?{RKA@t3pu*BtqZJFSd2d)+cc07-Z#4x&7Dnd{yg6)lz@`z%=Sl-`9Z~*io zck_Lshk9JRJs=t>1jmKB~>`6+(J z@(S}J2Q{Q{a-ASTnIViecW(FIagWQ%G41y?zS)gpooM z@c<2$7TykMs4LH*UUYfts(!Ncn`?eZl}f zg)wx@0N0J(X(OJ^=$2()HLn)=Cn~=zx(_9(B@L04%{F_Zn}5!~5Ec5D4ibN6G_AD} zzxY^T_JF##qM8~B%aZ1OC}X^kQu`JDwaRaZnt!YcRrP7fq>eIihJW1UY{Xhkn>NdX zKy|<6-wD*;GtE08sLYryW<-e)?7k;;B>e$u?v!QhU9jPK6*Y$o8{Tl`N`+QvG ze}71rVC)fis9TZ<>EJ2JR`80F^2rkB7dihm$1Ta2bR?&wz>e`)w<4)1{3SfS$uKfV z3R=JT!eY+i7+IIfl3SIgiR|KvBWH*s;OEuF5tq~wLOB^xP_Dc7-BbNjpC|dHYJrZCWj-ucmv4;YS~eN!LvwER`NCd`R4Xh5%zP$V^nU>j zdOkNvbyB_117;mhiTiL_TBcy&Grvl->zO_SlCCX5dFLd`q7x-lBj*&ykj^ zR3@z`y0<8XlBHEhlCk7IV=ofWsuF|d)ECS}qnWf?I#-o~5=JFQM8u+7I!^>dg|wEb zbu4wp#rHGayeYTT>MN+(x3O`nFMpOSERQdpzQv2ui|Z5#Qd zB(+GbXda|>CW55ky@mG13K0wfXAm8yoek3MJG!Hujn$5)Q(6wWb-l4ogu?jj2Q|srw?r z-TG0$OfmDx%(qcX`Fc`D!WS{3dN*V%SZas3$vFXQy98^y3oT~8Yv>$EX0!uiRae?m z_}pvK=rBy5Z_#_!8QEmix_@_*w8E8(2{R5kf^056;GzbLOPr2uqFYaG6Fkrv($n_51%7~QN<>9$WdjE=H}>(a41KM%d2x#e@K3{W|+=-h*mR&2C01e z2sMP;YjU)9h+1kxOKJ+g*W=&D@=$q4jF%@HyRtCwOmEmpS|Rr9V_2br*NOd^ z4LN#oxd5yL=#MPWN{9Vo^X-Wo{a7IF2hvYWB%eUCkAZq+=NQ=iLI9?~@ zr+|ky4Rgm7yEDuc2dIe941~qc8V_$7;?7|XLk6+nbrh}e&Tt20EWZ@dRFDoYbwhkn zjJ$th974Z0F${3wtVLk_Ty;*J-Pi zP0IwrAT!Lj34GcoSB8g?IKPt%!iLD-$s+f_eZg@9q!2Si?`F#fUqY`!{bM0O7V^G%VB|A zyMM>SKNg|KKP}+>>?n6|5MlPK3Vto&;nxppD;yk@z4DXPm0z9hxb+U&Fv4$y&G>q= z799L0$A2&#>CfSgCuu$+9W>s<-&yq3!C{F9N!{d?I|g|+Qd9@*d;GplgY5Fk$LOV+ zoMealKns!!80PWsJ%(}L61B!7l?j1_5P#LRrVv%NBhs{R`;aufHYb&b+mF%A+DGl5 zBemAHtbLFi++KT(wv9*?;awp>ROX~P?e<4#Uf5RKIV{c3NxmUz!LYO#Cxdz*CoRQp zSvX|#NN06=q_eTU5-T!RmUJ?Ht=XQF8t)f+GnY5nY5>-}WLR1+R5pou?l@Y|F@KEX zk=jh-yq=Rn9;riE*;Slo}PfNKhXO#;FrZCf%VZ9h7W z<63YWE^s_SlAVQh6B(En9i<9%4AT|2bTQ4Ph2)pI?f2S`$j?bp`>_3(`Fz&?ig-FJ zoO7KAh@4BDOU>sBXV84Eajr9;>wlbW&OSUt&dug?oAV;`+3oBzpI18%%1wA4blzmb z-{QPYJmn_2-F$A5JI!a8+-p8Bk*^U?^f5j7uZ}jEz0E3;XbahB2iZwS&l4jj4WRS6 z3O&!w=ymQSl~7LUE99noXd2y1)9E>yK`+ouR%sTOQ@Qjt@<;lErGLk1wrw7r zV)M})+amJXs_9hQa++&vrqgU&Xr8T)=G&5Vy6vOnvt37L*nU7&ws&ZO-9`)TGA**t zpby#0X|df;etRud+s~#Y_7zlPZ=_oLg%q&wraF6s>g@;VO#2sUseO=^+3%&Z?61(- z_IKzU`+Kw;Blil&LR#qv&{rzQnG|%i(Q3zLI@gh)2FE^H;~1dx9G|AOj(e%mSwT(C z71Zp!jar*i3S|_ik_3{n0L4KavYWWZ2x3MhyU!66E$h=L+A&-s$9X_w9Q_e;+`-{ZW# z^Zn2H_I~`}!vGeFRRY^DyKK#pORBr{&?X}ut`1a(x__(dt3y_-*Np0pX~q39D{Rns z!iXBWZO~+oZu>($Mrf0rjM>$JZar!n_0_!*e@yT7n=HfVT6#jbYZ0wYEXnTgPDZ0N zVE5?$1-v94G2@1jFyj##-E1Um(naG-8WuGy@rRAg)t9Oe0$RJ3OoWV8X4DXvW+ftx zk%S(O8h?#_3B9-1NHn&@ZAXtr=PXcAATV*GzFBXK>hVb9*`iMM-zvA6RwMH#2^901uxUFh&4fT% zmP?pjNsiRIMD)<6xZyOeThl_DN_ZJ*?KUIHgnx{vz`WKxj&!7HbM8{w?{Rued(M1v zKHsK{_q=YI88@Bf0*RW@cIV@=<{eGsG21xrTrWycT7*KBd!eD2zb1R(O@H~k7>Duv zHPwp=n8;t#1>7~fuM9IaD5w%BpwLtNCe_Sq9eal4oj2DB1#<+(MGR-P&Ig%3t%=!< zS$|KxI1a~an2Q>L$s;1$9nQJal4dk)Box$YsAKgCiEGni##jr|%So6Y4J@pYBF!;~ zhXwpKhc7&QZ$=e~Sb&ABZ4o)&U~N*dSU`2G^eQh-WCe9tA}~Ae369btLlB{GjOKB@yEDH!C7Q&df^#X zi~?{rCuAE|kAjKzt+r#t6s)1h840@A<%i5(O;$Q&tD(opg0)yzgm#=ucf4CSqkqYS zaTdivk5I~#=1Z9K5M*uV6H??6s9*ynT`vzr2@%Tkr4k+Tr_ib40$fPP7$yLA$cwJ@ zF@`94=op)$x^0t+QAsNY$pi!4e7hp~gO=|yD=^8JTvTiC(HAamYEQ}t z+hR~QoKTOz%)IHEg&6iC4vP=3mw&u4wvcSwi$vNBGQE5RoSUs^l+u{A+6s~aMMkXG z+1g4wD8^Y27Oe4f``K{+tm76n(*d6BUA4;pLa26`6RD6?Rq?2K1yMXVAk`&xbks*~{+``Mhg4cQEuw+aM zaI9{}9en8DCh*S9CojIk)qh|k?#iNiCQ}rAmr&iYRJiND ztt+j*c+}Fv&6x&7U~!(Sb1eAz1N@Nf`w?YxGJdhy+seiNNZEYIG1_<^?&pm^P8W?d ze(p@$nWC`Pxqpf8d&AIGNJn#Ty)j z1NbA^Y}pNQ>OfTdiAp+WR>C6390IrFj;YZglitGH8r7(GvVRpWjZd7|r24M{u66B) zs#VS$?R*!1FT&sO-ssvW8s5jh$-O=^9=7^y z75||~QA6zLW}Lu!YOZh1J$j46m zNH|;^a$U_RKgla5h>5(igl^ek(~2nL5a_0}ipvA_Xf0k*E-ExJNld0{LZ;F^DzqAL+IZGJ7<3i1szf zxMRkQ(|@;wj9%I7h{c*{;?g%giylU}Dz{iwb(1vGK<-vlnKs!|Mb9}iTt)Rl&NZka zkkugrMiY(ng3QseY!npaOf1jo3|r35nK+eTYh*`DHabuv@IFy zG7@V!LWE0&)bvqgQ8=-L-(vt#Z-&xaOj3G@Nqw1FfbNQ`!bFEl@z)0)+#Z5e#_hQ|Rd!KrEoRn^aFz zkzYzz%hher>ixcg6fW`=rr>Nx@enQ!sQqYR{<2^|eUfw?e8;B_`T)Kxkp8${U>g?k*VhCd zp^yYLvi}<#5TDjrx@{0U$jx*tQn+mhcXsq2e46a@44^-Sd;C6S2=}sK1LQ_OUhgO` z^4yN+e9Dv9TQ64y1Bw)0i4u)98(^+@R~eUUsG!Ye84 zFa7-?x3cqUXX)$G<2MgYiGWhjq?Q-CE(|sm-68_z>h_O2vME5nX;RodIf)=No(={I z_<&3QJcPg8kAI}_Vd+OH4z{NsFMmjv3;kunMSh94VNnqD?85uOps%nq=q?kU_JT5@ zwih;eQlhxr)7d^K#-~InWlc&<*#?{A(8f^+C_WmRR{B&Yh3pxhLU9-toLz%rCPi}} zE!cw^pQlXB3aACUpacU&ZlBUl(Jo4fxpbDVwDn^m{VG||ar9B)9}@K`(SJxmAWro& z_3yzfUqLoXg`H($!I;FTudPdo6FTJm2@^S|&42H(XbSRW7!)V&=I`{;mWicu@BT7z zQs!)F9t-K|aFaMsoJ_6z-ICrzjW5#yJRs>~)bugki)ST$8T%!D4F@EBliCNSA5!fl zN;OuKbR3m0rj=rrq}5`nq<<%iHIl|euXt6QA}$hFNqV)oR?_Rm4oPnoLy|ru_DQ-= zJTDFa;zjY2p{sg zWqz0I5y>-U{xR1Rl4r{NQ?6Ge&y@N7t~Vsll=-(^?@FF2^Y6JnkbgW==09{7N}eh4 z?h`%x-LM8D}+*41ZA#EG0D9KQjc2#z59Pq zO9u!y^MeiK3jhHB6_epc9Fs0q7m}w4lLmSnf6Gb(F%*XXShZTmYQ1gTje=G?4qg`Z zf*U~;6hT37na-R}qnQiIv@S#+#J6xEf(swOhZ4_JMMMtdob%^9e?s#9@%jc}19Jk8 z4-eKFdIEVQN4T|=j2t&EtMI{9_E$cx)DHN2-1mG28IEdMq557#dRO3U?22M($g zlriC81f!!ELd`)1V?{MBFnGYPgmrGp{4)cn6%<#sg5fMU9E|fi%iTOm9KgiN)zu3o zSD!J}c*e{V&__#si_#}hO9u$51d|3zY5@QM=aUgu9h0?tFMkPm8^?8iLjVN0f)0|R zWazNhlxTrCNF5d_LAD%TwkbkKL>+-8TV4VSawTAw*fNnD^2giQT{goNRR~OwAH5%vorH%=FNNm``;VB z_N`CeB%?_hv?RK-S(>S)VQBau{&NwD>j_ zF-Hwk*KNZb#pqexc5oKPcXjOO*cH#{XIq~NkPxH{TYm*Rtv_hwbV2JZd$e=Z)-pN0 z^PH`XkLz~lpy{|;F6Sq&pjD@}vs!0PGe z6v$ZT%$%iV1Z}J(*k7K8=sNv;I#+Ovvr?~~bXs?u{hF!CQ|_-`Y?!WYn_8|j3&GBu zl|F+DcYh8nxg49<-)ESHyI0Vo;oInYTMcVX9@5;g9>>x1BRMQ@KPJc%Za)^J6|_nr zKQ#*4^Z(G>Pt6Lgrp6!zX?X+rXibm;)WBbN1WBP~{Iw45)a0toTeof%G+Oh5Wryxb zN@p5YCm&YsN!Jd$jG8^|w^_Wo-1ad{*|(#*+kcnS97j-dxV>sGIk+cCchX&K1yxY6 z`dB};!Xf&3!*LyHut$Qlnc5WEME3}4k)j3H$aVHvxg78Y3_E@b3u@5wjX7b zPLz^7h65uMRj8d}5Y1tP55ozK;r0{r?;WHL>g4laujaX3dTd*h+xuy|LOa-f%M7RA zuz#V1WlscYXGzO0Xsu-c>6UPEVQ}o>+w7v~meKw6 zfS|`8k|tL(5VDPt0$*C)(&lVYGnVeCrsb+>%XBrvR5fz~VkMmn-RV#V&X1#`XH?fx zvxb>b_48WV%}uD=X5}V20@O1vluQ2hQ-2>^k+tl+2Al20(<||vxfpIJ~|9`dJ zVH^pxv&RS97h5DqN9ZW4!UT{rMgsH>#tHOouVIW{%W|QnHohN<4ZE5RR@l7FPk$#A zI?0%8pKlXW%QH2&OfWTY{1~5fO3=QyMi3vb*?iSmEU7hC;l7%nHAo*ucA`RmedXLF zXlD(SytNYn`{9Rs;@fw21qcpYFGUH*Xmdk{4fK z0AKh-FGJC#f0Ik!{d{T7B7elr2J8>e z4=VKi^h2D=Q8&0_LHc1j$T9pQ7-FcHxZj3w-{RF}MXBm@?_X&zG?V%-Bet=g# zgEZn=6W?w3jeoQ(!&ECWHqJ zs;lJ@+Tf9MhC9~LX7*WT*0A%cJEpn#(bX;0i-*TF1j2A3zeOFlEi7~=R7B$hpH(7@ zc$q9Z%JU#Am8%BTa1gvUGZPX)hL@#()Y8UP?D?tiCHan51waKUtqypCE-ALn&``k4jkeO@}6ROkhI5oJaRd?*oW z5XmD5>YOZAT4pPd`M`dOKE|;8c#wXMeqKQ__X$u$!F<91^W0T4GtRNpyh;fxIv+8{ zOV!mig|0Jq`E}FfEGH;5uUHx|3whm^-h~cRG|loa&)cs`#D7mW5K(xZ?6+)vAgAZC zD+2J-T)KRUZh~%1{k&VASQx^y`SF+OS6KX4kyjRJJpeT){PgS47=e2L=`KjGaKL_s zUIno%SwM4WAF(xl=4hpof(h_9QEfU}Rt7%rCFq{-h?=0}Z_#HJdX0XYPezSbpFe{d z0C)YJ60>{(bbnZJLT@3P<#<0>aI5md?+Lo2+D-Fke_x?5v0p-So~;%rL+cL|`Xc=y zDo2?BXJ-XJpB{>GjhRUa08Q0fc~|Te5H?$jM>&XZG_?d?@$c3DX04&{U<}^Kj^=z zll8%>K>i=dqr$~=S9jB6O9hsxyPZc556Zw=j_nVDRZX|_LS7YaUr=}9egcpXb&Lyu z)YmbNGJh^0d;nj66%_}BAGOYHUX^~)0N68LkJ^TyJHrdKncoeHWg@5uMJ!*CaF?vi zs}inQ2`7nFmB(0lPrqn_`mS~KaI)&6rO6}?TrFA@(Ja=?UzYTXI{;CnCeCzb>5&FP zU9f&`4m+(A>lG0a8$bbgJoRdhk?tvg@Ikz#RDUy9`Bv_`)Mkhjai_S8ErG{n6Y!ZX zjPs#^rE8v{eXb(WZW}1zS0~dl)qaDzZc6#Eb{ck_GRA z#30&5L=j;Tg=w(=Im_LHt$@}KL1QA*~192~ak5Zap zUm99S=A}`1@@=9=5f6x7EHE6dJZ-x$j_M#N`oWZ#8SoMRTSbJEkaI_E1S`LPb#u`l za~4L#=6*e^6>@H+e`vvSoIfb`u^orz|9^Gmf4h-i>_^V46i#@Dxdo?h3>Vd9UB7Q1 zd*h%uq=*CJ?O?Lm(&(J#sK(r_I|5=@p*QJ8=tPJL3W(!iGFv{}j#xpF;@rMTpd4td z<_1}s1;k09u3T^?RJY`6H5?F+aq(TFbgz!+$2p?$R`cYY_JBwWirgNmvn*Q5HGe{f z-XaT1oDGR#3t6;+$vF}g;7xCzl>r&9Od6(sppYNY?IXMuZ9`V@!`mKeeSE_wM4Gd+URu(#jex(s}ep9w1GC3 z7Kw+jq#o_EXrxGYA1~6D%cM+Ge1B+?9*7ocTWaW4s-L{|jmQn!kxEX{y*KxIy1Xsk zjnC7@NQ-xSD&Z?q_a#!IA$;sPe$gu?Z@nHJio8s36Lg7G@2AP18uG-3n|dSD^zhIP z+Lua-$Q13Lqz^#~2=HF178_n9HXiZ3Ovmd`>ukdKrc^2!X-ZAeBT)7dg@2>+{JWz! z=p-xnDEg15lCRLp=uPi))DZP-pCqq%wfcyWMMo@`orpju`U#jwh%@+&z~1$+@gb_i z)6qj`VXXJU%FkkS64rkme)%TMc?)t4l%`DCsP&j<&wVcTDtWIqWv3~3;0Bqggf}`x z?`&K}p9&;=Aun6(T&k=7S$}GZhkTxv`XW6!32V~_TI%bru-U&74|$7pp-A6@^%t>z zik|j#`C5GOo6l26yv4Vpk#1d>ruU>0Sp1{7@3N40)z%`t|2VeC&_KN}@=GU4?^hP}~YUu?KOKHT)vA#ce-FMp(9pP!wPTFk%# zEwqky;$|C=p1Ezu@6K6!t$>6N_Ie-e^%}k#xcn}ovllZSv|SPDuQ-}tU^i{{+`l1; z+iYOZMxq` zyNmevH37(cCUt;!hJWefMf#0t`kVyL=P%JpzSQp?pS<i{A@amJ0F;?aT#H3gGL(m+ zMd2x(2y7PxEPwgIW>H_-O1kRG@$x~jQ_UiPlcvRrqG+t>u>Js>8_Xp<>`syJiiA&! ztVK|;R}+4AD**Ck_Nds%Xh&S}{}jiCxVtDeH;a2t6-Dft*jg0#%HQsyNF;oXVK{$( zQQY6LPpMO5t9niY*so`U_cqrfS%ttA> zMrrXr{mf-r8(+hNdUxQONMdM>QWS?n{+OpF2q5te-AZ?0^44=hA%DU`#Rc;$`A425WvPKyy?$o4V#Hc#hepIh#q zrzgc`^ts)D{=4V}+2@w~FVe?kpIh#KoUY0~x7_FGtMoP5=a&0# zq5$MRx9AIxXym?ZxgQhVvd=B|)8ZMaXDKe4fFb_31FMfwok)^Lq|q0WrRvD@ZBR=G z2pQ0I&-V@h0C*ge;YJ*jtBNjvYflqF6o%gs=t3z%xd|2&*IQdyR=^LH8WYpRgrrep z4Mx6Aw}fxhSE$jN_`x6Gk20R2MM&C)-R$h{nfE#GnVgwFe}DZ3unAM( z^yK7C>62cU)*<-~eOtHo^)=lJyq4q2*a>{Y3mU}nkX(`x@nlm*hSem0>o7{ZNZ;O< zZbWN(%QigOG8~nI>Q5dw>RYT0OXvK4;<_A&n$p-%65n=wqR{bejviAOu@}cn>s#w3 zqd~{|=TQiObS+3ii(WV`2`mPoZQ7x1xMY3^WvfM@Sq*HPLJh+LQwQ=`ny&P1^Hu$T ztXM-zVD=*VoC&`n>n>@37!?>fN*sy>#GXLvspC8GGlAj!USU^YC|}skAcN~^Xqe0( zjqx#zAj>muU<=IUs~34|v06u2ahGbSeT-uAG|Vv*Bw$#pf8#qXFt zMfw|VuC{UeT)2WpJ6&O+E6jF;;~n9>cf~Ip6j-_@&PGFD0%Vu*QJ@Ht`C7Og!xt#L> zmqlJGEh<%*ATJUmZc(FfNSB##fy_`Y-70r{Iv3jEfR|~Ii!xC44vZ(KNj#>kjsE86 zE3FB*OayD~$|}3Y&(h6^X|1 z(TcJ}8{Ua3yL1loSfg!2gTekntVO7WNyFQCfwF2ti$UvL8C6{{IPBg01XK~$ThIQx z{)~aw>(9F2L#G36*kRDPqA$P*nq=!@bbQ#RzDpVIfYc*x9=}2N^*2z1E%3epP)i30 z>M4^xlbnuWe_MAGRTTb?O*?TCw6v5$6bS)qZqo=w4J~*9i;eVx4NwO!crrOjhE8U( z&P-ZZU9$We^ubqNd73QDTJqqV55D;u{1?`JQre~$mu9WZ%=z|x?{A;q|NiAy0GH5U z*nIM2xww(4aBEe#)zoy#s-^NN%WJl5hX=Oj8cnY%e+ZYt5!@FfY;fPO8p2xj+f6?; zUE_`~@~KwcX!4d}D<7hA<#M$$MY^)MV_$1K4gr3H8yA&|Ten>yr0v!TT@%u$ScDfR zrzVR=Rjj3cjDj)fWv?wQanp7LL)Me^LS6EzBMR%1w^~9L%8&g(G;d3f4uLKFIqs5J zYKSlle?R1Fyx?%RURbI;6jq>Nh+(uYf`e8J=hO2&ZQCoTU^AKRV>_^&!W{P-3%oVM zaQqOcL1!4cYP)vuF~dMQb1#lKj_HWu4TgBXPYuJQYWv&8km~(7Mlh=5I8HE}*mJ#? zmxhx%#+9e>eorO0)eg#m6uhb7G^KSg`Cbxlf9XizZH9>B@hZcqJ*7VTp6)w1tHLB1 z1}(?)MI0$rLIUS0;Z^atECLmzzb6FE#PKdBl;L{}$M%UdWEi4$AS4ew$#8O?ZRr(G z4syuHkcGi8a#*gRz@QP|7R93=j*A$L;eA}9id+JyWjkK`Mod00;{&DlA!QJFR3&lj zf1vI*O1ec{(V=0QA?ELLVls-W``ELsu7M`3`vI4MzhVcpJ!9#^KGjq|#b-J`!F7h$ z{dUEFmBLuMbYu>nV^(S3q+UC;7s@e_qZG#+N=oo0o$G1>6Y0a{9@&9;EU2+8k|7P6 zp?HMh|8#X5UnwpxGbHw;%WXHXn_~8nedvw09V+G$(lhoq7L}=qb+OaPSD&;$TuUtG(4;py( zh)8|Nord(*d1ZH-Dmw1MqU&RKiI)26r-hE(pqnmo4uixe^`qea7(_HA_R2KjdJ4$g!)7ve&Q^b1Tf+{(Vd6vInCd>i725IomG^(Ez(D8L!4qlUAX=)EV9!3JfWLB4n1z)!ums&0UuuVLUH zP)i30*5f6tnvk?lbhL{|8I78X7|_cA3p(L9<~X5y1L3{K8Sf*xL|5gToDT;aYig?m8z^z zQ`XdEMJqC#*O|ho!7x~+MzT<5g$turF~pS;RSY&GR;6TxR)3Q+&%yG`3&ngIwR*qK&t{TERu@0|fDrKKw3=RE&t-)Xh-$i& zl5|>BSn5)z)hg3d?<~8msU=ye>CHWR!9yT;PU|$KP*qADf(V?zj^n^g~nykv^I)Uz3{78Ty81{n~ zZsS&7WH)#Ach3%UyVD1s=Ahvw9*%Wt z<42vTt%|niux3Zww13+oK)-d~G>VKHM0ov>KXKaUH(Cc)#9GFVSc4EoUbnRudxi}T z8J!VNY=4g*Y7C*Ho7#^wUVt&67&ea4^1oBw%@h^ z+YZ+eK^VI5573*KZosq?pMj(u5257?^lBu&LF9`ao`sYf9&zx;uK2iv&$;8{ z4nFUSFF5$3JHFuHORo5YgFkV{CmcNEicdQDvO7NM;484|f=_+6!)x%g1CL;L9DE%% zT=1xaKZ8v-+-@x1OZ;|0_a9J82MFd71j+6K002-1li@}jlN6Rde_awnSQ^R>8l%uQ zO&WF!6qOdxN;eu7Q-nHAUeckHnK(0P3kdECiu+2%6$MdLP?%OK@`LB_gMXCA`(~0R zX;Tm9uJ&d7>n z%9A~GP*{Z zrpyh7B^|a-)|8b<&(!>OhWQ08$LV}WQ`RD4Od8d3O-;%vhK7#W<7u;XvbxQo0JX@f zY(C0RS6^zcd>jo287k@<4tg;k3q5e5hLHE@&4ooC)S|`w7N|jm>3tns$G}U4o!(2g=!}xLHp?+qF zvj$ztd<%96=4tCKGG@ADSX{=mNZ@ho6rr?EOQ1(G2i@2;GXb&S#U3YtCuVwc*4rJc zPm$kZf2+|!X~X6%(QMj{4u)mZOi!(P(dF3hX4ra9l=RKQ$v(kJFS#;ib+z9K^#Gle z6LKa>&4oMFJ4C&NBJ7hhPSIjcOno$M6iq+l;ExpH9rF68@D3-EgCCf}JJSgVPbI1$ z?JjPPX!_88InA}KX&=#cFH#s3Ix<6LeY==wf5DK*jP`hqF%u+|sI)3HfyywfAj=0O zMNUX2pLR;T(8c+$g&}Z#q9L>(D~t~l&X^VFXp@&w92f8tq+KXMZ&o!an%$#uo^hJh z^9-RjEvqE_s%H8{qw(juo4?SC{YhO*`|H*ibxm%ZF6r=2QC)bE`d3oZ(~?;a-(mX)b!|i%p!VVP>DN6tg*Ry97gUPUJj<}OxaYL1nXE}h zxs-O{twImUw z43Eo6nJ4_RTDIQALB8H!3nq37cE6>oNG;jZZhXh!vORPsMKfzJ8_*?O7DfGmcrL8A z(_NAhSH+JE?u?`xR1|ZThDb;2Dt`9hC;UQ%94^20-MA*;<$KO0{3b&9y(ENIe@&xj z6>X23)Ftc?ax=4pL5FZ06CPOjgG%2*lbx;+sVm6EHifaku2RZ6dm2zO1s^4+O| zX?^Rl!e{47y>uJGVh+yEaNe$4U2tTYyJ3nqt9nkQP8+X`9>;yxHT1=;SB4=QU*?nq zndTZfT|OzWa_zE$8FPQtuK2+Z>H-NyCcc=wWX>wq$q7{vij#xqCQBclE;KU_SpRHh zW?)cb0G=uW2QHH@&UKOjUxp5p-v+$&z!*iIUwCrEeC5gh!qSr;%oC7--UiJO%g(@H zgQD=VC|Kd1c_uQ*S7+LyC@PW!E7G5DDhEzd%(QbXn4J;PQoYKo1+C zI4^v%{X#z$(3LimCoU9YO4kMJJG0PS25}<7q9LXMM{Esm6)13%7{fk7Wdx5wm$C1R5emYB+b4!_g{ zCYC2a7ogf;<2t!#hh+G05lGD55CT^#LlBoxIEo9C9q6 zV^AjZEfZsU6$%s=ojiXT+hlLxY4o6EhgiZ7JP-%P5cLSCVgnh(`W^-bB@{)=b3uwG zE!U6%u3dpFT>%EaE{d8bl@K+c6+w`+ju^dTU{F9&yQvzYmVNS(GoZm{D-R;bE=#wApMmV(yJpr(t7y*s2{B8_zE)_ yL|YQw3&NAZiu6_*%Ye#&V4x{Sc^DWpP)tgl235p9dFD!GE+Jk92JyL|;s5}0b2K*q delta 34555 zcmX7vV`H6d(}mmEwr$(CZQE$vU^m*aZQE(=WXEZ2+l}qF_w)XN>&rEBu9;)4>0JOD zo(HR^Mh47P)@z^^pH!4#b(O8!;$>N+S+v5K5f8RrQ+Qv0_oH#e!pI2>yt4ij>fI9l zW&-hsVAQg%dpn3NRy$kb_vbM2sr`>bZ48b35m{D=OqX;p8A${^Dp|W&J5mXvUl#_I zN!~GCBUzj~C%K?<7+UZ_q|L)EGG#_*2Zzko-&Kck)Qd2%CpS3{P1co1?$|Sj1?E;PO z7alI9$X(MDly9AIEZ-vDLhpAKd1x4U#w$OvBtaA{fW9)iD#|AkMrsSaNz(69;h1iM1#_ z?u?O_aKa>vk=j;AR&*V-p3SY`CI}Uo%eRO(Dr-Te<99WQhi>y&l%UiS%W2m(d#woD zW?alFl75!1NiUzVqgqY98fSQNjhX3uZ&orB08Y*DFD;sjIddWoJF;S_@{Lx#SQk+9 zvSQ-620z0D7cy8-u_7u?PqYt?R0m2k%PWj%V(L|MCO(@3%l&pzEy7ijNv(VXU9byn z@6=4zL|qk*7!@QWd9imT9i%y}1#6+%w=s%WmsHbw@{UVc^?nL*GsnACaLnTbr9A>B zK)H-$tB`>jt9LSwaY+4!F1q(YO!E7@?SX3X-Ug4r($QrmJnM8m#;#LN`kE>?<{vbCZbhKOrMpux zTU=02hy${;n&ikcP8PqufhT9nJU>s;dyl;&~|Cs+o{9pCu{cRF+0{iyuH~6=tIZXVd zR~pJBC3Hf-g%Y|bhTuGyd~3-sm}kaX5=T?p$V?48h4{h2;_u{b}8s~Jar{39PnL7DsXpxcX#3zx@f9K zkkrw9s2*>)&=fLY{=xeIYVICff2Id5cc*~l7ztSsU@xuXYdV1(lLGZ5)?mXyIDf1- zA7j3P{C5s?$Y-kg60&XML*y93zrir8CNq*EMx)Kw)XA(N({9t-XAdX;rjxk`OF%4-0x?ne@LlBQMJe5+$Ir{Oj`@#qe+_-z!g5qQ2SxKQy1ex_x^Huj%u+S@EfEPP-70KeL@7@PBfadCUBt%`huTknOCj{ z;v?wZ2&wsL@-iBa(iFd)7duJTY8z-q5^HR-R9d*ex2m^A-~uCvz9B-1C$2xXL#>ow z!O<5&jhbM&@m=l_aW3F>vjJyy27gY}!9PSU3kITbrbs#Gm0gD?~Tub8ZFFK$X?pdv-%EeopaGB#$rDQHELW!8bVt`%?&>0 zrZUQ0!yP(uzVK?jWJ8^n915hO$v1SLV_&$-2y(iDIg}GDFRo!JzQF#gJoWu^UW0#? z*OC-SPMEY!LYYLJM*(Qov{#-t!3Z!CfomqgzFJld>~CTFKGcr^sUai5s-y^vI5K={ z)cmQthQuKS07e8nLfaIYQ5f}PJQqcmokx?%yzFH*`%k}RyXCt1Chfv5KAeMWbq^2MNft;@`hMyhWg50(!jdAn;Jyx4Yt)^^DVCSu?xRu^$*&&=O6#JVShU_N3?D)|$5pyP8A!f)`| z>t0k&S66T*es5(_cs>0F=twYJUrQMqYa2HQvy)d+XW&rai?m;8nW9tL9Ivp9qi2-` zOQM<}D*g`28wJ54H~1U!+)vQh)(cpuf^&8uteU$G{9BUhOL| zBX{5E1**;hlc0ZAi(r@)IK{Y*ro_UL8Ztf8n{Xnwn=s=qH;fxkK+uL zY)0pvf6-iHfX+{F8&6LzG;&d%^5g`_&GEEx0GU=cJM*}RecV-AqHSK@{TMir1jaFf&R{@?|ieOUnmb?lQxCN!GnAqcii9$ z{a!Y{Vfz)xD!m2VfPH=`bk5m6dG{LfgtA4ITT?Sckn<92rt@pG+sk>3UhTQx9ywF3 z=%B0LZN<=6-B4+UbYWxfQUOe8cmEDY3QL$;mOw&X2;q9x9qNz3J97)3^jb zdlzkDYLKm^5?3IV>t3fdWwNpq3qY;hsj=pk9;P!wVmjP|6Dw^ez7_&DH9X33$T=Q{>Nl zv*a*QMM1-2XQ)O=3n@X+RO~S`N13QM81^ZzljPJIFBh%x<~No?@z_&LAl)ap!AflS zb{yFXU(Uw(dw%NR_l7%eN2VVX;^Ln{I1G+yPQr1AY+0MapBnJ3k1>Zdrw^3aUig*! z?xQe8C0LW;EDY(qe_P!Z#Q^jP3u$Z3hQpy^w7?jI;~XTz0ju$DQNc4LUyX}+S5zh> zGkB%~XU+L?3pw&j!i|x6C+RyP+_XYNm9`rtHpqxvoCdV_MXg847oHhYJqO+{t!xxdbsw4Ugn($Cwkm^+36&goy$vkaFs zrH6F29eMPXyoBha7X^b+N*a!>VZ<&Gf3eeE+Bgz7PB-6X7 z_%2M~{sTwC^iQVjH9#fVa3IO6E4b*S%M;#WhHa^L+=DP%arD_`eW5G0<9Tk=Ci?P@ z6tJXhej{ZWF=idj32x7dp{zmQY;;D2*11&-(~wifGXLmD6C-XR=K3c>S^_+x!3OuB z%D&!EOk;V4Sq6eQcE{UEDsPMtED*;qgcJU^UwLwjE-Ww54d73fQ`9Sv%^H>juEKmxN+*aD=0Q+ZFH1_J(*$~9&JyUJ6!>(Nj zi3Z6zWC%Yz0ZjX>thi~rH+lqv<9nkI3?Ghn7@!u3Ef){G(0Pvwnxc&(YeC=Kg2-7z zr>a^@b_QClXs?Obplq@Lq-l5>W);Y^JbCYk^n8G`8PzCH^rnY5Zk-AN6|7Pn=oF(H zxE#8LkI;;}K7I^UK55Z)c=zn7OX_XVgFlEGSO}~H^y|wd7piw*b1$kA!0*X*DQ~O` z*vFvc5Jy7(fFMRq>XA8Tq`E>EF35{?(_;yAdbO8rrmrlb&LceV%;U3haVV}Koh9C| zTZnR0a(*yN^Hp9u*h+eAdn)d}vPCo3k?GCz1w>OOeme(Mbo*A7)*nEmmUt?eN_vA; z=~2}K_}BtDXJM-y5fn^v>QQo+%*FdZQFNz^j&rYhmZHgDA-TH47#Wjn_@iH4?6R{J z%+C8LYIy>{3~A@|y4kN8YZZp72F8F@dOZWp>N0-DyVb4UQd_t^`P)zsCoygL_>>x| z2Hyu7;n(4G&?wCB4YVUIVg0K!CALjRsb}&4aLS|}0t`C}orYqhFe7N~h9XQ_bIW*f zGlDCIE`&wwyFX1U>}g#P0xRRn2q9%FPRfm{-M7;}6cS(V6;kn@6!$y06lO>8AE_!O z{|W{HEAbI0eD$z9tQvWth7y>qpTKQ0$EDsJkQxAaV2+gE28Al8W%t`Pbh zPl#%_S@a^6Y;lH6BfUfZNRKwS#x_keQ`;Rjg@qj zZRwQXZd-rWngbYC}r6X)VCJ-=D54A+81%(L*8?+&r7(wOxDSNn!t(U}!;5|sjq zc5yF5$V!;%C#T+T3*AD+A({T)#p$H_<$nDd#M)KOLbd*KoW~9E19BBd-UwBX1<0h9 z8lNI&7Z_r4bx;`%5&;ky+y7PD9F^;Qk{`J@z!jJKyJ|s@lY^y!r9p^75D)_TJ6S*T zLA7AA*m}Y|5~)-`cyB+lUE9CS_`iB;MM&0fX**f;$n($fQ1_Zo=u>|n~r$HvkOUK(gv_L&@DE0b4#ya{HN)8bNQMl9hCva zi~j0v&plRsp?_zR zA}uI4n;^_Ko5`N-HCw_1BMLd#OAmmIY#ol4M^UjLL-UAat+xA+zxrFqKc@V5Zqan_ z+LoVX-Ub2mT7Dk_ z<+_3?XWBEM84@J_F}FDe-hl@}x@v-s1AR{_YD!_fMgagH6s9uyi6pW3gdhauG>+H? zi<5^{dp*5-9v`|m*ceT&`Hqv77oBQ+Da!=?dDO&9jo;=JkzrQKx^o$RqAgzL{ zjK@n)JW~lzxB>(o(21ibI}i|r3e;17zTjdEl5c`Cn-KAlR7EPp84M@!8~CywES-`mxKJ@Dsf6B18_!XMIq$Q3rTDeIgJ3X zB1)voa#V{iY^ju>*Cdg&UCbx?d3UMArPRHZauE}c@Fdk;z85OcA&Th>ZN%}=VU%3b9={Q(@M4QaeuGE(BbZ{U z?WPDG+sjJSz1OYFpdImKYHUa@ELn%n&PR9&I7B$<-c3e|{tPH*u@hs)Ci>Z@5$M?lP(#d#QIz}~()P7mt`<2PT4oHH}R&#dIx4uq943D8gVbaa2&FygrSk3*whGr~Jn zR4QnS@83UZ_BUGw;?@T zo5jA#potERcBv+dd8V$xTh)COur`TQ^^Yb&cdBcesjHlA3O8SBeKrVj!-D3+_p6%P zP@e{|^-G-C(}g+=bAuAy8)wcS{$XB?I=|r=&=TvbqeyXiuG43RR>R72Ry7d6RS;n^ zO5J-QIc@)sz_l6%Lg5zA8cgNK^GK_b-Z+M{RLYk5=O|6c%!1u6YMm3jJg{TfS*L%2 zA<*7$@wgJ(M*gyTzz8+7{iRP_e~(CCbGB}FN-#`&1ntct@`5gB-u6oUp3#QDxyF8v zOjxr}pS{5RpK1l7+l(bC)0>M;%7L?@6t}S&a zx0gP8^sXi(g2_g8+8-1~hKO;9Nn%_S%9djd*;nCLadHpVx(S0tixw2{Q}vOPCWvZg zjYc6LQ~nIZ*b0m_uN~l{&2df2*ZmBU8dv`#o+^5p>D5l%9@(Y-g%`|$%nQ|SSRm0c zLZV)45DS8d#v(z6gj&6|ay@MP23leodS8-GWIMH8_YCScX#Xr)mbuvXqSHo*)cY9g z#Ea+NvHIA)@`L+)T|f$Etx;-vrE3;Gk^O@IN@1{lpg&XzU5Eh3!w;6l=Q$k|%7nj^ z|HGu}c59-Ilzu^w<93il$cRf@C(4Cr2S!!E&7#)GgUH@py?O;Vl&joXrep=2A|3Vn zH+e$Ctmdy3B^fh%12D$nQk^j|v=>_3JAdKPt2YVusbNW&CL?M*?`K1mK*!&-9Ecp~>V1w{EK(429OT>DJAV21fG z=XP=%m+0vV4LdIi#(~XpaUY$~fQ=xA#5?V%xGRr_|5WWV=uoG_Z&{fae)`2~u{6-p zG>E>8j({w7njU-5Lai|2HhDPntQ(X@yB z9l?NGoKB5N98fWrkdN3g8ox7Vic|gfTF~jIfXkm|9Yuu-p>v3d{5&hC+ZD%mh|_=* zD5v*u(SuLxzX~owH!mJQi%Z=ALvdjyt9U6baVY<88B>{HApAJ~>`buHVGQd%KUu(d z5#{NEKk6Vy08_8*E(?hqZe2L?P2$>!0~26N(rVzB9KbF&JQOIaU{SumX!TsYzR%wB z<5EgJXDJ=1L_SNCNZcBWBNeN+Y`)B%R(wEA?}Wi@mp(jcw9&^1EMSM58?68gwnXF` zzT0_7>)ep%6hid-*DZ42eU)tFcFz7@bo=<~CrLXpNDM}tv*-B(ZF`(9^RiM9W4xC%@ZHv=>w(&~$Wta%)Z;d!{J;e@z zX1Gkw^XrHOfYHR#hAU=G`v43E$Iq}*gwqm@-mPac0HOZ0 zVtfu7>CQYS_F@n6n#CGcC5R%4{+P4m7uVlg3axX}B(_kf((>W?EhIO&rQ{iUO$16X zv{Abj3ZApUrcar7Ck}B1%RvnR%uocMlKsRxV9Qqe^Y_5C$xQW@9QdCcF%W#!zj;!xWc+0#VQ*}u&rJ7)zc+{vpw+nV?{tdd&Xs`NV zKUp|dV98WbWl*_MoyzM0xv8tTNJChwifP!9WM^GD|Mkc75$F;j$K%Y8K@7?uJjq-w zz*|>EH5jH&oTKlIzueAN2926Uo1OryC|CmkyoQZABt#FtHz)QmQvSX35o`f z<^*5XXxexj+Q-a#2h4(?_*|!5Pjph@?Na8Z>K%AAjNr3T!7RN;7c)1SqAJfHY|xAV z1f;p%lSdE8I}E4~tRH(l*rK?OZ>mB4C{3e%E-bUng2ymerg8?M$rXC!D?3O}_mka? zm*Y~JMu+_F7O4T;#nFv)?Ru6 z92r|old*4ZB$*6M40B;V&2w->#>4DEu0;#vHSgXdEzm{+VS48 z7U1tVn#AnQ3z#gP26$!dmS5&JsXsrR>~rWA}%qd{92+j zu+wYAqrJYOA%WC9nZ>BKH&;9vMSW_59z5LtzS4Q@o5vcrWjg+28#&$*8SMYP z!l5=|p@x6YnmNq>23sQ(^du5K)TB&K8t{P`@T4J5cEFL@qwtsCmn~p>>*b=37y!kB zn6x{#KjM{S9O_otGQub*K)iIjtE2NfiV~zD2x{4r)IUD(Y8%r`n;#)ujIrl8Sa+L{ z>ixGoZJ1K@;wTUbRRFgnltN_U*^EOJS zRo4Y+S`cP}e-zNtdl^S5#%oN#HLjmq$W^(Y6=5tM#RBK-M14RO7X(8Gliy3+&9fO; zXn{60%0sWh1_g1Z2r0MuGwSGUE;l4TI*M!$5dm&v9pO7@KlW@j_QboeDd1k9!7S)jIwBza-V#1)(7ht|sjY}a19sO!T z2VEW7nB0!zP=Sx17-6S$r=A)MZikCjlQHE)%_Ka|OY4+jgGOw=I3CM`3ui^=o0p7u z?xujpg#dRVZCg|{%!^DvoR*~;QBH8ia6%4pOh<#t+e_u!8gjuk_Aic=|*H24Yq~Wup1dTRQs0nlZOy+30f16;f7EYh*^*i9hTZ`h`015%{i|4 z?$7qC3&kt#(jI#<76Biz=bl=k=&qyaH>foM#zA7}N`Ji~)-f-t&tR4^do)-5t?Hz_Q+X~S2bZx{t+MEjwy3kGfbv(ij^@;=?H_^FIIu*HP_7mpV)NS{MY-Rr7&rvWo@Wd~{Lt!8|66rq`GdGu% z@<(<7bYcZKCt%_RmTpAjx=TNvdh+ZiLkMN+hT;=tC?%vQQGc7WrCPIYZwYTW`;x|N zrlEz1yf95FiloUU^(onr3A3>+96;;6aL?($@!JwiQ2hO|^i)b4pCJ7-y&a~B#J`#FO!3uBp{5GG*Cni@K85&o0q~6#LtppE&cVY z3Bv{xQ-;i}LN-60B2*1suMd=Fi%Y|7@52axZ|b=Wiwk^5eg{9X4}(q%4D5N5_Gm)` zg~VyFCwfkIKW(@@ZGAlTra6CO$RA_b*yz#){B82N7AYpQ9)sLQfhOAOMUV7$0|d$=_y&jl>va$3u-H z_+H*|UXBPLe%N2Ukwu1*)kt!$Y>(IH3`YbEt; znb1uB*{UgwG{pQnh>h@vyCE!6B~!k}NxEai#iY{$!_w54s5!6jG9%pr=S~3Km^EEA z)sCnnau+ZY)(}IK#(3jGGADw8V7#v~<&y5cF=5_Ypkrs3&7{}%(4KM7) zuSHVqo~g#1kzNwXc39%hL8atpa1Wd#V^uL=W^&E)fvGivt)B!M)?)Y#Ze&zU6O_I?1wj)*M;b*dE zqlcwgX#eVuZj2GKgBu@QB(#LHMd`qk<08i$hG1@g1;zD*#(9PHjVWl*5!;ER{Q#A9 zyQ%fu<$U?dOW=&_#~{nrq{RRyD8upRi}c-m!n)DZw9P>WGs>o1vefI}ujt_`O@l#Z z%xnOt4&e}LlM1-0*dd?|EvrAO-$fX8i{aTP^2wsmSDd!Xc9DxJB=x1}6|yM~QQPbl z0xrJcQNtWHgt*MdGmtj%x6SWYd?uGnrx4{m{6A9bYx`m z$*UAs@9?3s;@Jl19%$!3TxPlCkawEk12FADYJClt0N@O@Pxxhj+Kk(1jK~laR0*KGAc7%C4nI^v2NShTc4#?!p{0@p0T#HSIRndH;#Ts0YECtlSR}~{Uck+keoJq6iH)(Zc~C!fBe2~4(Wd> zR<4I1zMeW$<0xww(@09!l?;oDiq zk8qjS9Lxv$<5m#j(?4VLDgLz;8b$B%XO|9i7^1M;V{aGC#JT)c+L=BgCfO5k>CTlI zOlf~DzcopV29Dajzt*OcYvaUH{UJPaD$;spv%>{y8goE+bDD$~HQbON>W*~JD`;`- zZEcCPSdlCvANe z=?|+e{6AW$f(H;BND>uy1MvQ`pri>SafK5bK!YAE>0URAW9RS8#LWUHBOc&BNQ9T+ zJpg~Eky!u!9WBk)!$Z?!^3M~o_VPERYnk1NmzVYaGH;1h+;st==-;jzF~2LTn+x*k zvywHZg7~=aiJe=OhS@U>1fYGvT1+jsAaiaM;) zay2xsMKhO+FIeK?|K{G4SJOEt*eX?!>K8jpsZWW8c!X|JR#v(1+Ey5NM^TB1n|_40 z@Db2gH}PNT+3YEyqXP8U@)`E|Xat<{K5K;eK7O0yV72m|b!o43!e-!P>iW>7-9HN7 zmmc7)JX0^lPzF#>$#D~nU^3f!~Q zQWly&oZEb1847&czU;dg?=dS>z3lJkADL1innNtE(f?~OxM`%A_PBp?Lj;zDDomdg zn+lVJBnzA5DamDVIk!-AoSMv~QchAOt&5fk#G=s!$FD}9rL0yDjwDkw<9>|UUuyVm z&o7y|6Ut5WI0!G$M?NiMUy%;s3ugPKJU_+B!Z$eMFm}A**6Z8jHg)_qVmzG-uG7bj zfb6twRQ2wVgd)WY00}ux=jqy@YH4ldI*;T^2iAk+@0u`r_Fu(hmc3}!u-Pb>BDIf{ zCNDDv_Ko`U@})TZvuE=#74~E4SUh)<>8kxZ=7`E?#|c zdDKEoHxbEq;VVpkk^b&~>-y`uO~mX=X0bmP!=F1G1YiluyeEg!D*8Fq-h=NyE-2S;^F6j=QMtUzN4oPedvc*q(BCpbg~*As!D@U z3(sz|;Pe1hn08P_cDQ(klZ6 z;P`q(5_V?*kJYBBrA1^yDgJD|)X1FV_*~sO>?8Sy~I9WdK5K8bc7aeNC zDb{Fe>y3N^{mrD1+GyH{F?@9}YQ2Om3t`nt zQ(}MS8M?6Vk>B=*j*yibz6QCdR=ALgTUcKx61){O@1WkPp-v$$4}e#KgK`HG~2@#A?`BF8em`ah6+8hH-DNA2>@02WWk9(fzhL_iz|~H~qEViQ(*{ zV;3tjb<%&r!whm6B`XtWmmrMWi=#ZO&`{h9`->HVxQ)^_oOS{W z!BzVRjdx5@pCXl#87ovlp<^QU;s<*d$)+|vI;Ai(!8Tjll^mi6!o~CpnlgZAK>6=V zm38^kT`D$_$v@UYeFyVhnsMZI1m`E&8<{V07>bBEI1=fg3cji*N?7pBzuamD`X|^^ zm!)2v?s|6T&H-_^y`KM&$!0!9tai9x&)5<(&sY6B`3D{$$KMAX3@&`SW;X0 zB-}obt^I;|#o_bR>eOv?P>=UC6CGTXIM+lSu?Uy+R9~O;q|c2+FafBP;E)B5M9HJgRIpF|GvRi*E+JTBI~T?T*X}r) zefUd*(+3n_YHZZS(g8)+7=pNV9QR^>Qs8t+iEpbJS!9;wio&9rn=19C0G#Ax zM-tWHp_YlJvXWsUqJUr^`OYFA4wkgL`cSOV;w4?tp>GT1jq}-qPoN zp&G}*;+#+Zh&vqDOp>gRL#^O7;s2yWqs+U4_+R4`{l9rEt-ud(kZ*JZm#0M{4K(OH zb<7kgkgbakPE=G&!#cNkvSgpU{KLkc6)dNU$}BQelv+t+gemD5;)F-0(%cjYUFcm{ zxaUt??ycI({X5Gkk@KIR$WCqy4!wkeO_j)?O7=lFL@zJDfz zrJJRDePaPzCAB)hPOL%05T5D*hq|L5-GG&s5sB97pCT23toUrTxRB{!lejfX_xg(y z;VQ+X91I;EUOB;=mTkswkW0~F$ zS%M}ATlKkIg??F?I|%gdYBhU(h$LqkhE!Xx$7kPS{2U4wLujF_4O+d8^ej{ zgSo(;vA)|(KT8R_n_aQ$YqDQaI9Stqi7u=+l~~*u^3-WsfA$=w=VX6H%gf!6X|O#X z*U6Wg#naq%yrf&|`*$O!?cS94GD zk}Gx%{UU!kx|HFb+{f(RA2h+t#A!32`fxL}QlXUM{QF3m&{=7+hz@aXMq*FirZk?W zoQ~ZCOx>S?o>3`+tC&N0x4R`%m)%O$b@BkW;6zE+aBzeYi47~78w$d~uypaV*p$kQ zJf34Q+pp~vg6)yeTT&qWbnR2|SifwK2gA7fzy#W(DyM^bdCjnee42Ws>5mM9W6_`j zC(|n5Fa&=MT$$@?p~)!IlLezYa}=Uw21^Fz-I#?_AOk(7Ttxm;#>RDD_9EloqhvrS z&7fpbd$q_e21Al+bcz|o{(^p}AG>jX0B}ZZRfzk$WLbNLC{y|lZ|&a(=bOE6Mxum{ zM=Nd+-I2A-N&2giWM2oAH`O&QecJn6%uYl0GWlpx&2*)BIfl3h&2E(>#ODt4oG}Dq z__73?sw2-TOWq@d&gmYKdh`a}-_6YQ5```}bEBEmWLj))O z?*eUM4tw0Cwrr+4Ml^9JkKW9e4|_^oal0*sS-u_Xovjo8RJ18x_m7v!j$eR@-{2(Y z?&K4ZR8^T{MGHL#C(+ZAs6&k}r07Xqo1WzaMLo9V;I<9a6jx2wH2qeU?kv25MJxoj zJKzX`Un|;_e&KY%R2jU~<5lm-`$EjIJLDP~11_5?&W#t3I{~+0Ze++pOh2B4c1Mde zSgj$ODQQm7gk&w{wwfE1_@V(g!C=2Hd%Gwj{{-_K4S|nZu+vk}@k(?&13iccsLkQo z_t8#Ah$HVB-MRyzpab*OHOp zl`$tEcUcF9_=3*qh8KTaW$znGztA7Obzb`QW5IQN+8XC=l%+$FVgZ|*XCU?G4w)}! zmEY+2!(!%R5;h`>W(ACqB|7`GTSp4{d)eEC8O)Mhsr$dQG}WVBk$aN1->sTSV7E)K zBqr;^#^bZJJX4E_{9gdPo8e?Ry>ZrE&qM)zF5z20DP0`)IIm_!vm&s2mzl z2;EPI{HgFH-Mp&fIL^6f74>19^>o^AOj`uyL0+Nb##Slvi9K4LQSs>f+$j?cn9Z__C zAkyZ9C;#uRi3cDYoTA>AT<|*pt{K70oZKG*S1F$r?KE=$4~W3!u53yUvh~(kMrClS zXC?Dmgv4iS`>~wBPJJFL_C8x2tEg*PCDX2=rHQ@z+Zs)Kkr;FYG`GnbUXqdipzvHE z1aZ>G6|e`}Q#)Kru0)(SZnUCN#dN2H zd1}r&xGsaAeEed9#?|0HzMGA7pl2=aehy_zsRV8RKV6+^I8woDd%4J8v9hs$x{ zl*V61wSumovRVWtetd1eJ%i^#z`_~~^B;aeuD`6LgHL66F0b^G5@om^&_3REtGmhz z%j^9{U`BH7-~P_>c_yu9sE+kk)|2`C)-ygYhR?g~gH`OK@JFAGg0O)ng-JzSZMjw< z2f&vA7@qAhrVyoz64A!JaTVa>jb5=I0cbRuTv;gMF@4bX3DVV#!VWZEo>PWHeMQtU!!7ptMzb{H ze`E4ZG!rr4A8>j2AK(A0Vh6mNY0|*1BbLhs4?>jmi6fRaQwed-Z?0d=eT@Hg zLS(%af5#q%h@txY2KaYmJBu>}ZESUv-G02~cJ-(ADz6u8rLVECbAR7+KV~a!DI83H zd!Z(Ekz%vjA-|%4-YpgfymMzxm_RjZg%ruo zT4^x)f*%Ufvg_n`&55cK;~QChP6~Fy_Z67HA`UtdW)@$Xk-2+|opk6A@y0~3Qb;V% z%+B@ArKl|Q^DJW&xuBZD#~SurH7XXf*uE0@|ccNd&MA%Ts*1 zg7TU!xY}~*AOY+tAnFR(Fu)e@^9V!Rm65$;G$-?6e%7w7p9WT098%-R?u#J+zLot@ z4H7R>G8;q~_^uxC_Z=-548YRA`r`CsPDL!^$v0Yy<^M=Jryxz5ZVR_<+qP}nwrxzi z-)Y;nZQHhO+db{>IrD$#DkHP%swyKhV(qn`H9~3h0Bd33H*DAP0S!ypZqPF^1^tZJ z{z;HN?$WJ5{0jQNzYOc|KbJ(Pr42~YhW5ohNdY*rEk=({8q+F}hy)&ziN(@q1;>jL zBN<9(k1N!p2D%uHF0NxFut`XwEMc@ZH-|95>U)PY@}C=bmV_*dakL}J5DUpNZi-y& z+{i0>H@c-g|DBO)HJ>7$VVtn)z3X}H`FuN-t>gcqLas?Lk@MJb5?u@BTn0Q}E(}S~ zXrNX`ysRv*iOn1v@fBDeSDvvR>+;o>kj ztRqEZOWN!fqp(`XQ3ppvC)c{AeyS6b_8pN1M*~0=$U;P31!~Px`Obrz;GNs(8RrJvONy<{Dk1x0z zJJzhQBt{J@&DP6cHugB!q?xi~O`yJYHUsTI zmgulx%I<*?vPSl(!tj;LL$K*k zH(*d31iyB9aYAzw49W&qDi0>f;b5kA31nz(%2W`QFJqaX0&hM`KP1gfdRw?7@}$XB z!^cUI%C!?X!QVQxbqEFSbuP0>_3MTCof6!e4LMAfGRd0;Lt+w0WK@b4EkGHRqX!h{ zrYxwwH&-fM67X7zP&Qpup&vAOaKH|S*pcbI{ksFg@tfw)paaK)5khkys0GSTnAtfC z{mVJkCXt|G-SYwt0O4dM8Hf{L*&^nOeQ271ECyc5Y&z5R0%hCq6~} z$XW$kcz!nnCTAl}NyB0#ikwyg_M};inG%*x38`EYJ%FXdj&A`g)-wJ(R=C`O^r{W` z8$1r{G0X4g`uD+}vw4`H5!*B8TTsmeaYGk3x0{&aar7ocO6?dlGbyV480<#{%^93y zF(ei<%{OYi?n?L9#HL_R-00#zRzbbwVnJ0zt}4f|KNBkT6&=Kb=$E(@aC03vU~p)7$XA@ zq5*`*4Y&u*=Ju>+x}q&Xxsjn;Dd)6Otudner9zi z<*LpeG}*vJ58#P4|qXF-ul1|u*;=-@oGPtmBnQW6VY9(s`5GMsO@!;s_PKo_? z3HbGokZ|vaAA-guf5W0JDwpV}1u8;7XJ=wD;NgcLIJW8S5w!c%O*zU0%~)0M)`!Al-+OFsmPW1zniB%fqF;klqxz`Y z2@srWa3e?B3ot|nhE|Q7VIjr+$D7F^n?wm5g8w?Ro0i72K3u^g)&&F^9~@eHd33YY z9LR!!orc0vq$sd~eR~hW{4?R3Di;~mz{^G1X?#-!|Cli(#0-sm|GHYpcab`ZA=zi3 z5*m>sJyOij{!PgIJa?A0%wL*Ur1fLJdJW$a>&Xj5p_IO=SwyTp@nn&@6L4vIfT79aPyo{LQ4DhIz1 z5g*+hII!(cLGHc5ROH&^^o=02r*x>MxMPx{JFMmNvzJ?AI8p!u_H8L1a`{6~bF@L* zxszth=`>%Vi`=E{jJKd-+6pf^vo93EzqFfTcr)A&V{rERu__UAQVyE1imol78AFmB z7T;pNFxW^M+O3#;Tz^e*`AqsD?M*wPT6pnBFPA^kOTnZYHr@O(JUQ^#6bD&CC*?HG zRAKSXYv9DU)L{V(wM=te@V@Db3}97Sn9r2nroOz06!qV=)+%EKB^MR_K}p$zM5OD1 zzhYv+?%A`7dBrU(#&1hXF;7lzH`nENZKP2I{qp^NxBA8~N>?1H@uZ~Do{d+|KYx9I z_z)J7O(;xu0%0n3o4y7LnJKRPK?RV@_v_YLogYPH;}`>cZmDVyO#%-IMQVq6z9r>@ z?*AQC$=?|aqrY8xGx%vfk0ZeByTz18IrP0XTVlJyRx5!NALYPyjcn|)U5jl^<)_KZ z2C?1|dkBZ;h8e#)3gUPfdf80xu^8evspE%Xf~x zs%phX&YuB{y}>%PuOG>s&EW}5Y0`dyseV)!C|`1(U{Nd4c4>07ZFmdTJS2T3+dEw8 zK%f_x!O?H8+_Qd>$DsYNY!?tC^H;N+!fQS{!4-9c^;uXx)D3|joo_FlBTTdDM4nx{ zPve})D_u{PG>&^G=>$2N-dZ!eMx?9X7FmPNo)7|>Z|A-mNZ0{+884L6=f-{Q4bN3y zAWL{oJIh(js2$bDTaV&bh4Fn=4^M?@N~+$IXxytdnI4{RkYA$8j(}sb2TO$~49JHz z0$K$WB@axSqKsyG>m7&3IVR+?xXLfs7ytuJHH8{`ewhkH;?H7#an)*hPiBLi22jAI z{|tZ;dU=nDUVyfIurEm0VoB6kiaK#ju6RV?{3qaV`NQ4&$)fc4AAVKiXu_1$86nxh zX)Mif*|y>N;S~7UCXQhs3-%nqNuTu>=8wqtp$-#tC?bwc-{&k&0>0nRBku-b5X931zqll&%fn$1$->@El+EIA;L zfEYJY)kaTI%H z{A%hpZ?Xt=;#(++B0e)B>4_a3E7h#8upWz!G;VQBX0rjzKvy9N2LECS2@wrBoS;4G z1PgI50DD!wtwsZ&JoAGuum9s&+0NI&_n}!kUTvpD{tyG9jlSXyQ)m9H8VXoDY$j!w zo;imjJKl;E5u|n4Q?HQsy`*&=VY`SG+YFUqG*+;A9(wKfm_|6^SWh_6>1u63)H3zEGm5Uk)#z>J0XC1L+&pzieqnAo+7zlr$M4kl;-h zjo^h7U5Y3tbY@(_{#h1et^{nbOP9Nw*tJOD;WejSG-4d{(2X$tDM@-rK8SbUqMe}%IPqxOV}m#%mq0)auvNwT2R9)$1-o(2o zpIS;qwy8m^tEBC99O}bYKd7ALbB~$d<=eGd>WML+U0aAl>{Uc8CB|oVWMt zbPe9+6&V{l2Th1)Jx`K64?gUC_<>x#Wk*SOSA<&A=j2q zo_M`Lznpsg1h-W546hm(q@Rf=xL@w5QJ;HxIp?O`;sOMovgc4n%D5`kiDO6%Rhe2^ zzPa=8pd(2&HN-=5JzsiJ^(ZlLVpZD^5!$(rt0PVLQCzh7s#6_N1dRKtQv_vTgSQT5 z63+e@K`67zjbb@QdwMNF8G29tcxAl36SZAGxolCj9aS%>(Tl*6a0eW@3j4!&d!12v z%+~Xc=>VJqBcW!D#JX3#yk4O^;#|O3!ol;J%t8>wc!*6`+`~%?-QE_M{wa&vg14R~ z(M1VT-&l-M(N1>3pNjVfvCIk}d|H4&*7{*8!W-;^tFgD31O%~NtUaK_*-m7CSEt}T zm^Z02X#cQ$Mcw}TG{>1I`vmvNoxujnPra4aSwP55x37=0VvyV<)68QB-b$o-h7p*V z#QQ8?A7`=m`*+dTfYdm=;i1ptR|In}rUF^r&{bKbI@5DT$JEo;?-N}Z13}n16v?G2 z{?@ny^7|!rg(on8b97#GupiPA<(g=o;@P`4 zEx06)SiGKkIKFHzK1M`ctf?vQV#b-{ws=+0U^*LYoTK*pu;A#NB$$I=Tv{LLVQin~ z@aGTp?J<(c_1M!Jr8MK;XA8fcB+*DkFF@oAhQ=B1o*$<@;ZdGs_5O!BKi8XjF2L4n zA&(?SaRDWm+p0UTFXj1prs!*v$(q+s=8S1h(*H8pd5*8%HGN0mgw3yvfsxr4QYT)o zzdjal^6zA56|Z@csYH^3Qr2~ZR#p|Huuh0Yt|$~>oQZJDF75aeH%UlQv)fQ=3P{i1 zRt99gL`$b61Q`pdos?W6yd&%2IWK#}$wWOa9wJW&($J4h0M|9sFtQu9k)ZtYEQ#vu zS+uD(3`7T~t?I;f%z8N~nG&FVwxGXrTL!k9s#LB}FSo;a+V-j}H^myGwQq@jTIycD zP5A{w+a;^kOQW^C%9W{j^&o@)3!v~U(?wx42E5G*bd82&a1p6ax|pk)#8nG9risCw zOERH8;tq?Q4ymxf*9_aF-sTpLvETwD#sB#ID1D+WohEt0s557Ij5)ldexY+diQJ*l ziBo;1v*vx(F|lI8udAo450QIQTmPqf(7oULr5*0dE9i>i#D&k%WyfM*4{*?_%9k>g zg1_1%x?#`Xm7M@YZ?!zJs$AxS&8sBLI@c|-vSiG<*OZyw>CL*p6#N~p z#VywqpWdZ;{ylc5d7W8E7Jx_H+5e#N$h#{ni@#TlGqz`yah-qCC_;P8?N*>CPJ03b ze(YVDvbIR$#lJEkuf}L7F8q$fKCWz&>{uFg9JgTOmA*Rux-{|#+pO`!s!!4;PlE%9ys+;|)oK%&V$*FH!G2%|y(zz>X zUwdXer0HIIJkelANg_W!ofsyiN{zi2=}G1UL{`V81}1D1Sz zviLV^w-$RE9fE4@H+ys>u;OY!sgqe&V-oFE9Fn$P9HbpOI{}esLIvc zV5S-9(XjFzn1qzo2owwg_d%7_)cR*!d&%@S&D($cFFMXXd!GdUxw5tZ_W@zRbjVfU zzx13(Hc!$teqA2WOYo^+SHpRz16DOcYqaXHSMZl2Ax$)f^WC??al8lfX9)O_p9#Ml}LB(N8yJ! zj&_UD9K54Rt#yqvhklEMZ3bRC&)(^h`#kzq-#_QN?J6eLT$ zMWG-mP;HkB@5;2*lAP&1*4C)HWEs{gtp15Y%y|*%(3UOMu*v4kTi0@pWvg2Y%7yI* z%XNlZa$@AZ(Z#Elv`5MUei~VFCjF8El)@g&>(v;E; z;laavf&ANfk9*0LA@oP4QmbCBF-lB^Mj~wo)eGG57gqAKC>Hd80Eb+7b;iJzV5RsL z8>ddQH8PnC;l{M(t4c$M=q78GW6=*d#c`-jK$q#-{9c)UNO4eLm9c!DWcCth4O-FU zboSKPhL-lq3q<)m8Xw7+l=Z)H=rGgMI0H?KrPjc;iDzY5g|Ve$8?SE`8*sb1u*>dm zD~f9~j2H~6Oo2`_1 zq@_mmUbFQV25E7XJ)zBRQktT12@qHHy-@aCdAFWv4iZVN0B3}E;k(jg>X|eqOrqgM z4yBUuA*BHdnN9v;5>3#L$NFREyHW&Q*rWYa_q zhC~>M&bMFgXC6AeQ`P-s<}Ot_x^cb51r7ArPbRRs&Dd_TEeugnjR(O#V5i6OYjzRF zw1@Rvo;_wEfQA@P%I^9ljrhxxuqf9g^cWSKq~+kiVxa`&EBDqmB=C1G+XB7`TQeiV zR_k?`$&W&+ntIPeEtM9hqcj|yfW>x7&1Ht1@;!d#Wo%1hO+^Q{E?VD|`-OvV9G?tp;6{sI%L-u)Hw z;|`uN6~VqZ!g~K#B@W7?wDcbO?XS4hnW9kS1Hbi=U_m*~7`N~3oK;qFTX$$LQ#CkL z6I?a(HkF8SKJU8mT{K35ekfP3`05!M{gmrV0E-=IyqP=N;K<&jOnPcjdXrbk$%)z9cUe|#I0unK5^+qGx8#2 zz_!bmzVG*Uat*&f4P>&sV2RswlITV}wPz?_;(S;19}e}54fP|K5l_c2kU5(-Zh!7t zz=B2HktD~ap{s%*CDEl?x6o+91T-xH895-S1}M=*KhFM7Nm&1$OB++Robv0T`OBcJ zXNX%Xio0_ryjr)!Osc7au35UM`B}Ru4zN_o+C!+s&e7|}Zc;5?whP$@J@DE`>w-XH zlVmbrI4|-Z^2^I^EzuYKD+JA@8lx%>aLFZq7KT1~lAu}8cj$<-JJ4ljkcSA;{PNr)d-6P5Z!6Q=t!t*8%X)a|;_92=XXN=WMV))*gWR-wHzU(G6FPTfSjd9) zm8e1mfj4qFmlXO*a3};$&jgc$nfG>NR&iao(jYk`%E75h=K~dJ{Jqs%UH|aGHL8)-1MOyS2B?OJsyeA_YbGMDpE+>=NFcyoI;N z>1>3G4QR2~EP{L{x2e@E1U0jGGV5H$aeigDq&Dr zQ3FwJ+& zndX7VK+XD)t06uUY=)Cfo!ke%uDpOmq^bpEB`iv6(CKTGgEZUi4ddfNXJi_z4;)ob z?R+qj2SYX*zi8z=DXChEEDW+Cy>w-0agE|A7MoRJ4}-(|go-rP#sr%a(5k%wV z&Jllj+6XuSoIfZX9|mK!bbd)7TuaHBvoa(`9C$*XUh}hH1;Q7cTJQR)c>h}Hfr$aS z64c7#D^f{mN3s#2=SEf1$(*Vj{vZjF6Qc{a=VbTske7L^EY&A1I1sgXaYSH7(lF1V zZ<7`Rq33WZuu`!HK$wRr1=uE}#&JMftnZ&(P17gWF;>$TA&$ZQnIz>blTrW@49Z&H9yhgLBpFw(57K1dbIQW4fn1X(IiFWEKmPzV8gAa|ak)HAsmcQ7stP|q0hEzBNL=4YdXEkyfS zF+K+CVB#~(qd7eeZqR-VKIYJVmK2ePk``4I^PfQ*C7NUR z`w9lb?iHv2$4_p-+a+O}Fq6SnPiz>aV!~d=l3VdgDuwAPMR9eR`)b_`lg~{oX0lf1(zbBrnj4+-q zOl^#`)XKn=`()B-jExviKVTYrAKa27KAg3cboG+}D6*R;<`GC-b?i=e;aV7n(}XDS zK5xAEV=T^r#eThV+3C<^H>SuvAP&fw;Yn67eY%4=Y(p$~!`~h12 zQHM|f0#pQP_s$Q+TtMMvBdjQbLWw9cW?gl_+P z)2T94UJaYG2!yXITYjYl-@#5_47g{N|5=P~m|e}-F)*^L+{7O$#wv2e##5Y=A{>jN z6NhQSor9ulwP3gfxTF?V`P7AJ#E)ij$I`gc2fnmp&9w6qS2-Ct}6 z$#O%mKtP>I2VUBMt^Xm3LjP*D=xEyV?|8Psb91ZEj=gM(C3^Kcfvbx*$NK+MhP>W;OneZ{Q>eFEmxv}%ZCJ32=zr_OZd>6~v@ z6+3JzX%9qOvKS393r&R9O+te&#?{Q9nLkOV-eLg9!{WK}WyUWLZ7bQ5u26*u9c*T1 z_s1)j1k5&b8&5@YnmtS{tsmQaLW2%8D*8G-9w#PcVQh6sQY`!tBpU=8EZR!zfB{f{ za<+Err#ZNM4JEx5n9!zuC#KmeI*%tRXP}jpswzymT7J{YpXdzA{J7K)j1tBF8B3DL zZXkec{`rT_{__t_`!E7veO1rg1tFzVeUTBjut*3ZOq}A$r%sWXn4v4|rA+7uMvy9n zL~2WHKLg$BeD2Wq%?frTUM^c}?K?3#L+Q2-?PR+e1Fn-XUThl8^}8JOyDZz-wcFh5 zYJCJ%J_Pf~bX(0A?Z4hGw(mY?J$j#Vo&@9O>in*f)*`H6&(Z-5xx5}$V@dR)-lxgN z=DMA_EJO4+^w_+D7N>4=%{6AbvpDG<(b)xE5Ezo~oEg~cEM?mwyY?3ZtFE;RyDS`u z(^sa_s%B<)vktqh=1|?Uv6DXsA`D^B9%_mXqx1C=a#KurOE?49)P_ixiHAA)D)oqEjQ6_v0UC9mTtMu&kf8&7uRiiigPD{$Cf(&DuOj0 zr*5{zPyO@Kq(|Ttu@wxKanV=^OPOjh-_$MbNz})ou6*9nq_XQo86WJ@JN~-b=Ln_8>Nz_ZS#QpRGt+bzH*-;{#x7PFqie+ z7p5e})fcDq)J2z=z~%nrFGFjbVu~0ICDHW3=HgtCW)?Z(%Cx$z!QuszcOCe&3!Al2 z`793RnB{Jj4QpQ2N#oKT>aY~aNxz_6B2&vPdJadbC4qp#H^<@o50}m>7WR?NO0$ZI z9OKTM+jxMFWX9mi7(@j)1Ji6~?HLU!KT0Y5a^-?|XH^B?R@T zn&a_U_XFAsGrNX@S~g1<=uz@~dCcZO=1??VC@PML{g}lbuN?j|_1S=dJgbT~o}}hs zP_uYZ&0+mWY1fupe(+6nn6<9-)Xluk97yX-!!lqSXq~!kL-=+4$Dy>O$sKO7M^1QY zhZGZfiNQu+?sef?E>5sqj$kHmf;kMv<>Gu)!^4!#7T009vBzq(m2aoHu#+93HBq7T z;Fs8IHvUlmxCB2hkDbm&xwFQcXUD_&sdeu|EYhFpf7v5_LCcVua9aunVe)qoGmyg# zIGlj&IrLKg=id@t7s916d&Gf(%X7^FFR9^bz-;*o1~Sa=`cKfJ0i}X+pBKN=?}!dP zg`ZMtP6xSuvHb=5HYH%ELaGxwqH{ zpY>Ic^}J!OwM!VmNM!$nUg$qN9DLtKuBvn1(x-P+tA*UHoOc727>5?^J;JFo_ac@) zU57%w^U2ME z@z^ZsB!AhyOscE8;~Ft$)NL)GcLteq4d32fw??L0QuWt_M9IJMgZ71Jm%2khx|QN+ zkm4zQ@OjyM+l=Rv(!k?%cYwnf7HWs^M+P^zo5o?7;E)V0v*zf}(;?ms0oUK)wKmZY)mSTGN4X@2=ZU!Gy73M(ftmHJHLFKQDcu`d% zeqiW{G`?}AtEP zKCnHuWzXZ_Hc>{cP@h~M$#q}kG{52%zmhATR3AbNGR~*6(%^Gs@UZ3i%7%PJ1mB^S zcdcrFDbD6lEJGZ4k6JT;eB_JbgIkkOqkz0I{q`d^kWl6a!%w4V?Y!;8%uU(-UA4Ti z{pv2+5CN^ba{ALpu1&qm`sMP@_L=-a)@-zC1*`f)uV5MU$xJj51%?S^ zoo@;kqY@4Zw0B!+hIvTT8KK*~9H@u54r>s{MX_|#z`Z$55bDJo#=hz~k)7CTbf>Gn z=!u;@JViT~(>P7UDdIOL;6kPDzOZNl16jLo5tHS4a%~T&AlicnCwZ5pZ;+WIB3tJE zv|J^!X0Kb|8njISx#zoB(Pv#!6=D}Uq(6Dg*ll##3kfDxdHdBXN*8dZOM0I{eLTO4 z=L}zF35GJX4Wee`#h=aCB+ZV0xcaZiLCH3bOFYTmEn0qf?uC#lOPC7>+nVeO1KQ@S zcZ5Z0gfk8hH03QrC@NnEKNi15bWP;FEKsGi0iUHN4L&2_auv%tIM}UFfgRyp5HWt()pn#0P9+xF2H!8zMqf`WJ*9YB zq~m+%xLtVjza4>CO4*%thB2k;Gv1Ani%8)IP6Pm^BAigXgOUHWcQDEgB??AtdsOx5 z+pXKfU4>+8ViRUJ;h()e88jRLEzSN7%O|=MovCW3@VxK@Z*xS$WLG=u_Nenb0wP@Y z6zs##uQ7oFvcSdh5?6kZ!%8l$Xuz^Rc!lv4q?e$mv(=#@x)s_VFF50vGuE_Nr{4zXB>y?7FOMC5^sBZr`mS*t_@%LYN9wl z+lsqD#V5JR63GEr9^&9*f)kFs zJ-A(>>!h~d0%9*wd+AY+&oryzurfV{QP{&-AtDs}#iq;dal?A9jE;huq2gExb3z+- zVQB@UHlVfsy1$)dF`dcZuc(GLnim09jrI9nJ6<#=03FVrkuINg2`RTPloS^^@KYD6 z1-C-Oj2OI0y9Tdx>=dNHhOYVvx!J#4EMhold-PGClLuLA~k2VDl6cPuV4lI5c(w9@7sllth~H@)0+v~XYqqC6&*fSX~S4Bii^0& z=M)D(5FoZsKxB&M$J_7lbS>$kF=@B|Z$#D|LHJQIr$aO51ta6s96Ug*Jk;|>9Yd$! zoF2W+)lFzY)J<>U$PHwbe9>BKLAeo~e%=Qy#qhvK&`)b2 z(U9#8bba`eGr9tr$SvM4`y`lLavOzPm`l<%-(R<1urb(AX0RE=R=#&QI)klkwrJ5%D5YHZ!~s zGwK?zKZeX|uO*Y|xLjO#6uzO%iXWsSE8#zLOWc! z&2L8sdT;bhUW495)_fGCcOLM-@DfGcb1xjf(ezYJxYOv<7YE$lBCrkbfBA{`I(GH- z(yHy1h=bg~fE$aIbB_3l`|p$R_p0b(+aL(~b<-Am9H@?s!T2*7{+*Vj?pCpV5&WJO z*GbW%PLj|(hbd!fQK5Y-kgDHV!-I$y6G>Y|&uo9+79v}}$s=l$>#F-_F{TjUn~-!M zBN>n)@(LkzI0Sg?f1s}uBZi`wRB}ywU7wqq-PwaS%3nitaXb{&Q=x!xvOPfiQmmkd zWpe2@y7?wbI;hF|hlqf@x+3@a4$wLdJ1PZBoRc9oRGgdM+vm*;5XBZcMZ+@4_{aPUS|`NsD4YP2JUM zZEvA&!QLB$K*%gHy~y-RVs-C zkN^usP)S1pZXjj)nugy#?&vpiE^DS|QlhiBOc?nC$9CK}Ze)ihI{p-m$pgYV^5L~B zQTU>)x*fvKCNK*9j$@Gyt@@I2LF8c7YvDJDCf%1h0zVyNg7E~R$`6JE1EQk~-c1xG zE@xT)TesWHs}ny!5_7F_AyGL9K?Q~mP?>Vs!(oWZR42kf?*iTV*h5>tnzpljZL8IR zb7}l8q%Ckfh{^e3k^3pQMk=gLu60`Ja8HdkzVbeAU*exs*ajmRVp}O}l)TqX!?G7e z{4-~g?Gq%~)IJJ7p1k*WSnL3jqECe1OU}5nirS66_-$3FzMT5t3X zg{jgP^5?%zb(vMa!S|1cOYk4W!vG2KKd{YFIbPCk3_74HL`fWJASs{fxpzY@$(}Q- zK5I4TKS~`mfiDoDOm;XycF6mi|K|+d=lh=@U?9_V)BDDaZAnEw43`Ls1677I-+uFi zG?^$Fbc*pPun65{D!fH=3Oyp$WZAY!{JhzaUtIgYCWXf@)AkTa@x4xGjp0c zs7@JB012~&;z=SMbCp8d=Ga{l0(iwx<@o(f!OwmyH-gBN6wewq7A_h)oKg)koFPft zNfdie%F63S?rGDQR(N=bPuK>G0t^ax$0P8`N_cvR8rOf(O9T7$9#5!B;#!XUpLZXu z5C(OESAmE*2+hV}!bg$4K%`cQHBk!>##tW>1RbC%am`*|5IbvoLh!BqpAi2OmdXqf zHp%|!N;d!LN_26809n^14YVJJBe7aL87U~>HZ)VK%d|rZp(~zwNH#VGuX!vfal&Vv z-c)h33DOB@xl*~m5ZZ22sVRK>8I9+)QMVtsAB>r~SMkGMZaQ;Xi|?~Xxnmx;cYwYx z^nNxRxGcq7I!sO#b%$!0vQ(OqXm6T4mTilvMlYj|*i|=MK%kT2df;bZGW@NrgeX>( zf7eBsjJv}pNuEuHPEs42>}a`ut-O9lZDNh)_CsBpeHKvPKnpcWh^bC2QtnB5a4qy) zSrZhafuAkk5{yiM|zdiecKh zuc2R;6^;@i07fmepeofAJdX*knDzBA{3tyVYu6z#z;Lsi&x_bzzLEpfXtH*NrY_G`= z^X!;eI#hV*mmjjEOlo{TxQwSdUv0P$!Qvijpv9plBI@FUU#RJ)8Vn1ZGA$ATqF&s= zvcTS>Z8pepd>k=sjPY^3fpCB@aW8$Oq%fW;R?GpYoT@ki@N#2LxgTk1dYZHNrk@lx z7=yYr0FT$I>z~I0nXpPp$t3)}D?2^<@KWH#E{irFy2`)5r{AyvWHYzn`5@h;GVj0@ zJ@1fbD9gX=vQNR7PG5i}jFE}9#!;ote)FHdW?VVe6v4dWEz(R?!HC4KeVde*DGr=F zRotamm=!I~=_{|m;mCI4#5{C3_gBXan1<>!K!8O|)&K?O_L`}=uKCJ-s&+!XTk?wi z%Bwa_&k>4}`a` zFCG!c^Cdj#Bc2z2PXBCW$G)<%9X6;oZiigwvMLXQ$0f+2bKDCKCGR*cG>+;UTQ2bj z(2r#Od&Ulv*{?U~hq`j8W&8aggxHo<6*$&cDG#k;GS?mLx0^7mda35tz zHTnFA6vB^rczV1Ai8I&XyJX?jiEcQ}n;PYCl~EUPIxF@V%#c7LW`44<>ezAiG>1ff zeOSeCd#PW2z5z+<4Y?Qc#tb&+uH++5^G@!BaaDeVN8x=3ZB{R=Z5e+zf&13+nz{l% z{{#>B^OaIK}1Xh z;}?)W)sfwuf~?Ov1!oiQ-@WVG>D#(JL4Ob-h*l`y&hBY*!EkULKFdt9+VGJ?E=r85 zl*~dE)e4&l8Fdq`I@T2BAme(u7_)}y$TNu^lWWK-M8UQ(ZuBcA(qHG3; z&7bO_w9Cp!REZ3VB`&kfYOCmrNQxu7pbLoFkf)9Jkas&36ZnTBL?~cDug+T3bw?o! z$U-GUnOTkujjaB8vxcenWsZ4UrH*vMmACDj!95aG?gE5-g<6v8X9%kXThF|rP(0eu za*9aK6%^Qu4oyr(1t4hqmPX~~L7tB(;C{DH&MWDzUG+6I(;TGeM)jR#hK~O13LRwk zRc2;#m|qsRADyxC<6XC8u+lvVXoH+-HNTQXImy0_oM&D=ngI3OP?c>&k8&P2iV%hg zq{#n%P=0$dYJ2o$clJWqpVH&Q;S5Hv`T0-)mU2aa$XL#RH`0~|_g zmmfHkP7#d=iuiU1lL&5T+egS~-01WrWiiA=({_yWBnY@x5eX}`?y?3Xdic;`1dn5T zxTwLw{;Qt1MSWowZ}r+U?8Q+R46Avz>o>^}4zhvZaa_*Jd(2A!dP8ah=_*lh!W#a~ zNUm{^sD#HbDq!m*EK}(GzVn4N2GeNpEp8Z<_tctC_id9X=Irqhb_{b^H;~}qwZI&F z3t^MPXp4BuDv9@1Kr3*u zZ|&i`IKW!_Rv5(CaTJBndmX9B{YL8HJ2}u)`_>#J_-m{T-xpj%|2|{xmnVF#+X3=* zY*5{hDkk6M{+!Ved>d}mD@q^#{3qo9ZYb-+75cj*gH%I+d=}E+qSCK>vj4p z81UxB7>Gz}5QU^Pv-AJ*EHMW3g`EwB^^}ps>1E2$#r*H_{O{u)J@@1m$?Pu=va`3n z?so1N_WbU8U+4Nb|AN$Gv|%%33+!xpvv3iSLv&=qIUrD|3^*|rn7cNTWHgpaH0mTS zbXS-J>ZVOG~>BOwxVSa1sk6ivguYJD`$YgKkB!awl#vZ1NenaIidf zIo;H>3%L>R^l(kGI`c9&1a9H-s~68yw>3t6~N-Bv<9hyv4@0XlT|13}n_wh4#^(`bgWSiUFD z?SO{pz~eEqAvU|UZ-MPN$ZoAzAm@B5l}5B&MB(X&#FQ{BiwixOTe9@pn>F;%(9zOZ zly7ELHP0wS+Ikfr4P>I383O6E%8Ps6HYh5VLs3+bL1$J`TkTm6$wnI&{gh;r(^g9_ zB1RO-zhYoFDSl^oIQ*3Sm`H4%TTjHtuLbN&=j+P%iuVlxfEi zjsZUV9XdHY8m9muB8q5Vz z(`L%J6y+JTwbc>-nW(k@1!b!V8X7{S8M4^jErN(9CY}WtZ%l(hygPSA0+WuRy2zYP z{I1rh;dEB2eq9TUxCz{Gyr5B`eQAc=V{W%c+@W5W-mHRf!`2j21`y@SR^7Oz6_2Pt zkOomwUO=FaWS0^zE_8fOUJ%bwuxpLG@_{*8@bC&b7t2Op`l< z@kNX+GMUc*Zm2{Mv|>~c3<+pti9iF4V#K8sFm1soxJDi@ z0hJgP6;T1hrbc}rAns8Ko;#S9v5&XknRCva_O>&b{J*(Da_#Ad?20`5$%Xl&Puge2 zx?l9eH%e}NIwyYKT%Sue)L;7I7JYB)tpVNP7pm4j0n6@>Y|3y<8rov)IM#WzE@P_p zpPF3p<9y7UBK}GHof5CwW07klGghQ%{IeT#5013G-@n^&IFHZTJJ6g~ zCL1d0jcUJO-+8y)#+Wl0=`qCJo^!~ia8$-;rOBE~#*_zRZ*s~5n>IEYEtin@n6TMCEC;3v*irJ77~dTlkH+Ea~ni&gW~z zEBWCpC22aJfc1md!}q~j@)~H{%|IZpVtGYMh}wWjmPAVGFG{e*)g0Ukf*24y3)BXV zL{F7d(CXNXPzVFQlu~e}UL~fsmSnqLDoUS5FIMR1VZnVc3TinGDcHznFA6zTs<73? z4WUqG_@f*^v&jR_Q>a63^$bI30RuiF&nnl+1=px4kSzi_XB+AxOARqt@H;ZXlCce# zxlDYVFRiA{;DaYx(}XclB2S^eT1Q#1;p=9y6{`}J_sm<1Th)5PG zzzBlA<6+TFhl2c=Jl_@yJ}518aXJd2YFCAVu-7TMwT$KZefT7 zs5NxjtWvoM1u)bqHBp$PBs0RBf))u;m?bp>hDT6vTw&Lr!dBTtgj5XtcKJWphk_H; zeH09+T|vQZQ8Efz6lS0!cG`T`QE*MzYzhh@C0zhrg|>NSMAtY9%Huc+TF>Ppkl@@zX1imQDFMlS23i7E;Qs+kyyrF{7O&UZxN+ z-QgiSOj1$l30gw2$s1etFkp1{tI8Eq=&i{Q(-jkZqNBkxHjo*)Mn|Eg=J}ZZ*M!@$ m8X&e#V;O~v<{(@8u;?|riGH1;*CyBcIM_}B>Hc%VBjPV`^lBFX diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b4155..cea7a793a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..9b42019c7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 347f1a71645fa8adfb3c9b4fb061fd893ffe51a6 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 24 Jan 2025 08:16:40 +0100 Subject: [PATCH 041/201] [#2079] Don't ignore return value of ReactiveUpdateRowsCoordinatorOneToMany#deleteRows --- ...eactiveUpdateRowsCoordinatorOneToMany.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/collection/mutation/ReactiveUpdateRowsCoordinatorOneToMany.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/collection/mutation/ReactiveUpdateRowsCoordinatorOneToMany.java index 99bec6f5a..8b7478efa 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/collection/mutation/ReactiveUpdateRowsCoordinatorOneToMany.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/collection/mutation/ReactiveUpdateRowsCoordinatorOneToMany.java @@ -7,6 +7,7 @@ import java.util.Iterator; import java.util.concurrent.CompletionStage; +import java.util.function.Function; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; @@ -28,9 +29,9 @@ import static java.lang.invoke.MethodHandles.lookup; 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.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +import static org.hibernate.reactive.util.impl.CompletionStages.zeroFuture; import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER; import static org.hibernate.sql.model.MutationType.DELETE; import static org.hibernate.sql.model.MutationType.INSERT; @@ -70,15 +71,18 @@ public CompletionStage reactiveUpdateRows(Object key, PersistentCollection } private CompletionStage doReactiveUpdate(Object key, PersistentCollection collection, SharedSessionContractImplementor session) { - if ( rowMutationOperations.hasDeleteRow() ) { - deleteRows( key, collection, session ); - } + final Function> insertRowsFun = v -> { + if ( rowMutationOperations.hasInsertRow() ) { + return insertRows( key, collection, session ); + } - if ( rowMutationOperations.hasInsertRow() ) { - return insertRows( key, collection, session ); + return zeroFuture(); + }; + if ( rowMutationOperations.hasDeleteRow() ) { + return deleteRows( key, collection, session ) + .thenCompose( insertRowsFun ); } - - return completedFuture( 0 ); + return insertRowsFun.apply( null ); } private CompletionStage insertRows(Object key, PersistentCollection collection, SharedSessionContractImplementor session) { From 1bb573762e9d69667144e2761cf5e7b31b6c9597 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 28 Jan 2025 11:52:26 +0100 Subject: [PATCH 042/201] [#2084] Upgrade Vert.x SQL client to 4.5.12 --- README.md | 10 +++++----- build.gradle | 2 +- gradle.properties | 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, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7280fce86..01f3e17c0 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ Hibernate Reactive has been tested with: - MS SQL Server 2022 - Oracle 23 - [Hibernate ORM][] 7.0.0.Beta3 -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.11 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.11 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.11 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.11 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.11 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.12 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.12 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.12 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.12 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.12 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/build.gradle b/build.gradle index ed2007312..e9ca27a56 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.11' + vertxSqlClientVersion = '4.5.12' } testcontainersVersion = '1.20.4' diff --git a/gradle.properties b/gradle.properties index 0ee0c1936..911e9d9c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,9 +47,9 @@ hibernateOrmGradlePluginVersion = 7.0.0.Beta2 #skipOrmVersionParsing = true # Override default Vert.x Sql client version -#vertxSqlClientVersion = 4.5.11-SNAPSHOT +#vertxSqlClientVersion = 4.5.12-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 4.5.11 -#vertxWebtClientVersion = 4.5.11 +#vertxWebVersion = 4.5.12 +#vertxWebtClientVersion = 4.5.12 diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 3a97c09ad..e87720288 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.11} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index d953f1b52..aa3aec07e 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.11} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} +//DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index fd57d05c9..def81fa49 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -6,9 +6,9 @@ */ //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.11} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.11} -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.11} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //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 6b5b75cac..59aff501a 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.11} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 7ba1cb184..599b2a8a4 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.11} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 135b165c9..697cae5dc 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.11} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.11} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index d5f35c988..2e0fb971b 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.11} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.12} //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.11} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.11} -//DEPS io.vertx:vertx-unit:${vertx.version:4.5.11} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-unit:${vertx.version:4.5.12} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 From e4b2f0357a81dacd3a1df6121cad40f2c6a644c3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 30 Jan 2025 10:47:37 +0100 Subject: [PATCH 043/201] [#1885] Add createMutationQuery method that accept criterias --- .../org/hibernate/reactive/mutiny/Mutiny.java | 22 ++++++++++ .../mutiny/impl/MutinySessionImpl.java | 10 +++++ .../org/hibernate/reactive/stage/Stage.java | 44 +++++++++++++++++++ .../reactive/stage/impl/StageSessionImpl.java | 10 +++++ .../stage/impl/StageStatelessSessionImpl.java | 10 +++++ 5 files changed, 96 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index e0a0eb4e4..dc48f217e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -957,6 +957,28 @@ default Uni lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(String queryString); + /** + * Create an instance of {@link MutationQuery} for the given update tree. + * + * @param updateQuery the update criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + */ + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + + /** + * Create an instance of {@link MutationQuery} for the given delete tree. + * + * @param deleteQuery the delete criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + */ + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index f36dc31e0..b12a7725f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -124,6 +124,16 @@ public MutationQuery createMutationQuery(String queryString) { return new MutinyMutationQueryImpl<>( delegate.createReactiveQuery( queryString ), factory ); } + @Override + public MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( updateQuery ), factory ); + } + + @Override + public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ), factory ); + } + @Override @Deprecated public Query createQuery(String queryString) { return new MutinyQueryImpl<>( delegate.createReactiveQuery( queryString ), factory ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index f009b8dca..e55fd6ac2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -994,6 +994,28 @@ default CompletionStage lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(String queryString); + /** + * Create an instance of {@link MutationQuery} for the given update tree. + * + * @param updateQuery the update criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + */ + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + + /** + * Create an instance of {@link MutationQuery} for the given delete tree. + * + * @param deleteQuery the delete criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + */ + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an @@ -1663,6 +1685,28 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ MutationQuery createMutationQuery(String queryString); + /** + * Create an instance of {@link MutationQuery} for the given update tree. + * + * @param updateQuery the update criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + */ + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + + /** + * Create an instance of {@link MutationQuery} for the given delete tree. + * + * @param deleteQuery the delete criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + */ + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** * Create an instance of {@link Query} for the given SQL query string, * or SQL update, insert, or delete statement. In the case of an update, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index fc6914c6b..d1328ff8b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -117,6 +117,16 @@ public MutationQuery createMutationQuery(String queryString) { return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString ) ); } + @Override + public MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( updateQuery ) ); + } + + @Override + public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) ); + } + @Override public CompletionStage find(Class entityClass, Object primaryKey) { return delegate.reactiveFind( entityClass, primaryKey, null, null ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 6e05c747c..3eba1110d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -276,6 +276,16 @@ public MutationQuery createMutationQuery(String queryString) { return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString ) ); } + @Override + public MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( updateQuery ) ); + } + + @Override + public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) ); + } + @Override public SelectionQuery createQuery(String queryString, Class resultType) { return new StageSelectionQueryImpl<>( delegate.createReactiveQuery( queryString, resultType ) ); From e00bf42cb193141522a04c13f5045f4f5f2938ac Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 30 Jan 2025 13:04:14 +0100 Subject: [PATCH 044/201] [#1885] Add test for createMutationQuery methods that accept update and delete criterias --- .../reactive/CriteriaMutationQueryTest.java | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java new file mode 100644 index 000000000..7776d7568 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java @@ -0,0 +1,203 @@ +/* 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 org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.stage.Stage; + +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.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.criteria.Root; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 10, timeUnit = MINUTES) +public class CriteriaMutationQueryTest extends BaseReactiveTest { + Flour spelt = new Flour( 1, "Spelt", "An ancient grain, is a hexaploid species of wheat.", "Wheat flour" ); + 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" ); + + @Override + protected Collection> annotatedEntities() { + return List.of( Flour.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + test( context, getSessionFactory().withTransaction( s -> s.persist( spelt, rye, almond ) ) ); + } + + @Test + public void testStageUpdateCriteriaQuery(VertxTestContext context) { + String updatedDescription = "Most rye breads use a mix of rye and wheat flours"; + test( context, getSessionFactory() + .withTransaction( s -> s + .createMutationQuery( criteriaUpdate( getCriteriaBuilder( s ), updatedDescription, rye ) ) + .executeUpdate() + ) + .thenAccept( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .thenCompose( v -> getSessionFactory() + .withTransaction( s -> s.find( Flour.class, rye.getId() ) ) ) + .thenAccept( result -> assertThat( result.getDescription() ).isEqualTo( updatedDescription ) ) + ); + } + + @Test + public void testMutinyUpdateCriteriaQuery(VertxTestContext context) { + String updatedDescription = "made from ground almonds."; + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createMutationQuery( criteriaUpdate( getCriteriaBuilder( s ), updatedDescription, almond ) ) + .executeUpdate() + ) + .invoke( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( Flour.class, almond.getId() ) ) ) + .invoke( result -> assertThat( result.getDescription() ).isEqualTo( updatedDescription ) ) + ); + } + + @Test + public void testStageDeleteCriteriaQuery(VertxTestContext context) { + test( context, getSessionFactory() + .withTransaction( s -> s + .createMutationQuery( criteriaUpdate( getCriteriaBuilder( s ) ) ) + .executeUpdate() + ) + .thenAccept( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .thenCompose( v -> getSessionFactory() + .withTransaction( s -> s.find( Flour.class, spelt.getId() ) ) ) + .thenAccept( result -> assertThat( result ).isNull() ) + ); + } + + @Test + public void testMutinyDeleteCriteriaQuery(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( s -> { + return s.createMutationQuery( criteriaUpdate( getCriteriaBuilder( s ) ) ).executeUpdate(); + } ) + .invoke( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( Flour.class, spelt.getId() ) ) ) + .invoke( result -> assertThat( result ).isNull() ) + ); + } + + private CriteriaUpdate criteriaUpdate(CriteriaBuilder cb, String updatedDescription, Flour rye) { + CriteriaUpdate criteriaUpdate = cb.createCriteriaUpdate( Flour.class ); + Root from = criteriaUpdate.from( Flour.class ); + criteriaUpdate.set( from.get( "description" ), updatedDescription ); + criteriaUpdate.where( cb.equal( from.get( "id" ), rye.getId() ) ); + return criteriaUpdate; + } + + private CriteriaDelete criteriaUpdate(CriteriaBuilder criteriaBuilder) { + CriteriaDelete criteriaDelete = criteriaBuilder.createCriteriaDelete( Flour.class ); + Root from = criteriaDelete.from( Flour.class ); + criteriaDelete.where( criteriaBuilder.equal( from.get( "id" ), spelt.getId() ) ); + return criteriaDelete; + } + + private static HibernateCriteriaBuilder getCriteriaBuilder(Stage.Session session) { + return session.getFactory().getCriteriaBuilder(); + } + + private static HibernateCriteriaBuilder getCriteriaBuilder(Mutiny.Session session) { + return session.getFactory().getCriteriaBuilder(); + } + + @Entity(name = "Flour") + @Table(name = "Flour") + public static class Flour { + @Id + private Integer id; + private String name; + private String description; + private String type; + + public Flour() { + } + + public Flour(Integer id, String name, String description, String type) { + this.id = id; + this.name = name; + this.description = description; + this.type = type; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Flour flour = (Flour) o; + return Objects.equals( name, flour.name ) && + Objects.equals( description, flour.description ) && + Objects.equals( type, flour.type ); + } + + @Override + public int hashCode() { + return Objects.hash( name, description, type ); + } + } +} From 0080b513b8d4603557a4f7896471f976350e23c5 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 30 Jan 2025 16:44:28 +0100 Subject: [PATCH 045/201] [#1885] Add createMutationQuery methods that accept insert and insertSelect criterias --- .../org/hibernate/reactive/mutiny/Mutiny.java | 12 ++++++++++ .../mutiny/impl/MutinySessionImpl.java | 6 +++++ .../session/ReactiveQueryProducer.java | 4 ++-- .../session/impl/ReactiveSessionImpl.java | 8 +++---- .../impl/ReactiveStatelessSessionImpl.java | 8 +++---- .../org/hibernate/reactive/stage/Stage.java | 23 +++++++++++++++++++ .../reactive/stage/impl/StageSessionImpl.java | 6 +++++ .../stage/impl/StageStatelessSessionImpl.java | 6 +++++ 8 files changed, 63 insertions(+), 10 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index dc48f217e..c281fa1df 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -26,6 +26,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Order; import org.hibernate.query.Page; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.Identifier; import org.hibernate.reactive.common.ResultSetMapping; @@ -979,6 +980,17 @@ default Uni lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** + * Create a {@link MutationQuery} from the given insert select criteria tree + * + * @param insert the insert select criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + */ + MutationQuery createMutationQuery(JpaCriteriaInsert insert); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index b12a7725f..56cdc727b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -22,6 +22,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.graph.RootGraph; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.Identifier; import org.hibernate.reactive.common.ResultSetMapping; @@ -134,6 +135,11 @@ public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ), factory ); } + @Override + public MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ), factory ); + } + @Override @Deprecated public Query createQuery(String queryString) { return new MutinyQueryImpl<>( delegate.createReactiveQuery( queryString ), factory ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index e67605c81..9b720b5a8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -11,7 +11,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.query.ReactiveMutationQuery; @@ -79,7 +79,7 @@ public interface ReactiveQueryProducer extends ReactiveConnectionSupplier { ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete deleteQuery); - ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsertSelect insertSelect); + ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsert insert); ReactiveMutationQuery createNativeReactiveMutationQuery(String sqlString); 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 c746c9547..f81eab1f9 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 @@ -69,7 +69,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.IllegalMutationQueryException; -import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; @@ -78,7 +78,7 @@ import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; +import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -543,10 +543,10 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsertSelect insertSelect) { + public ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsert insert) { checkOpen(); try { - return createReactiveCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); + return createReactiveCriteriaQuery( (SqmInsertStatement) insert, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); 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 5e02b9389..2e9dd3df1 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 @@ -45,7 +45,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.IllegalMutationQueryException; -import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; @@ -55,7 +55,7 @@ import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; +import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -998,10 +998,10 @@ public ReactiveMutationQuery createReactiveMutationQuery(CriteriaDelete ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsertSelect insertSelect) { + public ReactiveMutationQuery createReactiveMutationQuery(JpaCriteriaInsert insert) { checkOpen(); try { - return createReactiveCriteriaQuery( (SqmInsertSelectStatement) insertSelect, null ); + return createReactiveCriteriaQuery( (SqmInsertStatement) insert, null ); } catch ( RuntimeException e ) { throw getExceptionConverter().convert( e ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index e55fd6ac2..bc3debc08 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -27,6 +27,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Order; import org.hibernate.query.Page; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.Identifier; import org.hibernate.reactive.common.ResultSetMapping; @@ -1016,6 +1017,17 @@ default CompletionStage lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** + * Create a {@link MutationQuery} from the given insert select criteria tree + * + * @param insert the insert select criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + */ + MutationQuery createMutationQuery(JpaCriteriaInsert insert); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an @@ -1707,6 +1719,17 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + /** + * Create a {@link MutationQuery} from the given insert select criteria tree + * + * @param insert the insert select criteria query + * + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + */ + MutationQuery createMutationQuery(JpaCriteriaInsert insert); + /** * Create an instance of {@link Query} for the given SQL query string, * or SQL update, insert, or delete statement. In the case of an update, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index d1328ff8b..20f9644a0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -22,6 +22,7 @@ import org.hibernate.LockOptions; import org.hibernate.graph.RootGraph; import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.Identifier; import org.hibernate.reactive.common.ResultSetMapping; @@ -127,6 +128,11 @@ public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) ); } + @Override + public MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ) ); + } + @Override public CompletionStage find(Class entityClass, Object primaryKey) { return delegate.reactiveFind( entityClass, primaryKey, null, null ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 3eba1110d..0d7c5d834 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -11,6 +11,7 @@ import jakarta.persistence.criteria.CriteriaUpdate; import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.session.ReactiveStatelessSession; @@ -286,6 +287,11 @@ public MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) ); } + @Override + public MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return new StageMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ) ); + } + @Override public SelectionQuery createQuery(String queryString, Class resultType) { return new StageSelectionQueryImpl<>( delegate.createReactiveQuery( queryString, resultType ) ); From 229a3a34d47ef1749944be0a122818c7c4958cb3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 30 Jan 2025 13:05:28 +0100 Subject: [PATCH 046/201] [#1885] change Stage#getCriteriaBuilder() and Mutiny#getCriteriaBuilder() to return HibernateCriteriaBuilder --- .../src/main/java/org/hibernate/reactive/mutiny/Mutiny.java | 3 ++- .../reactive/mutiny/impl/MutinySessionFactoryImpl.java | 4 ++-- .../src/main/java/org/hibernate/reactive/stage/Stage.java | 3 ++- .../reactive/stage/impl/StageSessionFactoryImpl.java | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c281fa1df..c561a4124 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -26,6 +26,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Order; import org.hibernate.query.Page; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.Identifier; @@ -2361,7 +2362,7 @@ default Uni withStatelessTransaction(Function> w * @return an instance of {@link CriteriaBuilder} for creating * criteria queries. */ - CriteriaBuilder getCriteriaBuilder(); + HibernateCriteriaBuilder getCriteriaBuilder(); /** * Obtain the JPA {@link Metamodel} for the persistence unit. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java index aecc99e37..f21bed53b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java @@ -15,6 +15,7 @@ import org.hibernate.Cache; import org.hibernate.internal.SessionCreationOptions; import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.reactive.common.spi.Implementor; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.context.impl.BaseKey; @@ -30,7 +31,6 @@ import org.hibernate.stat.Statistics; import io.smallrye.mutiny.Uni; -import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.metamodel.Metamodel; import static org.hibernate.reactive.common.InternalStateAssertions.assertUseOnEventLoop; @@ -245,7 +245,7 @@ public Uni withStatelessTransaction(String tenantId, BiFunction CompletionStage withStatelessTransaction(Function Date: Thu, 30 Jan 2025 15:26:38 +0100 Subject: [PATCH 047/201] [#1885] Add test for createMutationQuery methods that accept insert and insertSelect criterias --- .../reactive/CriteriaMutationQueryTest.java | 125 +++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java index 7776d7568..356a93bd3 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/CriteriaMutationQueryTest.java @@ -10,6 +10,10 @@ import java.util.Objects; import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsertValues; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaRoot; import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.stage.Stage; @@ -21,6 +25,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; +import jakarta.persistence.Tuple; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaUpdate; @@ -31,7 +36,10 @@ @Timeout(value = 10, timeUnit = MINUTES) public class CriteriaMutationQueryTest extends BaseReactiveTest { - Flour spelt = new Flour( 1, "Spelt", "An ancient grain, is a hexaploid species of wheat.", "Wheat flour" ); + private static final Integer SPELT_ID = 1; + private static final String SPELT_NAME = "Spelt"; + private static final String SPELT_TYPE = "Wheat flour"; + Flour spelt = new Flour( SPELT_ID, SPELT_NAME, "An ancient grain, is a hexaploid species of wheat.", SPELT_TYPE ); 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" ); @@ -102,6 +110,92 @@ public void testMutinyDeleteCriteriaQuery(VertxTestContext context) { ); } + @Test + public void testStageInsertCriteriaQuery(VertxTestContext context) { + final int id = 4; + final String flourName = "Rye"; + final String flourDescription = "Used to bake the traditional sourdough breads of Germany."; + final String flourType = "Wheat flour"; + test( context, getSessionFactory() + .withTransaction( s -> s + .createMutationQuery( insertCriteria( getCriteriaBuilder( s ), id, flourName, flourDescription, flourType ) ) + .executeUpdate() + ) + .thenAccept( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .thenCompose( v -> getSessionFactory() + .withTransaction( s -> s.find( Flour.class, id ) ) ) + .thenAccept( result -> { + assertThat( result ).isNotNull(); + assertThat( result.name ).isEqualTo( flourName ); + assertThat( result.description ).isEqualTo( flourDescription ); + assertThat( result.type ).isEqualTo( flourType ); + } ) + ); + } + + @Test + public void testMutinyInsertCriteriaQuery(VertxTestContext context) { + final int id = 4; + final String flourName = "Almond"; + final String flourDescription = "made from ground almonds."; + final String flourType = "Gluten free"; + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createMutationQuery( insertCriteria( getCriteriaBuilder( s ), id, flourName, flourDescription, flourType ) ) + .executeUpdate() + ) + .invoke( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( Flour.class, id ) ) ) + .invoke( result -> { + assertThat( result ).isNotNull(); + assertThat( result.name ).isEqualTo( flourName ); + assertThat( result.description ).isEqualTo( flourDescription ); + assertThat( result.type ).isEqualTo( flourType ); + } ) + ); + } + + @Test + public void testStageInsertSelectCriteriaQuery(VertxTestContext context) { + final int idOfTheNewFlour = 4; + test( context, getSessionFactory() + .withTransaction( s -> s + .createMutationQuery( insertSelectCriteria( getCriteriaBuilder( s ), idOfTheNewFlour ) ) + .executeUpdate() + ) + .thenAccept( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .thenCompose( v -> getSessionFactory() + .withTransaction( s -> s.find( Flour.class, idOfTheNewFlour ) ) ) + .thenAccept( result -> { + assertThat( result ).isNotNull(); + assertThat( result.name ).isEqualTo( SPELT_NAME ); + assertThat( result.description ).isNull(); + assertThat( result.type ).isEqualTo( SPELT_TYPE ); + } ) + ); + } + + @Test + public void testMutinyInsertSelectCriteriaQuery(VertxTestContext context) { + final int idOfTheNewFlour = 4; + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createMutationQuery( insertSelectCriteria( getCriteriaBuilder( s ), idOfTheNewFlour ) ) + .executeUpdate() + ) + .invoke( resultCount -> assertThat( resultCount ).isEqualTo( 1 ) ) + .chain( v -> getMutinySessionFactory() + .withTransaction( s -> s.find( Flour.class, idOfTheNewFlour ) ) ) + .invoke( result -> { + assertThat( result ).isNotNull(); + assertThat( result.name ).isEqualTo( SPELT_NAME ); + assertThat( result.description ).isNull(); + assertThat( result.type ).isEqualTo( SPELT_TYPE ); + } ) + ); + } + private CriteriaUpdate criteriaUpdate(CriteriaBuilder cb, String updatedDescription, Flour rye) { CriteriaUpdate criteriaUpdate = cb.createCriteriaUpdate( Flour.class ); Root from = criteriaUpdate.from( Flour.class ); @@ -117,6 +211,35 @@ private CriteriaDelete criteriaUpdate(CriteriaBuilder criteriaBuilder) { return criteriaDelete; } + private static JpaCriteriaInsertValues insertCriteria(HibernateCriteriaBuilder cb, int id, String name, String description, String type) { + JpaCriteriaInsertValues insert = cb.createCriteriaInsertValues( Flour.class ); + JpaRoot flour = insert.getTarget(); + insert.setInsertionTargetPaths( flour.get( "id" ), flour.get( "name" ), flour.get( "description" ), flour.get( "type" ) ); + insert.values( cb.values( cb.value( id ), cb.value( name ), cb.value( description ), cb.value( type ) ) ); + return insert; + } + + private static JpaCriteriaInsertSelect insertSelectCriteria( + HibernateCriteriaBuilder cb, + int idOfTheNewFlour) { + /* + The query executes and insert of Flour with id equals to 2 a name and type + selected from the existing spelt flour saved in the db + */ + JpaCriteriaInsertSelect insertSelect = cb.createCriteriaInsertSelect( Flour.class ); + // columns to insert + JpaRoot flour = insertSelect.getTarget(); + insertSelect.setInsertionTargetPaths( flour.get( "id" ), flour.get( "name" ), flour.get( "type" ) ); + // select query + JpaCriteriaQuery select = cb.createQuery( Tuple.class ); + JpaRoot root = select.from( Flour.class ); + select.multiselect( cb.literal( idOfTheNewFlour ), root.get( "name" ), root.get( "type" ) ); + select.where( cb.equal( root.get( "id" ), SPELT_ID ) ); + + insertSelect.select( select ); + return insertSelect; + } + private static HibernateCriteriaBuilder getCriteriaBuilder(Stage.Session session) { return session.getFactory().getCriteriaBuilder(); } From 5fa510b2b9b47daab27fd52aad49ed6568454a57 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 5 Feb 2025 10:10:38 +0100 Subject: [PATCH 048/201] [#2097] Add dependabot config to update GH actions --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..4d1c7e327 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + workflow-actions: + patterns: + - "*" + allow: + - dependency-name: "actions/*" + - dependency-name: "redhat-actions/*" From 42d5c0479d6bab0a9a49a360f3999fe82d39328a Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 5 Feb 2025 10:25:45 +0100 Subject: [PATCH 049/201] [#2097] Use SHA instead of versions in GH actions --- .github/workflows/build.yml | 36 ++++++++++++++++++------------------ .github/workflows/codeql.yml | 10 +++++----- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34914144a..8f519dac8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,7 +81,7 @@ jobs: - 5432:5432 steps: - name: Checkout ${{ inputs.branch }} - uses: actions/checkout@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ inputs.branch }} - name: Get year/month for cache key @@ -90,7 +90,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache-gradle with: path: | @@ -101,13 +101,13 @@ jobs: key: gradle-examples-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: 11 - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: 17 @@ -116,7 +116,7 @@ jobs: - name: Run examples in '${{ matrix.example }}' on ${{ matrix.db }} run: ./gradlew :${{ matrix.example }}:runAllExamplesOn${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 if: failure() with: name: reports-examples-${{ matrix.db }} @@ -130,7 +130,7 @@ jobs: db: [ 'MariaDB', 'MySQL', 'PostgreSQL', 'MSSQLServer', 'CockroachDB', 'Db2', 'Oracle' ] steps: - name: Checkout ${{ inputs.branch }} - uses: actions/checkout@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ inputs.branch }} - name: Get year/month for cache key @@ -139,7 +139,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache-gradle with: path: | @@ -150,13 +150,13 @@ jobs: key: gradle-db-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: 11 - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' @@ -166,7 +166,7 @@ jobs: - name: Build and Test with ${{ matrix.db }} run: ./gradlew build -PshowStandardOutput -Pdocker -Pdb=${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 if: failure() with: name: reports-db-${{ matrix.db }} @@ -196,7 +196,7 @@ jobs: - { name: "25-ea", java_version_numeric: 25, from: 'jdk.java.net', jvm_args: '--enable-preview' } steps: - name: Checkout ${{ inputs.branch }} - uses: actions/checkout@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ inputs.branch }} - name: Get year/month for cache key @@ -217,7 +217,7 @@ jobs: echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT - name: Cache Maven/Gradle Dependency/Dist Caches id: cache-maven - uses: actions/cache@v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 # if it's not a pull request, we restore and save the cache if: github.event_name != 'pull_request' with: @@ -234,7 +234,7 @@ jobs: ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}- ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}- - name: Restore Maven/Gradle Dependency/Dist Caches - uses: actions/cache/restore@v4 + uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 # if it's a pull request, we restore the cache, but we don't save it if: github.event_name == 'pull_request' with: @@ -250,13 +250,13 @@ jobs: - name: Set up latest JDK ${{ matrix.java.name }} from jdk.java.net if: matrix.java.from == 'jdk.java.net' - uses: oracle-actions/setup-java@v1 + uses: oracle-actions/setup-java@2e744f723b003fdd759727d0ff654c8717024845 # v1.4.0 with: website: jdk.java.net release: ${{ matrix.java.java_version_numeric }} - name: Set up latest JDK ${{ matrix.java.name }} from Adoptium if: matrix.java.from == '' || matrix.java.from == 'adoptium.net' - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: ${{ matrix.java.java_version_numeric }} @@ -266,14 +266,14 @@ jobs: run: echo "::set-output name=path::${JAVA_HOME}" - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: 11 check-latest: true - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@v2.2.0 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: 'temurin' java-version: 17 @@ -292,7 +292,7 @@ jobs: -Porg.gradle.java.installations.paths=${{ steps.mainjdk-exportpath.outputs.path }},${{ steps.testjdk-exportpath.outputs.path }} \ ${{ matrix.java.jvm_args && '-Ptest.jdk.launcher.args=' }}${{ matrix.java.jvm_args }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 if: failure() with: name: reports-java${{ matrix.java.name }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 60d7a6ec1..13d87abe0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,24 +24,24 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: distribution: temurin java-version: 17 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@7e3036b9cd87fc26dd06747b7aa4b96c27aaef3a # v3.28.4 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@7e3036b9cd87fc26dd06747b7aa4b96c27aaef3a # v3.28.4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@7e3036b9cd87fc26dd06747b7aa4b96c27aaef3a # v3.28.4 with: category: "/language:${{ matrix.language }}" From e1aff8f360d17dc90d077422f5e2739c233a646d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 7 Feb 2025 10:46:31 +0100 Subject: [PATCH 050/201] add project icon --- .idea/icon.svg | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .idea/icon.svg diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 000000000..2b19df67f --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 05f64432e4e659751ae003c552d7716c239362b3 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 5 Feb 2025 18:21:32 +0100 Subject: [PATCH 051/201] [#2055] Setting hibernate.query.mutation_strategy.global_temporary.create_tables to false has no effect --- .../ReactiveGlobalTemporaryTableStrategy.java | 74 +++++++++---------- .../ReactivePersistentTableStrategy.java | 70 +++++++++--------- 2 files changed, 73 insertions(+), 71 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java index 37c3d23bc..d0204ffff 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java @@ -27,8 +27,6 @@ public interface ReactiveGlobalTemporaryTableStrategy { - Log LOG = make( Log.class, lookup() ); - static String sessionIdentifier(SharedSessionContractImplementor session) { return session.getSessionIdentifier().toString(); } @@ -65,22 +63,23 @@ default void prepare(MappingModelCreationProcess mappingModelCreationProcess, Jd if ( !createIdTables ) { tableCreatedStage.complete( null ); } - - LOG.debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() ); - - connectionStage() - .thenCompose( this::createTable ) - .whenComplete( (connection, throwable) -> releaseConnection( connection ) - .thenAccept( v -> { - if ( throwable == null ) { - setDropIdTables( configService ); - tableCreatedStage.complete( null ); - } - else { - tableCreatedStage.completeExceptionally( throwable ); - } - } ) - ); + else { + make( Log.class, lookup() ).debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() ); + + connectionStage() + .thenCompose( this::createTable ) + .whenComplete( (connection, throwable) -> releaseConnection( connection ) + .thenAccept( v -> { + if ( throwable == null ) { + setDropIdTables( configService ); + tableCreatedStage.complete( null ); + } + else { + tableCreatedStage.completeExceptionally( throwable ); + } + } ) + ); + } } private CompletionStage releaseConnection(ReactiveConnection connection) { @@ -103,7 +102,7 @@ private CompletionStage releaseConnection(ReactiveConnection connection) { private static void logConnectionClosedError(Throwable t) { if ( t != null ) { - LOG.debugf( "Ignoring error closing the connection: %s", t.getMessage() ); + make( Log.class, lookup() ).debugf( "Ignoring error closing the connection: %s", t.getMessage() ); } } @@ -147,24 +146,25 @@ default void release( if ( !isDropIdTables() ) { tableDroppedStage.complete( null ); } - - setDropIdTables( false ); - - final TemporaryTable temporaryTable = getTemporaryTable(); - LOG.debugf( "Dropping global-tempk ID table : %s", temporaryTable.getTableExpression() ); - - connectionStage() - .thenCompose( this::dropTable ) - .whenComplete( (connection, throwable) -> releaseConnection( connection ) - .thenAccept( v -> { - if ( throwable == null ) { - tableDroppedStage.complete( null ); - } - else { - tableDroppedStage.completeExceptionally( throwable ); - } - } ) - ); + else { + setDropIdTables( false ); + + final TemporaryTable temporaryTable = getTemporaryTable(); + make( Log.class, lookup() ).debugf( "Dropping global-tempk ID table : %s", temporaryTable.getTableExpression() ); + + connectionStage() + .thenCompose( this::dropTable ) + .whenComplete( (connection, throwable) -> releaseConnection( connection ) + .thenAccept( v -> { + if ( throwable == null ) { + tableDroppedStage.complete( null ); + } + else { + tableDroppedStage.completeExceptionally( throwable ); + } + } ) + ); + } } private CompletionStage dropTable(ReactiveConnection connection) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableStrategy.java index 472dc0ce7..ba70601ad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactivePersistentTableStrategy.java @@ -68,22 +68,23 @@ default void prepare(MappingModelCreationProcess mappingModelCreationProcess, Jd if ( !createIdTables ) { tableCreatedStage.complete( null ); } - - LOG.debugf( "Creating persistent ID table : %s", getTemporaryTable().getTableExpression() ); - - connectionStage() - .thenCompose( this::createTable ) - .whenComplete( (connection, throwable) -> releaseConnection( connection ) - .thenAccept( v -> { - if ( throwable == null ) { - setDropIdTables( configService ); - tableCreatedStage.complete( null ); - } - else { - tableCreatedStage.completeExceptionally( throwable ); - } - } ) - ); + else { + LOG.debugf( "Creating persistent ID table : %s", getTemporaryTable().getTableExpression() ); + + connectionStage() + .thenCompose( this::createTable ) + .whenComplete( (connection, throwable) -> releaseConnection( connection ) + .thenAccept( v -> { + if ( throwable == null ) { + setDropIdTables( configService ); + tableCreatedStage.complete( null ); + } + else { + tableCreatedStage.completeExceptionally( throwable ); + } + } ) + ); + } } private CompletionStage releaseConnection(ReactiveConnection connection) { @@ -150,24 +151,25 @@ default void release( if ( !isDropIdTables() ) { tableDroppedStage.complete( null ); } - - setDropIdTables( false ); - - final TemporaryTable temporaryTable = getTemporaryTable(); - LOG.debugf( "Dropping persistent ID table : %s", temporaryTable.getTableExpression() ); - - connectionStage() - .thenCompose( this::dropTable ) - .whenComplete( (connection, throwable) -> releaseConnection( connection ) - .thenAccept( v -> { - if ( throwable == null ) { - tableDroppedStage.complete( null ); - } - else { - tableDroppedStage.completeExceptionally( throwable ); - } - } ) - ); + else { + setDropIdTables( false ); + + final TemporaryTable temporaryTable = getTemporaryTable(); + LOG.debugf( "Dropping persistent ID table : %s", temporaryTable.getTableExpression() ); + + connectionStage() + .thenCompose( this::dropTable ) + .whenComplete( (connection, throwable) -> releaseConnection( connection ) + .thenAccept( v -> { + if ( throwable == null ) { + tableDroppedStage.complete( null ); + } + else { + tableDroppedStage.completeExceptionally( throwable ); + } + } ) + ); + } } private CompletionStage dropTable(ReactiveConnection connection) { From 6bece369e0460f95294a0bf43a6783937155a327 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 12 Feb 2025 10:19:51 +0100 Subject: [PATCH 052/201] [#2055] Avoid logging temporary command twice --- .../internal/temptable/ReactiveTemporaryTableHelper.java | 6 ------ 1 file changed, 6 deletions(-) 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 cc05cef3d..126c168f8 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 @@ -67,11 +67,8 @@ public TemporaryTableCreationWork( @Override public CompletionStage reactiveExecute(ReactiveConnection connection) { - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - try { final String creationCommand = exporter.getSqlCreateCommand( temporaryTable ); - logStatement( creationCommand, jdbcServices ); return connection.executeUnprepared( creationCommand ) .handle( (integer, throwable) -> { @@ -115,11 +112,8 @@ public TemporaryTableDropWork( @Override public CompletionStage reactiveExecute(ReactiveConnection connection) { - final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - try { final String dropCommand = exporter.getSqlDropCommand( temporaryTable ); - logStatement( dropCommand, jdbcServices ); return connection.update( dropCommand ) .handle( (integer, throwable) -> { From 4d7c523317fc4429fe9ff10951b38a2e49868d76 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 12 Feb 2025 10:37:20 +0100 Subject: [PATCH 053/201] [#2055] SessionFactoryManager.stop() should always call SessionFactory.close() PersistentTemporaryTable dropping is obtained setting PersistentTableStrategy.DROP_ID_TABLES to true --- .../hibernate/reactive/BaseReactiveTest.java | 4 +++ .../testing/SessionFactoryManager.java | 36 ++----------------- 2 files changed, 6 insertions(+), 34 deletions(-) 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 6cd705f2b..1a5f51e3b 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 @@ -18,6 +18,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableStrategy; import org.hibernate.reactive.containers.DatabaseConfiguration; import org.hibernate.reactive.containers.DatabaseConfiguration.DBType; import org.hibernate.reactive.mutiny.Mutiny; @@ -105,6 +107,8 @@ public static void setDefaultProperties(Configuration configuration) { configuration.setProperty( Settings.SHOW_SQL, System.getProperty( Settings.SHOW_SQL, "false" ) ); configuration.setProperty( Settings.FORMAT_SQL, System.getProperty( Settings.FORMAT_SQL, "false" ) ); configuration.setProperty( Settings.HIGHLIGHT_SQL, System.getProperty( Settings.HIGHLIGHT_SQL, "true" ) ); + configuration.setProperty( PersistentTableStrategy.DROP_ID_TABLES, "true" ); + configuration.setProperty( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); } public static final SessionFactoryManager factoryManager = new SessionFactoryManager(); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/SessionFactoryManager.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/SessionFactoryManager.java index 8262b7c22..be4362a02 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/SessionFactoryManager.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/SessionFactoryManager.java @@ -5,20 +5,13 @@ */ package org.hibernate.reactive.testing; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; import org.hibernate.SessionFactory; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.spi.MappingMetamodelImplementor; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableStrategy; import org.hibernate.reactive.pool.ReactiveConnectionPool; -import org.hibernate.reactive.query.sqm.mutation.internal.temptable.ReactivePersistentTableStrategy; -import static org.hibernate.reactive.util.impl.CompletionStages.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -60,24 +53,8 @@ public ReactiveConnectionPool getReactiveConnectionPool() { public CompletionStage stop() { CompletionStage releasedStage = voidFuture(); if ( sessionFactory != null && sessionFactory.isOpen() ) { - SessionFactoryImplementor sessionFactoryImplementor = sessionFactory.unwrap( SessionFactoryImplementor.class ); - MappingMetamodelImplementor mappingMetamodel = sessionFactoryImplementor - .getRuntimeMetamodels() - .getMappingMetamodel(); - final List reactiveStrategies = new ArrayList<>(); - mappingMetamodel.forEachEntityDescriptor( - entityPersister -> addPersistentTableStrategy( reactiveStrategies, entityPersister ) - ); - if ( !reactiveStrategies.isEmpty() ) { - releasedStage = loop( reactiveStrategies, strategy -> { - ( (PersistentTableStrategy) strategy ) - .release( sessionFactory.unwrap( SessionFactoryImplementor.class ), null ); - return strategy.getDropTableActionStage(); - } ); - - releasedStage = releasedStage - .whenComplete( (unused, throwable) -> sessionFactory.close() ); - } + releasedStage = releasedStage + .whenComplete( (unused, throwable) -> sessionFactory.close() ); } return releasedStage .thenCompose( unused -> { @@ -93,13 +70,4 @@ public CompletionStage stop() { return closeFuture; } ); } - - private void addPersistentTableStrategy(List reactiveStrategies, EntityPersister entityPersister) { - if ( entityPersister.getSqmMultiTableMutationStrategy() instanceof ReactivePersistentTableStrategy ) { - reactiveStrategies.add( (ReactivePersistentTableStrategy) entityPersister.getSqmMultiTableMutationStrategy() ); - } - if ( entityPersister.getSqmMultiTableInsertStrategy() instanceof ReactivePersistentTableStrategy ) { - reactiveStrategies.add( (ReactivePersistentTableStrategy) entityPersister.getSqmMultiTableInsertStrategy() ); - } - } } From ebf17e1beab46dbaede8adb2f07882764a338832 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 12 Feb 2025 10:39:03 +0100 Subject: [PATCH 054/201] [#2055] Add test for temporary table strategies The test is for temporary tables created for storing ids --- .../schema/TemporaryIdTableStrategyTest.java | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java new file mode 100644 index 000000000..c0310d8a4 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java @@ -0,0 +1,236 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.schema; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.stream.Stream; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableStrategy; +import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.annotations.EnabledFor; +import org.hibernate.reactive.provider.Settings; +import org.hibernate.reactive.testing.SqlStatementTracker; +import org.hibernate.reactive.util.impl.CompletionStages; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +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.Inheritance; +import jakarta.persistence.InheritanceType; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.COCKROACHDB; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.ORACLE; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Test enabling and disabling of strategies for the creation of temporary tables to store ids. + * + * @see GlobalTemporaryTableStrategy + * @see PersistentTableStrategy + */ +@Timeout(value = 10, timeUnit = MINUTES) +public class TemporaryIdTableStrategyTest extends BaseReactiveTest { + private static SqlStatementTracker sqlStatementTracker; + + final static Dialect[] dialect = new Dialect[1]; + + @Override + protected Collection> annotatedEntities() { + return Set.of( Engineer.class, Doctor.class, Person.class ); + } + + public static Stream settings() { + return Stream.of( + arguments( true, 1, true, 1 ), + arguments( true, 1, false, 0 ), + // I'm assuming Hibernate won't drop the id tables if they haven't been created + arguments( false, 0, true, 0 ), + arguments( false, 0, false, 0 ) + ); + } + + @Override + protected Configuration constructConfiguration() { + Configuration configuration = super.constructConfiguration(); + configuration.setProperty( Settings.HBM2DDL_AUTO, "create" ); + // Collect all the logs, we are going to filter them later + sqlStatementTracker = new SqlStatementTracker( s -> true, configuration.getProperties() ); + return configuration; + } + + @Override + public CompletionStage deleteEntities(Class... entities) { + // Deleting entities is not necessary for this test + return voidFuture(); + } + + @Override + public void before(VertxTestContext context) { + // We need to start and close our own session factories for the test + } + + @AfterEach + @Override + public void after(VertxTestContext context) { + sqlStatementTracker.clear(); + super.after( context ); + } + + @Override + protected void addServices(StandardServiceRegistryBuilder builder) { + if ( sqlStatementTracker != null ) { + sqlStatementTracker.registerService( builder ); + } + } + + @ParameterizedTest(name = "Global Temporary tables - create: {0}, drop: {2}") + @MethodSource("settings") + @EnabledFor(value = ORACLE, reason = "It uses GlobalTemporaryTableStrategy by default") + public void testGlobalTemporaryTablesStrategy( + boolean enableCreateIdTables, + // Expected number of temporary tables created + int expectedTempTablesCreated, + boolean enableDropIdTables, + // Expected number of temporary tables dropped + int expectedTempTablesDropped, + VertxTestContext context) { + Configuration configuration = constructConfiguration(); + configuration.setProperty( GlobalTemporaryTableStrategy.CREATE_ID_TABLES, enableCreateIdTables ); + configuration.setProperty( GlobalTemporaryTableStrategy.DROP_ID_TABLES, enableDropIdTables ); + + testTemporaryIdTablesCreationAndDropping( configuration, expectedTempTablesCreated, expectedTempTablesDropped, context ); + } + + @ParameterizedTest(name = "Persistent tables - create: {0}, drop: {2}") + @MethodSource("settings") + @EnabledFor(value = COCKROACHDB, reason = "It uses PersistentTemporaryTableStrategy by default") + public void testPersistentTemporaryTablesStrategy( + boolean enableCreateIdTables, + // Expected number of temporary tables created + int expectedTempTablesCreated, + boolean enableDropIdTables, + // Expected number of temporary tables dropped + int expectedTempTablesDropped, + VertxTestContext context) { + + Configuration configuration = constructConfiguration(); + configuration.setProperty( PersistentTableStrategy.CREATE_ID_TABLES, enableCreateIdTables ); + configuration.setProperty( PersistentTableStrategy.DROP_ID_TABLES, enableDropIdTables ); + + testTemporaryIdTablesCreationAndDropping( configuration, expectedTempTablesCreated, expectedTempTablesDropped, context ); + } + + private void testTemporaryIdTablesCreationAndDropping( + Configuration configure, + int expectedTempTablesCreated, + int expectedTempTablesDropped, + VertxTestContext context) { + test( context, setupSessionFactory( configure ) + .thenCompose( v -> getSessionFactory().withSession( s -> { + dialect[0] = getDialect(); + assertThat( commandsCount( dialect[0].getTemporaryTableCreateCommand() ) ) + .as( "Unexpected number of temporary tables for ids CREATED" ) + .isEqualTo( expectedTempTablesCreated ); + sqlStatementTracker.clear(); + return voidFuture(); + } ) ) + // to ensure the factory is always closed even in case of exceptions + .handle( CompletionStages::handle ) + .thenCompose( this::closeFactory ) + .thenAccept( v -> assertThat( commandsCount( dialect[0].getTemporaryTableDropCommand() ) ) + .as( "Unexpected number of temporary tables for ids DROPPED" ) + .isEqualTo( expectedTempTablesDropped ) ) + ); + } + + // Always try to close the factory without losing the original error (if there was one) + private CompletionStage closeFactory(CompletionStages.CompletionStageHandler handler) { + return factoryManager.stop() + .handle( CompletionStages::handle ) + .thenCompose( factoryHandler -> handler + .getResultAsCompletionStage() + // When there's already an exception, we don't care about errors closing the factory + .thenCompose( factoryHandler::getResultAsCompletionStage ) ); + } + + private static long commandsCount(String temporaryTableCommand) { + return sqlStatementTracker.getLoggedQueries().stream() + .filter( q -> q.startsWith( temporaryTableCommand ) && q.contains( TemporaryTable.ID_TABLE_PREFIX ) ) + .count(); + } + + @Entity(name = "Person") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Person { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private boolean employed; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isEmployed() { + return employed; + } + + public void setEmployed(boolean employed) { + this.employed = employed; + } + } + + @Entity(name = "Doctor") + public static class Doctor extends Person { + } + + @Entity(name = "Engineer") + public static class Engineer extends Person { + + private boolean fellow; + + public boolean isFellow() { + return fellow; + } + + public void setFellow(boolean fellow) { + this.fellow = fellow; + } + } +} From 197588291ce39070ddf4bb1a633f86e42530827c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 13 Feb 2025 12:09:37 +0100 Subject: [PATCH 055/201] [#2055] Remove unecessary `.withSession` from test --- .../reactive/schema/TemporaryIdTableStrategyTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java index c0310d8a4..15ed24341 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/TemporaryIdTableStrategyTest.java @@ -146,14 +146,13 @@ private void testTemporaryIdTablesCreationAndDropping( int expectedTempTablesDropped, VertxTestContext context) { test( context, setupSessionFactory( configure ) - .thenCompose( v -> getSessionFactory().withSession( s -> { + .thenAccept( v -> { dialect[0] = getDialect(); assertThat( commandsCount( dialect[0].getTemporaryTableCreateCommand() ) ) .as( "Unexpected number of temporary tables for ids CREATED" ) .isEqualTo( expectedTempTablesCreated ); sqlStatementTracker.clear(); - return voidFuture(); - } ) ) + } ) // to ensure the factory is always closed even in case of exceptions .handle( CompletionStages::handle ) .thenCompose( this::closeFactory ) From 2da4c8e0701b58a00c657fc689365bf02a226162 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 13 Feb 2025 12:53:38 +0100 Subject: [PATCH 056/201] [#2113] Upgrade Hibernate ORM to 7.0.0.Beta4 Among the changes: * Remove use of FastSessionServices * Remove classes related to ResolveNaturalIdEvent * ReactivePersistenceContextAdaptor doesn't extend StatefulPersistenceContext anymore (it has changed the scope to package) --- README.md | 2 +- gradle.properties | 4 +- .../ReactivePersistenceContextAdapter.java | 713 ++++++++++++++++++ .../reactive/engine/impl/EntityTypes.java | 7 +- .../reactive/engine/impl/ForeignKeys.java | 1 + .../ReactiveCollectionRecreateAction.java | 4 +- .../impl/ReactiveCollectionRemoveAction.java | 6 +- .../impl/ReactiveCollectionUpdateAction.java | 6 +- .../ReactivePersistenceContextAdapter.java | 134 ---- ...ReactiveResolveNaturalIdEventListener.java | 29 - ...AbstractReactiveFlushingEventListener.java | 7 +- .../DefaultReactiveDeleteEventListener.java | 48 +- .../DefaultReactiveLoadEventListener.java | 38 +- .../DefaultReactiveMergeEventListener.java | 8 +- .../DefaultReactivePostLoadEventListener.java | 2 - ...ReactiveResolveNaturalIdEventListener.java | 135 ---- .../internal/DatabaseSnapshotExecutor.java | 2 +- ...activeCollectionBatchLoaderArrayParam.java | 17 +- .../ReactiveEntityBatchLoaderArrayParam.java | 6 +- ...ReactiveMultiIdEntityLoaderArrayParam.java | 80 +- .../ReactiveMultiIdEntityLoaderStandard.java | 91 +-- .../ReactiveNaturalIdLoaderDelegate.java | 2 +- .../ReactiveStandardBatchLoaderFactory.java | 2 +- .../ReactiveCacheEntityLoaderHelper.java | 148 ---- .../impl/ReactiveAbstractEntityPersister.java | 30 +- ...ReactiveJoinedSubclassEntityPersister.java | 5 + .../ReactiveSingleTableEntityPersister.java | 5 + .../ReactiveUnionSubclassEntityPersister.java | 5 + .../provider/impl/ReactiveIntegrator.java | 2 - .../ReactiveNamedObjectRepositoryImpl.java | 6 + .../ConcreteSqmSelectReactiveQueryPlan.java | 2 +- .../ReactiveSimpleDeleteQueryPlan.java | 2 +- .../ReactiveSimpleInsertQueryPlan.java | 2 +- .../ReactiveSimpleUpdateQueryPlan.java | 2 +- .../ReactiveAbstractCteMutationHandler.java | 2 +- .../cte/ReactiveCteInsertHandler.java | 11 +- ...tiveRestrictedDeleteExecutionDelegate.java | 2 +- .../session/impl/ReactiveSessionImpl.java | 166 ++-- .../impl/ReactiveStatelessSessionImpl.java | 12 +- .../StandardReactiveSelectExecutor.java | 2 +- ...ReactiveEntityDelayedFetchInitializer.java | 2 +- .../ReactiveEntityInitializerImpl.java | 4 +- .../descriptor/jdbc/ReactiveXmlJdbcType.java | 2 +- 43 files changed, 989 insertions(+), 767 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactivePersistenceContextAdapter.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/ReactiveResolveNaturalIdEventListener.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/entity/ReactiveCacheEntityLoaderHelper.java diff --git a/README.md b/README.md index 01f3e17c0..aabf550ac 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Hibernate Reactive has been tested with: - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 -- [Hibernate ORM][] 7.0.0.Beta3 +- [Hibernate ORM][] 7.0.0.Beta4 - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.12 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.12 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.12 diff --git a/gradle.properties b/gradle.properties index 911e9d9c4..b45735f0a 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.0.Beta3 +hibernateOrmVersion = 7.0.0.Beta4 # 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.Beta2 +#hibernateOrmGradlePluginVersion = 7.0.0.Beta4 # 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/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java new file mode 100644 index 000000000..92a678413 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java @@ -0,0 +1,713 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.hibernate.HibernateException; +import org.hibernate.Internal; +import org.hibernate.LockMode; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.BatchFetchQueue; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.CollectionKey; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.EntityHolder; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.EntityUniqueKey; +import org.hibernate.engine.spi.NaturalIdResolutions; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.Status; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; +import org.hibernate.reactive.session.ReactiveSession; +import org.hibernate.sql.results.graph.entity.EntityInitializer; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; +import org.hibernate.sql.results.spi.LoadContexts; + +import static java.lang.invoke.MethodHandles.lookup; +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.voidFuture; + +/** + * Add reactive methods to a {@link PersistenceContext}. + */ +public class ReactivePersistenceContextAdapter implements PersistenceContext { + + private static final Log LOG = make( Log.class, lookup() ); + + private final PersistenceContext delegate; + + /** + * Constructs a PersistentContext, bound to the given session. + */ + public ReactivePersistenceContextAdapter(PersistenceContext persistenceContext) { + this.delegate = persistenceContext; + } + + public CompletionStage reactiveInitializeNonLazyCollections() throws HibernateException { + final NonLazyCollectionInitializer initializer = new NonLazyCollectionInitializer(); + delegate.initializeNonLazyCollections( initializer ); + return initializer.stage; + } + + private class NonLazyCollectionInitializer implements Consumer> { + CompletionStage stage = voidFuture(); + + @Override + public void accept(PersistentCollection nonLazyCollection) { + if ( !nonLazyCollection.wasInitialized() ) { + stage = stage.thenCompose( v -> ( (ReactiveSession) getSession() ) + .reactiveInitializeCollection( nonLazyCollection, false ) ); + } + } + } + + /** + * @deprecated use {@link #reactiveInitializeNonLazyCollections} instead. + */ + @Deprecated + @Override + public void initializeNonLazyCollections() { + // still called by ResultSetProcessorImpl, so can't throw UnsupportedOperationException + } + + @Override + public void initializeNonLazyCollections(Consumer> initializeAction) { + throw LOG.nonReactiveMethodCall( "reactiveInitializeNonLazyCollection" ); + } + + @Deprecated + @Override + public Object[] getDatabaseSnapshot(Object id, EntityPersister persister) throws HibernateException { + throw LOG.nonReactiveMethodCall( "reactiveGetDatabaseSnapshot" ); + } + + private static final Object[] NO_ROW = new Object[] {StatefulPersistenceContext.NO_ROW}; + + public CompletionStage reactiveGetDatabaseSnapshot(Object id, EntityPersister persister) throws HibernateException { + SessionImplementor session = (SessionImplementor) getSession(); + final EntityKey key = session.generateEntityKey( id, persister ); + final Object[] cached = getEntitySnapshotsByKey() == null + ? null + : (Object[]) getEntitySnapshotsByKey().get( key ); + if ( cached != null ) { + return completedFuture( cached == NO_ROW ? null : cached ); + } + else { + return ( (ReactiveEntityPersister) persister ) + .reactiveGetDatabaseSnapshot( id, session ) + .thenApply( snapshot -> { + getOrInitializeEntitySnapshotsByKey().put( key, snapshot ); + return snapshot; + } ); + } + } + + @Override + public boolean isStateless() { + return delegate.isStateless(); + } + + @Override + public SharedSessionContractImplementor getSession() { + return delegate.getSession(); + } + + @Override + public LoadContexts getLoadContexts() { + return delegate.getLoadContexts(); + } + + @Override + public boolean hasLoadContext() { + return delegate.hasLoadContext(); + } + + @Override + public PersistentCollection useUnownedCollection(CollectionKey key) { + return delegate.useUnownedCollection( key ); + } + + @Override + public BatchFetchQueue getBatchFetchQueue() { + return delegate.getBatchFetchQueue(); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public void setEntryStatus(EntityEntry entry, Status status) { + delegate.setEntryStatus( entry, status ); + } + + @Override + public void afterTransactionCompletion() { + delegate.afterTransactionCompletion(); + } + + @Override + public Object[] getCachedDatabaseSnapshot(EntityKey key) { + return delegate.getCachedDatabaseSnapshot( key ); + } + + @Override + public Object getNaturalIdSnapshot(Object id, EntityPersister persister) { + return delegate.getNaturalIdSnapshot( id, persister ); + } + + @Override + public void addEntity(EntityKey key, Object entity) { + delegate.addEntity( key, entity ); + } + + @Override + public Object getEntity(EntityKey key) { + return delegate.getEntity( key ); + } + + @Override + public boolean containsEntity(EntityKey key) { + return delegate.containsEntity( key ); + } + + @Override + public Object removeEntity(EntityKey key) { + return delegate.removeEntity( key ); + } + + @Override + public void addEntity(EntityUniqueKey euk, Object entity) { + delegate.addEntity( euk, entity ); + } + + @Override + public Object getEntity(EntityUniqueKey euk) { + return delegate.getEntity( euk ); + } + + @Override + public EntityEntry getEntry(Object entity) { + return delegate.getEntry( entity ); + } + + @Override + public EntityEntry removeEntry(Object entity) { + return delegate.removeEntry( entity ); + } + + @Override + public boolean isEntryFor(Object entity) { + return delegate.isEntryFor( entity ); + } + + @Override + public CollectionEntry getCollectionEntry(PersistentCollection coll) { + return delegate.getCollectionEntry( coll ); + } + + @Override + public EntityEntry addEntity( + Object entity, + Status status, + Object[] loadedState, + EntityKey entityKey, + Object version, + LockMode lockMode, + boolean existsInDatabase, + EntityPersister persister, + boolean disableVersionIncrement) { + return delegate.addEntity( + entity, + status, + loadedState, + entityKey, + version, + lockMode, + existsInDatabase, + persister, + disableVersionIncrement + ); + } + + @Override + public EntityEntry addEntry( + Object entity, + Status status, + Object[] loadedState, + Object rowId, + Object id, + Object version, + LockMode lockMode, + boolean existsInDatabase, + EntityPersister persister, + boolean disableVersionIncrement) { + return delegate.addEntry( + entity, + status, + loadedState, + rowId, + id, + version, + lockMode, + existsInDatabase, + persister, + disableVersionIncrement + ); + } + + @Override + public EntityEntry addReferenceEntry(Object entity, Status status) { + return delegate.addReferenceEntry( entity, status ); + } + + @Override + public boolean containsCollection(PersistentCollection collection) { + return delegate.containsCollection( collection ); + } + + @Override + public boolean containsProxy(Object proxy) { + return delegate.containsProxy( proxy ); + } + + @Override + public boolean reassociateIfUninitializedProxy(Object value) { + return delegate.reassociateIfUninitializedProxy( value ); + } + + @Override + public void reassociateProxy(Object value, Object id) { + delegate.reassociateProxy( value, id ); + } + + @Override + public Object unproxy(Object maybeProxy) { + return delegate.unproxy( maybeProxy ); + } + + @Override + public Object unproxyAndReassociate(Object maybeProxy) { + return delegate.unproxyAndReassociate( maybeProxy ); + } + + @Override + public void checkUniqueness(EntityKey key, Object object) { + delegate.checkUniqueness( key, object ); + } + + @Override + public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key, Object object) { + return delegate.narrowProxy( proxy, persister, key, object ); + } + + @Override + public Object proxyFor(EntityPersister persister, EntityKey key, Object impl) { + return delegate.proxyFor( persister, key, impl ); + } + + @Override + public Object proxyFor(Object impl) { + return delegate.proxyFor( impl ); + } + + @Override + public Object proxyFor(EntityHolder holder, EntityPersister persister) { + return delegate.proxyFor( holder, persister ); + } + + @Override + public void addEnhancedProxy(EntityKey key, PersistentAttributeInterceptable entity) { + delegate.addEnhancedProxy( key, entity ); + } + + @Override + public Object getCollectionOwner(Object key, CollectionPersister collectionPersister) { + return delegate.getCollectionOwner( key, collectionPersister ); + } + + @Override + public Object getLoadedCollectionOwnerOrNull(PersistentCollection collection) { + return delegate.getLoadedCollectionOwnerOrNull( collection ); + } + + @Override + public Object getLoadedCollectionOwnerIdOrNull(PersistentCollection collection) { + return delegate.getLoadedCollectionOwnerIdOrNull( collection ); + } + + @Override + public void addUninitializedCollection(CollectionPersister persister, PersistentCollection collection, Object id) { + delegate.addUninitializedCollection( persister, collection, id ); + } + + @Override + public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection collection) { + delegate.addUninitializedDetachedCollection( persister, collection ); + } + + @Override + public void addNewCollection(CollectionPersister persister, PersistentCollection collection) { + delegate.addNewCollection( persister, collection ); + } + + @Override + public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection collection) { + delegate.addInitializedDetachedCollection( collectionPersister, collection ); + } + + @Override + public void replaceCollection(CollectionPersister persister, PersistentCollection oldCollection, PersistentCollection collection) { + delegate.replaceCollection( persister, oldCollection, collection ); + } + + @Override + public CollectionEntry addInitializedCollection(CollectionPersister persister, PersistentCollection collection, Object id) { + return delegate.addInitializedCollection( persister, collection, id ); + } + + @Override + public PersistentCollection getCollection(CollectionKey collectionKey) { + return delegate.getCollection( collectionKey ); + } + + @Override + public void addNonLazyCollection(PersistentCollection collection) { + delegate.addNonLazyCollection( collection ); + } + + @Override + public PersistentCollection getCollectionHolder(Object array) { + return delegate.getCollectionHolder( array ); + } + + @Override + public void addCollectionHolder(PersistentCollection holder) { + delegate.addCollectionHolder( holder ); + } + + @Override + public PersistentCollection removeCollectionHolder(Object array) { + return delegate.removeCollectionHolder( array ); + } + + @Override + public Serializable getSnapshot(PersistentCollection coll) { + return delegate.getSnapshot( coll ); + } + + @Override + public Object getProxy(EntityKey key) { + return delegate.getProxy( key ); + } + + @Override + public void addProxy(EntityKey key, Object proxy) { + delegate.addProxy( key, proxy ); + } + + @Override + public Object removeProxy(EntityKey key) { + return delegate.removeProxy( key ); + } + + @Override + public EntityHolder claimEntityHolderIfPossible(EntityKey key, Object entity, JdbcValuesSourceProcessingState processingState, EntityInitializer initializer) { + return delegate.claimEntityHolderIfPossible( key, entity, processingState, initializer ); + } + + @Override + public EntityHolder addEntityHolder(EntityKey key, Object entity) { + return delegate.addEntityHolder( key, entity ); + } + + @Override + public EntityHolder getEntityHolder(EntityKey key) { + return delegate.getEntityHolder( key ); + } + + @Override + public boolean containsEntityHolder(EntityKey key) { + return delegate.containsEntityHolder( key ); + } + + @Override + public EntityHolder removeEntityHolder(EntityKey key) { + return delegate.removeEntityHolder( key ); + } + + @Override + public void postLoad(JdbcValuesSourceProcessingState processingState, Consumer loadedConsumer) { + delegate.postLoad( processingState, loadedConsumer ); + } + + @Internal + @Override + public Map getEntitiesByKey() { + return delegate.getEntitiesByKey(); + } + + @Internal + @Override + public Map getEntityHoldersByKey() { + return delegate.getEntityHoldersByKey(); + } + + @Override + public Map.Entry[] reentrantSafeEntityEntries() { + return delegate.reentrantSafeEntityEntries(); + } + + @Override + public int getNumberOfManagedEntities() { + return delegate.getNumberOfManagedEntities(); + } + + @Internal + @Override + public Map, CollectionEntry> getCollectionEntries() { + return delegate.getCollectionEntries(); + } + + @Override + public void forEachCollectionEntry(BiConsumer, CollectionEntry> action, boolean concurrent) { + delegate.forEachCollectionEntry( action, concurrent ); + } + + @Deprecated + @Override + public Map> getCollectionsByKey() { + return delegate.getCollectionsByKey(); + } + + @Override + public int getCascadeLevel() { + return delegate.getCascadeLevel(); + } + + @Override + public int incrementCascadeLevel() { + return delegate.incrementCascadeLevel(); + } + + @Override + public int decrementCascadeLevel() { + return delegate.decrementCascadeLevel(); + } + + @Override + public boolean isFlushing() { + return delegate.isFlushing(); + } + + @Override + public void setFlushing(boolean flushing) { + delegate.setFlushing( flushing ); + } + + @Override + public void beforeLoad() { + delegate.beforeLoad(); + } + + @Override + public void afterLoad() { + delegate.afterLoad(); + } + + @Override + public boolean isLoadFinished() { + return delegate.isLoadFinished(); + } + + @Override + public String toString() { + return delegate.toString(); + } + + @Override + public Object getOwnerId(String entityName, String propertyName, Object childEntity, Map mergeMap) { + return delegate.getOwnerId( entityName, propertyName, childEntity, mergeMap ); + } + + @Override + public Object getIndexInOwner(String entity, String property, Object childObject, Map mergeMap) { + return delegate.getIndexInOwner( entity, property, childObject, mergeMap ); + } + + @Override + public void addNullProperty(EntityKey ownerKey, String propertyName) { + delegate.addNullProperty( ownerKey, propertyName ); + } + + @Override + public boolean isPropertyNull(EntityKey ownerKey, String propertyName) { + return delegate.isPropertyNull( ownerKey, propertyName ); + } + + @Override + public boolean isDefaultReadOnly() { + return delegate.isDefaultReadOnly(); + } + + @Override + public void setDefaultReadOnly(boolean readOnly) { + delegate.setDefaultReadOnly( readOnly ); + } + + @Override + public boolean isReadOnly(Object entityOrProxy) { + return delegate.isReadOnly( entityOrProxy ); + } + + @Override + public void setReadOnly(Object entityOrProxy, boolean readOnly) { + delegate.setReadOnly( entityOrProxy, readOnly ); + } + + @Override + public boolean isRemovingOrphanBeforeUpdates() { + return delegate.isRemovingOrphanBeforeUpdates(); + } + + @Override + public void beginRemoveOrphanBeforeUpdates() { + delegate.beginRemoveOrphanBeforeUpdates(); + } + + @Override + public void endRemoveOrphanBeforeUpdates() { + delegate.endRemoveOrphanBeforeUpdates(); + } + + @Override + public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Object generatedId) { + delegate.replaceDelayedEntityIdentityInsertKeys( oldKey, generatedId ); + } + + @Internal + @Override + public void replaceEntityEntryRowId(Object entity, Object rowId) { + delegate.replaceEntityEntryRowId( entity, rowId ); + } + + @Override + public void addChildParent(Object child, Object parent) { + delegate.addChildParent( child, parent ); + } + + @Override + public void removeChildParent(Object child) { + delegate.removeChildParent( child ); + } + + @Override + public void registerInsertedKey(EntityPersister persister, Object id) { + delegate.registerInsertedKey( persister, id ); + } + + @Override + public boolean wasInsertedDuringTransaction(EntityPersister persister, Object id) { + return delegate.wasInsertedDuringTransaction( persister, id ); + } + + @Override + public boolean containsNullifiableEntityKey(Supplier sek) { + return delegate.containsNullifiableEntityKey( sek ); + } + + @Override + public void registerNullifiableEntityKey(EntityKey key) { + delegate.registerNullifiableEntityKey( key ); + } + + @Override + public boolean isNullifiableEntityKeysEmpty() { + return delegate.isNullifiableEntityKeysEmpty(); + } + + @Override + public boolean containsDeletedUnloadedEntityKey(EntityKey ek) { + return delegate.containsDeletedUnloadedEntityKey( ek ); + } + + @Override + public void registerDeletedUnloadedEntityKey(EntityKey key) { + delegate.registerDeletedUnloadedEntityKey( key ); + } + + @Override + public void removeDeletedUnloadedEntityKey(EntityKey key) { + delegate.removeDeletedUnloadedEntityKey( key ); + } + + @Override + public boolean containsDeletedUnloadedEntityKeys() { + return delegate.containsDeletedUnloadedEntityKeys(); + } + + @Override + public int getCollectionEntriesSize() { + return delegate.getCollectionEntriesSize(); + } + + @Override + public CollectionEntry removeCollectionEntry(PersistentCollection collection) { + return delegate.removeCollectionEntry( collection ); + } + + @Override + public void clearCollectionsByKey() { + delegate.clearCollectionsByKey(); + } + + @Override + public PersistentCollection addCollectionByKey(CollectionKey collectionKey, PersistentCollection persistentCollection) { + return delegate.addCollectionByKey( collectionKey, persistentCollection ); + } + + @Override + public void removeCollectionByKey(CollectionKey collectionKey) { + delegate.removeCollectionByKey( collectionKey ); + } + + @Internal + @Override + public Map getEntitySnapshotsByKey() { + return delegate.getEntitySnapshotsByKey(); + } + + @Override + @Internal + public Map getOrInitializeEntitySnapshotsByKey() { + return delegate.getOrInitializeEntitySnapshotsByKey(); + } + + @Override + public Iterator managedEntitiesIterator() { + return delegate.managedEntitiesIterator(); + } + + @Override + public NaturalIdResolutions getNaturalIdResolutions() { + return delegate.getNaturalIdResolutions(); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index fb1c29fe4..0bb0c75ae 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -76,8 +76,7 @@ public static CompletionStage resolve(EntityType entityType, Object idOr * @see OneToOneType#isNull(Object, SharedSessionContractImplementor) */ static boolean isNull(EntityType entityType, Object owner, SharedSessionContractImplementor session) { - if ( entityType instanceof OneToOneType ) { - OneToOneType type = (OneToOneType) entityType; + if ( entityType instanceof OneToOneType type ) { String propertyName = type.getPropertyName(); if ( propertyName != null ) { final EntityPersister ownerPersister = session.getFactory() @@ -126,7 +125,7 @@ static CompletionStage loadByUniqueKey( entityName, uniqueKeyPropertyName, key, - entityType.getIdentifierOrUniqueKeyType( factory ), + entityType.getIdentifierOrUniqueKeyType( factory.getRuntimeMetamodels() ), factory ); @@ -273,7 +272,7 @@ private static CompletionStage resolveIdOrUniqueKey( .reactiveFetch( id, true ) .thenCompose( fetched -> { Object idOrUniqueKey = entityType - .getIdentifierOrUniqueKeyType( session.getFactory() ) + .getIdentifierOrUniqueKeyType( session.getFactory().getRuntimeMetamodels() ) .replace( fetched, null, session, owner, copyCache ); if ( idOrUniqueKey instanceof CompletionStage ) { return ( (CompletionStage) idOrUniqueKey ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java index 5a77bd9f6..087623e44 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java @@ -11,6 +11,7 @@ import org.hibernate.TransientObjectException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.internal.NonNullableTransientDependencies; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SelfDirtinessTracker; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java index 008755086..37f8eaa7e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java @@ -56,7 +56,7 @@ public void execute() throws HibernateException { } private void preRecreate() { - getFastSessionServices() + getEventListenerGroups() .eventListenerGroup_PRE_COLLECTION_RECREATE .fireLazyEventOnEachListener( this::newPreCollectionRecreateEvent, PreCollectionRecreateEventListener::onPreRecreateCollection ); } @@ -66,7 +66,7 @@ private PreCollectionRecreateEvent newPreCollectionRecreateEvent() { } private void postRecreate() { - getFastSessionServices() + getEventListenerGroups() .eventListenerGroup_POST_COLLECTION_RECREATE .fireLazyEventOnEachListener( this::newPostCollectionRecreateEvent, PostCollectionRecreateEventListener::onPostRecreateCollection ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java index 4b086cf34..0f2bdefea 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java @@ -100,7 +100,8 @@ public void execute() throws HibernateException { } private void preRemove() { - getFastSessionServices().eventListenerGroup_PRE_COLLECTION_REMOVE + getEventListenerGroups() + .eventListenerGroup_PRE_COLLECTION_REMOVE .fireLazyEventOnEachListener( this::newPreCollectionRemoveEvent, PreCollectionRemoveEventListener::onPreRemoveCollection ); } @@ -115,7 +116,8 @@ private PreCollectionRemoveEvent newPreCollectionRemoveEvent() { } private void postRemove() { - getFastSessionServices().eventListenerGroup_POST_COLLECTION_REMOVE + getEventListenerGroups() + .eventListenerGroup_POST_COLLECTION_REMOVE .fireLazyEventOnEachListener( this::newPostCollectionRemoveEvent, PostCollectionRemoveEventListener::onPostRemoveCollection ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java index eae872d56..8806b3e9d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java @@ -118,7 +118,8 @@ public void execute() throws HibernateException { } private void preUpdate() { - getFastSessionServices().eventListenerGroup_PRE_COLLECTION_UPDATE + getEventListenerGroups() + .eventListenerGroup_PRE_COLLECTION_UPDATE .fireLazyEventOnEachListener( this::newPreCollectionUpdateEvent, PreCollectionUpdateEventListener::onPreUpdateCollection ); } @@ -132,7 +133,8 @@ private PreCollectionUpdateEvent newPreCollectionUpdateEvent() { } private void postUpdate() { - getFastSessionServices().eventListenerGroup_POST_COLLECTION_UPDATE + getEventListenerGroups() + .eventListenerGroup_POST_COLLECTION_UPDATE .fireLazyEventOnEachListener( this::newPostCollectionUpdateEvent, PostCollectionUpdateEventListener::onPostUpdateCollection ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactivePersistenceContextAdapter.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactivePersistenceContextAdapter.java deleted file mode 100644 index 33750cc0d..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactivePersistenceContextAdapter.java +++ /dev/null @@ -1,134 +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.engine.impl; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; - -import org.hibernate.HibernateException; -import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.internal.StatefulPersistenceContext; -import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; -import org.hibernate.reactive.session.ReactiveSession; - -import static java.lang.invoke.MethodHandles.lookup; -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.voidFuture; - -/** - * Add reactive methods to a {@link PersistenceContext}. - */ -public class ReactivePersistenceContextAdapter extends StatefulPersistenceContext { - - private static final Log LOG = make( Log.class, lookup() ); - - private HashMap entitySnapshotsByKey; - - /** - * Constructs a PersistentContext, bound to the given session. - * - * @param session The session "owning" this context. - */ - public ReactivePersistenceContextAdapter(SharedSessionContractImplementor session) { - super( session ); - } - - public CompletionStage reactiveInitializeNonLazyCollections() throws HibernateException { - final NonLazyCollectionInitializer initializer = new NonLazyCollectionInitializer(); - initializeNonLazyCollections( initializer ); - return initializer.stage; - } - - private class NonLazyCollectionInitializer implements Consumer> { - CompletionStage stage = voidFuture(); - - @Override - public void accept(PersistentCollection nonLazyCollection) { - if ( !nonLazyCollection.wasInitialized() ) { - stage = stage.thenCompose( v -> ( (ReactiveSession) getSession() ) - .reactiveInitializeCollection( nonLazyCollection, false ) ); - } - } - } - - /** - * @deprecated use {@link #reactiveInitializeNonLazyCollections} instead. - */ - @Deprecated - @Override - public void initializeNonLazyCollections() { - // still called by ResultSetProcessorImpl, so can't throw UnsupportedOperationException - } - - @Deprecated - @Override - public Object[] getDatabaseSnapshot(Object id, EntityPersister persister) throws HibernateException { - throw LOG.nonReactiveMethodCall( "reactiveGetDatabaseSnapshot" ); - } - - private static final Object[] NO_ROW = new Object[]{ StatefulPersistenceContext.NO_ROW }; - - public CompletionStage reactiveGetDatabaseSnapshot(Object id, EntityPersister persister) - throws HibernateException { - - SessionImplementor session = (SessionImplementor) getSession(); - final EntityKey key = session.generateEntityKey( id, persister ); - final Object[] cached = entitySnapshotsByKey == null ? null : entitySnapshotsByKey.get( key ); - if ( cached != null ) { - return completedFuture( cached == NO_ROW ? null : cached ); - } - else { - return ( (ReactiveEntityPersister) persister ) - .reactiveGetDatabaseSnapshot( id, session ) - .thenApply( snapshot -> { - if ( entitySnapshotsByKey == null ) { - entitySnapshotsByKey = new HashMap<>( 8 ); - } - entitySnapshotsByKey.put( key, snapshot == null ? NO_ROW : snapshot ); - return snapshot; - } ); - } - } - - //All below methods copy/pasted from superclass because entitySnapshotsByKey is private: - - @Override - public Object[] getCachedDatabaseSnapshot(EntityKey key) { - final Object[] snapshot = entitySnapshotsByKey == null ? null : entitySnapshotsByKey.get( key ); - if ( snapshot == NO_ROW ) { - throw new IllegalStateException( - "persistence context reported no row snapshot for " - + infoString( key.getEntityName(), key.getIdentifier() ) - ); - } - return snapshot; - } - - @Override - public void clear() { - super.clear(); - entitySnapshotsByKey = null; - } - - @Override - public Object removeEntity(EntityKey key) { - Object result = super.removeEntity(key); - if (entitySnapshotsByKey != null ) { - entitySnapshotsByKey.remove(key); - } - return result; - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/ReactiveResolveNaturalIdEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/ReactiveResolveNaturalIdEventListener.java deleted file mode 100644 index fe0fbb96a..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/ReactiveResolveNaturalIdEventListener.java +++ /dev/null @@ -1,29 +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.event; - -import org.hibernate.event.spi.ResolveNaturalIdEvent; - -import java.util.concurrent.CompletionStage; - -/** - * Defines the contract for handling of resolve natural id events generated from a session. - * - * @author Eric Dalquist - * @author Steve Ebersole - * - * @see org.hibernate.event.spi.ResolveNaturalIdEventListener - */ -public interface ReactiveResolveNaturalIdEventListener { - - /** - * Handle the given resolve natural id event. - * - * @param event The resolve natural id event to be handled. - */ - CompletionStage onReactiveResolveNaturalId(ResolveNaturalIdEvent event); - -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java index 2772cc15d..b257229b2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java @@ -139,9 +139,8 @@ protected void logFlushResults(FlushEvent event) { session.getActionQueue().numberOfCollectionRemovals(), persistenceContext.getCollectionEntriesSize() ); - new EntityPrinter( session.getFactory() ).toString( - persistenceContext.getEntityHoldersByKey().entrySet() - ); + new EntityPrinter( session.getFactory() ) + .logEntities( persistenceContext.getEntityHoldersByKey().entrySet() ); } /** @@ -227,7 +226,7 @@ private int flushEntities(final FlushEvent event, final PersistenceContext persi final EventSource source = event.getSession(); final Iterable flushListeners = source.getFactory() - .getFastSessionServices() + .getEventListenerGroups() .eventListenerGroup_FLUSH_ENTITY .listeners(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java index 66151f03b..664b09da3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java @@ -13,7 +13,6 @@ import org.hibernate.TransientObjectException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; -import org.hibernate.classic.Lifecycle; import org.hibernate.engine.internal.CascadePoint; import org.hibernate.engine.internal.Nullability; import org.hibernate.engine.spi.EntityEntry; @@ -22,13 +21,13 @@ import org.hibernate.engine.spi.Status; import org.hibernate.event.internal.OnUpdateVisitor; import org.hibernate.event.internal.PostDeleteEventListenerStandardImpl; +import org.hibernate.event.service.spi.EventListenerGroups; import org.hibernate.event.service.spi.JpaBootstrapSensitive; import org.hibernate.event.spi.DeleteContext; import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.DeleteEventListener; import org.hibernate.event.spi.EventSource; import org.hibernate.internal.EmptyInterceptor; -import org.hibernate.internal.FastSessionServices; import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.CallbackType; @@ -205,17 +204,6 @@ private CompletionStage fetchAndDelete(DeleteEvent event, DeleteContext tr } - protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) { - if ( persister.implementsLifecycle() ) { - LOG.debug( "Calling onDelete()" ); - if ( ( (Lifecycle) entity ).onDelete( session ) ) { - LOG.debug( "Deletion vetoed by onDelete()" ); - return true; - } - } - return false; - } - private CompletionStage deleteTransientInstance(DeleteEvent event, DeleteContext transientEntities, Object entity) { LOG.trace( "Entity was not persistent in delete processing" ); @@ -291,23 +279,20 @@ private CompletionStage delete( Object version, EntityEntry entry) { callbackRegistry.preRemove( entity ); - if ( !invokeDeleteLifecycle( source, entity, persister ) ) { - return deleteEntity( - source, - entity, - entry, - event.isCascadeDeleteEnabled(), - event.isOrphanRemovalBeforeUpdates(), - persister, - transientEntities - ) - .thenAccept( v -> { - if ( source.getFactory().getSessionFactoryOptions().isIdentifierRollbackEnabled() ) { - persister.resetIdentifier( entity, id, version, source ); - } - } ); - } - return voidFuture(); + return deleteEntity( + source, + entity, + entry, + event.isCascadeDeleteEnabled(), + event.isOrphanRemovalBeforeUpdates(), + persister, + transientEntities + ) + .thenAccept( v -> { + if ( source.getFactory().getSessionFactoryOptions().isIdentifierRollbackEnabled() ) { + persister.resetIdentifier( entity, id, version, source ); + } + } ); } /** @@ -315,7 +300,6 @@ private CompletionStage delete( */ private boolean canBeDeletedWithoutLoading(EventSource source, EntityPersister persister) { return source.getInterceptor() == EmptyInterceptor.INSTANCE - && !persister.implementsLifecycle() && !persister.hasSubclasses() //TODO: should be unnecessary, using EntityPersister.getSubclassPropertyTypeClosure(), etc && !persister.hasCascadeDelete() && !persister.hasNaturalIdentifier() @@ -325,7 +309,7 @@ private boolean canBeDeletedWithoutLoading(EventSource source, EntityPersister p } private static boolean hasCustomEventListeners(EventSource source) { - final FastSessionServices fss = source.getFactory().getFastSessionServices(); + final EventListenerGroups fss = source.getFactory().getEventListenerGroups(); // Bean Validation adds a PRE_DELETE listener // and Envers adds a POST_DELETE listener return fss.eventListenerGroup_PRE_DELETE.count() > 0 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 8f821a986..9804a02f6 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,8 +27,7 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LoadEvent; import org.hibernate.event.spi.LoadEventListener; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.PersistenceContextEntry; +import org.hibernate.loader.internal.CacheLoadHelper; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; @@ -40,7 +39,6 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.reactive.event.ReactiveLoadEventListener; -import org.hibernate.reactive.loader.entity.ReactiveCacheEntityLoaderHelper; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; @@ -49,7 +47,10 @@ 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.pretty.MessageHelper.infoString; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound; import static org.hibernate.reactive.session.impl.SessionUtil.throwEntityNotFound; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -399,7 +400,7 @@ private static CompletionStage loadWithProxyFactory(LoadEvent event, Ent if ( proxy != null ) { LOG.trace( "Entity proxy found in session cache" ); - if ( LOG.isDebugEnabled() && HibernateProxy.extractLazyInitializer( proxy ).isUnwrap() ) { + if ( LOG.isDebugEnabled() && extractLazyInitializer( proxy ).isUnwrap() ) { LOG.debug( "Ignoring NO_PROXY to honor laziness" ); } @@ -429,7 +430,7 @@ private static PersistentAttributeInterceptable createBatchLoadableEnhancedProxy } private static Object proxyOrCached(LoadEvent event, EntityPersister persister, EntityKey keyToLoad) { - final Object cachedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( + final Object cachedEntity = loadFromSecondLevelCache( event.getSession(), null, LockMode.NONE, @@ -450,7 +451,7 @@ private static Object proxyOrCached(LoadEvent event, EntityPersister persister, return options.isCheckDeleted() && wasDeleted( persistenceContext, existing ) ? null : existing; } if ( persister.hasSubclasses() ) { - final Object cachedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( + final Object cachedEntity = loadFromSecondLevelCache( event.getSession(), null, LockMode.NONE, @@ -652,9 +653,9 @@ private CompletionStage doLoad( return nullFuture(); } else { - final PersistenceContextEntry persistenceContextEntry = - ReactiveCacheEntityLoaderHelper.INSTANCE.loadFromSessionCache( event, keyToLoad, options ); - final Object entity = persistenceContextEntry.getEntity(); + 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(); } @@ -668,9 +669,7 @@ private static CompletionStage initializeIfNecessary(Object entity) { if ( isPersistentAttributeInterceptable( entity ) ) { final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( entity ); final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor) { - final EnhancementAsProxyLazinessInterceptor lazinessInterceptor = - (EnhancementAsProxyLazinessInterceptor) interceptor; + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { final SharedSessionContractImplementor session = lazinessInterceptor.getLinkedSession(); if ( session == null ) { throw LOG.sessionClosedLazyInitializationException(); @@ -691,7 +690,8 @@ private CompletionStage loadFromCacheOrDatasource( EntityPersister persister, EntityKey keyToLoad) { final EventSource session = event.getSession(); - final Object entity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(event, persister, keyToLoad); + final Object entity = session + .loadFromSecondLevelCache( persister, keyToLoad, event.getInstanceToLoad(), event.getLockMode() ); if ( entity != null ) { if ( LOG.isTraceEnabled() ) { LOG.tracev( @@ -740,6 +740,10 @@ private void cacheNaturalId(LoadEvent event, EntityPersister persister, EventSou * @return The object loaded from the datasource, or null if not found. */ protected CompletionStage loadFromDatasource(LoadEvent event, EntityPersister persister) { + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Entity not resolved in any cache, loading from datastore: " + + infoString( persister, event.getEntityId(), event.getFactory() ) ); + } return ( (ReactiveEntityPersister) persister ) .reactiveLoad( event.getEntityId(), @@ -754,16 +758,14 @@ protected CompletionStage loadFromDatasource(LoadEvent event, EntityPers // persister/loader/initializer sensitive to this fact - possibly // passing LoadType along - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entity ); - if ( lazyInitializer != null ) { - entity = lazyInitializer.getImplementation(); - } + final LazyInitializer lazyInitializer = extractLazyInitializer( entity ); + final Object impl = lazyInitializer != null ? lazyInitializer.getImplementation() : entity; final StatisticsImplementor statistics = event.getSession().getFactory().getStatistics(); if ( event.isAssociationFetch() && statistics.isStatisticsEnabled() ) { statistics.fetchEntity( event.getEntityClassName() ); } - return entity; + return impl; } ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java index 7f258a1d4..e6cc4cd95 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java @@ -94,7 +94,9 @@ protected Map getMergeMap(MergeContext context) { @Override public CompletionStage reactiveOnMerge(MergeEvent event) throws HibernateException { final EventSource session = event.getSession(); - final EntityCopyObserver entityCopyObserver = createEntityCopyObserver( session ); + final EntityCopyObserver entityCopyObserver = session.getFactory() + .getEntityCopyObserver() + .createEntityCopyObserver(); final MergeContext mergeContext = new MergeContext( session, entityCopyObserver ); return reactiveOnMerge( event, mergeContext ) .thenAccept( v -> entityCopyObserver.topLevelMergeComplete( session ) ) @@ -104,10 +106,6 @@ public CompletionStage reactiveOnMerge(MergeEvent event) throws HibernateE } ); } - private EntityCopyObserver createEntityCopyObserver(final EventSource session) { - return session.getFactory().getFastSessionServices().entityCopyObserverFactory.createEntityCopyObserver(); - } - /** * Handle the given merge event. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactivePostLoadEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactivePostLoadEventListener.java index 053c8465f..e1af57441 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactivePostLoadEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactivePostLoadEventListener.java @@ -6,7 +6,6 @@ package org.hibernate.reactive.event.impl; import org.hibernate.AssertionFailure; -import org.hibernate.classic.Lifecycle; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostLoadEvent; @@ -21,7 +20,6 @@ /** * We do two things here: *
      - *
    • Call {@link Lifecycle} interface if necessary
    • *
    • Perform needed {@link EntityEntry#getLockMode()} related processing
    • *
    * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java deleted file mode 100644 index 783c02ec8..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java +++ /dev/null @@ -1,135 +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.event.impl; - -import java.lang.invoke.MethodHandles; -import java.util.concurrent.CompletionStage; - -import org.hibernate.HibernateException; -import org.hibernate.engine.spi.NaturalIdResolutions; -import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.ResolveNaturalIdEvent; -import org.hibernate.event.spi.ResolveNaturalIdEventListener; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.reactive.event.ReactiveResolveNaturalIdEventListener; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; -import org.hibernate.stat.spi.StatisticsImplementor; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.NANOSECONDS; -import static org.hibernate.pretty.MessageHelper.infoString; -import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; - -/** - * A reactive {@link org.hibernate.event.internal.DefaultResolveNaturalIdEventListener}. - */ -public class DefaultReactiveResolveNaturalIdEventListener implements ReactiveResolveNaturalIdEventListener, ResolveNaturalIdEventListener { - - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - @Override - public void onResolveNaturalId(ResolveNaturalIdEvent event) throws HibernateException { - throw new UnsupportedOperationException(); - } - - @Override - public CompletionStage onReactiveResolveNaturalId(ResolveNaturalIdEvent event) throws HibernateException { - return resolveNaturalId( event ).thenAccept( event::setEntityId ); - } - - /** - * Coordinates the efforts to load a given entity. First, an attempt is - * made to load the entity from the session-level cache. If not found there, - * an attempt is made to locate it in second-level cache. Lastly, an - * attempt is made to load it directly from the datasource. - * - * @param event The load event - * - * @return The loaded entity, or null. - */ - protected CompletionStage resolveNaturalId(ResolveNaturalIdEvent event) { - final EntityPersister persister = event.getEntityPersister(); - - if ( LOG.isTraceEnabled() ) { - LOG.tracev( - "Attempting to resolve: {0}#{1}", - infoString( persister ), - event.getNaturalIdValues() - ); - } - - final Object entityId = resolveFromCache( event ); - if ( entityId != null ) { - if ( LOG.isTraceEnabled() ) { - LOG.tracev( - "Resolved object in cache: {0}#{1}", - infoString( persister ), - event.getNaturalIdValues() ); - } - return completedFuture( entityId ); - } - - if ( LOG.isTraceEnabled() ) { - LOG.tracev( - "Object not resolved in any cache: {0}#{1}", - infoString( persister ), - event.getNaturalIdValues() - ); - } - - return loadFromDatasource( event ); - } - - /** - * Attempts to resolve the entity id corresponding to the event's natural id values from the session - * - * @param event The load event - * @return The entity from the cache, or null. - */ - protected Object resolveFromCache(ResolveNaturalIdEvent event) { - return getNaturalIdResolutions( event ) - .findCachedIdByNaturalId( event.getOrderedNaturalIdValues(), event.getEntityPersister() ); - } - - /** - * Performs the process of loading an entity from the configured - * underlying datasource. - * - * @param event The load event - * - * @return The object loaded from the datasource, or null if not found. - */ - protected CompletionStage loadFromDatasource(ResolveNaturalIdEvent event) { - final EventSource session = event.getSession(); - final EntityPersister entityPersister = event.getEntityPersister(); - final StatisticsImplementor statistics = session.getFactory().getStatistics(); - final boolean statisticsEnabled = statistics.isStatisticsEnabled(); - final long startTime = statisticsEnabled ? System.nanoTime() : 0; - - return ( (ReactiveEntityPersister) entityPersister ) - .reactiveLoadEntityIdByNaturalId( event.getOrderedNaturalIdValues(), event.getLockOptions(), session ) - .thenApply( pk -> { - if ( statisticsEnabled ) { - long milliseconds = MILLISECONDS.convert( System.nanoTime() - startTime, NANOSECONDS ); - statistics.naturalIdQueryExecuted( entityPersister.getRootEntityName(), milliseconds ); - } - - //PK can be null if the entity doesn't exist - if ( pk != null ) { - getNaturalIdResolutions( event ) - .cacheResolutionFromLoad( pk, event.getOrderedNaturalIdValues(), entityPersister ); - } - - return pk; - } ); - } - - private static NaturalIdResolutions getNaturalIdResolutions(ResolveNaturalIdEvent event) { - return event.getSession().getPersistenceContextInternal().getNaturalIdResolutions(); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/DatabaseSnapshotExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/DatabaseSnapshotExecutor.java index 3fdd5acf1..33e8465b2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/DatabaseSnapshotExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/DatabaseSnapshotExecutor.java @@ -79,7 +79,7 @@ class DatabaseSnapshotExecutor { DatabaseSnapshotExecutor::visitEmptyFetchList, true, new LoadQueryInfluencers( sessionFactory ), - sessionFactory + sessionFactory.getSqlTranslationEngine() ); final NavigablePath rootPath = new NavigablePath( entityDescriptor.getEntityName() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveCollectionBatchLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveCollectionBatchLoaderArrayParam.java index efeafdd02..0960a2ebc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveCollectionBatchLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveCollectionBatchLoaderArrayParam.java @@ -18,9 +18,9 @@ import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.internal.MultiKeyLoadHelper; import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; import org.hibernate.query.spi.QueryOptions; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; @@ -34,7 +34,6 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; -import org.hibernate.type.BasicType; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER; @@ -65,18 +64,14 @@ public ReactiveCollectionBatchLoaderArrayParam( ); } - final SimpleForeignKeyDescriptor keyDescriptor = (SimpleForeignKeyDescriptor) getLoadable().getKeyDescriptor(); - + final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor(); + final JdbcMapping jdbcMapping = keyDescriptor.getSingleJdbcMapping(); + final Class jdbcJavaTypeClass = jdbcMapping.getJdbcJavaType().getJavaTypeClass(); arrayElementType = keyDescriptor.getJavaType().getJavaTypeClass(); - Class arrayClass = Array.newInstance( arrayElementType, 0 ).getClass(); - final BasicType arrayBasicType = getSessionFactory().getTypeConfiguration() - .getBasicTypeRegistry() - .getRegisteredType( arrayClass ); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - arrayBasicType, - keyDescriptor.getJdbcMapping(), - arrayClass, + keyDescriptor.getSingleJdbcMapping(), + jdbcJavaTypeClass, getSessionFactory() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java index 9ff3c2ba7..ba69fd407 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java @@ -63,12 +63,10 @@ public ReactiveEntityBatchLoaderArrayParam( } identifierMapping = (BasicEntityIdentifierMapping) getLoadable().getIdentifierMapping(); - final Class arrayClass = - Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 ).getClass(); + final Class idClass = identifierMapping.getJavaType().getJavaTypeClass(); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - sessionFactory.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ), identifierMapping.getJdbcMapping(), - arrayClass, + idClass, sessionFactory ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java index fd7cb8794..fa0b1885d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java @@ -21,17 +21,15 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.LoadEvent; -import org.hibernate.event.spi.LoadEventListener; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; -import org.hibernate.loader.ast.internal.LoaderHelper; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.internal.MultiIdEntityLoaderArrayParam; import org.hibernate.loader.ast.internal.MultiKeyLoadLogging; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.QueryOptions; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; @@ -44,11 +42,12 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; -import org.hibernate.type.BasicType; +import static org.hibernate.event.spi.LoadEventListener.GET; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.resolveArrayJdbcMapping; +import static org.hibernate.loader.internal.CacheLoadHelper.loadFromSessionCache; import static org.hibernate.reactive.loader.ast.internal.ReactiveLoaderHelper.loadByArrayParameter; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.loop; @@ -67,15 +66,12 @@ public ReactiveMultiIdEntityLoaderArrayParam( SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); final Class arrayClass = createTypedArray( 0 ).getClass(); - JdbcMapping jdbcMapping = getIdentifierMapping().getJdbcMapping(); - BasicType registeredType = getSessionFactory().getTypeConfiguration() - .getBasicTypeRegistry() - .getRegisteredType( arrayClass ); - JdbcMapping arrayJdbcMapping1 = resolveArrayJdbcMapping( registeredType, jdbcMapping, arrayClass, getSessionFactory() ); -// JavaType objectJavaType = getSessionFactory().getTypeConfiguration() -// .getJavaTypeRegistry() -// .resolveDescriptor( ReactiveArrayJdbcType.class ); - arrayJdbcMapping = arrayJdbcMapping1; + final Class idClass = getIdentifierMapping().getJavaType().getJavaTypeClass(); + arrayJdbcMapping = resolveArrayJdbcMapping( + getIdentifierMapping().getJdbcMapping(), + idClass, + getSessionFactory() + ); jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping ); } @@ -113,24 +109,17 @@ protected CompletionStage> performOrderedMultiLoad( final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) { - LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) - ); - Object managedEntity = null; if ( loadOptions.isSessionCheckingEnabled() ) { // look for it in the Session first - final CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.loadFromSessionCacheStatic( - loadEvent, + final PersistenceContextEntry persistenceContextEntry = loadFromSessionCache( entityKey, - LoadEventListener.GET + lockOptions, + GET, + session ); - managedEntity = persistenceContextEntry.getEntity(); + managedEntity = persistenceContextEntry.entity(); if ( managedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() @@ -142,12 +131,9 @@ protected CompletionStage> performOrderedMultiLoad( } if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { + final EntityPersister persister = getLoadable().getEntityPersister(); // look for it in the SessionFactory - managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey - ); + managedEntity = session.loadFromSecondLevelCache( persister, entityKey, null, lockOptions.getLockMode() ); } if ( managedEntity != null ) { @@ -217,9 +203,7 @@ protected CompletionStage> performOrderedMultiLoad( } ) .thenApply( ignore -> { final PersistenceContext persistenceContext = session.getPersistenceContext(); - for ( int i = 0; i < idsToLoadFromDatabaseResultIndexes.size(); i++ ) { - final Integer resultIndex = idsToLoadFromDatabaseResultIndexes.get( i ); - + for ( final Integer resultIndex : idsToLoadFromDatabaseResultIndexes ) { // the element value at this position in the result List should be // the EntityKey for that entity - reuse it final EntityKey entityKey = (EntityKey) result.get( resultIndex ); @@ -337,32 +321,24 @@ protected final K[] processResolvableEntities( for ( int i = 0; i < ids.length; i++ ) { final Object id; if ( coerce ) { - //noinspection unchecked - id = (K) getLoadable().getIdentifierMapping().getJavaType().coerce( ids[i], session ); + id = getLoadable().getIdentifierMapping().getJavaType().coerce( ids[i], session ); } else { id = ids[i]; } final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); - final LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) - ); - Object resolvedEntity = null; // look for it in the Session first - final CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.loadFromSessionCacheStatic( - loadEvent, + final PersistenceContextEntry persistenceContextEntry = loadFromSessionCache( entityKey, - LoadEventListener.GET + lockOptions, + GET, + session ); if ( loadOptions.isSessionCheckingEnabled() ) { - resolvedEntity = persistenceContextEntry.getEntity(); + resolvedEntity = persistenceContextEntry.entity(); if ( resolvedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() @@ -374,11 +350,8 @@ protected final K[] processResolvableEntities( } if ( resolvedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { - resolvedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey - ); + final EntityPersister persister = getLoadable().getEntityPersister(); + resolvedEntity = session.loadFromSecondLevelCache( persister, entityKey, null, lockOptions.getLockMode() ); } if ( resolvedEntity != null ) { @@ -391,7 +364,6 @@ protected final K[] processResolvableEntities( if ( nonResolvedIds == null ) { nonResolvedIds = new ArrayList<>(); } - //noinspection unchecked,CastCanBeRemovedNarrowingVariableType nonResolvedIds.add( (K) id ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java index 9a87f2359..92f4e996a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java @@ -20,19 +20,15 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.LoadEvent; -import org.hibernate.event.spi.LoadEventListener; import org.hibernate.internal.util.collections.CollectionHelper; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; -import org.hibernate.loader.ast.internal.LoaderHelper; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry; import org.hibernate.mapping.PersistentClass; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.QueryOptions; @@ -50,6 +46,8 @@ import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; +import static org.hibernate.event.spi.LoadEventListener.GET; +import static org.hibernate.loader.internal.CacheLoadHelper.loadFromSessionCache; 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.voidFuture; @@ -114,24 +112,17 @@ protected CompletionStage> performOrderedMultiLoad( final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) { - LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) - ); - Object managedEntity = null; if ( loadOptions.isSessionCheckingEnabled() ) { // look for it in the Session first - CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE.loadFromSessionCache( - loadEvent, + PersistenceContextEntry persistenceContextEntry = loadFromSessionCache( entityKey, - LoadEventListener.GET + lockOptions, + GET, + session ); - managedEntity = persistenceContextEntry.getEntity(); + managedEntity = persistenceContextEntry.entity(); if ( managedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() && !persistenceContextEntry.isManaged() ) { // put a null in the result @@ -141,12 +132,10 @@ protected CompletionStage> performOrderedMultiLoad( } if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { + final EntityPersister persister = getLoadable().getEntityPersister(); // look for it in the SessionFactory - managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey - ); + managedEntity = session + .loadFromSecondLevelCache( persister, entityKey, null, lockOptions.getLockMode() ); } if ( managedEntity != null ) { @@ -324,34 +313,26 @@ protected CompletionStage> performUnorderedMultiLoad( final List nonManagedIds = new ArrayList<>(); final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); - for ( int i = 0; i < ids.length; i++ ) { + for ( Object o : ids ) { final Object id = coerce - ? getEntityDescriptor().getIdentifierMapping().getJavaType().coerce( ids[i], session ) - : ids[i]; + ? getEntityDescriptor().getIdentifierMapping().getJavaType().coerce( o, session ) + : o; final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); - LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - getReadOnlyFromLoadQueryInfluencers( session ) - ); - - Object managedEntity = null; + Object cachedEntity = null; // look for it in the Session first - CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE - .loadFromSessionCache( - loadEvent, - entityKey, - LoadEventListener.GET - ); + PersistenceContextEntry persistenceContextEntry = loadFromSessionCache( + entityKey, + lockOptions, + GET, + session + ); if ( loadOptions.isSessionCheckingEnabled() ) { - managedEntity = persistenceContextEntry.getEntity(); + cachedEntity = persistenceContextEntry.entity(); - if ( managedEntity != null + if ( cachedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() && !persistenceContextEntry.isManaged() ) { foundAnyManagedEntities = true; @@ -360,18 +341,20 @@ protected CompletionStage> performUnorderedMultiLoad( } } - if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { - managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey + if ( cachedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { + final EntityPersister persister = getLoadable().getEntityPersister(); + cachedEntity = session.loadFromSecondLevelCache( + persister, + entityKey, + null, + lockOptions.getLockMode() ); } - if ( managedEntity != null ) { + if ( cachedEntity != null ) { foundAnyManagedEntities = true; //noinspection unchecked - result.add( (T) managedEntity ); + result.add( (T) cachedEntity ); } else { nonManagedIds.add( id ); @@ -427,14 +410,4 @@ protected CompletionStage> performUnorderedMultiLoad( } ) .thenApply( v -> result ); } - - private Boolean getReadOnlyFromLoadQueryInfluencers(SharedSessionContractImplementor session) { - Boolean readOnly = null; - final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers(); - if ( loadQueryInfluencers != null ) { - readOnly = loadQueryInfluencers.getReadOnly(); - } - return readOnly; - } - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java index 56fef5be9..ede1c6cf7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java @@ -238,7 +238,7 @@ public CompletionStage reactiveSelectByNaturalId( fetchProcessor, true, new LoadQueryInfluencers( sessionFactory ), - sessionFactory + sessionFactory.getSqlTranslationEngine() ); final TableGroup rootTableGroup = entityDescriptor().createRootTableGroup( diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveStandardBatchLoaderFactory.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveStandardBatchLoaderFactory.java index 5d5c03fb5..fa5d075ba 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveStandardBatchLoaderFactory.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveStandardBatchLoaderFactory.java @@ -33,7 +33,7 @@ public EntityBatchLoader createEntityBatchLoader( // NOTE : don't use the EntityIdentifierMapping here because it will not be known until later final Type identifierType = entityDescriptor.getEntityPersister().getIdentifierType(); - final int idColumnCount = identifierType.getColumnSpan( factory ); + final int idColumnCount = identifierType.getColumnSpan( factory.getRuntimeMetamodels() ); if ( idColumnCount == 1 && MultiKeyLoadHelper.supportsSqlArrayType( dialect ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/entity/ReactiveCacheEntityLoaderHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/entity/ReactiveCacheEntityLoaderHelper.java deleted file mode 100644 index 703148cd7..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/entity/ReactiveCacheEntityLoaderHelper.java +++ /dev/null @@ -1,148 +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.loader.entity; - -import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock; -import org.hibernate.HibernateException; -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.EntityKey; -import org.hibernate.engine.spi.SessionImplementor; -import org.hibernate.engine.spi.Status; -import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.LoadEvent; -import org.hibernate.event.spi.LoadEventListener; -import org.hibernate.internal.CoreLogging; -import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.EntityStatus; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.PersistenceContextEntry; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; -import org.hibernate.sql.results.LoadingLogger; - -/** - * @author Gavin King - * - * @see org.hibernate.loader.ast.internal.CacheEntityLoaderHelper - */ -public class ReactiveCacheEntityLoaderHelper { - - public static final ReactiveCacheEntityLoaderHelper INSTANCE = new ReactiveCacheEntityLoaderHelper(); - - private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ReactiveCacheEntityLoaderHelper.class ); - - private ReactiveCacheEntityLoaderHelper() {} - - /** - * Attempts to locate the entity in the session-level cache. - *

    - * If allowed to return nulls, then if the entity happens to be found in - * the session cache, we check the entity type for proper handling - * of entity hierarchies. - *

    - * If checkDeleted was set to true, then if the entity is found in the - * session-level cache, its current status within the session cache - * is checked to see if it has previously been scheduled for deletion. - * - * @param event The load event - * @param keyToLoad The EntityKey representing the entity to be loaded. - * @param options The load options. - * - * @return The entity from the session-level cache, or null. - * - * @throws HibernateException Generally indicates problems applying a lock-mode. - */ - public PersistenceContextEntry loadFromSessionCache( - final LoadEvent event, - final EntityKey keyToLoad, - final LoadEventListener.LoadType options) throws HibernateException { - - SessionImplementor session = event.getSession(); - Object old = session.getEntityUsingInterceptor( keyToLoad ); - - if ( old != null ) { - // this object was already loaded - EntityEntry oldEntry = session.getPersistenceContext().getEntry( old ); - if ( options.isCheckDeleted() ) { - if ( oldEntry.getStatus().isDeletedOrGone() ) { - LoadingLogger.LOGGER.debug( - "Load request found matching entity in context, but it is scheduled for removal; returning null" ); - return new PersistenceContextEntry( old, EntityStatus.REMOVED_ENTITY_MARKER ); - } - } - if ( options.isAllowNulls() ) { - final EntityPersister persister = event.getSession() - .getFactory() - .getRuntimeMetamodels() - .getMappingMetamodel() - .getEntityDescriptor( keyToLoad.getEntityName() ); - if ( !persister.isInstance( old ) ) { - LOG.debug( - "Load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null" - ); - return new PersistenceContextEntry( old, EntityStatus.INCONSISTENT_RTN_CLASS_MARKER ); - } - } - upgradeLock( old, oldEntry, event.getLockOptions(), event.getSession() ); - } - - return new PersistenceContextEntry( old, EntityStatus.MANAGED ); - } - - /** - * see org.hibernate.event.internal.AbstractLockUpgradeEventListener#upgradeLock(Object, EntityEntry, LockOptions, EventSource) - */ - private static void upgradeLock(Object object, EntityEntry entry, LockOptions lockOptions, EventSource source) { - - LockMode requestedLockMode = lockOptions.getLockMode(); - if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) { - // The user requested a "greater" (i.e. more restrictive) form of pessimistic lock - - if ( entry.getStatus() != Status.MANAGED ) { - throw new ObjectDeletedException( - "attempted to lock a deleted instance", - entry.getId(), - entry.getPersister().getEntityName() - ); - } - - final EntityPersister persister = entry.getPersister(); - final boolean cachingEnabled = persister.canWriteToCache(); - SoftLock lock = null; - Object ck = null; - try { - if ( cachingEnabled ) { - EntityDataAccess cache = persister.getCacheAccessStrategy(); - ck = cache.generateCacheKey( entry.getId(), persister, source.getFactory(), source.getTenantIdentifier() ); - lock = cache.lockItem( source, ck, entry.getVersion() ); - } - - if ( persister.isVersioned() && requestedLockMode == LockMode.PESSIMISTIC_FORCE_INCREMENT ) { - // todo : should we check the current isolation mode explicitly? - Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), source ); - entry.forceLocked( object, nextVersion ); - } - else { - ( (ReactiveEntityPersister) persister ) - .reactiveLock( entry.getId(), entry.getVersion(), object, lockOptions, source ); - } - 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( source, ck, lock ); - } - } - - } - } -} 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 a29799b23..c1015939c 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 @@ -39,13 +39,12 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.LoadEvent; import org.hibernate.generator.OnExecutionGenerator; import org.hibernate.generator.values.GeneratedValuesMutationDelegate; import org.hibernate.internal.util.collections.ArrayHelper; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.spi.NaturalIdLoader; +import org.hibernate.loader.ast.spi.SingleIdEntityLoader; import org.hibernate.mapping.PersistentClass; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMappingsList; @@ -222,7 +221,7 @@ default CompletionStage reactiveLock( offset++; } getIdentifierType().nullSafeSet( statement, id, offset, session ); - offset += getIdentifierType().getColumnSpan( getFactory() ); + offset += getIdentifierType().getColumnSpan( getFactory().getRuntimeMetamodels() ); if ( isVersioned() ) { getVersionType().nullSafeSet( statement, version, offset, session ); } @@ -521,26 +520,19 @@ private CompletionStage loadFromDatabaseOrCache( EntityKey entityKey, Object identifier) { - // note that stateless sessions don't interact with second-level cache - if ( session instanceof EventSource && canReadFromCache() ) { - Object cached = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - new LoadEvent( identifier, entity, (EventSource) session, false ), - this, - entityKey - ); - if ( cached != null ) { - return completedFuture( cached ); + if ( canReadFromCache() && session.isEventSource() ) { + final EventSource eventSource = (EventSource) session; + Object loaded = eventSource.loadFromSecondLevelCache( this, entityKey, entity, LockMode.NONE ); + if ( loaded != null ) { + return completedFuture( loaded ); } } - - return getReactiveSingleIdEntityLoader().load( - identifier, - entity, - LockOptions.NONE, - session - ); + return ( (ReactiveSingleIdEntityLoader) determineLoaderToUse( session ) ) + .load( identifier, entity, LockOptions.NONE, session ); } + SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session); + boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue); Object initializeLazyProperty(String fieldName, Object entity, SharedSessionContractImplementor session); 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 0de5391f0..c4e688ce6 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,6 +139,11 @@ protected AttributeMapping buildPluralAttributeMapping( ); } + @Override + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { + return super.determineLoaderToUse( session ); + } + @Override protected InsertCoordinator buildInsertCoordinator() { return ReactiveCoordinatorFactory.buildInsertCoordinator( this, getFactory() ); 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 bc52a7b6e..72b8c040e 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,6 +86,11 @@ public GeneratedValuesMutationDelegate createInsertDelegate() { return ReactiveAbstractEntityPersister.super.createReactiveInsertDelegate(); } + @Override + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { + return super.determineLoaderToUse( session ); + } + @Override protected GeneratedValuesMutationDelegate createUpdateDelegate() { return ReactiveAbstractEntityPersister.super.createReactiveUpdateDelegate(); 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 420200027..c844f7644 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,6 +139,11 @@ protected AttributeMapping buildPluralAttributeMapping( ); } + @Override + public SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session) { + return super.determineLoaderToUse( session ); + } + @Override public NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) { return ReactiveAbstractEntityPersister.super.generateNaturalIdMapping(creationProcess, bootEntityDescriptor); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java index 0d1876320..5607d5935 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java @@ -25,7 +25,6 @@ import org.hibernate.reactive.event.impl.DefaultReactivePersistOnFlushEventListener; import org.hibernate.reactive.event.impl.DefaultReactivePostLoadEventListener; import org.hibernate.reactive.event.impl.DefaultReactiveRefreshEventListener; -import org.hibernate.reactive.event.impl.DefaultReactiveResolveNaturalIdEventListener; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.service.ServiceRegistry; @@ -70,7 +69,6 @@ private void attachEventContextManagingListenersIfRequired(ServiceRegistry servi eventListenerRegistry.getEventListenerGroup( EventType.LOAD ).appendListener( new DefaultReactiveLoadEventListener() ); eventListenerRegistry.getEventListenerGroup( EventType.INIT_COLLECTION ).appendListener( new DefaultReactiveInitializeCollectionEventListener() ); eventListenerRegistry.getEventListenerGroup( EventType.POST_LOAD ).appendListener( new DefaultReactivePostLoadEventListener() ); - eventListenerRegistry.getEventListenerGroup( EventType.RESOLVE_NATURAL_ID ).appendListener( new DefaultReactiveResolveNaturalIdEventListener() ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java index 2ac40168c..ef2d70afe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java @@ -22,6 +22,7 @@ import org.hibernate.reactive.query.sql.spi.ReactiveNamedNativeQueryMemento; import org.hibernate.reactive.query.sql.spi.ReactiveNamedSqmQueryMemento; +import jakarta.persistence.Query; import jakarta.persistence.TypedQueryReference; public class ReactiveNamedObjectRepositoryImpl implements NamedObjectRepository { @@ -42,6 +43,11 @@ public NamedSqmQueryMemento getSqmQueryMemento(String queryName) { return wrapSqmQueryMemento( delegate.getSqmQueryMemento( queryName ) ); } + @Override + public void registerNamedQuery(String name, Query query) { + delegate.registerNamedQuery( name, query ); + } + @Override public void visitSqmQueryMementos(Consumer> action) { delegate.visitSqmQueryMementos( action ); 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 01ee41c90..7c7cfd062 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 @@ -261,7 +261,7 @@ private static CacheableSqmInterpretation buildCacheableSqmInterpretation( domainParameterXref, executionContext.getQueryParameterBindings(), executionContext.getSession().getLoadQueryInfluencers(), - sessionFactory, + sessionFactory.getSqlTranslationEngine(), true ); 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 43271d29e..a17d1eb73 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 @@ -82,7 +82,7 @@ protected SqlAstTranslator createTranslato domainParameterXref, executionContext.getQueryParameterBindings(), executionContext.getSession().getLoadQueryInfluencers(), - factory + factory.getSqlTranslationEngine() ); sqmInterpretation = (SqmTranslation) translator.translate(); 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 index 9d76895b7..bf67d16ea 100644 --- 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 @@ -102,7 +102,7 @@ private SqlAstTranslator createInsertTrans domainParameterXref, executionContext.getQueryParameterBindings(), executionContext.getSession().getLoadQueryInfluencers(), - factory + factory.getSqlTranslationEngine() ) .translate(); 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 index 962149b5b..f49f848f5 100644 --- 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 @@ -103,7 +103,7 @@ private SqlAstTranslator createUpdateTrans domainParameterXref, executionContext.getQueryParameterBindings(), executionContext.getSession().getLoadQueryInfluencers(), - factory + factory.getSqlTranslationEngine() ); final SqmTranslation sqmInterpretation = translator.translate(); 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 74f278fe7..20d0e31c5 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 @@ -96,7 +96,7 @@ default CompletionStage reactiveExecute(DomainQueryExecutionContext exe executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), - factory + factory.getSqlTranslationEngine() ); final Map, List> parameterResolutions; if ( getDomainParameterXref().getSqmParameterCount() == 0 ) { 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 5a4b1c5af..1e063f287 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 @@ -83,12 +83,15 @@ public class ReactiveCteInsertHandler extends CteInsertHandler implements Reacti 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; } @Override @@ -120,7 +123,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), - factory + factory.getSqlTranslationEngine() ); final TableGroup insertingTableGroup = sqmConverter.getMutatingTableGroup(); @@ -182,7 +185,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec 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, getSessionFactory() ) ) { + if ( additionalInsertValues.applySelections( querySpec, sessionFactory ) ) { final CteColumn rowNumberColumn = getCteTable().getCteColumns() .get( getCteTable().getCteColumns().size() - 1 ); final ColumnReference columnReference = new ColumnReference( @@ -207,7 +210,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, - getSessionFactory() + sessionFactory ) ) ); @@ -342,7 +345,7 @@ public CompletionStage reactiveExecute(DomainQueryExecutionContext exec ); final String fragment = ( (BulkInsertionCapableIdentifierGenerator) entityDescriptor.getGenerator() ) .determineBulkInsertionIdentifierGenerationSelectFragment( - getSessionFactory().getSqlStringGenerationContext() + sessionFactory.getSqlStringGenerationContext() ); rowsWithSequenceQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 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 dc2a833c4..0e1097b63 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 @@ -117,7 +117,7 @@ public ReactiveRestrictedDeleteExecutionDelegate( queryOptions, loadQueryInfluencers, queryParameterBindings, - sessionFactory + sessionFactory.getSqlTranslationEngine() ); } 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 f81eab1f9..87aaeb060 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 @@ -28,7 +28,7 @@ import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.internal.StatefulPersistenceContext; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; @@ -55,7 +55,6 @@ import org.hibernate.event.spi.PersistEvent; import org.hibernate.event.spi.RefreshContext; import org.hibernate.event.spi.RefreshEvent; -import org.hibernate.event.spi.ResolveNaturalIdEvent; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; @@ -63,7 +62,11 @@ import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionImpl; import org.hibernate.jpa.spi.NativeQueryTupleTransformer; +import org.hibernate.loader.LoaderLogging; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.internal.LoadAccessContext; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.proxy.HibernateProxy; @@ -87,7 +90,6 @@ import org.hibernate.reactive.common.InternalStateAssertions; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.engine.ReactiveActionQueue; -import org.hibernate.reactive.engine.impl.ReactivePersistenceContextAdapter; import org.hibernate.reactive.event.ReactiveDeleteEventListener; import org.hibernate.reactive.event.ReactiveFlushEventListener; import org.hibernate.reactive.event.ReactiveLoadEventListener; @@ -95,9 +97,9 @@ import org.hibernate.reactive.event.ReactiveMergeEventListener; import org.hibernate.reactive.event.ReactivePersistEventListener; import org.hibernate.reactive.event.ReactiveRefreshEventListener; -import org.hibernate.reactive.event.ReactiveResolveNaturalIdEventListener; import org.hibernate.reactive.event.impl.DefaultReactiveAutoFlushEventListener; import org.hibernate.reactive.event.impl.DefaultReactiveInitializeCollectionEventListener; +import org.hibernate.reactive.loader.ast.spi.ReactiveNaturalIdLoader; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; @@ -186,8 +188,8 @@ private void threadCheck() { } @Override - protected StatefulPersistenceContext createPersistenceContext() { - return new ReactivePersistenceContextAdapter( this ); + protected PersistenceContext createPersistenceContext() { + return new ReactivePersistenceContextAdapter( super.createPersistenceContext() ); } @Override @@ -691,7 +693,7 @@ public CompletionStage reactiveInitializeCollection(PersistentCollection eventListenerGroupInitCollection = fastSessionServices.eventListenerGroup_INIT_COLLECTION; + EventListenerGroup eventListenerGroupInitCollection = getFactory().getEventListenerGroups().eventListenerGroup_INIT_COLLECTION; return eventListenerGroupInitCollection .fireEventOnEachListener( event, @@ -732,7 +734,7 @@ private CompletionStage firePersist(PersistEvent event) { checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - return fastSessionServices.eventListenerGroup_PERSIST + return getFactory().getEventListenerGroups().eventListenerGroup_PERSIST .fireEventOnEachListener( event, (ReactivePersistEventListener l) -> l::reactiveOnPersist ) .handle( (v, e) -> { checkNoUnresolvedActionsAfterOperation(); @@ -750,7 +752,7 @@ else if ( e instanceof RuntimeException ) { private CompletionStage firePersist(PersistContext copiedAlready, PersistEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_PERSIST + return getFactory().getEventListenerGroups().eventListenerGroup_PERSIST .fireEventOnEachListener( event, copiedAlready, (ReactivePersistEventListener l) -> l::reactiveOnPersist ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -774,7 +776,7 @@ public CompletionStage reactivePersistOnFlush(Object entity, PersistContex private CompletionStage firePersistOnFlush(PersistContext copiedAlready, PersistEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_PERSIST + return getFactory().getEventListenerGroups().eventListenerGroup_PERSIST .fireEventOnEachListener( event, copiedAlready, (ReactivePersistEventListener l) -> l::reactiveOnPersist ) .whenComplete( (v, e) -> delayedAfterCompletion() ); } @@ -802,13 +804,13 @@ public CompletionStage reactiveRemove( boolean isCascadeDeleteEnabled, DeleteContext transientEntities) { checkOpenOrWaitingForAutoClose(); - final boolean removingOrphanBeforeUpates = persistenceContext().isRemovingOrphanBeforeUpates(); - if ( LOG.isTraceEnabled() && removingOrphanBeforeUpates ) { + final boolean removingOrphanBeforeUpdates = persistenceContext().isRemovingOrphanBeforeUpdates(); + if ( LOG.isTraceEnabled() && removingOrphanBeforeUpdates ) { logRemoveOrphanBeforeUpdates( "before continuing", entityName, entityName ); } return fireRemove( - new DeleteEvent( entityName, child, isCascadeDeleteEnabled, removingOrphanBeforeUpates, this ), + new DeleteEvent( entityName, child, isCascadeDeleteEnabled, removingOrphanBeforeUpdates, this ), transientEntities ); } @@ -830,10 +832,8 @@ private void logRemoveOrphanBeforeUpdates(String timing, String entityName, Obje private CompletionStage fireRemove(DeleteEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( - event, - (ReactiveDeleteEventListener l) -> l::reactiveOnDelete - ) + return getFactory().getEventListenerGroups().eventListenerGroup_DELETE + .fireEventOnEachListener( event, (ReactiveDeleteEventListener l) -> l::reactiveOnDelete ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -854,9 +854,8 @@ else if ( e instanceof RuntimeException ) { private CompletionStage fireRemove(DeleteEvent event, DeleteContext transientEntities) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, transientEntities, - (ReactiveDeleteEventListener l) -> l::reactiveOnDelete - ) + return getFactory().getEventListenerGroups().eventListenerGroup_DELETE + .fireEventOnEachListener( event, transientEntities, (ReactiveDeleteEventListener l) -> l::reactiveOnDelete ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -892,7 +891,7 @@ private CompletionStage fireMerge(MergeEvent event) { checkTransactionSynchStatus(); checkNoUnresolvedActionsBeforeOperation(); - return fastSessionServices.eventListenerGroup_MERGE + return getFactory().getEventListenerGroups().eventListenerGroup_MERGE .fireEventOnEachListener( event, (ReactiveMergeEventListener l) -> l::reactiveOnMerge ) .handle( (v, e) -> { checkNoUnresolvedActionsAfterOperation(); @@ -914,7 +913,7 @@ else if ( e instanceof RuntimeException ) { private CompletionStage fireMerge(MergeContext copiedAlready, MergeEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_MERGE + return getFactory().getEventListenerGroups().eventListenerGroup_MERGE .fireEventOnEachListener( event, copiedAlready,(ReactiveMergeEventListener l) -> l::reactiveOnMerge ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -954,7 +953,7 @@ public CompletionStage reactiveAutoFlushIfRequired(Set querySpa // } AutoFlushEvent event = new AutoFlushEvent( querySpaces, this ); - return fastSessionServices.eventListenerGroup_AUTO_FLUSH + return getFactory().getEventListenerGroups().eventListenerGroup_AUTO_FLUSH .fireEventOnEachListener( event, (DefaultReactiveAutoFlushEventListener l) -> l::reactiveOnAutoFlush ) .thenApply( v -> event.isFlushRequired() ); } @@ -987,7 +986,7 @@ private CompletionStage doFlush() { throw LOG.flushDuringCascadeIsDangerous(); } - return fastSessionServices.eventListenerGroup_FLUSH + return getFactory().getEventListenerGroups().eventListenerGroup_FLUSH .fireEventOnEachListener( new FlushEvent( this ), (ReactiveFlushEventListener l) -> l::reactiveOnFlush ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -1036,10 +1035,8 @@ CompletionStage fireRefresh(RefreshEvent event) { } pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( - event, - (ReactiveRefreshEventListener l) -> l::reactiveOnRefresh - ) + return getFactory().getEventListenerGroups().eventListenerGroup_REFRESH + .fireEventOnEachListener( event, (ReactiveRefreshEventListener l) -> l::reactiveOnRefresh ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -1059,12 +1056,8 @@ CompletionStage fireRefresh(RefreshEvent event) { private CompletionStage fireRefresh(RefreshContext refreshedAlready, RefreshEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_REFRESH - .fireEventOnEachListener( - event, - refreshedAlready, - (ReactiveRefreshEventListener l) -> l::reactiveOnRefresh - ) + return getFactory().getEventListenerGroups().eventListenerGroup_REFRESH + .fireEventOnEachListener( event, refreshedAlready, (ReactiveRefreshEventListener l) -> l::reactiveOnRefresh ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -1084,10 +1077,8 @@ public CompletionStage reactiveLock(Object object, LockOptions lockOptions private CompletionStage fireLock(LockEvent event) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_LOCK.fireEventOnEachListener( - event, - (ReactiveLockEventListener l) -> l::reactiveOnLock - ) + return getFactory().getEventListenerGroups().eventListenerGroup_LOCK + .fireEventOnEachListener( event, (ReactiveLockEventListener l) -> l::reactiveOnLock ) .handle( (v, e) -> { delayedAfterCompletion(); @@ -1175,11 +1166,21 @@ public CompletionStage> reactiveFind(Class entityClass, Object... @Override public CompletionStage reactiveFind(Class entityClass, Map ids) { - final EntityPersister persister = getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); - return new NaturalIdLoadAccessImpl( persister ).resolveNaturalId( ids ) + final ReactiveEntityPersister persister = entityPersister( entityClass ); + final Object normalizedIdValues = persister.getNaturalIdMapping().normalizeInput( ids ); + return new NaturalIdLoadAccessImpl( this, persister, requireEntityPersister( entityClass ) ) + .resolveNaturalId( normalizedIdValues ) .thenCompose( id -> reactiveFind( entityClass, id, null, null ) ); } + private ReactiveEntityPersister entityPersister(Class entityClass) { + return (ReactiveEntityPersister) getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); + } + + private EntityPersister requireEntityPersister(Class entityClass) { + return getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); + } + private CompletionStage fireReactiveLoad(LoadEvent event, LoadEventListener.LoadType loadType) { checkOpenOrWaitingForAutoClose(); @@ -1190,20 +1191,11 @@ private CompletionStage fireReactiveLoad(LoadEvent event, LoadEventListene private CompletionStage fireLoadNoChecks(LoadEvent event, LoadEventListener.LoadType loadType) { pulseTransactionCoordinator(); - return fastSessionServices.eventListenerGroup_LOAD + return getFactory().getEventListenerGroups().eventListenerGroup_LOAD .fireEventOnEachListener( event, loadType,(ReactiveLoadEventListener l) -> l::reactiveOnLoad ); } - private CompletionStage fireResolveNaturalId(ResolveNaturalIdEvent event) { - checkOpenOrWaitingForAutoClose(); - return fastSessionServices.eventListenerGroup_RESOLVE_NATURAL_ID.fireEventOnEachListener( - event, - (ReactiveResolveNaturalIdEventListener l) -> l::onReactiveResolveNaturalId - ) - .whenComplete( (c, e) -> delayedAfterCompletion() ); - } - @Override public void delayedAfterCompletion() { //disable for now, but figure out what to do here @@ -1472,8 +1464,7 @@ public CompletionStage> multiLoad(Object... ids) { Object[] sids = new Object[ids.length]; System.arraycopy( ids, 0, sids, 0, ids.length ); - return perform( () -> (CompletionStage) - ( (ReactiveEntityPersister) entityPersister ) + return perform( () -> (CompletionStage) ( (ReactiveEntityPersister) entityPersister ) .reactiveMultiLoad( sids, ReactiveSessionImpl.this, this ) ); } @@ -1517,12 +1508,16 @@ public CompletionStage> multiLoad(List ids) { } private class NaturalIdLoadAccessImpl { - private final EntityPersister entityPersister; + private final LoadAccessContext context; + private final ReactiveEntityPersister entityPersister; + private final EntityMappingType entityDescriptor; private LockOptions lockOptions; private boolean synchronizationEnabled = true; - private NaturalIdLoadAccessImpl(EntityPersister entityPersister) { + private NaturalIdLoadAccessImpl(LoadAccessContext context, ReactiveEntityPersister entityPersister, EntityMappingType entityDescriptor) { + this.context = context; this.entityPersister = entityPersister; + this.entityDescriptor = entityDescriptor; if ( !entityPersister.hasNaturalIdentifier() ) { throw LOG.entityDidNotDefinedNaturalId( entityPersister.getEntityName() ); @@ -1538,13 +1533,35 @@ protected void synchronizationEnabled(boolean synchronizationEnabled) { this.synchronizationEnabled = synchronizationEnabled; } - protected final CompletionStage resolveNaturalId(Map naturalIdParameters) { + /** + * @see org.hibernate.loader.internal.BaseNaturalIdLoadAccessImpl#doGetReference(Object) + */ + protected final CompletionStage resolveNaturalId(Object normalizedNaturalIdValue) { performAnyNeededCrossReferenceSynchronizations(); - ResolveNaturalIdEvent event = - new ResolveNaturalIdEvent( naturalIdParameters, entityPersister, ReactiveSessionImpl.this ); - return fireResolveNaturalId( event ) - .thenApply( v -> event.getEntityId() == INVALID_NATURAL_ID_REFERENCE ? null : event.getEntityId() ); + context.checkOpenOrWaitingForAutoClose(); + context.pulseTransactionCoordinator(); + + final SessionImplementor session = getSession(); + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + final Object cachedResolution = persistenceContext.getNaturalIdResolutions() + .findCachedIdByNaturalId( normalizedNaturalIdValue, entityPersister() ); + if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) { + // the entity is deleted, although not yet flushed - return null + return nullFuture(); + } + else if ( cachedResolution != null ) { + return completedFuture( cachedResolution ); + } + else { + LoaderLogging.LOADER_LOGGER.debugf( + "Selecting entity identifier by natural-id for `#getReference` handling - %s : %s", + entityPersister().getEntityName(), + normalizedNaturalIdValue + ); + return ( (ReactiveNaturalIdLoader) entityPersister().getNaturalIdLoader() ) + .resolveNaturalIdToId( normalizedNaturalIdValue, session ); + } } protected void performAnyNeededCrossReferenceSynchronizations() { @@ -1552,7 +1569,9 @@ protected void performAnyNeededCrossReferenceSynchronizations() { // synchronization (this process) was disabled return; } - if ( entityPersister.getEntityMetamodel().hasImmutableNaturalId() ) { + + final NaturalIdMapping naturalIdMapping = entityDescriptor.getNaturalIdMapping(); + if ( !naturalIdMapping.isMutable() ) { // only mutable natural-ids need this processing return; } @@ -1562,7 +1581,7 @@ protected void performAnyNeededCrossReferenceSynchronizations() { } final PersistenceContext persistenceContext = getPersistenceContextInternal(); -// final boolean debugEnabled = log.isDebugEnabled(); + final boolean loggerDebugEnabled = LoaderLogging.LOADER_LOGGER.isDebugEnabled(); for ( Object pk : persistenceContext.getNaturalIdResolutions() .getCachedPkResolutions( entityPersister ) ) { final EntityKey entityKey = generateEntityKey( pk, entityPersister ); @@ -1570,12 +1589,13 @@ protected void performAnyNeededCrossReferenceSynchronizations() { final EntityEntry entry = persistenceContext.getEntry( entity ); if ( entry == null ) { -// if ( debugEnabled ) { -// log.debug( -// "Cached natural-id/pk resolution linked to null EntityEntry in persistence context : " -// + MessageHelper.infoString( entityPersister, pk, getFactory() ) -// ); -// } + if ( loggerDebugEnabled ) { + LoaderLogging.LOADER_LOGGER.debugf( + "Cached natural-id/pk resolution linked to null EntityEntry in persistence context : %s#%s", + entityDescriptor.getEntityName(), + pk + ); + } continue; } @@ -1588,21 +1608,19 @@ protected void performAnyNeededCrossReferenceSynchronizations() { continue; } - persistenceContext.getNaturalIdResolutions() - .handleSynchronization( pk, entity, entityPersister ); + persistenceContext.getNaturalIdResolutions().handleSynchronization( pk, entity, entityPersister() ); } } protected final ReactiveIdentifierLoadAccessImpl getIdentifierLoadAccess() { - final ReactiveIdentifierLoadAccessImpl identifierLoadAccess = new ReactiveIdentifierLoadAccessImpl<>( - entityPersister ); + final ReactiveIdentifierLoadAccessImpl identifierLoadAccess = new ReactiveIdentifierLoadAccessImpl<>( entityPersister ); if ( this.lockOptions != null ) { identifierLoadAccess.with( lockOptions ); } return identifierLoadAccess; } - protected EntityPersister entityPersister() { + protected ReactiveEntityPersister entityPersister() { return entityPersister; } } @@ -1688,7 +1706,7 @@ public void removeOrphanBeforeUpdates(String entityName, Object child) { public CompletionStage reactiveRemoveOrphanBeforeUpdates(String entityName, Object child) { // TODO: The removeOrphan concept is a temporary "hack" for HHH-6484. This should be removed once action/task // ordering is improved. - final StatefulPersistenceContext persistenceContext = (StatefulPersistenceContext) getPersistenceContextInternal(); + final PersistenceContext persistenceContext = getPersistenceContextInternal(); persistenceContext.beginRemoveOrphanBeforeUpdates(); return fireRemove( new DeleteEvent( entityName, child, false, true, this ) ) .thenAccept( v -> { @@ -1709,7 +1727,7 @@ private void logRemoveOrphanBeforeUpdates( String timing, String entityName, Object entity, - StatefulPersistenceContext persistenceContext) { + PersistenceContext persistenceContext) { if ( LOG.isTraceEnabled() ) { final EntityEntry entityEntry = persistenceContext.getEntry( entity ); LOG.tracef( 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 2e9dd3df1..230c4a5fb 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 @@ -62,7 +62,7 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; -import org.hibernate.reactive.engine.impl.ReactivePersistenceContextAdapter; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.reactive.id.ReactiveIdentifierGenerator; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; @@ -133,7 +133,7 @@ public class ReactiveStatelessSessionImpl extends StatelessSessionImpl implement public ReactiveStatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options, ReactiveConnection connection) { super( factory, options ); reactiveConnection = connection; - persistenceContext = new ReactivePersistenceContextAdapter( this ); + persistenceContext = new ReactivePersistenceContextAdapter( super.getPersistenceContext() ); batchingHelperSession = new ReactiveStatelessSessionImpl( factory, options, reactiveConnection, persistenceContext ); influencers = new LoadQueryInfluencers( factory ); } @@ -315,13 +315,13 @@ private CompletionStage reactiveInsert(Object entity, ReactiveEntityPersis } private boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) { - if ( fastSessionServices.eventListenerGroup_PRE_INSERT.isEmpty() ) { + if ( getFactory().getEventListenerGroups().eventListenerGroup_PRE_INSERT.isEmpty() ) { return false; } else { boolean veto = false; final PreInsertEvent event = new PreInsertEvent( entity, id, state, persister, null ); - for ( PreInsertEventListener listener : fastSessionServices.eventListenerGroup_PRE_INSERT.listeners() ) { + for ( PreInsertEventListener listener : getFactory().getEventListenerGroups().eventListenerGroup_PRE_INSERT.listeners() ) { veto |= listener.onPreInsert( event ); } return veto; @@ -329,9 +329,9 @@ private boolean firePreInsert(Object entity, Object id, Object[] state, EntityPe } private void firePostInsert(Object entity, Object id, Object[] state, EntityPersister persister) { - if ( !fastSessionServices.eventListenerGroup_POST_INSERT.isEmpty() ) { + if ( !getFactory().getEventListenerGroups().eventListenerGroup_POST_INSERT.isEmpty() ) { final PostInsertEvent event = new PostInsertEvent( entity, id, state, persister, null ); - for ( PostInsertEventListener listener : fastSessionServices.eventListenerGroup_POST_INSERT.listeners() ) { + for ( PostInsertEventListener listener : getFactory().getEventListenerGroups().eventListenerGroup_POST_INSERT.listeners() ) { listener.onPostInsert( event ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java index bcf33c942..43b7c1554 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java @@ -19,7 +19,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.query.TupleTransformer; -import org.hibernate.reactive.engine.impl.ReactivePersistenceContextAdapter; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; import org.hibernate.reactive.sql.exec.spi.ReactiveSelectExecutor; import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityDelayedFetchInitializer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityDelayedFetchInitializer.java index 1a1660725..c40ee20d3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityDelayedFetchInitializer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntityDelayedFetchInitializer.java @@ -131,7 +131,7 @@ public CompletionStage reactiveResolveInstance(EntityDelayedFetchInitializ final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName(); final Type uniqueKeyPropertyType = uniqueKeyPropertyName == null ? concreteDescriptor.getIdentifierType() - : session.getFactory().getReferencedPropertyType( concreteDescriptor.getEntityName(), uniqueKeyPropertyName ); + : session.getFactory().getRuntimeMetamodels().getReferencedPropertyType( concreteDescriptor.getEntityName(), uniqueKeyPropertyName ); final EntityUniqueKey euk = new EntityUniqueKey( concreteDescriptor.getEntityName(), uniqueKeyPropertyName, 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 a00287eab..8601d434f 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 @@ -20,7 +20,6 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.LazyInitializer; @@ -45,6 +44,7 @@ import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY; 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.metamodel.mapping.ForeignKeyDescriptor.Nature.TARGET; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -576,7 +576,7 @@ private boolean isProxyInstance(Object proxy) { // FIXME: I could change the scope of this method in ORM private Object resolveInstanceFromCache(ReactiveEntityInitializerData data) { - return CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( + return loadFromSecondLevelCache( data.getRowProcessingState().getSession().asEventSource(), null, data.getLockMode(), diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java index 1362e3a20..3c3667aa3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java @@ -48,7 +48,7 @@ protected X fromString(String string, JavaType javaType, WrapperOptions o if ( javaType.getJavaType() == SQLXML.class ) { throw LOG.unsupportedXmlType(); } - return options.getSessionFactory().getFastSessionServices().getXmlFormatMapper() + return options.getSessionFactory().getSessionFactoryOptions().getXmlFormatMapper() .fromString( string, javaType, options ); } From edf37c2d83bfa2b276642d36ed740718fc63ce04 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 22 Jan 2025 12:18:13 +0100 Subject: [PATCH 057/201] Remove IDEA grammar warnings in javadoc and comments --- .../main/java/org/hibernate/reactive/engine/impl/Cascade.java | 2 +- .../event/impl/AbstractReactiveFlushingEventListener.java | 2 +- .../hibernate/reactive/id/impl/BlockingIdentifierGenerator.java | 2 +- .../AbstractReactiveInformationSchemaBasedExtractorImpl.java | 2 +- .../internal/ReactiveEntitySelectFetchInitializerBuilder.java | 2 +- .../org/hibernate/reactive/util/async/impl/AsyncCloseable.java | 2 +- .../org/hibernate/reactive/util/async/impl/AsyncIterator.java | 2 +- .../org/hibernate/reactive/util/async/impl/AsyncTrampoline.java | 2 +- .../java/org/hibernate/reactive/BatchingConnectionTest.java | 2 +- .../org/hibernate/reactive/containers/CockroachDBDatabase.java | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java index 36466b556..d8fce7681 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java @@ -447,7 +447,7 @@ private static CompletionStage cascadeLogicalOneToOneOrphanRemoval( * * @param type The type representing the attribute metadata * - * @return True if the attribute represents a logical one to one association + * @return True if the attribute represents a logical one-to-one association */ private static boolean isLogicalOneToOne(Type type) { return type.isEntityType() && ( (EntityType) type ).isLogicalOneToOne(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java index b257229b2..9e4ac049d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java @@ -74,7 +74,7 @@ private ReactiveActionQueue actionQueue(EventSource session) { /** * Coordinates the processing necessary to get things ready for executions - * as db calls by preping the session caches and moving the appropriate + * as db calls by prepping the session caches and moving the appropriate * entities and collections to their respective execution queues. * * @param event The flush event. 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 224410853..a38e88097 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 @@ -51,7 +51,7 @@ public abstract class BlockingIdentifierGenerator implements ReactiveIdentifierG */ protected abstract CompletionStage nextHiValue(ReactiveConnectionSupplier session); - //Not strictly necessary to put these fields into a dedicated class, but it help + //Not strictly necessary to put these fields into a dedicated class, but it helps //to reason about what the current state is and what the CombinerExecutor is //supposed to work on. private static class GeneratorState { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java index 61a5534b4..7ca69b474 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java @@ -351,7 +351,7 @@ protected String getDatabaseCatalogColumnName(String catalogColumnName, String s * used for storing the schema name, or , if there * is no valid column containing the schema name. *

    - * MySQL, for example, does not have a valid column in + * MySQL, for example, does not have a valid column * in the information_schema to store the schema name. * (@see MySqlReactiveInformationExtractorImpl) * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializerBuilder.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializerBuilder.java index c4cb87838..a3b7d46f1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializerBuilder.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializerBuilder.java @@ -100,7 +100,7 @@ public static EntityInitializer createInitializer( throw new IllegalStateException( "Should be unreachable" ); } - // FIXME: Use the one in ORM + // FIXME: Use the one in ORM: EntitySelectFetchInitializerBuilder#determineBatchMode public static BatchMode determineBatchMode( EntityPersister entityPersister, InitializerParent parent, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncCloseable.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncCloseable.java index 806c38a96..d32b32146 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncCloseable.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncCloseable.java @@ -17,7 +17,7 @@ *

    * Examples of such resources are manually managed memory, open file handles, socket descriptors * etc. While similar to {@link AutoCloseable}, this interface should be used when the resource - * release operation may possibly be async. For example, if an object is thread-safe and has many + * release operation may be async. For example, if an object is thread-safe and has many * consumers, an implementation may require all current ongoing operations to complete before * resources are relinquished. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncIterator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncIterator.java index 2680f56f8..0786e311e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncIterator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncIterator.java @@ -51,7 +51,7 @@ * below. The difference is that the parallelization in that case is from producing values in * parallel, not consuming values in parallel. * - *

    To implement an AsyncIterator you must only implement the {@link #nextStage()} method- + *

    To implement an AsyncIterator you must only implement the {@link #nextStage()} method - * however, it is recommended that users avoid actually using nextStage to consume the results of * iteration. It is less expressive and it can also be error prone; it is easy to cause a stack * overflow by incorrectly recursing on calls to nextStage. You should prefer to use the other diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncTrampoline.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncTrampoline.java index cc2ef5702..b8feee8c6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncTrampoline.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/async/impl/AsyncTrampoline.java @@ -174,7 +174,7 @@ T poll() { * *

    * Effectively produces {@code fn(seed).thenCompose(fn).thenCompose(fn)... .thenCompose(fn)} until - * an value fails the predicate. Note that predicate will be applied on seed (like a while loop, + * a value fails the predicate. Note that predicate will be applied on seed (like a while loop, * the initial value is tested). If the predicate or fn throw an exception, * or the {@link CompletionStage} returned by fn completes exceptionally, iteration will stop and * an exceptional stage will be returned. diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java index 1cdc423f0..0ce3d2e9d 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java @@ -41,7 +41,7 @@ protected Configuration constructConfiguration() { configuration.setProperty( AvailableSettings.STATEMENT_BATCH_SIZE, "5"); // Construct a tracker that collects query statements via the SqlStatementLogger framework. - // Pass in configuration properties to hand-off any actual logging properties + // Pass in configuration properties to hand off any actual logging properties sqlTracker = new SqlStatementTracker( BatchingConnectionTest::filter, configuration.getProperties() ); return configuration; } 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 103723127..ae4d34d39 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 @@ -63,7 +63,7 @@ private static String disableSslMode(String url) { } /** - * Temporary tables support is experimental but we need it when updating entities in a hierarchy + * We need temporary tables when updating entities in a hierarchy */ private static void enableTemporaryTables() { runSql( "SET CLUSTER SETTING sql.defaults.experimental_temporary_tables.enabled = 'true';" ); From 6b1997282a9a89c10d4711d020eba008324853b4 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 22 Jan 2025 12:44:48 +0100 Subject: [PATCH 058/201] Remove warnings from tests --- .../EagerElementCollectionForBasicTypeSetTest.java | 2 +- .../reactive/IdentityGenerationWithBatchingTest.java | 2 +- .../reactive/IdentityGeneratorDynamicInsertTest.java | 2 +- .../hibernate/reactive/IdentityGeneratorTest.java | 2 +- .../IdentityGeneratorWithColumnTransformerTest.java | 2 +- .../reactive/MultithreadedInsertionTest.java | 12 +++++++++--- .../hibernate/reactive/OneToManyArrayMergeTest.java | 4 +--- .../java/org/hibernate/reactive/OneToManyTest.java | 6 ------ .../test/java/org/hibernate/reactive/QueryTest.java | 2 +- .../java/org/hibernate/reactive/SoftDeleteTest.java | 2 +- 10 files changed, 17 insertions(+), 19 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerElementCollectionForBasicTypeSetTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerElementCollectionForBasicTypeSetTest.java index ba183b406..176e30c37 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerElementCollectionForBasicTypeSetTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerElementCollectionForBasicTypeSetTest.java @@ -442,7 +442,7 @@ private static void assertPhones(VertxTestContext context, Person person, String assertNotNull( person ); String[] sortedExpected = Arrays.stream( expectedPhones ).sorted() .sorted( String.CASE_INSENSITIVE_ORDER ) - .collect( Collectors.toList() ) + .toList() .toArray( new String[expectedPhones.length] ); List sortedActual = person.getPhones().stream() .sorted( String.CASE_INSENSITIVE_ORDER ) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGenerationWithBatchingTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGenerationWithBatchingTest.java index acad8feba..3492532c1 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGenerationWithBatchingTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGenerationWithBatchingTest.java @@ -63,7 +63,7 @@ public void test(VertxTestContext context) { } private Book[] asBooks(String[] titles) { - return Arrays.asList( titles ).stream().map( Book::new ).toArray( Book[]::new ); + return Arrays.stream( titles ).map( Book::new ).toArray( Book[]::new ); } @Entity(name = "Book") diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorDynamicInsertTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorDynamicInsertTest.java index 2454de0d3..9be6f748a 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorDynamicInsertTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorDynamicInsertTest.java @@ -70,7 +70,7 @@ private CompletionStage populateDb() { return getSessionFactory() .withTransaction( (session, tx) -> session.persist( identities.toArray() ) ) .thenAccept( ignore -> { - Long assignedId = 0L; + long assignedId = 0L; for ( EntityWithIdentity identity : identities ) { assertNotNull( identity.id ); assertTrue( identity.id > assignedId ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTest.java index 61cc6c503..1d9a6c8b3 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorTest.java @@ -68,7 +68,7 @@ private CompletionStage populateDb() { return getSessionFactory() .withTransaction( (session, tx) -> session.persist( identities.toArray() ) ) .thenAccept( ignore -> { - Long assignedId = 0L; + long assignedId = 0L; for ( EntityWithIdentity identity : identities ) { assertNotNull( identity.id ); assertTrue( identity.id > assignedId ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorWithColumnTransformerTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorWithColumnTransformerTest.java index 3982e0c6b..a4d584719 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorWithColumnTransformerTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/IdentityGeneratorWithColumnTransformerTest.java @@ -72,7 +72,7 @@ private CompletionStage populateDb() { return getSessionFactory() .withTransaction( (session, tx) -> session.persist( identities.toArray() ) ) .thenAccept( ignore -> { - Long assignedId = 0L; + long assignedId = 0L; for ( EntityWithIdentity identity : identities ) { assertNotNull( identity.id ); assertTrue( identity.id > assignedId ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java index 9e31c20be..bd2a36d1e 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MultithreadedInsertionTest.java @@ -37,6 +37,7 @@ import jakarta.persistence.Table; import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.fail; import static org.hibernate.cfg.AvailableSettings.SHOW_SQL; import static org.hibernate.reactive.BaseReactiveTest.setDefaultProperties; import static org.hibernate.reactive.provider.Settings.POOL_CONNECT_TIMEOUT; @@ -233,11 +234,16 @@ public void reached() { public void waitForEveryone() { try { - countDownLatch.await( TIMEOUT_MINUTES, TimeUnit.MINUTES ); - prettyOut( "Everyone has now breached '" + label + "'" ); + boolean reachedZero = countDownLatch.await( TIMEOUT_MINUTES, MINUTES ); + if ( reachedZero ) { + prettyOut( "Everyone has now breached '" + label + "'" ); + } + else { + fail( "Time out reached" ); + } } catch ( InterruptedException e ) { - e.printStackTrace(); + fail( e ); } } } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java index e2eb826d9..8553f1624 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyArrayMergeTest.java @@ -111,9 +111,7 @@ public User(Long id, String firstname, String lastname, Role... roles) { this.firstname = firstname; this.lastname = lastname; this.roles = new Role[roles.length]; - for ( int i = 0; i < roles.length; i++ ) { - this.roles[i] = roles[i]; - } + System.arraycopy( roles, 0, this.roles, 0, roles.length ); } public Long getId() { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyTest.java index c3bec53b1..f0242ed71 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OneToManyTest.java @@ -45,9 +45,6 @@ public void testPersistAll(VertxTestContext context) { Author author = new Author( "Iain M Banks" ); author.books.add( book1 ); author.books.add( book2 ); - final Book[] bookArray = new Book[2]; - bookArray[0] = book1; - bookArray[1] = book2; test( context, getMutinySessionFactory() .withTransaction( session -> session.persistAll( book1, book2, author ) ) @@ -67,9 +64,6 @@ public void testFetchJoinQueryGetSingleResult(VertxTestContext context) { Author author = new Author( "Iain M Banks" ); author.books.add( book1 ); author.books.add( book2 ); - final Book[] bookArray = new Book[2]; - bookArray[0] = book1; - bookArray[1] = book2; test( context, getMutinySessionFactory() .withTransaction( session -> session.persistAll( book1, book2, author ) ) 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 d6146b2e5..c65c79ee4 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 @@ -138,7 +138,7 @@ public void testCriteriaEntityQuery(VertxTestContext context) { update.set( b.get( "title" ), "XXX" ); CriteriaDelete delete = builder.createCriteriaDelete( Book.class ); - b = delete.from( Book.class ); + delete.from( Book.class ); test( context, diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/SoftDeleteTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/SoftDeleteTest.java index 4ba2e844e..f77d86f61 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/SoftDeleteTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/SoftDeleteTest.java @@ -172,7 +172,7 @@ private void testSoftDelete( } ) ) // Delete an entity - .call( deleteEntity::get ) + .call( deleteEntity ) // Test select all .call( () -> getMutinySessionFactory().withTransaction( s -> s .createSelectionQuery( "from " + entityClass.getSimpleName() + " order by id", Object.class ) From bd48c1273a6990de8d5a6b0b6236d569c528970d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 22 Jan 2025 12:59:16 +0100 Subject: [PATCH 059/201] Remove IDEA warnings A mischellaneous collection of fixes for warnings that I've noticed working on this issue. --- build.gradle | 2 +- .../hibernate/reactive/engine/impl/Cascade.java | 5 +---- .../id/impl/BlockingIdentifierGenerator.java | 17 ++++++++--------- ...atedSequenceReactiveIdentifierGenerator.java | 11 ----------- .../impl/TableReactiveIdentifierGenerator.java | 10 ---------- .../ReactiveMultiIdEntityLoaderArrayParam.java | 1 - ...veSingleIdEntityLoaderProvidedQueryImpl.java | 2 +- .../ast/internal/ReactiveSingleIdLoadPlan.java | 2 +- .../entity/impl/ReactiveEntityPersister.java | 3 ++- .../ReactiveInsertCoordinatorStandard.java | 14 ++++++-------- .../pool/impl/DefaultSqlClientPool.java | 5 ++--- .../impl/DefaultSqlClientPoolConfiguration.java | 10 +++++----- .../pool/impl/ExternalSqlClientPool.java | 2 +- .../provider/impl/ReactiveIntegrator.java | 5 ----- ...tiveInformationSchemaBasedExtractorImpl.java | 2 +- .../MySqlReactiveInformationExtractorImpl.java | 7 +------ ...tgreSqlReactiveInformationExtractorImpl.java | 7 +------ ...lServerReactiveInformationExtractorImpl.java | 2 +- ...ReactiveStandardMutationExecutorService.java | 3 +-- .../internal/ReactiveInitializersList.java | 2 +- .../spi/ReactiveListResultsConsumer.java | 3 +-- .../reactive/util/impl/CompletionStages.java | 4 ++-- .../java/org/hibernate/reactive/types/Json.java | 2 +- 23 files changed, 38 insertions(+), 83 deletions(-) diff --git a/build.gradle b/build.gradle index e9ca27a56..ee1cba98c 100644 --- a/build.gradle +++ b/build.gradle @@ -73,7 +73,7 @@ subprojects { ext.publishScript = rootProject.rootDir.absolutePath + '/publish.gradle' - tasks.withType( JavaCompile ) { + tasks.withType( JavaCompile ).configureEach { options.encoding = 'UTF-8' } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java index d8fce7681..a13426966 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java @@ -235,10 +235,7 @@ else if ( action.performOnLazyProperty() && type.isEntityType() ) { false ) ); } - else { - // Nothing to do, so just skip cascading to this lazy attribute. - continue; - } + // Nothing to do, so just skip cascading to this lazy attribute. } else { Object child = persister.getValue( parent, i ); 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 a38e88097..c820f513f 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 @@ -5,19 +5,19 @@ */ package org.hibernate.reactive.id.impl; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import org.hibernate.reactive.id.ReactiveIdentifierGenerator; +import org.hibernate.reactive.session.ReactiveConnectionSupplier; + 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 org.hibernate.reactive.id.ReactiveIdentifierGenerator; -import org.hibernate.reactive.session.ReactiveConnectionSupplier; - -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; /** @@ -138,7 +138,6 @@ public Task execute(GeneratorState state) { // value in the table, so just increment the lo // value and return the next id in the block completedFuture( local ).whenComplete( this::acceptAsReturnValue ); - return null; } else { nextHiValue( connectionSupplier ) @@ -155,8 +154,8 @@ public Task execute(GeneratorState state) { } ); } } ); - return null; } + return null; } private void acceptAsReturnValue(final Long aLong, final Throwable throwable) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java index dd9702c69..b973c869b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java @@ -19,7 +19,6 @@ import java.util.Properties; -import static org.hibernate.internal.util.config.ConfigurationHelper.getInt; import static org.hibernate.internal.util.config.ConfigurationHelper.getString; /** @@ -71,16 +70,6 @@ protected String determineSegmentValue(Properties params) { return null; } - @Override - protected int determineInitialValue(Properties params) { - return getInt( SequenceStyleGenerator.INITIAL_PARAM, params, SequenceStyleGenerator.DEFAULT_INITIAL_VALUE ); - } - - @Override - protected int determineIncrement(Properties params) { - return getInt( SequenceStyleGenerator.INCREMENT_PARAM, params, SequenceStyleGenerator.DEFAULT_INCREMENT_SIZE ); - } - @Override protected Object[] updateParameters(long currentValue, long updatedValue) { return new Object[] { updatedValue, currentValue }; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java index 79fb63aa6..21a863b0b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java @@ -12,8 +12,6 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.boot.model.relational.Database; -import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.OracleDialect; @@ -189,14 +187,6 @@ protected CompletionStage nextHiValue(ReactiveConnectionSupplier session) } ); } - @Override - public void registerExportables(Database database) { - } - - @Override - public void initialize(SqlStringGenerationContext context) { - } - @Override public Object generate(SharedSessionContractImplementor session, Object object) throws HibernateException { throw new UnsupportedOperationException(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java index fa0b1885d..8495d115b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java @@ -65,7 +65,6 @@ public ReactiveMultiIdEntityLoaderArrayParam( EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); - final Class arrayClass = createTypedArray( 0 ).getClass(); final Class idClass = getIdentifierMapping().getJavaType().getJavaTypeClass(); arrayJdbcMapping = resolveArrayJdbcMapping( getIdentifierMapping().getJdbcMapping(), diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java index 6b849d346..b2ec82649 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java @@ -46,7 +46,7 @@ public EntityMappingType getLoadable() { public CompletionStage load(Object pkValue, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { // noinspection unchecked final QueryImplementor query = namedQueryMemento - .toQuery( session, (Class) entityDescriptor.getMappedJavaType().getJavaTypeClass() ); + .toQuery( session, entityDescriptor.getMappedJavaType().getJavaTypeClass() ); //noinspection unchecked query.setParameter( (Parameter) query.getParameters().iterator().next(), pkValue ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java index 83c3292f5..458cdf518 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java @@ -81,7 +81,7 @@ public CompletionStage load(Object restrictedValue, Object entityInstance, Bo } ); } - private void invokeAfterLoadActions(Callback callback, SharedSessionContractImplementor session, T entity) { + private void invokeAfterLoadActions(Callback callback, SharedSessionContractImplementor session, G entity) { if ( entity != null && getLoadable() != null) { callback.invokeAfterLoadActions( entity, (EntityMappingType) getLoadable(), session ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index ba365a463..ee35d9fca 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -16,6 +16,7 @@ import org.hibernate.generator.values.GeneratedValues; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinator; import jakarta.persistence.metamodel.Attribute; @@ -33,7 +34,7 @@ public interface ReactiveEntityPersister extends EntityPersister { * @see EntityPersister#insert(Object, Object[], Object, SharedSessionContractImplementor) */ default CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) { - return insertReactive( id, fields, object, session ); + return ( (ReactiveInsertCoordinator) getInsertCoordinator() ).reactiveInsert( object, id, fields, session ); }; /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java index db2671be9..3fa811b99 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinatorStandard.java @@ -435,14 +435,12 @@ protected void breakDownJdbcValue( final String tableName = tableDetails.getTableName(); tableDetails.getKeyMapping().breakDownKeyJdbcValues( id, - (jdbcValue, columnMapping) -> { - jdbcValueBindings.bindValue( - jdbcValue, - tableName, - columnMapping.getColumnName(), - ParameterUsage.SET - ); - }, + (jdbcValue, columnMapping) -> jdbcValueBindings.bindValue( + jdbcValue, + tableName, + columnMapping.getColumnName(), + ParameterUsage.SET + ), session ); } 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 35c3980e0..67476f71e 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 @@ -37,7 +37,6 @@ import io.vertx.sqlclient.spi.Driver; import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; /** * A pool of reactive connections backed by a Vert.x {@link Pool}. @@ -128,7 +127,7 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) { } @Override - public void configure(Map configuration) { + public void configure(Map configuration) { uri = jdbcUrl( configuration ); } @@ -247,7 +246,7 @@ private Driver findDriver(URI uri, ServiceConfigurationError originalError) { if ( selected.size() > 1 ) { List driverClasses = selected.stream() .map( driver -> driver.getClass().getCanonicalName() ) - .collect( toList() ); + .toList(); throw new ConfigurationException( "Multiple drivers found matching for URI scheme \"" + scheme + "\". Please, pick one: " + driverClasses, originalError ); } return selected.get( 0 ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java index b60600bb8..e0613b78c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java @@ -232,15 +232,15 @@ private int oraclePort(URI uri) { if ( s.indexOf( '/' ) != -1 ) { // Example: 1234/ s = s.substring( 0, s.indexOf( '/' ) ); - return Integer.valueOf( s ); + return Integer.parseInt( s ); } if ( s.indexOf( '?' ) != -1 ) { // Example: 1234?param=value s = s.substring( 0, s.indexOf( '?' ) ); - return Integer.valueOf( s ); + return Integer.parseInt( s ); } // Example: 1234 - return Integer.valueOf( s ); + return Integer.parseInt( s ); } return -1; } @@ -330,7 +330,7 @@ private int extractPort(URI uri) { if ( startOfPort == -1 ) { return -1; } - return Integer.valueOf( hostPortString.substring( startOfPort + 1 ) ); + return Integer.parseInt( hostPortString.substring( startOfPort + 1 ) ); } private String findHost(URI uri, String scheme) { @@ -348,7 +348,7 @@ private String findHost(URI uri, String scheme) { } private int findPort(URI uri, String scheme) { - int port = -1; + int port; if ( "oracle".equals( scheme ) ) { port = oraclePort( uri ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ExternalSqlClientPool.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ExternalSqlClientPool.java index 602ecd7b0..86067882a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ExternalSqlClientPool.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ExternalSqlClientPool.java @@ -59,7 +59,7 @@ public final class ExternalSqlClientPool extends SqlClientPool { private final Pool pool; private final SqlStatementLogger sqlStatementLogger; - private SqlExceptionHelper sqlExceptionHelper; + private final SqlExceptionHelper sqlExceptionHelper; public ExternalSqlClientPool(Pool pool, SqlStatementLogger sqlStatementLogger, SqlExceptionHelper sqlExceptionHelper) { this.pool = pool; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java index 5607d5935..c1fb0deb9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java @@ -28,7 +28,6 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.service.ServiceRegistry; -import org.hibernate.service.spi.SessionFactoryServiceRegistry; /** * Integrates Hibernate Reactive with Hibernate ORM by @@ -47,10 +46,6 @@ public void integrate(Metadata metadata, BootstrapContext bootstrapContext, Sess attachEventContextManagingListenersIfRequired( sessionFactory.getServiceRegistry() ); } - @Override - public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { - } - private void attachEventContextManagingListenersIfRequired(ServiceRegistry serviceRegistry) { if ( ReactiveModeCheck.isReactiveRegistry( serviceRegistry ) ) { LOG.startHibernateReactive(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java index 7ca69b474..e1846d1bf 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/AbstractReactiveInformationSchemaBasedExtractorImpl.java @@ -192,7 +192,7 @@ protected boolean appendClauseAndParameterIfNotNullOrEmpty( StringBuilder sb, List parameters) { - if ( parameter != null && ( ! String.class.isInstance( parameter ) || ! ( (String) parameter ).isEmpty() ) ) { + if ( parameter != null && ( !( parameter instanceof String string ) || !string.isEmpty() ) ) { parameters.add( parameter ); sb.append( clause ); sb.append( parameterMarker( parameters.size() ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/MySqlReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/MySqlReactiveInformationExtractorImpl.java index 6cc016dda..0504e44ee 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/MySqlReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/MySqlReactiveInformationExtractorImpl.java @@ -46,11 +46,6 @@ protected int dataTypeCode(String typeName) { } } - @Override - protected String getResultSetTableTypesPhysicalTableConstant() { - return "BASE TABLE"; - } - protected String getDatabaseCatalogColumnName(String catalogColumnName, String schemaColumnName ) { return schemaColumnName; } @@ -64,7 +59,7 @@ protected T processPrimaryKeysResultSet( String catalogFilter, String schemaFilter, Identifier tableName, - ExtractionContext.ResultSetProcessor processor) throws SQLException { + ExtractionContext.ResultSetProcessor processor) { // This functionality is not used by ORM. throw new UnsupportedOperationException(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java index ee4a012fe..62fff7ecc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/PostgreSqlReactiveInformationExtractorImpl.java @@ -32,17 +32,12 @@ public PostgreSqlReactiveInformationExtractorImpl(ExtractionContext extractionCo super( extractionContext ); } - @Override - protected String getResultSetTableTypesPhysicalTableConstant() { - return "BASE TABLE"; - } - @Override protected T processPrimaryKeysResultSet( String catalogFilter, String schemaFilter, Identifier tableName, - ExtractionContext.ResultSetProcessor processor) throws SQLException { + ExtractionContext.ResultSetProcessor processor) { // This functionality is not used by ORM. throw new UnsupportedOperationException(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java index 2cec39ce7..eea7fc477 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/SqlServerReactiveInformationExtractorImpl.java @@ -158,7 +158,7 @@ protected T processPrimaryKeysResultSet( String catalogFilter, String schemaFilter, Identifier tableName, - ExtractionContext.ResultSetProcessor processor) throws SQLException { + ExtractionContext.ResultSetProcessor processor) { throw new UnsupportedOperationException(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/ReactiveStandardMutationExecutorService.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/ReactiveStandardMutationExecutorService.java index c02fc0551..46fffd02b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/ReactiveStandardMutationExecutorService.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/ReactiveStandardMutationExecutorService.java @@ -72,9 +72,8 @@ public MutationExecutor createExecutor( } private static GeneratedValuesMutationDelegate generatedValuesDelegate(MutationOperationGroup operationGroup) { - GeneratedValuesMutationDelegate generatedValuesMutationDelegate = operationGroup.asEntityMutationOperationGroup() != null + return operationGroup.asEntityMutationOperationGroup() != null ? operationGroup.asEntityMutationOperationGroup().getMutationDelegate() : null; - return generatedValuesMutationDelegate; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java index 312f510f5..49c7deda4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java @@ -87,7 +87,7 @@ ReactiveInitializersList build(final Map> initiali } private Initializer[] toArray(final ArrayList> initializers) { - return initializers.toArray( new Initializer[initializers.size()] ); + return initializers.toArray( new Initializer[0] ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java index 7c23bc739..9c9008eb0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java @@ -284,8 +284,7 @@ public boolean addUnique(R result) { } public boolean add(R result) { - results.add( result ); - return true; + return results.add( result ); } public List getResults() { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java index bb168d8f7..eb311aba6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java @@ -301,7 +301,7 @@ public R getResult() throws T { if ( throwable == null ) { return result; } - throw (T) throwable; + throw throwable; } public CompletionStage getResultAsCompletionStage() { @@ -467,7 +467,7 @@ public static CompletionStage loop(int start, int end, IntPredicate filter } public static CompletionStage whileLoop(Supplier> loopSupplier) { - return asyncWhile( loopSupplier::get ); + return asyncWhile( loopSupplier ); } public static CompletionStage whileLoop(Supplier whileCondition, Supplier> loopSupplier) { diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/Json.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/Json.java index 2df6f008a..9af3a2b76 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/Json.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/Json.java @@ -61,7 +61,7 @@ public void nullSafeSet(PreparedStatement st, JsonObject value, int index, Share @Override public JsonObject deepCopy(JsonObject value) { - return value == null ? null : ( (JsonObject) value ).copy(); + return value == null ? null : value.copy(); } @Override From 14cab0fea0a169ff58771edc6c05c3dca0200242 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 6 Feb 2025 10:01:55 +0100 Subject: [PATCH 060/201] Remove unused methods from ReactiveGlobalTemporaryTableStrategy --- .../ReactiveGlobalTemporaryTableInsertStrategy.java | 10 ---------- .../ReactiveGlobalTemporaryTableMutationStrategy.java | 10 ---------- .../ReactiveGlobalTemporaryTableStrategy.java | 4 ---- 3 files changed, 24 deletions(-) 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 ce76569ce..2f7f7bb37 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 @@ -84,14 +84,4 @@ public boolean isDropIdTables() { public void setDropIdTables(boolean dropIdTables) { this.dropIdTables = dropIdTables; } - - @Override - public CompletionStage getDropTableActionStage() { - return tableDroppedStage; - } - - @Override - public CompletionStage getCreateTableActionStage() { - return tableCreatedStage; - } } 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 15c2bd293..66b001520 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 @@ -81,16 +81,6 @@ public CompletionStage reactiveExecuteDelete( ).reactiveExecute( context ) ); } - @Override - public CompletionStage getDropTableActionStage() { - return tableDroppedStage; - } - - @Override - public CompletionStage getCreateTableActionStage() { - return tableCreatedStage; - } - @Override public boolean isPrepared() { return prepared; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java index d0204ffff..7a396753c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java @@ -41,10 +41,6 @@ static String sessionIdentifier(SharedSessionContractImplementor session) { TemporaryTable getTemporaryTable(); - CompletionStage getDropTableActionStage(); - - CompletionStage getCreateTableActionStage(); - SessionFactoryImplementor getSessionFactory(); default void prepare(MappingModelCreationProcess mappingModelCreationProcess, JdbcConnectionAccess connectionAccess, CompletableFuture tableCreatedStage) { From fedfcd03731aa22d14517c30d3c4da02ddd6440c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 6 Feb 2025 10:03:39 +0100 Subject: [PATCH 061/201] Remove old LGTM configuration file --- lgtm.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 lgtm.yml diff --git a/lgtm.yml b/lgtm.yml deleted file mode 100644 index 8c608f1f0..000000000 --- a/lgtm.yml +++ /dev/null @@ -1,8 +0,0 @@ -##################################################### -# LGTM.com configuration file -##################################################### - -extraction: - java: - index: - java_version: 11 From dde7d5beb1e08012fcbd0b61cafe8b269a75c1e2 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 14 Feb 2025 16:51:04 +0100 Subject: [PATCH 062/201] [#2114] Upgrade Vert.x SQL client to 4.5.13 --- README.md | 10 +++++----- build.gradle | 2 +- gradle.properties | 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, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index aabf550ac..746235200 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ Hibernate Reactive has been tested with: - MS SQL Server 2022 - Oracle 23 - [Hibernate ORM][] 7.0.0.Beta4 -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.12 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.12 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.12 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.12 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.12 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.13 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.13 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.13 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.13 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.13 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/build.gradle b/build.gradle index ee1cba98c..aef4fcc26 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.12' + vertxSqlClientVersion = '4.5.13' } testcontainersVersion = '1.20.4' diff --git a/gradle.properties b/gradle.properties index b45735f0a..299ccf3b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,9 +47,9 @@ hibernateOrmVersion = 7.0.0.Beta4 #skipOrmVersionParsing = true # Override default Vert.x Sql client version -#vertxSqlClientVersion = 4.5.12-SNAPSHOT +#vertxSqlClientVersion = 4.5.13-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 4.5.12 -#vertxWebtClientVersion = 4.5.12 +#vertxWebVersion = 4.5.13 +#vertxWebtClientVersion = 4.5.13 diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index e87720288..9e041af06 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.12} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index aa3aec07e..feabab3f7 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.12} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index def81fa49..7c0f4cef0 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -6,9 +6,9 @@ */ //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.12} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.12} -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //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 59aff501a..dd6ceb3c3 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.12} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 599b2a8a4..b31d4e8d8 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.12} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 697cae5dc..ab43700ed 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.12} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.12} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 2e0fb971b..7357011ed 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.12} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.13} //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.12} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.12} -//DEPS io.vertx:vertx-unit:${vertx.version:4.5.12} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-unit:${vertx.version:4.5.13} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 From 5775785ce812644c4074fdd217e5b24a29bfd1fb Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 23 Jan 2025 16:34:47 +0100 Subject: [PATCH 063/201] [#1905] Fix exception when using find with lock --- .../ReactivePersistenceContextAdapter.java | 87 ++++++++++++++++++- .../engine/impl/ReactiveCallbackImpl.java | 70 +++++++++++++++ .../ReactiveNaturalIdLoaderDelegate.java | 4 +- .../internal/ReactiveSingleIdLoadPlan.java | 31 ++++--- ...veSingleUniqueKeyEntityLoaderStandard.java | 4 +- .../ast/spi/ReactiveAfterLoadAction.java | 26 ++++++ .../spi/ReactiveAbstractSelectionQuery.java | 16 +++- .../sql/internal/ReactiveNativeQueryImpl.java | 6 ++ .../sqm/internal/ReactiveQuerySqmImpl.java | 6 ++ .../ReactiveSqmSelectionQueryImpl.java | 6 ++ .../reactive/session/ReactiveSession.java | 2 + .../session/impl/ReactiveSessionImpl.java | 6 ++ .../ReactiveDeferredResultSetAccess.java | 19 ++++ .../spi/ReactiveListResultsConsumer.java | 33 +++++-- .../spi/ReactiveSingleResultConsumer.java | 24 ++++- 15 files changed, 313 insertions(+), 27 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCallbackImpl.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveAfterLoadAction.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java index 92a678413..3dfc8dcd0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java @@ -7,6 +7,7 @@ import java.io.Serializable; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletionStage; import java.util.function.BiConsumer; @@ -30,11 +31,16 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.event.service.spi.EventListenerGroup; +import org.hibernate.event.spi.PostLoadEvent; +import org.hibernate.event.spi.PostLoadEventListener; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.reactive.session.ReactiveSession; +import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.sql.results.spi.LoadContexts; @@ -42,6 +48,7 @@ import static java.lang.invoke.MethodHandles.lookup; 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.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -456,7 +463,7 @@ public EntityHolder removeEntityHolder(EntityKey key) { @Override public void postLoad(JdbcValuesSourceProcessingState processingState, Consumer loadedConsumer) { - delegate.postLoad( processingState, loadedConsumer ); + throw LOG.nonReactiveMethodCall( "reactivePostLoad(JdbcValuesSourceProcessingState, Consumer) )" ); } @Internal @@ -710,4 +717,82 @@ public Iterator managedEntitiesIterator() { public NaturalIdResolutions getNaturalIdResolutions() { return delegate.getNaturalIdResolutions(); } + + /** + * Reactive version of {@link StatefulPersistenceContext#postLoad(JdbcValuesSourceProcessingState, Consumer)} + */ + public CompletionStage reactivePostLoad( + JdbcValuesSourceProcessingState processingState, + Consumer holderConsumer) { + final ReactiveCallbackImpl callback = (ReactiveCallbackImpl) processingState + .getExecutionContext().getCallback(); + return processHolders( + holderConsumer, + processingState.getLoadingEntityHolders(), + getSession().getFactory().getEventListenerGroups().eventListenerGroup_POST_LOAD, + processingState.getPostLoadEvent(), + callback + ).thenCompose( v -> processHolders( + holderConsumer, + processingState.getReloadedEntityHolders(), + null, + null, + callback + ) ); + } + + private CompletionStage processHolders( + Consumer holderConsumer, + List loadingEntityHolders, + EventListenerGroup listenerGroup, + PostLoadEvent postLoadEvent, + ReactiveCallbackImpl callback) { + if ( loadingEntityHolders != null ) { + return loop( loadingEntityHolders, + holder -> processLoadedEntityHolder( + holder, + listenerGroup, + postLoadEvent, + callback, + holderConsumer + ) + ).thenAccept( v -> loadingEntityHolders.clear() ); + } + return voidFuture(); + } + + /** + * Reactive version of {@link StatefulPersistenceContext#processLoadedEntityHolder(EntityHolder, EventListenerGroup, PostLoadEvent, Callback, Consumer)} + */ + private CompletionStage processLoadedEntityHolder( + EntityHolder holder, + EventListenerGroup listenerGroup, + PostLoadEvent postLoadEvent, + ReactiveCallbackImpl callback, + Consumer holderConsumer) { + if ( holderConsumer != null ) { + holderConsumer.accept( holder ); + } + if ( holder.getEntity() == null ) { + // It's possible that we tried to load an entity and found out it doesn't exist, + // in which case we added an entry with a null proxy and entity. + // Remove that empty entry on post load to avoid unwanted side effects + getEntitiesByKey().remove( holder.getEntityKey() ); + } + else { + if ( postLoadEvent != null ) { + postLoadEvent.reset(); + postLoadEvent.setEntity( holder.getEntity() ) + .setId( holder.getEntityKey().getIdentifier() ) + .setPersister( holder.getDescriptor() ); + listenerGroup.fireEventOnEachListener( postLoadEvent, PostLoadEventListener::onPostLoad ); + if ( callback != null ) { + return callback + .invokeReactiveLoadActions( holder.getEntity(), holder.getDescriptor(), getSession() ) + .thenAccept( v -> holder.resetEntityInitialier() ); + } + } + } + return voidFuture(); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCallbackImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCallbackImpl.java new file mode 100644 index 000000000..609f021ba --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCallbackImpl.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.engine.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.loader.ast.spi.AfterLoadAction; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.reactive.loader.ast.spi.ReactiveAfterLoadAction; +import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.sql.exec.spi.Callback; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; +import static org.hibernate.reactive.util.impl.CompletionStages.loop; + +/** + * Reactive equivalent of {@link org.hibernate.sql.exec.internal.CallbackImpl} + */ +public class ReactiveCallbackImpl implements Callback { + private static final Log LOG = make( Log.class, lookup() ); + + private final List afterLoadActions; + + public ReactiveCallbackImpl() { + this.afterLoadActions = new ArrayList<>( 1 ); + } + + @Override + public void registerAfterLoadAction(AfterLoadAction afterLoadAction) { + throw LOG.nonReactiveMethodCall( "registerReactiveAfterLoadAction(ReactiveCallbackImpl)" ); + } + + public void registerReactiveAfterLoadAction(ReactiveAfterLoadAction afterLoadAction) { + afterLoadActions.add( afterLoadAction ); + } + + @Override + public void invokeAfterLoadActions( + Object entity, + EntityMappingType entityMappingType, + SharedSessionContractImplementor session) { + throw LOG.nonReactiveMethodCall( "invokeAfterLoadActions(Object, EntityMappingType, SharedSessionContractImplementor)" ); + } + + /** + * Reactive version of {@link org.hibernate.sql.exec.internal.CallbackImpl#invokeAfterLoadActions(Object, EntityMappingType, SharedSessionContractImplementor)} + */ + public CompletionStage invokeReactiveLoadActions( + Object entity, + EntityMappingType entityMappingType, + SharedSessionContractImplementor session) { + return loop( + afterLoadActions, afterLoadAction -> + afterLoadAction.reactiveAfterLoad( entity, entityMappingType, session ) + ); + } + + @Override + public boolean hasAfterLoadActions() { + return !afterLoadActions.isEmpty(); + } + +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java index ede1c6cf7..08b83cd47 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveNaturalIdLoaderDelegate.java @@ -27,6 +27,7 @@ import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.query.internal.SimpleQueryOptions; import org.hibernate.query.spi.QueryOptions; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.loader.ast.spi.ReactiveNaturalIdLoader; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; @@ -38,7 +39,6 @@ import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.BaseExecutionContext; -import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -335,7 +335,7 @@ public NaturalIdLoaderWithOptionsExecutionContext( QueryOptions queryOptions) { super( session ); this.queryOptions = queryOptions; - callback = new CallbackImpl(); + callback = new ReactiveCallbackImpl(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java index 458cdf518..8cb8e2ae2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java @@ -20,16 +20,19 @@ import org.hibernate.query.internal.SimpleQueryOptions; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.results.spi.RowTransformer; + +import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture; public class ReactiveSingleIdLoadPlan extends SingleIdLoadPlan> { @@ -61,7 +64,7 @@ public CompletionStage load(Object restrictedValue, Object entityInstance, Bo } assert offset == getJdbcParameters().size(); final QueryOptions queryOptions = new SimpleQueryOptions( getLockOptions(), readOnly ); - final Callback callback = new CallbackImpl(); + final ReactiveCallbackImpl callback = new ReactiveCallbackImpl(); EntityMappingType loadable = (EntityMappingType) getLoadable(); ExecutionContext executionContext = executionContext( restrictedValue, @@ -73,21 +76,27 @@ public CompletionStage load(Object restrictedValue, Object entityInstance, Bo ); // FIXME: Should we get this from jdbcServices.getSelectExecutor()? return StandardReactiveSelectExecutor.INSTANCE - .list( getJdbcSelect(), jdbcParameterBindings, executionContext, getRowTransformer(), resultConsumer( singleResultExpected ) ) + .list( getJdbcSelect(), jdbcParameterBindings, executionContext, rowTransformer(), resultConsumer( singleResultExpected ) ) .thenApply( this::extractEntity ) - .thenApply( entity -> { - invokeAfterLoadActions( callback, session, entity ); - return (T) entity; - } ); + .thenCompose( entity -> invokeAfterLoadActions( callback, session, entity ) ); + } + + private RowTransformer rowTransformer() { + // Because of the generics, the compiler expect this to return RowTransformer> + // but it actually returns RowTransformer. I don't know at the moment how to fix this in a cleaner way + return (RowTransformer) getRowTransformer(); } - private void invokeAfterLoadActions(Callback callback, SharedSessionContractImplementor session, G entity) { - if ( entity != null && getLoadable() != null) { - callback.invokeAfterLoadActions( entity, (EntityMappingType) getLoadable(), session ); + private CompletionStage invokeAfterLoadActions(ReactiveCallbackImpl callback, SharedSessionContractImplementor session, T entity) { + if ( entity != null && getLoadable() != null ) { + return callback + .invokeReactiveLoadActions( entity, (EntityMappingType) getLoadable(), session ) + .thenApply( v -> entity ); } + return nullFuture(); } - private Object extractEntity(List list) { + private T extractEntity(List list) { return list.isEmpty() ? null : list.get( 0 ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleUniqueKeyEntityLoaderStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleUniqueKeyEntityLoaderStandard.java index 62e5d0d44..09490cdad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleUniqueKeyEntityLoaderStandard.java @@ -24,13 +24,13 @@ import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.query.spi.QueryOptions; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.BaseExecutionContext; -import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -179,7 +179,7 @@ public SingleUKEntityLoaderExecutionContext(SharedSessionContractImplementor ses super( session ); //Careful, readOnly is possibly null this.queryOptions = readOnly == null ? QueryOptions.NONE : readOnly ? QueryOptions.READ_ONLY : QueryOptions.READ_WRITE; - callback = new CallbackImpl(); + callback = new ReactiveCallbackImpl(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveAfterLoadAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveAfterLoadAction.java new file mode 100644 index 000000000..e7e20d2e8 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveAfterLoadAction.java @@ -0,0 +1,26 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.loader.ast.spi; + +import java.util.concurrent.CompletionStage; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; + +/** + * Reactive version of {@link org.hibernate.loader.ast.spi.AfterLoadAction} + */ +public interface ReactiveAfterLoadAction { + /** + * @see org.hibernate.loader.ast.spi.AfterLoadAction#afterLoad(Object, EntityMappingType, SharedSessionContractImplementor) + * + * The action trigger - the {@code entity} is being loaded + */ + CompletionStage reactiveAfterLoad( + Object entity, + EntityMappingType entityMappingType, + SharedSessionContractImplementor session); +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java index f01b5d315..1137e0a62 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java @@ -6,7 +6,10 @@ package org.hibernate.reactive.query.spi; import java.lang.invoke.MethodHandles; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.function.Consumer; @@ -30,12 +33,14 @@ import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.query.sqm.internal.AggregatedSelectReactiveQueryPlan; import org.hibernate.reactive.query.sqm.internal.ConcreteSqmSelectReactiveQueryPlan; import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; import org.hibernate.reactive.sql.results.spi.ReactiveSingleResultConsumer; +import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.internal.TupleMetadata; import jakarta.persistence.NoResultException; @@ -76,6 +81,8 @@ public class ReactiveAbstractSelectionQuery { private final Function, R> uniqueElement; private final InterpretationsKeySource interpretationsKeySource; + private Callback callback; + // I'm sure we can avoid some of this by making some methods public in ORM, // but this allows me to prototype faster. We can refactor the code later. public ReactiveAbstractSelectionQuery( @@ -363,4 +370,11 @@ public void enableFetchProfile(String profileName) { } fetchProfiles.add( profileName ); } + + public Callback getCallback() { + if ( callback == null ) { + callback = new ReactiveCallbackImpl(); + } + return callback; + } } 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 bd38d53d4..6d162882a 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 @@ -46,6 +46,7 @@ import org.hibernate.reactive.query.sql.spi.ReactiveNativeQueryImplementor; import org.hibernate.reactive.query.sql.spi.ReactiveNonSelectQueryPlan; import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; +import org.hibernate.sql.exec.spi.Callback; import org.hibernate.type.BasicTypeReference; import jakarta.persistence.AttributeConverter; @@ -192,6 +193,11 @@ public R getSingleResultOrNull() { return selectionQueryDelegate.getSingleResultOrNull(); } + @Override + public Callback getCallback() { + return selectionQueryDelegate.getCallback(); + } + @Override public CompletionStage getReactiveSingleResultOrNull() { return selectionQueryDelegate.getReactiveSingleResultOrNull(); 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 4e9e8f001..096196576 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 @@ -61,6 +61,7 @@ import org.hibernate.reactive.query.sqm.mutation.spi.ReactiveSqmMultiTableMutationStrategy; import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; import org.hibernate.reactive.session.ReactiveSqmQueryImplementor; +import org.hibernate.sql.exec.spi.Callback; import org.hibernate.transform.ResultTransformer; import jakarta.persistence.CacheRetrieveMode; @@ -174,6 +175,11 @@ public R getSingleResultOrNull() { return selectionQueryDelegate.getSingleResultOrNull(); } + @Override + public Callback getCallback() { + return selectionQueryDelegate.getCallback(); + } + @Override public R uniqueResult() { return selectionQueryDelegate.uniqueResult(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java index d24ecb2db..bca5e2a65 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java @@ -39,6 +39,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.reactive.query.spi.ReactiveAbstractSelectionQuery; import org.hibernate.reactive.query.sqm.ReactiveSqmSelectionQuery; +import org.hibernate.sql.exec.spi.Callback; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; @@ -218,6 +219,11 @@ public CompletionStage getReactiveResultCount() { .getReactiveResultsCount( getSqmStatement().createCountQuery(), this ); } + @Override + public Callback getCallback() { + return selectionQueryDelegate.getCallback(); + } + @Override public List getResultList() { return selectionQueryDelegate.getResultList(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index 94b95318f..f53ed4fca 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -78,6 +78,8 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveLock(Object entity, LockOptions lockMode); + CompletionStage reactiveLock(String entityName, Object entity, LockOptions lockMode); + CompletionStage reactiveGet(Class entityClass, Object id); CompletionStage reactiveFind(Class entityClass, Object id, LockOptions lockOptions, EntityGraph fetchGraph); 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 87aaeb060..fb20deaaf 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 @@ -1074,6 +1074,12 @@ public CompletionStage reactiveLock(Object object, LockOptions lockOptions return fireLock( new LockEvent( object, lockOptions, this ) ); } + @Override + public CompletionStage reactiveLock(String entityName, Object object, LockOptions lockOptions) { + checkOpen(); + return fireLock( new LockEvent( entityName, object, lockOptions, this ) ); + } + private CompletionStage fireLock(LockEvent event) { pulseTransactionCoordinator(); 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 a6f7ed675..3f88bcffb 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 @@ -13,14 +13,17 @@ import java.util.concurrent.CompletionStage; import org.hibernate.HibernateException; +import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; +import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.session.ReactiveConnectionSupplier; +import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; @@ -60,6 +63,22 @@ public ReactiveDeferredResultSetAccess( this.sqlStatementLogger = executionContext.getSession().getJdbcServices().getSqlStatementLogger(); } + /** + * Reactive version of {@link org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess#registerAfterLoadAction(ExecutionContext, LockOptions)} + * calling {@link ReactiveSession#reactiveLock(String, Object, LockOptions)} + */ + @Override + protected void registerAfterLoadAction(ExecutionContext executionContext, LockOptions lockOptionsToUse) { + ( (ReactiveCallbackImpl) executionContext.getCallback() ).registerReactiveAfterLoadAction( + (entity, persister, session) -> + ( (ReactiveSession) session ).reactiveLock( + persister.getEntityName(), + entity, + lockOptionsToUse + ) + ); + } + @Override public ResultSet getResultSet() { if ( resultSet == null ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java index 9c9008eb0..e7283647f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveListResultsConsumer.java @@ -13,6 +13,7 @@ import java.util.function.Supplier; import org.hibernate.HibernateException; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.ResultListTransformer; @@ -103,7 +104,7 @@ public CompletionStage> consume( } return falseFuture(); } ) ) - .thenApply( v -> finishUp( rowReader, rowProcessingState, jdbcValuesSourceProcessingState, results, readRows, queryOptions ) ) + .thenCompose( v -> finishUp( rowReader, rowProcessingState, jdbcValuesSourceProcessingState, results, readRows, queryOptions ) ) .handle( CompletionStages::handle ) .thenCompose( handler -> { end( jdbcValues, session, jdbcValuesSourceProcessingState, persistenceContext, handler.getThrowable() ); @@ -111,18 +112,36 @@ public CompletionStage> consume( } ); } - private List finishUp( + private CompletionStage> finishUp( ReactiveRowReader rowReader, ReactiveRowProcessingState rowProcessingState, JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState, Results results, int[] readRows, QueryOptions queryOptions) { rowReader.finishUp( rowProcessingState ); - jdbcValuesSourceProcessingState.finishUp( readRows[0] > 1 ); + return finishUp( readRows[0] > 1, rowProcessingState.getSession(), jdbcValuesSourceProcessingState ) + .thenApply( v -> { + final ResultListTransformer resultListTransformer = (ResultListTransformer) queryOptions.getResultListTransformer(); + return resultListTransformer != null + ? resultListTransformer.transformList( results.getResults() ) + : results.getResults(); + } ); + } - final ResultListTransformer resultListTransformer = (ResultListTransformer) queryOptions.getResultListTransformer(); - return resultListTransformer != null - ? resultListTransformer.transformList( results.getResults() ) - : results.getResults(); + /** + * Reactive equivalent of {@link org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl#finishUp(boolean)} + */ + private static CompletionStage finishUp( + boolean registerSubselects, + SharedSessionContractImplementor session, + JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState) { + jdbcValuesSourceProcessingState.finishLoadingCollections(); + return ( (ReactivePersistenceContextAdapter) session.getPersistenceContextInternal() ) + .reactivePostLoad( + jdbcValuesSourceProcessingState, + registerSubselects + ? jdbcValuesSourceProcessingState.getExecutionContext()::registerLoadingEntityHolder + : null + ); } /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java index 814db1e4b..df0774afe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java @@ -8,12 +8,17 @@ import java.util.concurrent.CompletionStage; import org.hibernate.Incubating; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet; +import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; +/** + * @see org.hibernate.sql.results.spi.SingleResultConsumer + */ @Incubating public class ReactiveSingleResultConsumer implements ReactiveResultsConsumer { @@ -29,15 +34,28 @@ public CompletionStage consume( return rowProcessingState.next() .thenCompose( hasNext -> rowReader .reactiveReadRow( rowProcessingState, processingOptions ) - .thenApply( result -> { + .thenCompose( result -> { rowProcessingState.finishRowProcessing( true ); rowReader.finishUp( rowProcessingState ); - jdbcValuesSourceProcessingState.finishUp( false ); - return result; + return finishUp( session, jdbcValuesSourceProcessingState, result ); } ) ); } + /** + * Reactive version of {@link JdbcValuesSourceProcessingStateStandardImpl#finishUp(boolean)} + */ + private static CompletionStage finishUp( + SharedSessionContractImplementor session, + JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState, + T result) { + jdbcValuesSourceProcessingState.finishLoadingCollections(); + final ExecutionContext executionContext = jdbcValuesSourceProcessingState.getExecutionContext(); + return ( (ReactivePersistenceContextAdapter) session.getPersistenceContextInternal() ) + .reactivePostLoad( jdbcValuesSourceProcessingState, executionContext::registerLoadingEntityHolder ) + .thenApply( v -> result ); + } + @Override public boolean canResultsBeCached() { return false; From 54b0e0ba91df50c83a1dde6d2843a1427aea4538 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 23 Jan 2025 13:50:31 +0100 Subject: [PATCH 064/201] [#1905] Add test for Reactive find with lock in Quarkus with reactive hibernate --- .../reactive/FindByIdWithLockTest.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/FindByIdWithLockTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/FindByIdWithLockTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/FindByIdWithLockTest.java new file mode 100644 index 000000000..e78c549ca --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/FindByIdWithLockTest.java @@ -0,0 +1,133 @@ +/* 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 java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.LockModeType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@Timeout(value = 10, timeUnit = TimeUnit.MINUTES) +public class FindByIdWithLockTest extends BaseReactiveTest { + private static final Long CHILD_ID = 1L; + + @Override + protected Collection> annotatedEntities() { + return List.of( Parent.class, Child.class ); + } + + @Test + public void testFindChild(VertxTestContext context) { + Parent parent = new Parent( 1L, "Lio" ); + Child child = new Child( CHILD_ID, "And" ); + test( + context, getMutinySessionFactory() + .withTransaction( session -> session.persistAll( parent, child ) ) + .chain( () -> getMutinySessionFactory() + .withTransaction( session -> session + .find( Child.class, CHILD_ID, LockModeType.PESSIMISTIC_WRITE ) + .invoke( c -> { + assertThat( c ).isNotNull(); + assertThat( c.getId() ).isEqualTo( CHILD_ID ); + } + ) ) ) + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany(fetch = FetchType.EAGER) + public List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public void add(Child child) { + if ( children == null ) { + children = new ArrayList<>(); + } + children.add( child ); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public List getChildren() { + return children; + } + } + + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + public String name; + + @ManyToOne + public Parent parent; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + public Child(Long id, String name, Parent parent) { + this.id = id; + this.name = name; + this.parent = parent; + parent.add( this ); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Parent getParent() { + return parent; + } + } + + +} From 991778362cfc85b5ce45fe713f2fae9202b2213d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 17 Feb 2025 11:01:49 +0100 Subject: [PATCH 065/201] [#1905] Replace NO_PARAM_BINDINGS with .empty() It's deprecated --- .../reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java index 8cb8e2ae2..3d636eed9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdLoadPlan.java @@ -165,7 +165,7 @@ public CollectionKey getCollectionKey() { @Override public QueryParameterBindings getQueryParameterBindings() { - return QueryParameterBindings.NO_PARAM_BINDINGS; + return QueryParameterBindings.empty(); } @Override From 2ae7cebef3441d0d7de12fb96b4b9588d02e482e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 17 Feb 2025 12:05:11 +0100 Subject: [PATCH 066/201] [#1514] Add test to ReactiveStatelessProxyUpdateTest We need to test with Mutiny and with CompletionStage because the exception is thrown by the API classes. I've also cleaned up a bit the code to avoid NullPointerException in case of failures (it should throw the correct assertion error). --- .../ReactiveStatelessProxyUpdateTest.java | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessProxyUpdateTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessProxyUpdateTest.java index 477996fc8..3927279d7 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessProxyUpdateTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessProxyUpdateTest.java @@ -30,6 +30,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -83,26 +84,47 @@ public void testUnfetchedEntityException(VertxTestContext context) { } @Test - public void testLazyInitializationException(VertxTestContext context) { + public void testLazyInitializationExceptionWithMutiny(VertxTestContext context) { Game lol = new Game( "League of Legends" ); GameCharacter ck = new GameCharacter( "Caitlyn Kiramman" ); ck.setGame( lol ); - test( context, assertThrown( LazyInitializationException.class, getMutinySessionFactory() + test( context, getMutinySessionFactory() .withTransaction( s -> s.persistAll( lol, ck ) ) .chain( targetId -> getMutinySessionFactory() .withStatelessSession( session -> session.get( GameCharacter.class, ck.getId() ) ) ) - .call( charFound -> getMutinySessionFactory() - .withStatelessTransaction( s -> { + .call( charFound -> assertThrown( + LazyInitializationException.class, getMutinySessionFactory().withStatelessTransaction( s -> { Game game = charFound.getGame(); // LazyInitializationException here because we haven't fetched the entity game.setGameTitle( "League of Legends V2" ); - context.failNow( "We were expecting a LazyInitializationException" ); - return null; + return Uni.createFrom().voidItem(); } ) + ) ) + ); + } + + @Test + public void testLazyInitializationExceptionWithStage(VertxTestContext context) { + Game lol = new Game( "League of Legends" ); + GameCharacter ck = new GameCharacter( "Caitlyn Kiramman" ); + ck.setGame( lol ); + + test( context, getSessionFactory() + .withTransaction( s -> s.persist( lol, ck ) ) + .thenCompose( targetId -> getSessionFactory() + .withStatelessSession( session -> session.get( GameCharacter.class, ck.getId() ) ) ) - ) ); + .thenCompose( charFound -> assertThrown( + LazyInitializationException.class, getSessionFactory().withStatelessTransaction( s -> { + Game game = charFound.getGame(); + // LazyInitializationException here because we haven't fetched the entity + game.setGameTitle( "League of Legends V2" ); + return voidFuture(); + } ) + ) ) + ); } @Test From 2fe3e4a7c0493ea4a5da0460c6d8d24ae343f1d4 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 17 Feb 2025 12:39:11 +0100 Subject: [PATCH 067/201] [#1514] Test lazy exception with transaction --- .../LazyInitializationExceptionTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyInitializationExceptionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyInitializationExceptionTest.java index bb04c0fda..55268f906 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyInitializationExceptionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/LazyInitializationExceptionTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import io.smallrye.mutiny.Uni; import io.vertx.junit5.Timeout; import io.vertx.junit5.VertxTestContext; import jakarta.persistence.Entity; @@ -32,6 +33,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown; +import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -61,6 +63,31 @@ public void populateDB(VertxTestContext context) { test( context, getSessionFactory().withTransaction( session -> session.persist( artemisia, liuto, sev ) ) ); } + @Test + public void testLazyInitializationExceptionWithTransactionWithMutiny(VertxTestContext context) { + test( context, assertThrown( LazyInitializationException.class, getMutinySessionFactory() + .withSession( ms -> ms + .createSelectionQuery( "from Artist", Artist.class ) + .getSingleResult() ) + .call( artist -> getMutinySessionFactory().withTransaction( s -> Uni.createFrom() + // .size should throw LazyInitializationException + .item( artist.getPaintings().size() ) ) ) + ) + ); + } + + @Test + public void testLazyInitializationExceptionWithTransactionWithStage(VertxTestContext context) { + test( context, assertThrown( LazyInitializationException.class, getSessionFactory() + .withSession( ss -> ss + .createSelectionQuery( "from Artist", Artist.class ) + .getSingleResult() ) + .thenCompose( artist -> getSessionFactory() + .withTransaction( s -> completedFuture( artist.getPaintings().size() ) ) ) + ) + ); + } + @Test public void testLazyInitializationExceptionWithMutiny(VertxTestContext context) { test( context, assertThrown( LazyInitializationException.class, openMutinySession() From 0861b894dc6cf616584a0319d15d87c685f7e3da Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 17 Feb 2025 12:39:27 +0100 Subject: [PATCH 068/201] [#1514] Catch errors thrown while executing a transactions Before, we were ignoring some errors if they were happening in the worker provided to the transactions. Because of this, the transaction wasn't always rollbacked or closed. --- .../org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java | 3 ++- .../reactive/mutiny/impl/MutinyStatelessSessionImpl.java | 2 +- .../org/hibernate/reactive/stage/impl/StageSessionImpl.java | 3 ++- .../reactive/stage/impl/StageStatelessSessionImpl.java | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 56cdc727b..929987b55 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -508,7 +508,8 @@ Uni execute(Function> work) { * roll back) and an error thrown by the work. */ Uni executeInTransaction(Function> work) { - return work.apply( this ) + return Uni.createFrom() + .deferred( () -> work.apply( this ) ) // only flush() if the work completed with no exception .call( this::flush ).call( this::beforeCompletion ) // in the case of an exception or cancellation diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index ba1bee23e..105672837 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -267,7 +267,7 @@ Uni execute(Function> work) { * and an error thrown by the work. */ Uni executeInTransaction(Function> work) { - return work.apply( this ) + return Uni.createFrom().deferred( () -> work.apply( this ) ) // in the case of an exception or cancellation // we need to rollback the transaction .onFailure().call( this::rollback ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index 20f9644a0..78ce3c891 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -46,6 +46,7 @@ import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** * Implements the {@link Stage.Session} API. This delegating class is @@ -434,7 +435,7 @@ CompletionStage execute(Function> work) * roll back) and an error thrown by the work. */ CompletionStage executeInTransaction(Function> work) { - return work.apply( this ) + return voidFuture().thenCompose( v -> work.apply( this ) ) // only flush() if the work completed with no exception .thenCompose( result -> flush().thenApply( v -> result ) ) // have to capture the error here and pass it along, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 0d7c5d834..8a27d5a07 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -26,6 +26,7 @@ import java.util.function.Function; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** * Implements the {@link Stage.StatelessSession} API. This delegating @@ -207,7 +208,8 @@ CompletionStage execute(Function> work) * and an error thrown by the work. */ CompletionStage executeInTransaction(Function> work) { - return work.apply( this ) + return voidFuture() + .thenCompose( v -> work.apply( this ) ) // have to capture the error here and pass it along, // since we can't just return a CompletionStage that // rolls back the transaction from the handle() function From beeae17e828d8124de1953364707a9b408626f66 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 14 Mar 2023 12:48:07 +0000 Subject: [PATCH 069/201] [#1514] Hardening of current transaction state --- .../reactive/pool/impl/SqlClientConnection.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java index 89a53eb76..3e8647f6c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientConnection.java @@ -286,8 +286,12 @@ private SqlConnection client() { @Override public CompletionStage beginTransaction() { + if ( transaction != null ) { + throw new IllegalStateException( "Can't begin a new transaction as an active transaction is already associated to this connection" ); + } return connection.begin() .onSuccess( tx -> LOG.tracef( "Transaction started: %s", tx ) ) + .onFailure( v -> LOG.errorf( "Failed to start a transaction: %s", transaction ) ) .toCompletionStage() .thenAccept( this::setTransaction ); } @@ -296,6 +300,7 @@ public CompletionStage beginTransaction() { public CompletionStage commitTransaction() { return transaction.commit() .onSuccess( v -> LOG.tracef( "Transaction committed: %s", transaction ) ) + .onFailure( v -> LOG.errorf( "Failed to commit transaction: %s", transaction ) ) .toCompletionStage() .whenComplete( this::clearTransaction ); } @@ -303,6 +308,7 @@ public CompletionStage commitTransaction() { @Override public CompletionStage rollbackTransaction() { return transaction.rollback() + .onFailure( v -> LOG.errorf( "Failed to rollback transaction: %s", transaction ) ) .onSuccess( v -> LOG.tracef( "Transaction rolled back: %s", transaction ) ) .toCompletionStage() .whenComplete( this::clearTransaction ); @@ -310,8 +316,12 @@ public CompletionStage rollbackTransaction() { @Override public CompletionStage close() { + if ( transaction != null ) { + throw new IllegalStateException( "Connection being closed with a live transaction associated to it" ); + } return connection.close() .onSuccess( event -> LOG.tracef( "Connection closed: %s", connection ) ) + .onFailure( v -> LOG.errorf( "Failed to close a connection: %s", connection ) ) .toCompletionStage(); } @@ -357,6 +367,7 @@ private void setTransaction(Transaction tx) { } private void clearTransaction(Void v, Throwable x) { + LOG.tracef( "Clearing current transaction instance from connection: %s", transaction ); transaction = null; } From 9ee2927efb3c5eb3fa2a1c0e13d332232f403454 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 13:25:33 +0100 Subject: [PATCH 070/201] fix up weird signature of ReactiveEntityPersister.insertReactive() --- .../engine/impl/ReactiveEntityRegularInsertAction.java | 2 +- .../persister/entity/impl/ReactiveEntityPersister.java | 6 ++---- .../entity/impl/ReactiveJoinedSubclassEntityPersister.java | 4 ++-- .../entity/impl/ReactiveSingleTableEntityPersister.java | 4 ++-- .../entity/impl/ReactiveUnionSubclassEntityPersister.java | 7 +++---- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityRegularInsertAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityRegularInsertAction.java index 062f0374f..977179c73 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityRegularInsertAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityRegularInsertAction.java @@ -63,7 +63,7 @@ public CompletionStage reactiveExecute() throws HibernateException { final ReactiveEntityPersister reactivePersister = (ReactiveEntityPersister) persister; final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); return stage - .thenCompose( v -> reactivePersister.insertReactive( id, getState(), instance, session, false ) ) + .thenCompose( v -> reactivePersister.insertReactive( id, getState(), instance, session ) ) .thenCompose( generatedValues -> { final EntityEntry entry = persistenceContext.getEntry( instance ); if ( entry == null ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index ee35d9fca..965cf7442 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -16,7 +16,6 @@ import org.hibernate.generator.values.GeneratedValues; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinator; import jakarta.persistence.metamodel.Attribute; @@ -33,15 +32,14 @@ public interface ReactiveEntityPersister extends EntityPersister { * * @see EntityPersister#insert(Object, Object[], Object, SharedSessionContractImplementor) */ - default CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) { - return ( (ReactiveInsertCoordinator) getInsertCoordinator() ).reactiveInsert( object, id, fields, session ); - }; + CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session); /** * Insert the given instance state without blocking, but it allows to specify if it's an identity insert or a regular one. * * @see EntityPersister#insert(Object, Object[], Object, SharedSessionContractImplementor) */ + @Deprecated(forRemoval = true) CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session, boolean isIdentityType); /** 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 c4e688ce6..2b53e48b1 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 @@ -183,10 +183,10 @@ public NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess cre @Override public CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) { - return insertReactive( id, fields, object, session, true ); + return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( object, id, fields, session, false ); } - @Override + @Override @Deprecated(forRemoval = true) public CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session, boolean isIdentityInsert) { return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( object, id, fields, session, isIdentityInsert ); } 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 72b8c040e..1808483ed 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 @@ -343,10 +343,10 @@ public CompletionStage insertReactive(Object[] fields, Object e @Override public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session) { - return insertReactive( id, fields, entity, session, true ); + return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, false ); } - @Override + @Override @Deprecated(forRemoval = true) public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session, boolean isIdentityInsert) { return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, isIdentityInsert ); } 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 c844f7644..8e657b37c 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 @@ -310,13 +310,12 @@ private CompletionStage doReactiveLoad(Object id, Object optionalObject, @Override public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session) { - return insertReactive( id, fields, entity, session, true ); + return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, false ); } - @Override + @Override @Deprecated(forRemoval = true) public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session, boolean isIdentityInsert) { - return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ) - .coordinateReactiveInsert( entity, id, fields, session, isIdentityInsert ); + return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, isIdentityInsert ); } @Override From 0a25dc1778c05a5a5a3c419eb8574e78618ad9b2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 14:28:24 +0100 Subject: [PATCH 071/201] just delete the obsolete overload --- .../persister/entity/impl/ReactiveEntityPersister.java | 8 -------- .../impl/ReactiveJoinedSubclassEntityPersister.java | 5 ----- .../entity/impl/ReactiveSingleTableEntityPersister.java | 5 ----- .../entity/impl/ReactiveUnionSubclassEntityPersister.java | 5 ----- 4 files changed, 23 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index 965cf7442..0a36d048d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -34,14 +34,6 @@ public interface ReactiveEntityPersister extends EntityPersister { */ CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session); - /** - * Insert the given instance state without blocking, but it allows to specify if it's an identity insert or a regular one. - * - * @see EntityPersister#insert(Object, Object[], Object, SharedSessionContractImplementor) - */ - @Deprecated(forRemoval = true) - CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session, boolean isIdentityType); - /** * Insert the given instance state without blocking. * 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 2b53e48b1..b87ba6910 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 @@ -186,11 +186,6 @@ public CompletionStage insertReactive(Object id, Object[] field return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( object, id, fields, session, false ); } - @Override @Deprecated(forRemoval = true) - public CompletionStage insertReactive(Object id, Object[] fields, Object object, SharedSessionContractImplementor session, boolean isIdentityInsert) { - return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( object, id, fields, session, isIdentityInsert ); - } - @Override public CompletionStage insertReactive(Object[] fields, Object object, SharedSessionContractImplementor session) { return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( object, null, fields, session, true ); 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 1808483ed..381a83b78 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 @@ -346,11 +346,6 @@ public CompletionStage insertReactive(Object id, Object[] field return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, false ); } - @Override @Deprecated(forRemoval = true) - public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session, boolean isIdentityInsert) { - return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, isIdentityInsert ); - } - @Override public CompletionStage deleteReactive(Object id, Object version, Object entity, SharedSessionContractImplementor session) { return ( (ReactiveAbstractDeleteCoordinator) getDeleteCoordinator() ).reactiveDelete( entity, id, version, session ); 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 8e657b37c..cb7995acb 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 @@ -313,11 +313,6 @@ public CompletionStage insertReactive(Object id, Object[] field return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, false ); } - @Override @Deprecated(forRemoval = true) - public CompletionStage insertReactive(Object id, Object[] fields, Object entity, SharedSessionContractImplementor session, boolean isIdentityInsert) { - return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ).coordinateReactiveInsert( entity, id, fields, session, isIdentityInsert ); - } - @Override public CompletionStage insertReactive(Object[] fields, Object entity, SharedSessionContractImplementor session) { return ( (ReactiveInsertCoordinatorStandard) getInsertCoordinator() ) From c48015eaf5f4b09eac7618da138614625509540c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 14:26:18 +0100 Subject: [PATCH 072/201] [#2122] add StatelessSession.get() accepting multiple ids and add missing overloads of Mutiny.StatelessSession.createQuery() Also add tests for Mutiny.StatelessSession which were missing --- .../org/hibernate/reactive/mutiny/Mutiny.java | 33 +++ .../impl/MutinyStatelessSessionImpl.java | 17 ++ .../session/ReactiveStatelessSession.java | 8 +- .../impl/ReactiveStatelessSessionImpl.java | 37 ++- .../org/hibernate/reactive/stage/Stage.java | 12 + .../stage/impl/StageStatelessSessionImpl.java | 5 + .../reactive/MutinyStatelessSessionTest.java | 271 ++++++++++++++++++ .../ReactiveStatelessSessionTest.java | 17 ++ 8 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c561a4124..e6cdab9d5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -40,6 +40,7 @@ import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; import jakarta.persistence.EntityGraph; +import jakarta.persistence.FindOption; import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; @@ -617,6 +618,8 @@ default Uni find(Class entityClass, Object id, LockModeType lockModeTy * @param ids the identifiers * * @return a list of persistent instances and nulls via a {@code Uni} + * + * @see org.hibernate.Session#findMultiple(Class, List, FindOption...) */ Uni> find(Class entityClass, Object... ids); @@ -1569,6 +1572,18 @@ interface StatelessSession extends Closeable { */ Uni get(Class entityClass, Object id); + /** + * Retrieve multiple rows. + * + * @param entityClass The class of the entity to retrieve + * @param ids The ids of the entities to retrieve + * + * @return a list of detached entity instances, via a {@code Uni} + * + * @see org.hibernate.StatelessSession#getMultiple(Class, List) + */ + Uni> get(Class entityClass, Object... ids); + /** * Retrieve a row, obtaining the specified lock mode. * @@ -1737,6 +1752,24 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ SelectionQuery createQuery(CriteriaQuery criteriaQuery); + /** + * Create an instance of {@link MutationQuery} for the given criteria update. + * + * @param criteriaUpdate The {@link CriteriaUpdate} + * + * @return The {@link MutationQuery} instance for manipulation and execution + */ + MutationQuery createQuery(CriteriaUpdate criteriaUpdate); + + /** + * Create an instance of {@link MutationQuery} for the given criteria delete. + * + * @param criteriaDelete The {@link CriteriaDelete} + * + * @return The {@link MutationQuery} instance for manipulation and execution + */ + MutationQuery createQuery(CriteriaDelete criteriaDelete); + /** * Insert a row. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index 105672837..473196712 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -7,7 +7,9 @@ import io.smallrye.mutiny.Uni; import jakarta.persistence.EntityGraph; +import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.reactive.common.ResultSetMapping; @@ -53,6 +55,11 @@ public Uni get(Class entityClass, Object id) { return uni( () -> delegate.reactiveGet( entityClass, id ) ); } + @Override + public Uni> get(Class entityClass, Object... ids) { + return uni( () -> delegate.reactiveGet( entityClass, ids ) ); + } + @Override public Uni get(Class entityClass, Object id, LockMode lockMode) { return uni( () -> delegate.reactiveGet( entityClass, id, lockMode, null ) ); @@ -114,6 +121,16 @@ public SelectionQuery createQuery(CriteriaQuery criteriaQuery) { return new MutinySelectionQueryImpl<>( delegate.createReactiveQuery( criteriaQuery ), factory ); } + @Override + public Mutiny.MutationQuery createQuery(CriteriaUpdate criteriaUpdate) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( criteriaUpdate ), factory ); + } + + @Override + public Mutiny.MutationQuery createQuery(CriteriaDelete criteriaDelete) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( criteriaDelete ), factory ); + } + @Override public Uni insert(Object entity) { return uni( () -> delegate.reactiveInsert( entity ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java index a0b1f81f0..33e34917b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java @@ -10,6 +10,8 @@ import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import jakarta.persistence.EntityGraph; + +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -26,11 +28,13 @@ @Incubating public interface ReactiveStatelessSession extends ReactiveQueryProducer, ReactiveSharedSessionContractImplementor { - CompletionStage reactiveGet(Class entityClass, Object id); + CompletionStage reactiveGet(Class entityClass, Object id); + + CompletionStage> reactiveGet(Class entityClass, Object... id); CompletionStage reactiveGet(String entityName, Object id); - CompletionStage reactiveGet(Class entityClass, Object id, LockMode lockMode, EntityGraph fetchGraph); + CompletionStage reactiveGet(Class entityClass, Object id, LockMode lockMode, EntityGraph fetchGraph); CompletionStage reactiveGet(String entityName, Object id, LockMode lockMode, EntityGraph fetchGraph); 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 230c4a5fb..7ca780e40 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 @@ -5,6 +5,8 @@ */ package org.hibernate.reactive.session.impl; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Supplier; @@ -46,6 +48,8 @@ import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.IllegalMutationQueryException; import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; @@ -204,17 +208,46 @@ public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { } @Override - public CompletionStage reactiveGet(Class entityClass, Object id) { + public CompletionStage reactiveGet(Class entityClass, Object id) { return reactiveGet( entityClass.getName(), id, LockMode.NONE, null ); } + @Override + public CompletionStage> reactiveGet(Class entityClass, Object... ids) { + checkOpen(); + for (Object id : ids) { + if ( id == null ) { + throw new IllegalArgumentException("Null id"); + } + } + + final EntityPersister persister = getEntityPersister( entityClass.getName() ); + + final JpaCriteriaQuery query = getCriteriaBuilder().createQuery(entityClass); + final JpaRoot from = query.from(entityClass); + query.where( from.get( persister.getIdentifierPropertyName() ).in(ids) ); + return createReactiveQuery(query).getReactiveResultList() + .thenApply( resultList -> { + final List idList = new ArrayList<>( resultList.size() ); + for (T entity : resultList) { + idList.add( persister.getIdentifier(entity, this) ); + } + final List list = new ArrayList<>( ids.length ); + for (Object id : ids) { + final int pos = idList.indexOf(id); + list.add( pos < 0 ? null : resultList.get(pos) ); + } + return list; + }); + } + @Override public CompletionStage reactiveGet(String entityName, Object id) { return reactiveGet( entityName, id, LockMode.NONE, null ); } @Override - public CompletionStage reactiveGet(Class entityClass, Object id, LockMode lockMode, EntityGraph fetchGraph) { + public CompletionStage reactiveGet(Class entityClass, Object id, LockMode lockMode, EntityGraph fetchGraph) { return reactiveGet( entityClass.getName(), id, LockMode.NONE, fetchGraph ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 7741201a0..1cf8f4fed 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1604,6 +1604,18 @@ interface StatelessSession extends Closeable { */ CompletionStage get(Class entityClass, Object id); + /** + * Retrieve multiple rows. + * + * @param entityClass The class of the entity to retrieve + * @param ids The ids of the entities to retrieve + * + * @return a list of detached entity instances, via a {@code Uni} + * + * @see org.hibernate.StatelessSession#getMultiple(Class, List) + */ + CompletionStage> get(Class entityClass, Object... ids); + /** * Retrieve a row, obtaining the specified lock mode. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 8a27d5a07..7724a5cd1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -50,6 +50,11 @@ public CompletionStage get(Class entityClass, Object id) { return delegate.reactiveGet( entityClass, id ); } + @Override + public CompletionStage> get(Class entityClass, Object... ids) { + return delegate.reactiveGet( entityClass, ids ); + } + @Override public CompletionStage get(Class entityClass, Object id, LockMode lockMode) { return delegate.reactiveGet( entityClass, id, lockMode, null ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java new file mode 100644 index 000000000..cde496e57 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java @@ -0,0 +1,271 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.*; +import jakarta.persistence.criteria.*; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.junit.jupiter.api.Assertions.*; + +@Timeout(value = 10, timeUnit = MINUTES) + +public class MutinyStatelessSessionTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( GuineaPig.class ); + } + + @Test + public void testStatelessSession(VertxTestContext context) { + GuineaPig pig = new GuineaPig( "Aloi" ); + test( context, getMutinySessionFactory().withStatelessSession( ss -> ss + .insert( pig ) + .chain( v -> ss.createSelectionQuery( "from GuineaPig where name=:n", GuineaPig.class ) + .setParameter( "n", pig.name ) + .getResultList() ) + .invoke( list -> { + assertFalse( list.isEmpty() ); + assertEquals( 1, list.size() ); + assertThatPigsAreEqual( pig, list.get( 0 ) ); + } ) + .chain( v -> ss.get( GuineaPig.class, pig.id ) ) + .chain( p -> { + assertThatPigsAreEqual( pig, p ); + p.name = "X"; + return ss.update( p ); + } ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "X" ) ) + .chain( v -> ss.createMutationQuery( "update GuineaPig set name='Y'" ).executeUpdate() ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "Y" ) ) + .chain( v -> ss.delete( pig ) ) + .chain( v -> ss.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList() ) + .invoke( list -> assertTrue( list.isEmpty() ) ) ) + ); + } + + @Test + public void testStatelessSessionWithNamed(VertxTestContext context) { + GuineaPig pig = new GuineaPig( "Aloi" ); + test( context, getMutinySessionFactory().withStatelessSession( ss -> ss + .insert( pig ) + .chain( v -> ss.createNamedQuery( "findbyname", GuineaPig.class ) + .setParameter( "n", pig.name ) + .getResultList() ) + .invoke( list -> { + assertFalse( list.isEmpty() ); + assertEquals( 1, list.size() ); + assertThatPigsAreEqual( pig, list.get( 0 ) ); + } ) + .chain( v -> ss.get( GuineaPig.class, pig.id ) ) + .chain( p -> { + assertThatPigsAreEqual( pig, p ); + p.name = "X"; + return ss.update( p ); + } ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "X" ) ) + .chain( v -> ss.createNamedQuery( "updatebyname" ).executeUpdate() ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "Y" ) ) + .chain( v -> ss.delete( pig ) ) + .chain( v -> ss.createNamedQuery( "findall" ).getResultList() ) + .invoke( list -> assertTrue( list.isEmpty() ) ) ) + ); + } + + @Test + public void testStatelessSessionWithNative(VertxTestContext context) { + GuineaPig pig = new GuineaPig( "Aloi" ); + test( context, getMutinySessionFactory().openStatelessSession() + .chain( ss -> ss.insert( pig ) + .chain( v -> ss + .createNativeQuery( "select * from Piggy where name=:n", GuineaPig.class ) + .setParameter( "n", pig.name ) + .getResultList() ) + .invoke( list -> { + assertFalse( list.isEmpty() ); + assertEquals( 1, list.size() ); + assertThatPigsAreEqual( pig, list.get( 0 ) ); + } ) + .chain( v -> ss.get( GuineaPig.class, pig.id ) ) + .chain( p -> { + assertThatPigsAreEqual( pig, p ); + p.name = "X"; + return ss.update( p ); + } ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "X" ) ) + .chain( v -> ss.createNativeQuery( "update Piggy set name='Y'" ) + .executeUpdate() ) + .invoke( rows -> assertEquals( 1, rows ) ) + .chain( v -> ss.refresh( pig ) ) + .invoke( v -> assertEquals( pig.name, "Y" ) ) + .chain( v -> ss.delete( pig ) ) + .chain( v -> ss.createNativeQuery( "select id from Piggy" ).getResultList() ) + .invoke( list -> assertTrue( list.isEmpty() ) ) + .chain( v -> ss.close() ) ) + ); + } + + @Test + public void testStatelessSessionGetMultiple(VertxTestContext context) { + GuineaPig a = new GuineaPig("A"); + GuineaPig b = new GuineaPig("B"); + GuineaPig c = new GuineaPig("C"); + test( context, getMutinySessionFactory().openStatelessSession() + .chain( ss -> ss.insertMultiple( List.of(a, b, c) ) + .chain( v -> ss.get( GuineaPig.class, a.id, c.id ) ) + .invoke( list -> { + assertEquals( 2, list.size() ); + assertThatPigsAreEqual( a, list.get( 0 ) ); + assertThatPigsAreEqual( c, list.get( 1 ) ); + }) + .chain( v -> ss.close() ) ) + ); + } + + @Test + public void testStatelessSessionCriteria(VertxTestContext context) { + GuineaPig pig = new GuineaPig( "Aloi" ); + + CriteriaBuilder cb = getSessionFactory().getCriteriaBuilder(); + + CriteriaQuery query = cb.createQuery( GuineaPig.class ); + Root gp = query.from( GuineaPig.class ); + query.where( cb.equal( gp.get( "name" ), cb.parameter( String.class, "n" ) ) ); + + CriteriaUpdate update = cb.createCriteriaUpdate( GuineaPig.class ); + update.from( GuineaPig.class ); + update.set( "name", "Bob" ); + + CriteriaDelete delete = cb.createCriteriaDelete( GuineaPig.class ); + delete.from( GuineaPig.class ); + + test( context, getMutinySessionFactory().openStatelessSession() + .chain( ss -> ss.insert( pig ) + .chain( v -> ss.createQuery( query ) + .setParameter( "n", pig.name ) + .getResultList() ) + .invoke( list -> { + assertFalse( list.isEmpty() ); + assertEquals( 1, list.size() ); + assertThatPigsAreEqual( pig, list.get( 0 ) ); + } ) + .chain( v -> ss.createQuery( update ).executeUpdate() ) + .invoke( rows -> assertEquals( 1, rows ) ) + .chain( v -> ss.createQuery( delete ).executeUpdate() ) + .invoke( rows -> assertEquals( 1, rows ) ) + .chain( v -> ss.close() ) ) + ); + } + + @Test + public void testTransactionPropagation(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessSession( + session -> session.withTransaction( transaction -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ) + .getResultList() + .chain( list -> { + assertNotNull( session.currentTransaction() ); + assertFalse( session.currentTransaction().isMarkedForRollback() ); + session.currentTransaction().markForRollback(); + assertTrue( session.currentTransaction().isMarkedForRollback() ); + assertTrue( transaction.isMarkedForRollback() ); + return session.withTransaction( t -> { + assertTrue( t.isMarkedForRollback() ); + return session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); + } ); + } ) ) + ) ); + } + + @Test + public void testSessionPropagation(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessSession( + session -> session.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList() + .chain( list -> getMutinySessionFactory().withStatelessSession( s -> { + assertEquals( session, s ); + return s.createSelectionQuery( "from GuineaPig", GuineaPig.class ).getResultList(); + } ) ) + ) ); + } + + private void assertThatPigsAreEqual( GuineaPig expected, GuineaPig actual) { + assertNotNull( actual ); + assertEquals( expected.getId(), actual.getId() ); + assertEquals( expected.getName(), actual.getName() ); + } + + @NamedQuery(name = "findbyname", query = "from GuineaPig where name=:n") + @NamedQuery(name = "updatebyname", query = "update GuineaPig set name='Y'") + @NamedQuery(name = "findall", query = "from GuineaPig") + + @Entity(name = "GuineaPig") + @Table(name = "Piggy") + public static class GuineaPig { + @Id + @GeneratedValue + private Integer id; + private String name; + @Version + private int version; + + public GuineaPig() { + } + + public GuineaPig(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return id + ": " + name; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + GuineaPig guineaPig = (GuineaPig) o; + return Objects.equals( name, guineaPig.name ); + } + + @Override + public int hashCode() { + return Objects.hash( name ); + } + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessSessionTest.java index c2f6ac31b..b4d2cbf1f 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessSessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessSessionTest.java @@ -135,6 +135,23 @@ public void testStatelessSessionWithNative(VertxTestContext context) { ); } + @Test + public void testStatelessSessionGetMultiple(VertxTestContext context) { + GuineaPig a = new GuineaPig("A"); + GuineaPig b = new GuineaPig("B"); + GuineaPig c = new GuineaPig("C"); + test( context, getSessionFactory().openStatelessSession() + .thenCompose( ss -> ss.insertMultiple( List.of(a, b, c) ) + .thenCompose( v -> ss.get( GuineaPig.class, a.id, c.id ) ) + .thenAccept( list -> { + assertEquals( 2, list.size() ); + assertThatPigsAreEqual( a, list.get( 0 ) ); + assertThatPigsAreEqual( c, list.get( 1 ) ); + }) + .thenCompose( v -> ss.close() ) ) + ); + } + @Test public void testStatelessSessionCriteria(VertxTestContext context) { GuineaPig pig = new GuineaPig( "Aloi" ); From 0895a28ff1e5d4f8b071f657802715a14153a0b6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 14:57:07 +0100 Subject: [PATCH 073/201] fix code which was not passing the LockMode --- .../reactive/session/impl/ReactiveStatelessSessionImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7ca780e40..ac64223a8 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 @@ -248,7 +248,7 @@ public CompletionStage reactiveGet(String entityName, Object id) { @Override public CompletionStage reactiveGet(Class entityClass, Object id, LockMode lockMode, EntityGraph fetchGraph) { - return reactiveGet( entityClass.getName(), id, LockMode.NONE, fetchGraph ); + return reactiveGet( entityClass.getName(), id, lockMode, fetchGraph ); } @Override @@ -443,7 +443,7 @@ public CompletionStage reactiveRefresh(String entityName, Object entity) { @Override public CompletionStage reactiveRefresh(Object entity, LockMode lockMode) { - return reactiveRefresh( bestGuessEntityName( entity ), entity, LockMode.NONE ); + return reactiveRefresh( bestGuessEntityName( entity ), entity, lockMode ); } @Override From dde66e05e978086f264a8b9b8a15ac8fa623339c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 14:57:24 +0100 Subject: [PATCH 074/201] squash some warnings --- .../session/impl/ReactiveSessionImpl.java | 31 ++++++++-------- .../impl/ReactiveStatelessSessionImpl.java | 35 +++++++++---------- 2 files changed, 30 insertions(+), 36 deletions(-) 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 fb20deaaf..9f5ca8c95 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 @@ -273,8 +273,8 @@ public CompletionStage reactiveFetch(T association, boolean unproxy) { return nullFuture(); } - if ( association instanceof HibernateProxy ) { - LazyInitializer initializer = ( (HibernateProxy) association ).getHibernateLazyInitializer(); + if ( association instanceof HibernateProxy proxy ) { + LazyInitializer initializer = proxy.getHibernateLazyInitializer(); if ( !initializer.isUninitialized() ) { return completedFuture( unproxy ? (T) initializer.getImplementation() : association ); } @@ -290,13 +290,12 @@ public CompletionStage reactiveFetch(T association, boolean unproxy) { } ); } } - else if ( association instanceof PersistentCollection ) { - final PersistentCollection persistentCollection = (PersistentCollection) association; - if ( persistentCollection.wasInitialized() ) { + else if (association instanceof PersistentCollection collection) { + if ( collection.wasInitialized() ) { return completedFuture( association ); } else { - return reactiveInitializeCollection( persistentCollection, false ) + return reactiveInitializeCollection( collection, false ) // don't reassociate the collection instance, because // its owner isn't associated with this session .thenApply( v -> association ); @@ -305,9 +304,8 @@ else if ( association instanceof PersistentCollection ) { else if ( isPersistentAttributeInterceptable( association ) ) { final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( association ); final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - EnhancementAsProxyLazinessInterceptor eapli = (EnhancementAsProxyLazinessInterceptor) interceptor; - return forceInitialize( association, null, eapli.getIdentifier(), eapli.getEntityName(), this ) + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { + return forceInitialize( association, null, lazinessInterceptor.getIdentifier(), lazinessInterceptor.getEntityName(), this ) .thenApply( i -> association ); } @@ -369,7 +367,7 @@ public ReactiveQuery createReactiveQuery(String queryString, Class exp delayedAfterCompletion(); try { - final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); + final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( queryString, interpretation, expectedResultType, this ); applyQuerySettingsAndHints( query ); @@ -482,7 +480,7 @@ private ReactiveSelectionQuery interpretAndCreateSelectionQuery(String hq delayedAfterCompletion(); try { - final HqlInterpretation interpretation = interpretHql( hql, resultType ); + final HqlInterpretation interpretation = interpretHql( hql, resultType ); checkSelectionQuery( hql, interpretation ); return createSelectionQuery( hql, resultType, interpretation ); } @@ -492,7 +490,7 @@ private ReactiveSelectionQuery interpretAndCreateSelectionQuery(String hq } } - private ReactiveSelectionQuery createSelectionQuery(String hql, Class resultType, HqlInterpretation interpretation) { + private ReactiveSelectionQuery createSelectionQuery(String hql, Class resultType, HqlInterpretation interpretation) { final ReactiveSqmSelectionQueryImpl query = new ReactiveSqmSelectionQueryImpl<>( hql, interpretation, resultType, this ); if ( resultType != null ) { @@ -1671,9 +1669,8 @@ public void setBatchSize(Integer batchSize) { @Override @SuppressWarnings("unchecked") public Class getEntityClass(T entity) { - if ( entity instanceof HibernateProxy ) { - return (Class) ( (HibernateProxy) entity ) - .getHibernateLazyInitializer() + if ( entity instanceof HibernateProxy proxy ) { + return (Class) proxy.getHibernateLazyInitializer() .getPersistentClass(); } else { @@ -1684,8 +1681,8 @@ public Class getEntityClass(T entity) { @Override public Object getEntityId(Object entity) { - if ( entity instanceof HibernateProxy ) { - return ( (HibernateProxy) entity ).getHibernateLazyInitializer() + if ( entity instanceof HibernateProxy proxy ) { + return proxy.getHibernateLazyInitializer() .getIdentifier(); } else { 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 ac64223a8..bc588dae8 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 @@ -397,8 +397,8 @@ public CompletionStage reactiveDelete(Object entity) { @Override public CompletionStage reactiveUpdate(Object entity) { checkOpen(); - if ( entity instanceof HibernateProxy ) { - final LazyInitializer hibernateLazyInitializer = ( (HibernateProxy) entity ).getHibernateLazyInitializer(); + if ( entity instanceof HibernateProxy proxy ) { + final LazyInitializer hibernateLazyInitializer = proxy.getHibernateLazyInitializer(); return hibernateLazyInitializer.isUninitialized() ? failedFuture( LOG.uninitializedProxyUpdate( entity.getClass() ) ) : executeReactiveUpdate( hibernateLazyInitializer.getImplementation() ); @@ -730,22 +730,21 @@ public CompletionStage reactiveFetch(T association, boolean unproxy) { return completedFuture( unproxy ? (T) initializer.getImplementation() : association ); } } - else if ( association instanceof PersistentCollection ) { - final PersistentCollection persistentCollection = (PersistentCollection) association; - if ( persistentCollection.wasInitialized() ) { + else if ( association instanceof PersistentCollection collection ) { + if ( collection.wasInitialized() ) { return completedFuture( association ); } else { final ReactiveCollectionPersister collectionDescriptor = (ReactiveCollectionPersister) getFactory().getMappingMetamodel() - .getCollectionDescriptor( persistentCollection.getRole() ); + .getCollectionDescriptor( collection.getRole() ); - final Object key = persistentCollection.getKey(); - persistenceContext.addUninitializedCollection( collectionDescriptor, persistentCollection, key ); - persistentCollection.setCurrentSession( this ); + final Object key = collection.getKey(); + persistenceContext.addUninitializedCollection( collectionDescriptor, collection, key ); + collection.setCurrentSession( this ); return collectionDescriptor.reactiveInitialize( key, this ) .whenComplete( (v, e) -> { - persistentCollection.unsetSession( this ); + collection.unsetSession( this ); if ( persistenceContext.isLoadFinished() ) { persistenceContext.clear(); } @@ -756,13 +755,11 @@ else if ( association instanceof PersistentCollection ) { else if ( isPersistentAttributeInterceptable( association ) ) { final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( association ); final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - final EnhancementAsProxyLazinessInterceptor proxyInterceptor = - (EnhancementAsProxyLazinessInterceptor) interceptor; - proxyInterceptor.setSession( this ); - return forceInitialize( association, null, proxyInterceptor.getIdentifier(), proxyInterceptor.getEntityName(), this ) + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { + lazinessInterceptor.setSession( this ); + return forceInitialize( association, null, lazinessInterceptor.getIdentifier(), lazinessInterceptor.getEntityName(), this ) .whenComplete( (i,e) -> { - proxyInterceptor.unsetSession(); + lazinessInterceptor.unsetSession(); if ( persistenceContext.isLoadFinished() ) { persistenceContext.clear(); } @@ -860,7 +857,7 @@ public ReactiveSqmQueryImplementor createReactiveQuery(String queryString delayedAfterCompletion(); try { - final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); + final HqlInterpretation interpretation = interpretHql( queryString, expectedResultType ); final ReactiveQuerySqmImpl query = new ReactiveQuerySqmImpl<>( queryString, interpretation, expectedResultType, this ); applyQuerySettingsAndHints( query ); @@ -973,7 +970,7 @@ private ReactiveSelectionQuery interpretAndCreateSelectionQuery(String hq delayedAfterCompletion(); try { - final HqlInterpretation interpretation = interpretHql( hql, resultType ); + final HqlInterpretation interpretation = interpretHql( hql, resultType ); checkSelectionQuery( hql, interpretation ); return createSelectionQuery( hql, resultType, interpretation ); } @@ -983,7 +980,7 @@ private ReactiveSelectionQuery interpretAndCreateSelectionQuery(String hq } } - private ReactiveSelectionQuery createSelectionQuery(String hql, Class resultType, HqlInterpretation interpretation) { + private ReactiveSelectionQuery createSelectionQuery(String hql, Class resultType, HqlInterpretation interpretation) { final ReactiveSqmSelectionQueryImpl query = new ReactiveSqmSelectionQueryImpl<>( hql, interpretation, resultType, this ); if ( resultType != null ) { From c8bc1f65a23c9badbf5b50470aa83b73c03a53f4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 17 Feb 2025 21:12:34 +0100 Subject: [PATCH 075/201] three code cleanups --- .../AbstractReactiveSaveEventListener.java | 30 +++---- .../session/ReactiveQueryProducer.java | 3 + .../session/impl/ReactiveSessionImpl.java | 34 +++---- .../impl/ReactiveStatelessSessionImpl.java | 90 ++++++++----------- 4 files changed, 75 insertions(+), 82 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java index 2bed60008..6ca8a8ec2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java @@ -180,23 +180,21 @@ private CompletionStage generateId( EventSource source, ReactiveIdentifierGenerator generator, EntityPersister persister) { - return generator - .generate( (ReactiveConnectionSupplier) source, entity ) + return generator.generate( (ReactiveConnectionSupplier) source, entity ) .thenApply( id -> { - final Object generatedId = castToIdentifierType( id, persister ); - if ( generatedId == null ) { - throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); - } - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Generated identifier: %s, using strategy: %s", - persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), - generator.getClass().getName() - ); - } - return generatedId; - } - ); + final Object generatedId = castToIdentifierType( id, persister ); + if ( generatedId == null ) { + throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); + } + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Generated identifier: %s, using strategy: %s", + persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), + generator.getClass().getName() + ); + } + return generatedId; + } ); } /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index 9b720b5a8..25a7402ef 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -63,10 +63,13 @@ public interface ReactiveQueryProducer extends ReactiveConnectionSupplier { ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Class resultClass); + @Deprecated(forRemoval = true) ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Class resultClass, String tableAlias); + @Deprecated(forRemoval = true) ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName); + @Deprecated(forRemoval = true) ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName, Class resultClass); ReactiveSelectionQuery createReactiveSelectionQuery(String hqlString, Class resultType); 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 9f5ca8c95..fc8ee8ae6 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 @@ -415,23 +415,21 @@ public void prepareForQueryExecution(boolean requiresTxn) { @Override public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Class resultClass) { final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString ); - return addResultType( resultClass, query ); + handleTupleResultType( resultClass, query ); + addEntityOrResultType( resultClass, query ); + return query; } - private ReactiveNativeQuery addResultType(Class resultClass, ReactiveNativeQuery query) { - if ( Tuple.class.equals( resultClass ) ) { - query.setTupleTransformer( new NativeQueryTupleTransformer() ); - } - else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { + private void addEntityOrResultType(Class resultClass, ReactiveNativeQuery query) { + if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { query.addEntity( "alias1", resultClass.getName(), LockMode.READ ); } - else if ( resultClass != Object.class && resultClass != Object[].class ) { + else if ( resultClass != Object.class && resultClass != Object[].class && resultClass != Tuple.class ) { query.addResultTypeClass( resultClass ); } - return query; } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Class resultClass, String tableAlias) { final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString ); if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { @@ -443,7 +441,7 @@ public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Cl } } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName) { checkOpen(); pulseTransactionCoordinator(); @@ -460,12 +458,10 @@ public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, St } } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName, Class resultClass) { final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString, resultSetMappingName ); - if ( Tuple.class.equals( resultClass ) ) { - query.setTupleTransformer( new NativeQueryTupleTransformer() ); - } + handleTupleResultType( resultClass, query ); return query; } @@ -610,7 +606,15 @@ private void addAffectedEntities(AffectedEntities affectedEntities, NativeQueryI @Override public ReactiveNativeQuery createReactiveNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { final ReactiveNativeQuery query = createReactiveNativeQuery( queryString, affectedEntities ); - return addResultType( resultType, query ); + handleTupleResultType( resultType, query ); + addEntityOrResultType( resultType, query ); + return query; + } + + private static void handleTupleResultType(Class resultType, ReactiveNativeQuery query) { + if ( Tuple.class.equals(resultType) ) { + query.setTupleTransformer( NativeQueryTupleTransformer.INSTANCE ); + } } @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 bc588dae8..8e8e338a6 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 @@ -51,10 +51,8 @@ import org.hibernate.query.criteria.JpaCriteriaQuery; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.hql.spi.SqmQueryImplementor; -import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryImplementor; -import org.hibernate.query.sql.internal.NativeQueryImpl; import org.hibernate.query.sql.spi.NativeQueryImplementor; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.tree.SqmStatement; @@ -372,12 +370,18 @@ private void firePostInsert(Object entity, Object id, Object[] state, EntityPers private CompletionStage generateId(EntityPersister persister, Object entity, Generator generator) { if ( generator.generatesOnInsert() ) { - return generator instanceof ReactiveIdentifierGenerator - ? ( (ReactiveIdentifierGenerator) generator ).generate( this, this ) - : completedFuture( ( (BeforeExecutionGenerator) generator ).generate( this, entity, null, INSERT ) ); - } + if ( generator instanceof ReactiveIdentifierGenerator reactiveGenerator ) { + return reactiveGenerator.generate(this, this); + } + else if ( generator instanceof BeforeExecutionGenerator beforeExecutionGenerator ) { + return completedFuture( beforeExecutionGenerator.generate(this, entity, null, INSERT) ); + } + else { + throw new IllegalArgumentException( "Unsupported generator type: " + generator.getClass().getName() ); + } + } else { - Object id = persister.getIdentifier( entity, this ); + final Object id = persister.getIdentifier( entity, this ); if ( id == null ) { throw new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ); } @@ -878,8 +882,7 @@ public ReactiveNativeQueryImplementor createReactiveNativeQuery(String sq delayedAfterCompletion(); try { - ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( sqlString, this); - + final ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( sqlString, this ); if ( isEmpty( query.getComment() ) ) { query.setComment( "dynamic native SQL query" ); } @@ -893,53 +896,46 @@ public ReactiveNativeQueryImplementor createReactiveNativeQuery(String sq @Override public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Class resultClass) { - ReactiveNativeQuery query = createReactiveNativeQuery( sqlString ); - if ( Tuple.class.equals( resultClass ) ) { - query.setTupleTransformer( new NativeQueryTupleTransformer() ); - } - else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { + final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString ); + handleTupleResultType( resultClass, query ); + addEntityOrResultType( resultClass, query ); + return query; + } + + private void addEntityOrResultType(Class resultClass, ReactiveNativeQuery query) { + if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { query.addEntity( "alias1", resultClass.getName(), LockMode.READ ); } - else { - ( (NativeQueryImpl) query ).addScalar( 1, resultClass ); + else if ( resultClass != Object.class && resultClass != Object[].class && resultClass != Tuple.class ) { + query.addResultTypeClass( resultClass ); } - return query; } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery( String sqlString, Class resultClass, String tableAlias) { final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString ); - if ( getFactory().getMappingMetamodel().isEntityClass(resultClass) ) { + if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { query.addEntity( tableAlias, resultClass.getName(), LockMode.READ ); return query; } - - throw new UnknownEntityTypeException( "unable to locate persister: " + resultClass.getName() ); + else { + throw new UnknownEntityTypeException( "unable to locate persister: " + resultClass.getName() ); + } } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName) { checkOpen(); pulseTransactionCoordinator(); delayedAfterCompletion(); try { - if ( isNotEmpty( resultSetMappingName ) ) { - final NamedResultSetMappingMemento resultSetMappingMemento = getFactory().getQueryEngine() - .getNamedObjectRepository() - .getResultSetMappingMemento( resultSetMappingName ); - - if ( resultSetMappingMemento == null ) { - throw new HibernateException( "Could not resolve specified result-set mapping name : " + resultSetMappingName ); - } - return new ReactiveNativeQueryImpl<>( sqlString, resultSetMappingMemento, this ); - } - else { - return new ReactiveNativeQueryImpl<>( sqlString, this ); - } + return isNotEmpty( resultSetMappingName ) + ? new ReactiveNativeQueryImpl<>( sqlString, getResultSetMappingMemento( resultSetMappingName ), this ) + : new ReactiveNativeQueryImpl<>( sqlString, this ); //TODO: why no applyQuerySettingsAndHints( query ); ??? } catch (RuntimeException he) { @@ -947,15 +943,13 @@ public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, St } } - @Override + @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery( String sqlString, String resultSetMappingName, Class resultClass) { final ReactiveNativeQuery query = createReactiveNativeQuery( sqlString, resultSetMappingName ); - if ( Tuple.class.equals( resultClass ) ) { - query.setTupleTransformer( new NativeQueryTupleTransformer() ); - } + handleTupleResultType( resultClass, query ); return query; } @@ -1097,21 +1091,15 @@ public ReactiveNativeQuery createReactiveNativeQuery( Class resultType, AffectedEntities affectedEntities) { final ReactiveNativeQuery query = createReactiveNativeQuery( queryString, affectedEntities ); - return addResultType( resultType, query ); + handleTupleResultType( resultType, query ); + addEntityOrResultType( resultType, query ); + return query; } - //TODO: copy/paste from ORM, change visibility - private ReactiveNativeQuery addResultType(Class resultClass, ReactiveNativeQuery query) { - if ( Tuple.class.equals( resultClass ) ) { - query.setTupleTransformer( new NativeQueryTupleTransformer() ); + private static void handleTupleResultType(Class resultType, ReactiveNativeQuery query) { + if ( Tuple.class.equals(resultType) ) { + query.setTupleTransformer( NativeQueryTupleTransformer.INSTANCE ); } - else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) { - query.addEntity( "alias1", resultClass.getName(), LockMode.READ ); - } - else if ( resultClass != Object.class && resultClass != Object[].class ) { - query.addScalar( 1, resultClass ); - } - return query; } @Override From 98b3e22790b6933cbfb81867a0ae65a0e964fee2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 20 Feb 2025 11:13:34 +0100 Subject: [PATCH 076/201] use safe casts --- .../java/org/hibernate/reactive/mutiny/Mutiny.java | 13 ++++++------- .../java/org/hibernate/reactive/stage/Stage.java | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index e6cdab9d5..a8faefbbd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -18,7 +18,6 @@ import org.hibernate.LockMode; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.AbstractPersistentCollection; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -2452,18 +2451,18 @@ static Uni fetch(T association) { } final SharedSessionContractImplementor session; - if ( association instanceof HibernateProxy ) { - session = ( (HibernateProxy) association ).getHibernateLazyInitializer().getSession(); + if ( association instanceof HibernateProxy proxy ) { + session = proxy.getHibernateLazyInitializer().getSession(); } - else if ( association instanceof PersistentCollection ) { + else if ( association instanceof AbstractPersistentCollection collection ) { //this unfortunately doesn't work for stateless session because the session ref gets set to null - session = ( (AbstractPersistentCollection) association ).getSession(); + session = collection.getSession(); } else if ( isPersistentAttributeInterceptable( association ) ) { final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( association ); final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - session = ( (EnhancementAsProxyLazinessInterceptor) interceptor ).getLinkedSession(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { + session = lazinessInterceptor.getLinkedSession(); } else { return Uni.createFrom().item( association ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 1cf8f4fed..711a91ab9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -19,7 +19,6 @@ import org.hibernate.LockMode; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.AbstractPersistentCollection; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -2499,17 +2498,17 @@ static CompletionStage fetch(T association) { } final SharedSessionContractImplementor session; - if ( association instanceof HibernateProxy) { - session = ( (HibernateProxy) association ).getHibernateLazyInitializer().getSession(); + if ( association instanceof HibernateProxy proxy ) { + session = proxy.getHibernateLazyInitializer().getSession(); } - else if ( association instanceof PersistentCollection) { - session = ( (AbstractPersistentCollection) association ).getSession(); + else if ( association instanceof AbstractPersistentCollection collection ) { + session = collection.getSession(); } else if ( isPersistentAttributeInterceptable( association ) ) { final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( association ); final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor) { - session = ( (EnhancementAsProxyLazinessInterceptor) interceptor ).getLinkedSession(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor) { + session = lazinessInterceptor.getLinkedSession(); } else { return CompletionStages.completedFuture( association ); From b8d1190e54eb30cd44337a443d9d9176b3b4c62c Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 20 Feb 2025 15:57:34 +0000 Subject: [PATCH 077/201] Update project version to : `3.0.0.Beta2` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..08a599ec8 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.Beta2 \ No newline at end of file From 1d3b099298c442b392a929ac20e6143b7df97330 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 20 Feb 2025 15:58:29 +0000 Subject: [PATCH 078/201] Update project version to : `3.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 08a599ec8..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.Beta2 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From b4de2db5b31c8e3ee0ead5c2a1124e8ca1048c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 03:51:47 +0000 Subject: [PATCH 079/201] Bump the workflow-actions group with 2 updates Bumps the workflow-actions group with 2 updates: [actions/cache](https://github.com/actions/cache) and [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `actions/cache` from 4.2.0 to 4.2.2 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/1bd1e32a3bdc45362d1e726936510720a7c30a57...d4323d4df104b026a6aa633fdb11d772146be0bf) Updates `actions/upload-artifact` from 4.6.0 to 4.6.1 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08...4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: workflow-actions - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch dependency-group: workflow-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f519dac8..d63e27906 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 id: cache-gradle with: path: | @@ -116,7 +116,7 @@ jobs: - name: Run examples in '${{ matrix.example }}' on ${{ matrix.db }} run: ./gradlew :${{ matrix.example }}:runAllExamplesOn${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 if: failure() with: name: reports-examples-${{ matrix.db }} @@ -139,7 +139,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 id: cache-gradle with: path: | @@ -166,7 +166,7 @@ jobs: - name: Build and Test with ${{ matrix.db }} run: ./gradlew build -PshowStandardOutput -Pdocker -Pdb=${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 if: failure() with: name: reports-db-${{ matrix.db }} @@ -217,7 +217,7 @@ jobs: echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT - name: Cache Maven/Gradle Dependency/Dist Caches id: cache-maven - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 # if it's not a pull request, we restore and save the cache if: github.event_name != 'pull_request' with: @@ -234,7 +234,7 @@ jobs: ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}- ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}- - name: Restore Maven/Gradle Dependency/Dist Caches - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 # if it's a pull request, we restore the cache, but we don't save it if: github.event_name == 'pull_request' with: @@ -292,7 +292,7 @@ jobs: -Porg.gradle.java.installations.paths=${{ steps.mainjdk-exportpath.outputs.path }},${{ steps.testjdk-exportpath.outputs.path }} \ ${{ matrix.java.jvm_args && '-Ptest.jdk.launcher.args=' }}${{ matrix.java.jvm_args }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 if: failure() with: name: reports-java${{ matrix.java.name }} From 65497c7205e90742e2bef291485b67697ff227f9 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 25 Feb 2025 16:00:47 +0100 Subject: [PATCH 080/201] [#2134] Use flat badges in README to match what other projects do --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 746235200..482a0af59 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ [![Hibernate team logo](http://static.jboss.org/hibernate/images/hibernate_logo_whitebkg_200px.png)](https://hibernate.org/reactive) -[![Main branch build status](https://github.com/hibernate/hibernate-reactive/workflows/Hibernate%20Reactive%20CI/badge.svg?style=flat)](https://github.com/hibernate/hibernate-reactive/actions?query=workflow%3A%22Hibernate+Reactive+CI%22) -[![Apache 2.0 license](https://img.shields.io/badge/License-APACHE%202.0-green.svg?logo=APACHE&style=flat)](https://opensource.org/licenses/Apache-2.0) -[![Latest version on Maven Central](https://img.shields.io/maven-central/v/org.hibernate.reactive/hibernate-reactive-core.svg?label=Maven%20Central&logo=apache-maven&style=flat)](https://search.maven.org/search?q=g:org.hibernate.reactive) -[![Developers stream on Zulip](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg?logo=zulip&style=flat)](https://hibernate.zulipchat.com/#narrow/stream/205413-hibernate-reactive-dev) -[![Hibernate Reactive documentation](https://img.shields.io/badge/Hibernate-Documentation-orange.svg?logo=Hibernate&style=flat)](https://hibernate.org/reactive/documentation/) +[![Main branch build status](https://img.shields.io/github/actions/workflow/status/hibernate/hibernate-reactive/build.yml?label=Hibernate%20Reactive%20CI&style=for-the-badge)](https://github.com/hibernate/hibernate-reactive/actions?query=workflow%3A%22Hibernate+Reactive+CI%22) +[![Apache 2.0 license](https://img.shields.io/badge/License-APACHE%202.0-green.svg?logo=APACHE&style=for-the-badge)](https://opensource.org/licenses/Apache-2.0) +[![Latest version on Maven Central](https://img.shields.io/maven-central/v/org.hibernate.reactive/hibernate-reactive-core.svg?label=Maven%20Central&logo=apache-maven&style=for-the-badge)](https://search.maven.org/search?q=g:org.hibernate.reactive) +[![Developers stream on Zulip](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg?logo=zulip&style=for-the-badge)](https://hibernate.zulipchat.com/#narrow/stream/205413-hibernate-reactive-dev) +[![Hibernate Reactive documentation](https://img.shields.io/badge/Hibernate-Documentation-orange.svg?logo=Hibernate&style=for-the-badge)](https://hibernate.org/reactive/documentation/) # Hibernate Reactive From f4bd7ad4a632c575546569b9f1278287f34887aa Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 25 Feb 2025 16:03:13 +0100 Subject: [PATCH 081/201] [#2134] Add Reproducible Central badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 482a0af59..72b3a7e63 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Latest version on Maven Central](https://img.shields.io/maven-central/v/org.hibernate.reactive/hibernate-reactive-core.svg?label=Maven%20Central&logo=apache-maven&style=for-the-badge)](https://search.maven.org/search?q=g:org.hibernate.reactive) [![Developers stream on Zulip](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg?logo=zulip&style=for-the-badge)](https://hibernate.zulipchat.com/#narrow/stream/205413-hibernate-reactive-dev) [![Hibernate Reactive documentation](https://img.shields.io/badge/Hibernate-Documentation-orange.svg?logo=Hibernate&style=for-the-badge)](https://hibernate.org/reactive/documentation/) +[![Reproducible Builds](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/org/hibernate/reactive/hibernate-reactive/badge.json&style=for-the-badge)](https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/org/hibernate/reactive/hibernate-reactive/README.md) # Hibernate Reactive From 41119ba7b573f5fcb04a18ebe9be7ce6e7a7bd28 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 20 Feb 2025 17:21:01 +0100 Subject: [PATCH 082/201] [#2108] StatelessSession insertAll in batch does not do batching --- .../org/hibernate/reactive/mutiny/Mutiny.java | 12 +++++--- .../impl/MutinyStatelessSessionImpl.java | 10 +++---- .../impl/ReactiveStatelessSessionImpl.java | 30 ++++++++++++------- .../org/hibernate/reactive/stage/Stage.java | 12 +++++--- .../stage/impl/StageStatelessSessionImpl.java | 8 ++--- 5 files changed, 45 insertions(+), 27 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index a8faefbbd..a648a7bd7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1779,7 +1779,8 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp Uni insert(Object entity); /** - * Insert multiple rows. + * Insert multiple rows, using the number of the + * given entities as the batch size. * * @param entities new transient instances * @@ -1817,7 +1818,8 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp Uni delete(Object entity); /** - * Delete multiple rows. + * Delete multiple rows, using the number of the + * given entities as the batch size. * * @param entities detached entity instances * @@ -1855,7 +1857,8 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp Uni update(Object entity); /** - * Update multiple rows. + * Update multiple rows, using the number of the + * given entities as the batch size. * * @param entities detached entity instances * @@ -1915,7 +1918,8 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp Uni refresh(Object entity); /** - * Refresh the entity instance state from the database. + * Refresh the entity instance state from the database, using the number of the + * given entities as the batch size. * * @param entities The entities to be refreshed. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index 473196712..a80d79750 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -138,7 +138,7 @@ public Uni insert(Object entity) { @Override public Uni insertAll(Object... entities) { - return uni( () -> delegate.reactiveInsertAll( entities ) ); + return uni( () -> delegate.reactiveInsertAll( entities.length, entities ) ); } @Override @@ -158,12 +158,12 @@ public Uni delete(Object entity) { @Override public Uni deleteAll(Object... entities) { - return uni( () -> delegate.reactiveDeleteAll( entities ) ); + return uni( () -> delegate.reactiveDeleteAll( entities.length, entities ) ); } @Override public Uni deleteAll(int batchSize, Object... entities) { - return uni( () -> delegate.reactiveDeleteAll( entities ) ); + return uni( () -> delegate.reactiveDeleteAll( batchSize, entities ) ); } @Override @@ -178,7 +178,7 @@ public Uni update(Object entity) { @Override public Uni updateAll(Object... entities) { - return uni( () -> delegate.reactiveUpdateAll( entities ) ); + return uni( () -> delegate.reactiveUpdateAll( entities.length, entities ) ); } @Override @@ -208,7 +208,7 @@ public Uni upsert(String entityName, Object entity) { @Override public Uni refreshAll(Object... entities) { - return uni( () -> delegate.reactiveRefreshAll( entities ) ); + return uni( () -> delegate.reactiveRefreshAll( entities.length, entities ) ); } @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 8e8e338a6..53b928903 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 @@ -128,7 +128,7 @@ public class ReactiveStatelessSessionImpl extends StatelessSessionImpl implement private final ReactiveConnection reactiveConnection; - private final ReactiveStatelessSession batchingHelperSession; + private final ReactiveStatelessSessionImpl batchingHelperSession; private final PersistenceContext persistenceContext; @@ -150,10 +150,9 @@ private ReactiveStatelessSessionImpl( PersistenceContext persistenceContext) { super( factory, options ); this.persistenceContext = persistenceContext; - Integer batchSize = getConfiguredJdbcBatchSize(); - reactiveConnection = batchSize == null || batchSize < 2 - ? connection - : new BatchingConnection( connection, batchSize ); + // Setting batch size to 0 because `StatelessSession` does not consider + // the value of `hibernate.jdbc.batch_size` + reactiveConnection = new BatchingConnection( connection, 0 ); batchingHelperSession = this; influencers = new LoadQueryInfluencers( factory ); } @@ -551,9 +550,12 @@ public CompletionStage reactiveInsertAll(Object... entities) { @Override public CompletionStage reactiveInsertAll(int batchSize, Object... entities) { + final Integer jdbcBatchSize = batchingHelperSession.getJdbcBatchSize(); + batchingHelperSession.setJdbcBatchSize( batchSize ); final ReactiveConnection connection = batchingConnection( batchSize ); return loop( entities, batchingHelperSession::reactiveInsert ) - .thenCompose( v -> connection.executeBatch() ); + .thenCompose( v -> connection.executeBatch() ) + .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); } @Override @@ -564,9 +566,12 @@ public CompletionStage reactiveUpdateAll(Object... entities) { @Override public CompletionStage reactiveUpdateAll(int batchSize, Object... entities) { + final Integer jdbcBatchSize = batchingHelperSession.getJdbcBatchSize(); + batchingHelperSession.setJdbcBatchSize( batchSize ); final ReactiveConnection connection = batchingConnection( batchSize ); return loop( entities, batchingHelperSession::reactiveUpdate ) - .thenCompose( v -> connection.executeBatch() ); + .thenCompose( v -> connection.executeBatch() ) + .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); } @Override @@ -577,9 +582,11 @@ public CompletionStage reactiveDeleteAll(Object... entities) { @Override public CompletionStage reactiveDeleteAll(int batchSize, Object... entities) { + final Integer jdbcBatchSize = batchingHelperSession.getJdbcBatchSize(); + batchingHelperSession.setJdbcBatchSize( batchSize ); final ReactiveConnection connection = batchingConnection( batchSize ); - return loop( entities, batchingHelperSession::reactiveDelete ) - .thenCompose( v -> connection.executeBatch() ); + return loop( entities, batchingHelperSession::reactiveDelete ).thenCompose( v -> connection.executeBatch() ) + .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); } @@ -591,9 +598,12 @@ public CompletionStage reactiveRefreshAll(Object... entities) { @Override public CompletionStage reactiveRefreshAll(int batchSize, Object... entities) { + final Integer jdbcBatchSize = batchingHelperSession.getJdbcBatchSize(); + batchingHelperSession.setJdbcBatchSize( batchSize ); final ReactiveConnection connection = batchingConnection( batchSize ); return loop( entities, batchingHelperSession::reactiveRefresh ) - .thenCompose( v -> connection.executeBatch() ); + .thenCompose( v -> connection.executeBatch() ) + .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); } private ReactiveConnection batchingConnection(int batchSize) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 711a91ab9..074f63e58 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1836,7 +1836,8 @@ default CompletionStage get(Class entityClass, Object id, LockModeType CompletionStage insert(Object entity); /** - * Insert multiple rows. + * Insert multiple rows, using the number of the + * given entities as the batch size. * * @param entities new transient instances * @@ -1874,7 +1875,8 @@ default CompletionStage get(Class entityClass, Object id, LockModeType CompletionStage delete(Object entity); /** - * Delete multiple rows. + * Delete multiple rows, using the number of the + * given entities as the batch size. * * @param entities detached entity instances * @@ -1912,7 +1914,8 @@ default CompletionStage get(Class entityClass, Object id, LockModeType CompletionStage update(Object entity); /** - * Update multiple rows. + * Update multiple rows, using the number of the + * given entities as the batch size. * * @param entities a detached entity instance * @@ -1950,7 +1953,8 @@ default CompletionStage get(Class entityClass, Object id, LockModeType CompletionStage refresh(Object entity); /** - * Refresh the entity instance state from the database. + * Refresh the entity instance state from the database, using the number of the + * given entities as the batch size. * * @param entities The entities to be refreshed. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 7724a5cd1..c97be1a94 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -67,7 +67,7 @@ public CompletionStage insert(Object entity) { @Override public CompletionStage insert(Object... entities) { - return delegate.reactiveInsertAll( entities ); + return delegate.reactiveInsertAll( entities.length, entities ); } @Override @@ -87,7 +87,7 @@ public CompletionStage delete(Object entity) { @Override public CompletionStage delete(Object... entities) { - return delegate.reactiveDeleteAll( entities ); + return delegate.reactiveDeleteAll( entities.length, entities ); } @Override @@ -107,7 +107,7 @@ public CompletionStage update(Object entity) { @Override public CompletionStage update(Object... entities) { - return delegate.reactiveUpdateAll( entities ); + return delegate.reactiveUpdateAll( entities.length, entities ); } @Override @@ -127,7 +127,7 @@ public CompletionStage refresh(Object entity) { @Override public CompletionStage refresh(Object... entities) { - return delegate.reactiveRefreshAll( entities ); + return delegate.reactiveRefreshAll( entities.length, entities ); } @Override From de13a2784058c84147f2b991087c7e9dbb1d7bc2 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 12 Feb 2025 10:57:21 +0100 Subject: [PATCH 083/201] [#2108] Add test for StatelessSession insertAll in batch does not do batching --- ...ReactiveStatelessDefaultBatchSizeTest.java | 622 ++++++++++++++++++ .../reactive/BatchingConnectionTest.java | 69 ++ 2 files changed, 691 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java new file mode 100644 index 000000000..c4d74261e --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java @@ -0,0 +1,622 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.stage.Stage; +import org.hibernate.reactive.testing.SqlStatementTracker; + +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 java.util.List; +import java.util.Objects; +import java.util.Set; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * The test aims to check that methods accepting the batch size as parameter e.g. {@link Stage.StatelessSession#insert(int, Object...)} + * work when {@link AvailableSettings.STATEMENT_BATCH_SIZE} hasn't been set. + */ +@Timeout(value = 10, timeUnit = MINUTES) +public class ReactiveStatelessDefaultBatchSizeTest extends BaseReactiveTest { + private static SqlStatementTracker sqlTracker; + + private static final String PIG_ONE_NAME = "One"; + private static final String PIG_TWO_NAME = "Two"; + private static final String PIG_THREE_NAME = "Three"; + private static final String PIG_FOUR_NAME = "Four"; + private static final String PIG_FIVE_NAME = "Five"; + private static final String PIG_SIX_NAME = "Six"; + + private static final GuineaPig PIG_ONE = new GuineaPig( 11, PIG_ONE_NAME ); + private static final GuineaPig PIG_TWO = new GuineaPig( 22, PIG_TWO_NAME ); + private static final GuineaPig PIG_THREE = new GuineaPig( 33, PIG_THREE_NAME ); + private static final GuineaPig PIG_FOUR = new GuineaPig( 44, PIG_FOUR_NAME ); + private static final GuineaPig PIG_FIVE = new GuineaPig( 55, PIG_FIVE_NAME ); + private static final GuineaPig PIG_SIX = new GuineaPig( 66, PIG_SIX_NAME ); + + private static final GuineaPig[] PIGS = { PIG_ONE, PIG_TWO, PIG_THREE, PIG_FOUR, PIG_FIVE, PIG_SIX, }; + + @Override + protected Set> annotatedEntities() { + return Set.of( GuineaPig.class ); + } + + @Override + protected Configuration constructConfiguration() { + Configuration configuration = super.constructConfiguration(); + + // Construct a tracker that collects query statements via the SqlStatementLogger framework. + // Pass in configuration properties to hand off any actual logging properties + sqlTracker = new SqlStatementTracker( + ReactiveStatelessDefaultBatchSizeTest::filter, + configuration.getProperties() + ); + return configuration; + } + + @BeforeEach + public void clearTracker() { + sqlTracker.clear(); + } + + @Override + protected void addServices(StandardServiceRegistryBuilder builder) { + sqlTracker.registerService( builder ); + } + + private static boolean filter(String s) { + String[] accepted = { "insert ", "update ", "delete " }; + for ( String valid : accepted ) { + if ( s.toLowerCase().startsWith( valid ) ) { + return true; + } + } + return false; + } + + @Test + public void testMutinyBatchingInsert(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( () -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + ); + } + + @Test + public void testMutinyBatchingInsertMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertMultiple( List.of( PIGS ) ) ) + .invoke( () -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + .invoke( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + .invoke( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) + ) + ) + ); + } + + @Test + public void testMutinyBatchingInsertAllNoBatchSizeParameter(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( PIGS ) ) + .invoke( () -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + .invoke( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + .invoke( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) + ) + ) + ); + } + + @Test + public void testStageBatchingInsert(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) + ) + ) + ); + } + + @Test + public void testStageBatchingInsertMultiple(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insertMultiple( List.of(PIGS) ) ) + .thenAccept( v -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) + ) + ) + ); + } + + @Test + public void testStageBatchingInsertNoBatchSizeParameter(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( PIGS ) ) + .thenAccept( v -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "insert into pig \\(name,id\\) values (.*)" ); + } ) + .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) + ) + ) + ); + } + + @Test + public void testMutinyBatchingDelete(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + ) + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( + s -> + s.deleteAll( 10, pigs.subList( 0, 2 ).toArray() ) + ) + ) + .invoke( () -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + ) + .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testMutinyBatchingDeleteMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + ) + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( + s -> s.deleteMultiple( pigs.subList( 0, 2 ) ) ) + ) + .invoke( () -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + ) + .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testMutinyBatchingDeleteAllNoBatchSizeParameter(VertxTestContext context) { + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + ) + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( + s -> s.deleteAll( pigs.subList( 0, 2 ).toArray() ) ) + ) + .invoke( () -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + ) + .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testStageBatchingDelete(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> { + sqlTracker.clear(); + return s.delete( 10, pigs.subList( 0, 2 ).toArray() ); + } + ) ) + .thenAccept( vo -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "delete from pig where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() + ) + .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testStageBatchingDeleteMultiple(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> { + sqlTracker.clear(); + return s.deleteMultiple( pigs.subList( 0, 2 ) ); + } + ) ) + .thenAccept( vo -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "delete from pig where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() ) + .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testStageBatchingDeleteNoBatchSizeParameter(VertxTestContext context) { + test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> { + sqlTracker.clear(); + return s.delete( pigs.subList( 0, 2 ).toArray() ); + } + ) ) + .thenAccept( vo -> { + // We expect only one delete query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches( "delete from pig where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p", GuineaPig.class ) + .getResultList() ) + .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) + ) ) + ); + } + + @Test + public void testMutinyBatchingUpdate(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> { + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.updateAll( 10, new GuineaPig[] { guineaPigOne, guineaPigTwo } ); + } ) + ) ) + .invoke( () -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .invoke( guineaPigs -> { + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); + } ) ) ) + ); + } + + @Test + public void testMutinyBatchingUpdateMultiple(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> { + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.updateMultiple( List.of( guineaPigOne, guineaPigTwo ) ); + } ) + ) ) + .invoke( () -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .invoke( guineaPigs -> { + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); + } ) ) ) + ); + } + + @Test + public void testMutinyBatchingUpdateAllNoBatchSizeParameter(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .invoke( pigs -> sqlTracker.clear() ) + .chain( pigs -> { + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.updateAll( guineaPigOne, guineaPigTwo ); + } ) + ) ) + .invoke( () -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .invoke( guineaPigs -> { + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); + } ) ) ) + ); + } + + @Test + public void testStageBatchingUpdate(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + sqlTracker.clear(); + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.update( 10, new GuineaPig[] { guineaPigOne, guineaPigTwo } ); + } ) + ) + .thenAccept( vo -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .thenAccept( guineaPigs -> + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) + ) + ) ) ) + ); + } + + @Test + public void testStageBatchingUpdateMultiple(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + sqlTracker.clear(); + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.updateMultiple( List.of( guineaPigOne, guineaPigTwo ) ); + } ) + ) + .thenAccept( vo -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .thenAccept( guineaPigs -> + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) + ) + ) ) ) + ); + } + + @Test + public void testStageBatchingUpdateNoBatchSizeParameter(VertxTestContext context) { + final String pigOneUpdatedName = "One updated"; + final String pigTwoUpdatedName = "Two updated"; + test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + sqlTracker.clear(); + GuineaPig guineaPigOne = pigs.get( 0 ); + guineaPigOne.setName( pigOneUpdatedName ); + GuineaPig guineaPigTwo = pigs.get( 1 ); + guineaPigTwo.setName( pigTwoUpdatedName ); + return s.update( guineaPigOne, guineaPigTwo ); + } ) + ) + .thenAccept( vo -> { + // We expect only one update query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); + } ) + .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) + .getResultList() + .thenAccept( guineaPigs -> + checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) + ) + ) ) ) + ); + } + + private static void checkPigsAreCorrectlyUpdated(List guineaPigs, String pigOneUpdatedName, String pigTwoUpdatedName) { + assertThat( guineaPigs.get( 0 ).getName() ).isEqualTo( pigOneUpdatedName ); + assertThat( guineaPigs.get( 1 ).getName() ).isEqualTo( pigTwoUpdatedName ); + assertThat( guineaPigs.get( 2 ).getName() ).isEqualTo( PIG_THREE_NAME ); + assertThat( guineaPigs.get( 3 ).getName() ).isEqualTo( PIG_FOUR_NAME ); + assertThat( guineaPigs.get( 4 ).getName() ).isEqualTo( PIG_FIVE_NAME ); + assertThat( guineaPigs.get( 5 ).getName() ).isEqualTo( PIG_SIX_NAME ); + } + + @Entity(name = "GuineaPig") + @Table(name = "pig") + public static class GuineaPig { + @Id + private Integer id; + private String name; + + public GuineaPig() { + } + + public GuineaPig(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return id + ": " + name; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + GuineaPig guineaPig = (GuineaPig) o; + return Objects.equals( name, guineaPig.name ); + } + + @Override + public int hashCode() { + return Objects.hash( name ); + } + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java index 0ce3d2e9d..75d90375e 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BatchingConnectionTest.java @@ -145,6 +145,75 @@ public void testBatching(VertxTestContext context) { ); } + @Test + public void testBatchingWithStateless(VertxTestContext context) { + final GuineaPig[] pigs = { + new GuineaPig( 11, "One" ), + new GuineaPig( 22, "Two" ), + new GuineaPig( 33, "Three" ), + new GuineaPig( 44, "Four" ), + new GuineaPig( 55, "Five" ), + new GuineaPig( 66, "Six" ), + }; + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, pigs ) ) + .invoke( () -> { + // We expect only one insert query + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches("insert into pig \\(name,version,id\\) values (.*)" ); + sqlTracker.clear(); + } ) + ); + } + + @Test + public void testMutinyInsertAllWithStateless(VertxTestContext context) { + final GuineaPig[] pigs = { + new GuineaPig( 11, "One" ), + new GuineaPig( 22, "Two" ), + new GuineaPig( 33, "Three" ), + new GuineaPig( 44, "Four" ), + new GuineaPig( 55, "Five" ), + new GuineaPig( 66, "Six" ), + }; + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( pigs ) ) + .invoke( () -> { + // We expect only 1 insert query, despite hibernate.jdbc.batch_size is set to 5, insertAll by default use the pigs.length as batch size + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches("insert into pig \\(name,version,id\\) values (.*)" ); + sqlTracker.clear(); + } ) + ); + } + + @Test + public void testStageInsertWithStateless(VertxTestContext context) { + final GuineaPig[] pigs = { + new GuineaPig( 11, "One" ), + new GuineaPig( 22, "Two" ), + new GuineaPig( 33, "Three" ), + new GuineaPig( 44, "Four" ), + new GuineaPig( 55, "Five" ), + new GuineaPig( 66, "Six" ), + }; + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( pigs ) ) + .thenAccept( v -> { + // We expect only 1 insert query, despite hibernate.jdbc.batch_size is set to 5, insertAll by default use the pigs.length as batch size + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so we cannot do an exact match + assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) + .matches("insert into pig \\(name,version,id\\) values (.*)" ); + sqlTracker.clear(); + } ) + ); + } + @Test public void testBatchingConnection(VertxTestContext context) { test( context, openSession() From 52f1ec7920c4c9bca6930c4bcb976ae49be5a648 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 6 Mar 2025 18:13:51 +0100 Subject: [PATCH 084/201] [#2139] Refactoring of ReactiveStatelessDefaultBatchSizeTest * Rename it to ReactiveStatelessWithBatchTest * Clean up unit tests --- ...ReactiveStatelessDefaultBatchSizeTest.java | 622 ------------------ .../ReactiveStatelessWithBatchTest.java | 422 ++++++++++++ 2 files changed, 422 insertions(+), 622 deletions(-) delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java deleted file mode 100644 index c4d74261e..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessDefaultBatchSizeTest.java +++ /dev/null @@ -1,622 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate; - -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; -import org.hibernate.cfg.AvailableSettings; -import org.hibernate.cfg.Configuration; -import org.hibernate.reactive.BaseReactiveTest; -import org.hibernate.reactive.stage.Stage; -import org.hibernate.reactive.testing.SqlStatementTracker; - -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 java.util.List; -import java.util.Objects; -import java.util.Set; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The test aims to check that methods accepting the batch size as parameter e.g. {@link Stage.StatelessSession#insert(int, Object...)} - * work when {@link AvailableSettings.STATEMENT_BATCH_SIZE} hasn't been set. - */ -@Timeout(value = 10, timeUnit = MINUTES) -public class ReactiveStatelessDefaultBatchSizeTest extends BaseReactiveTest { - private static SqlStatementTracker sqlTracker; - - private static final String PIG_ONE_NAME = "One"; - private static final String PIG_TWO_NAME = "Two"; - private static final String PIG_THREE_NAME = "Three"; - private static final String PIG_FOUR_NAME = "Four"; - private static final String PIG_FIVE_NAME = "Five"; - private static final String PIG_SIX_NAME = "Six"; - - private static final GuineaPig PIG_ONE = new GuineaPig( 11, PIG_ONE_NAME ); - private static final GuineaPig PIG_TWO = new GuineaPig( 22, PIG_TWO_NAME ); - private static final GuineaPig PIG_THREE = new GuineaPig( 33, PIG_THREE_NAME ); - private static final GuineaPig PIG_FOUR = new GuineaPig( 44, PIG_FOUR_NAME ); - private static final GuineaPig PIG_FIVE = new GuineaPig( 55, PIG_FIVE_NAME ); - private static final GuineaPig PIG_SIX = new GuineaPig( 66, PIG_SIX_NAME ); - - private static final GuineaPig[] PIGS = { PIG_ONE, PIG_TWO, PIG_THREE, PIG_FOUR, PIG_FIVE, PIG_SIX, }; - - @Override - protected Set> annotatedEntities() { - return Set.of( GuineaPig.class ); - } - - @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); - - // Construct a tracker that collects query statements via the SqlStatementLogger framework. - // Pass in configuration properties to hand off any actual logging properties - sqlTracker = new SqlStatementTracker( - ReactiveStatelessDefaultBatchSizeTest::filter, - configuration.getProperties() - ); - return configuration; - } - - @BeforeEach - public void clearTracker() { - sqlTracker.clear(); - } - - @Override - protected void addServices(StandardServiceRegistryBuilder builder) { - sqlTracker.registerService( builder ); - } - - private static boolean filter(String s) { - String[] accepted = { "insert ", "update ", "delete " }; - for ( String valid : accepted ) { - if ( s.toLowerCase().startsWith( valid ) ) { - return true; - } - } - return false; - } - - @Test - public void testMutinyBatchingInsert(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) - .invoke( () -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - ); - } - - @Test - public void testMutinyBatchingInsertMultiple(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertMultiple( List.of( PIGS ) ) ) - .invoke( () -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - .invoke( v -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - .invoke( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) - ) - ) - ); - } - - @Test - public void testMutinyBatchingInsertAllNoBatchSizeParameter(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( PIGS ) ) - .invoke( () -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - .invoke( v -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - .invoke( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) - ) - ) - ); - } - - @Test - public void testStageBatchingInsert(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) - ) - ) - ); - } - - @Test - public void testStageBatchingInsertMultiple(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insertMultiple( List.of(PIGS) ) ) - .thenAccept( v -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) - ) - ) - ); - } - - @Test - public void testStageBatchingInsertNoBatchSizeParameter(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( PIGS ) ) - .thenAccept( v -> { - // We expect only one insert query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "insert into pig \\(name,id\\) values (.*)" ); - } ) - .thenAccept( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - .thenAccept( pigs -> assertThat( pigs ).hasSize( PIGS.length ) ) - ) - ) - ); - } - - @Test - public void testMutinyBatchingDelete(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - ) - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( - s -> - s.deleteAll( 10, pigs.subList( 0, 2 ).toArray() ) - ) - ) - .invoke( () -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - ) - .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testMutinyBatchingDeleteMultiple(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - ) - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( - s -> s.deleteMultiple( pigs.subList( 0, 2 ) ) ) - ) - .invoke( () -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - ) - .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testMutinyBatchingDeleteAllNoBatchSizeParameter(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s.insertAll( PIGS ) ) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - ) - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( - s -> s.deleteAll( pigs.subList( 0, 2 ).toArray() ) ) - ) - .invoke( () -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "delete from pig where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - ) - .invoke( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testStageBatchingDelete(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - .thenCompose( pigs -> { - sqlTracker.clear(); - return s.delete( 10, pigs.subList( 0, 2 ).toArray() ); - } - ) ) - .thenAccept( vo -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "delete from pig where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() - ) - .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testStageBatchingDeleteMultiple(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - .thenCompose( pigs -> { - sqlTracker.clear(); - return s.deleteMultiple( pigs.subList( 0, 2 ) ); - } - ) ) - .thenAccept( vo -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "delete from pig where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() ) - .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testStageBatchingDeleteNoBatchSizeParameter(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ).getResultList() - .thenCompose( pigs -> { - sqlTracker.clear(); - return s.delete( pigs.subList( 0, 2 ).toArray() ); - } - ) ) - .thenAccept( vo -> { - // We expect only one delete query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ) - .matches( "delete from pig where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p", GuineaPig.class ) - .getResultList() ) - .thenAccept( guineaPigs -> assertThat( guineaPigs.size() ).isEqualTo( 4 ) ) - ) ) - ); - } - - @Test - public void testMutinyBatchingUpdate(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> { - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.updateAll( 10, new GuineaPig[] { guineaPigOne, guineaPigTwo } ); - } ) - ) ) - .invoke( () -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .invoke( guineaPigs -> { - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); - } ) ) ) - ); - } - - @Test - public void testMutinyBatchingUpdateMultiple(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> { - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.updateMultiple( List.of( guineaPigOne, guineaPigTwo ) ); - } ) - ) ) - .invoke( () -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .invoke( guineaPigs -> { - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); - } ) ) ) - ); - } - - @Test - public void testMutinyBatchingUpdateAllNoBatchSizeParameter(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test( context, getMutinySessionFactory().withStatelessTransaction( s -> s .insertAll( 10, PIGS )) - .invoke( sqlTracker::clear ) - .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .invoke( pigs -> sqlTracker.clear() ) - .chain( pigs -> { - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.updateAll( guineaPigOne, guineaPigTwo ); - } ) - ) ) - .invoke( () -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .chain( () -> getMutinySessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .invoke( guineaPigs -> { - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ); - } ) ) ) - ); - } - - @Test - public void testStageBatchingUpdate(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .thenApply( pigs -> { - sqlTracker.clear(); - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.update( 10, new GuineaPig[] { guineaPigOne, guineaPigTwo } ); - } ) - ) - .thenAccept( vo -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .thenAccept( guineaPigs -> - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) - ) - ) ) ) - ); - } - - @Test - public void testStageBatchingUpdateMultiple(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .thenApply( pigs -> { - sqlTracker.clear(); - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.updateMultiple( List.of( guineaPigOne, guineaPigTwo ) ); - } ) - ) - .thenAccept( vo -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .thenAccept( guineaPigs -> - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) - ) - ) ) ) - ); - } - - @Test - public void testStageBatchingUpdateNoBatchSizeParameter(VertxTestContext context) { - final String pigOneUpdatedName = "One updated"; - final String pigTwoUpdatedName = "Two updated"; - test(context, getSessionFactory().withStatelessTransaction( s -> s.insert( 10, PIGS ) ) - .thenAccept( v -> sqlTracker.clear() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction(s -> s - .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) - .getResultList() - .thenApply( pigs -> { - sqlTracker.clear(); - GuineaPig guineaPigOne = pigs.get( 0 ); - guineaPigOne.setName( pigOneUpdatedName ); - GuineaPig guineaPigTwo = pigs.get( 1 ); - guineaPigTwo.setName( pigTwoUpdatedName ); - return s.update( guineaPigOne, guineaPigTwo ); - } ) - ) - .thenAccept( vo -> { - // We expect only one update query - assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); - // Parameters are different for different dbs, so we cannot do an exact match - assertThat( sqlTracker.getLoggedQueries().get( 0 ) ).matches( "update pig set name=.* where id=.*" ); - } ) - .thenCompose( vo -> getSessionFactory().withStatelessTransaction( s -> s - .createQuery( "select p from GuineaPig p order by id", GuineaPig.class ) - .getResultList() - .thenAccept( guineaPigs -> - checkPigsAreCorrectlyUpdated( guineaPigs, pigOneUpdatedName, pigTwoUpdatedName ) - ) - ) ) ) - ); - } - - private static void checkPigsAreCorrectlyUpdated(List guineaPigs, String pigOneUpdatedName, String pigTwoUpdatedName) { - assertThat( guineaPigs.get( 0 ).getName() ).isEqualTo( pigOneUpdatedName ); - assertThat( guineaPigs.get( 1 ).getName() ).isEqualTo( pigTwoUpdatedName ); - assertThat( guineaPigs.get( 2 ).getName() ).isEqualTo( PIG_THREE_NAME ); - assertThat( guineaPigs.get( 3 ).getName() ).isEqualTo( PIG_FOUR_NAME ); - assertThat( guineaPigs.get( 4 ).getName() ).isEqualTo( PIG_FIVE_NAME ); - assertThat( guineaPigs.get( 5 ).getName() ).isEqualTo( PIG_SIX_NAME ); - } - - @Entity(name = "GuineaPig") - @Table(name = "pig") - public static class GuineaPig { - @Id - private Integer id; - private String name; - - public GuineaPig() { - } - - public GuineaPig(Integer id, String name) { - this.id = id; - this.name = name; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return id + ": " + name; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - GuineaPig guineaPig = (GuineaPig) o; - return Objects.equals( name, guineaPig.name ); - } - - @Override - public int hashCode() { - return Objects.hash( name ); - } - } -} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java new file mode 100644 index 000000000..f934090b8 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java @@ -0,0 +1,422 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.testing.SqlStatementTracker; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.smallrye.mutiny.Uni; +import io.vertx.junit5.Timeout; +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletionStage; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test the stateless session actually execute the operations in batch. + */ +@Timeout(value = 10, timeUnit = MINUTES) +public class ReactiveStatelessWithBatchTest extends BaseReactiveTest { + private static SqlStatementTracker sqlTracker; + + private static final Object[] PIGS = { + new GuineaPig( 11, "One" ), + new GuineaPig( 22, "Two" ), + new GuineaPig( 33, "Three" ), + new GuineaPig( 44, "Four" ), + new GuineaPig( 55, "Five" ), + new GuineaPig( 66, "Six" ) + }; + + private static final Object[] PIGS_AFTER_DELETE = List.of( PIGS ) + .subList( 2, PIGS.length ) + .toArray(); + + private static final Object[] PIGS_AFTER_UPDATE = { + new GuineaPig( 11, "One updated" ), + new GuineaPig( 22, "Two updated" ), + new GuineaPig( 33, "Three" ), + new GuineaPig( 44, "Four" ), + new GuineaPig( 55, "Five" ), + new GuineaPig( 66, "Six" ) + }; + + @Override + protected Set> annotatedEntities() { + return Set.of( GuineaPig.class ); + } + + @Override + protected Configuration constructConfiguration() { + Configuration configuration = super.constructConfiguration(); + + // Construct a tracker that collects query statements via the SqlStatementLogger framework. + // Pass in configuration properties to hand off any actual logging properties + sqlTracker = new SqlStatementTracker( + ReactiveStatelessWithBatchTest::filter, + configuration.getProperties() + ); + return configuration; + } + + @BeforeEach + public void clearTracker() { + sqlTracker.clear(); + } + + @Override + protected void addServices(StandardServiceRegistryBuilder builder) { + sqlTracker.registerService( builder ); + } + + private static boolean filter(String s) { + String[] accepted = { "insert ", "update ", "delete " }; + for ( String valid : accepted ) { + if ( s.toLowerCase().startsWith( valid ) ) { + return true; + } + } + return false; + } + + @Test + public void testMutinyBatchingInsert(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( () -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + public void testMutinyBatchingInsertMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertMultiple( List.of( PIGS ) ) ) + .invoke( () -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + public void testMutinyBatchingInsertAllNoBatchSizeParameter(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( PIGS ) ) + .invoke( () -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + public void testStageBatchingInsert(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + + @Test + public void testStageBatchingInsertMultiple(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insertMultiple( List.of( PIGS ) ) ) + .thenAccept( v -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + + @Test + public void testStageBatchingInsertNoBatchSizeParameter(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( PIGS ) ) + .thenAccept( v -> assertSqlLogTracker( "insert into pig \\(name,id\\) values (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + + @Test + public void testMutinyBatchingDelete(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + ) ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( s -> s + .deleteAll( 10, pigs.subList( 0, 2 ).toArray() ) + ) ) + .invoke( () -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_DELETE ) ) ) + ); + } + + @Test + public void testMutinyBatchingDeleteMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + ) ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( s -> s + .deleteMultiple( pigs.subList( 0, 2 ) ) ) + ) + .invoke( () -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_DELETE ) ) ) + ); + } + + @Test + public void testMutinyBatchingDeleteAllNoBatchSizeParameter(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + ) ) + .chain( pigs -> getMutinySessionFactory().withStatelessTransaction( s -> s + .deleteAll( pigs.subList( 0, 2 ).toArray() ) + ) ) + .invoke( () -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_DELETE ) ) ) + ); + } + + @Test + public void testStageBatchingDelete(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenRun( sqlTracker::clear ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> s.delete( 10, pigs.subList( 0, 2 ).toArray() ) ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_DELETE ) ) + ); + } + + @Test + public void testStageBatchingDeleteMultiple(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenRun( sqlTracker::clear ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> s.deleteMultiple( pigs.subList( 0, 2 ) ) ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_DELETE ) ) + ); + } + + @Test + public void testStageBatchingDeleteNoBatchSizeParameter(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenRun( sqlTracker::clear ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p", GuineaPig.class ).getResultList() + .thenCompose( pigs -> s.delete( pigs.subList( 0, 2 ).toArray() ) ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "delete from pig where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_DELETE ) ) + ); + } + + @Test + public void testMutinyBatchingUpdate(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .chain( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.updateAll( 10, pigs.toArray() ); + } ) + ) ) + .invoke( () -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_UPDATE ) ) ) + ); + } + + @Test + public void testMutinyBatchingUpdateMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .chain( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.updateMultiple( pigs.subList( 0, 2 ) ); + } ) ) + ) + .invoke( () -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_UPDATE ) ) ) + ); + } + + @Test + public void testMutinyBatchingUpdateAllNoBatchSizeParameter(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.insertAll( 10, PIGS ) ) + .invoke( sqlTracker::clear ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( s -> s + .createQuery( "select p from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .chain( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.updateAll( pigs.toArray() ); + } ) ) + ) + .invoke( () -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .call( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS_AFTER_UPDATE ) ) ) + ); + } + + @Test + public void testStageBatchingUpdate(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenAccept( v -> sqlTracker.clear() ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.update( 10, pigs.toArray() ); + } ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_UPDATE ) ) + ); + } + + @Test + public void testStageBatchingUpdateMultiple(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenRun( sqlTracker::clear ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.updateMultiple( pigs ); + } ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_UPDATE ) ) + ); + } + + @Test + public void testStageBatchingUpdateNoBatchSizeParameter(VertxTestContext context) { + test(context, getSessionFactory() + .withStatelessTransaction( s -> s.insert( 10, PIGS ) ) + .thenRun( sqlTracker::clear ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) + .getResultList() + .thenApply( pigs -> { + pigs.get( 0 ).setName( "One updated" ); + pigs.get( 1 ).setName( "Two updated" ); + return s.update( pigs.get( 0 ), pigs.get( 1 ) ); + } ) + ) ) + .thenAccept( v -> assertSqlLogTracker( "update pig set name=.* where id=.*" ) ) + .thenCompose( v -> assertExpectedResult( PIGS_AFTER_UPDATE ) ) + ); + } + + private CompletionStage assertExpectedResult(Object[] expected) { + return getSessionFactory().withStatelessTransaction( s -> s + .createQuery( "from GuineaPig p order by id", Object.class ) + .getResultList() + .thenAccept( pigs -> assertThat( pigs ).containsExactly( expected ) ) ); + } + + private static void assertSqlLogTracker(String queryRegex) { + // We expect only one query for each batched operations + assertThat( sqlTracker.getLoggedQueries() ).hasSize( 1 ); + // Parameters are different for different dbs, so the regex must keep that in consideration + assertThat( sqlTracker.getLoggedQueries() ).allMatch( s -> s.matches( queryRegex ) ); + } + + @Entity(name = "GuineaPig") + @Table(name = "pig") + public static class GuineaPig { + @Id + private Integer id; + private String name; + + public GuineaPig() { + } + + public GuineaPig(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return id + ": " + name; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + GuineaPig guineaPig = (GuineaPig) o; + return Objects.equals( name, guineaPig.name ); + } + + @Override + public int hashCode() { + return Objects.hash( name ); + } + } +} From 55ed2a116852bb5da4d00b67bb46dd61952dcac0 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 7 Mar 2025 10:48:17 +0100 Subject: [PATCH 085/201] [#2139] Add batching upsert to the StatelessSession --- .../org/hibernate/reactive/mutiny/Mutiny.java | 36 +++++++++++++++++++ .../impl/MutinyStatelessSessionImpl.java | 15 ++++++++ .../session/ReactiveStatelessSession.java | 2 ++ .../impl/ReactiveStatelessSessionImpl.java | 10 ++++++ .../org/hibernate/reactive/stage/Stage.java | 36 +++++++++++++++++++ .../stage/impl/StageStatelessSessionImpl.java | 23 +++++++++--- 6 files changed, 118 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index a648a7bd7..59edbb875 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1908,6 +1908,42 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp @Incubating Uni upsert(String entityName, Object entity); + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the size of the given array + * as batch size. + * + * @param entities the entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + Uni upsertAll(Object... entities); + + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the specified batch size. + * + * @param batchSize the batch size + * @param entities the list of entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + Uni upsertAll(int batchSize, Object... entities); + + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the size of the given list + * as batch size. + * + * @param entities the entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + Uni upsertMultiple(List entities); + /** * Refresh the entity instance state from the database. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index a80d79750..d6e8e984e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -206,6 +206,21 @@ public Uni upsert(String entityName, Object entity) { return uni( () -> delegate.reactiveUpsert( entityName, entity ) ); } + @Override + public Uni upsertAll(Object... entities) { + return uni( () -> delegate.reactiveUpsertAll( entities.length, entities ) ); + } + + @Override + public Uni upsertAll(int batchSize, Object... entities) { + return uni( () -> delegate.reactiveUpsertAll( batchSize, entities ) ); + } + + @Override + public Uni upsertMultiple(List entities) { + return uni( () -> delegate.reactiveUpsertAll( entities.size(), entities.toArray() ) ); + } + @Override public Uni refreshAll(Object... entities) { return uni( () -> delegate.reactiveRefreshAll( entities.length, entities ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java index 33e34917b..e6d2aa1ee 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java @@ -48,6 +48,8 @@ public interface ReactiveStatelessSession extends ReactiveQueryProducer, Reactiv CompletionStage reactiveUpsert(String entityName, Object entity); + CompletionStage reactiveUpsertAll(int batchSize, Object... entities); + CompletionStage reactiveRefresh(Object entity); CompletionStage reactiveRefresh(String entityName, Object entity); 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 53b928903..0c018d789 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 @@ -542,6 +542,16 @@ public CompletionStage reactiveUpsert(String entityName, Object entity) { .mergeReactive( id, state, null, false, null, oldVersion, entity, null, this ); } + @Override + public CompletionStage reactiveUpsertAll(int batchSize, Object... entities) { + final Integer jdbcBatchSize = batchingHelperSession.getJdbcBatchSize(); + batchingHelperSession.setJdbcBatchSize( batchSize ); + final ReactiveConnection connection = batchingConnection( batchSize ); + return loop( entities, batchingHelperSession::reactiveUpsert ) + .thenCompose( v -> connection.executeBatch() ) + .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); + } + @Override public CompletionStage reactiveInsertAll(Object... entities) { return loop( entities, batchingHelperSession::reactiveInsert ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 074f63e58..b490f2fb3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -2021,6 +2021,42 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ CompletionStage upsert(String entityName, Object entity); + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the size of the given array + * as batch size. + * + * @param entities the entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + CompletionStage upsertAll(Object... entities); + + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the specified batch size. + * + * @param batchSize the batch size + * @param entities the list of entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + CompletionStage upsertAll(int batchSize, Object... entities); + + /** + * Use a SQL {@code merge into} statement to perform + * an upsert on multiple rows using the size of the given list + * as batch size. + * + * @param entities the entities to upsert + * + * @see org.hibernate.StatelessSession#upsert(Object) + */ + @Incubating + CompletionStage upsertMultiple(List entities); + /** * Asynchronously fetch an association that's configured for lazy loading. * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index c97be1a94..aadd76285 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -5,10 +5,6 @@ */ package org.hibernate.reactive.stage.impl; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.query.criteria.JpaCriteriaInsert; @@ -20,6 +16,10 @@ import org.hibernate.reactive.stage.Stage.Query; import org.hibernate.reactive.stage.Stage.SelectionQuery; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -155,6 +155,21 @@ public CompletionStage upsert(String entityName, Object entity) { return delegate.reactiveUpsert( entityName, entity ); } + @Override + public CompletionStage upsertAll(Object... entities) { + return delegate.reactiveUpsertAll( entities.length, entities ); + } + + @Override + public CompletionStage upsertAll(int batchSize, Object... entities) { + return delegate.reactiveUpsertAll( batchSize, entities ); + } + + @Override + public CompletionStage upsertMultiple(List entities) { + return delegate.reactiveUpsertAll( entities.size(), entities.toArray() ); + } + @Override public CompletionStage fetch(T association) { return delegate.reactiveFetch( association, false ); From 2acd8940365b64a9d1b306f8c8488d5b7583d2b3 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 7 Mar 2025 10:49:16 +0100 Subject: [PATCH 086/201] [#2139] Add test for upsert with batching --- .../ReactiveStatelessWithBatchTest.java | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java index f934090b8..563fad40b 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java @@ -8,6 +8,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; import org.hibernate.reactive.BaseReactiveTest; +import org.hibernate.reactive.annotations.EnabledFor; import org.hibernate.reactive.testing.SqlStatementTracker; import org.junit.jupiter.api.BeforeEach; @@ -26,6 +27,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; /** * Test the stateless session actually execute the operations in batch. @@ -85,7 +87,7 @@ protected void addServices(StandardServiceRegistryBuilder builder) { } private static boolean filter(String s) { - String[] accepted = { "insert ", "update ", "delete " }; + String[] accepted = { "merge ", "insert ", "update ", "delete " }; for ( String valid : accepted ) { if ( s.toLowerCase().startsWith( valid ) ) { return true; @@ -94,6 +96,66 @@ private static boolean filter(String s) { return false; } + @Test + @EnabledFor(POSTGRESQL) + public void testMutinyMergeUpsertAll(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.upsertAll( PIGS ) ) + .invoke( () -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + @EnabledFor(POSTGRESQL) + public void testMutinyMergeUpsertAllWithBatchSize(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.upsertAll( 10, PIGS ) ) + .invoke( () -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + @EnabledFor(POSTGRESQL) + public void testMutinyMergeUpsertMultiple(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withStatelessTransaction( s -> s.upsertMultiple( List.of( PIGS ) ) ) + .invoke( () -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .chain( () -> Uni.createFrom().completionStage( assertExpectedResult( PIGS ) ) ) + ); + } + + @Test + @EnabledFor(POSTGRESQL) + public void testStageMergeUpsertAll(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.upsertAll( PIGS ) ) + .thenRun( () -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + + @Test + @EnabledFor(POSTGRESQL) + public void testStageMergeUpsertAllWithBatchSize(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.upsertAll( 10, PIGS ) ) + .thenRun(() -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + + @Test + @EnabledFor(POSTGRESQL) + public void testStageMergeUpsertMultiple(VertxTestContext context) { + test( context, getSessionFactory() + .withStatelessTransaction( s -> s.upsertMultiple( List.of( PIGS ) ) ) + .thenRun( () -> assertSqlLogTracker( "merge into pig as t using (.*)" ) ) + .thenCompose( v -> assertExpectedResult( PIGS ) ) + ); + } + @Test public void testMutinyBatchingInsert(VertxTestContext context) { test( context, getMutinySessionFactory() From ede60d8e084b6a8ff1b13a94ff25318925dfcc58 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 7 Mar 2025 11:04:34 +0100 Subject: [PATCH 087/201] [#2139] Explain why we set the default batch size to 0 --- .../session/impl/ReactiveStatelessSessionImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 0c018d789..4941dda50 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 @@ -150,8 +150,11 @@ private ReactiveStatelessSessionImpl( PersistenceContext persistenceContext) { super( factory, options ); this.persistenceContext = persistenceContext; - // Setting batch size to 0 because `StatelessSession` does not consider - // the value of `hibernate.jdbc.batch_size` + // StatelessSession should not allow JDBC batching, because that would change + // its "immediate synchronous execution" model into something more like transactional + // write-behind and be confusing. For this reason, the default batch size is always set to 0. + // When a user calls the CRUD operations for batching, we set the batch size to the same number of + // objects to process, therefore, there is no write-behind behavior. reactiveConnection = new BatchingConnection( connection, 0 ); batchingHelperSession = this; influencers = new LoadQueryInfluencers( factory ); From 71f1899bc56ccf3bc1c1e69e465fab538bd85ea6 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 14 Mar 2025 10:50:34 +0100 Subject: [PATCH 088/201] [#2143] Upgrade Testcontainers to 1.20.6 --- build.gradle | 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/build.gradle b/build.gradle index aef4fcc26..808127db4 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ ext { vertxSqlClientVersion = '4.5.13' } - testcontainersVersion = '1.20.4' + testcontainersVersion = '1.20.6' logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion } diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 9e041af06..9b713c8ea 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:cockroachdb:1.20.4 +//DEPS org.testcontainers:cockroachdb:1.20.6 //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 feabab3f7..a13d21b6d 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:db2:1.20.4 +//DEPS org.testcontainers:db2:1.20.6 //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 dd6ceb3c3..f6b528925 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mariadb:1.20.4 +//DEPS org.testcontainers:mariadb:1.20.6 //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 b31d4e8d8..f8307495d 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mysql:1.20.4 +//DEPS org.testcontainers:mysql:1.20.6 //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 ab43700ed..b49a6433b 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.20.4 +//DEPS org.testcontainers:postgresql:1.20.6 //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 7357011ed..51ba7f267 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.20.4 -//DEPS org.testcontainers:mysql:1.20.4 -//DEPS org.testcontainers:db2:1.20.4 -//DEPS org.testcontainers:mariadb:1.20.4 -//DEPS org.testcontainers:cockroachdb:1.20.4 +//DEPS org.testcontainers:postgresql:1.20.6 +//DEPS org.testcontainers:mysql:1.20.6 +//DEPS org.testcontainers:db2:1.20.6 +//DEPS org.testcontainers:mariadb:1.20.6 +//DEPS org.testcontainers:cockroachdb:1.20.6 // //// Testcontainer needs the JDBC drivers to start the containers //// Hibernate Reactive doesn't use them From 6a9b9f4d3f1aa37a127516fa11d0abf97aca7c48 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 14 Mar 2025 10:39:02 +0100 Subject: [PATCH 089/201] [#2064] Add CONTRIBUTING.md --- CONTRIBUTING.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..2df00b224 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,107 @@ +# Contributing + +Contributions from the community are essential in keeping Hibernate (and any Open Source +project really) strong and successful. + +# Legal + +All original contributions to Hibernate are licensed under the +[Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0). + +The Apache 2.0 license text is included verbatim in the [LICENSE](LICENSE) file in the root directory +of the Hibernate Reactive repository. + +All contributions are subject to the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). + +The DCO text is available verbatim in the [dco.txt](dco.txt) file in the root directory +of the Hibernate Reactive repository. + +## Guidelines + +While we try to keep requirements for contributing to a minimum, there are a few guidelines +we ask that you mind. + +For code contributions, these guidelines include: +* Respect the project code style - find templates for [IntelliJ IDEA](https://hibernate.org/community/contribute/intellij-idea/) or [Eclipse](https://hibernate.org/community/contribute/eclipse-ide/) +* Have a corresponding GitHub [issue](https://github.com/hibernate/hibernate-reactive/issues) and be sure to include + the key for this issue in your commit messages. +* Have a set of appropriate tests. + For your convenience, a [set of test templates](https://github.com/hibernate/hibernate-test-case-templates/tree/main/reactive) + have been made available. + + When submitting bug reports, the tests should reproduce the initially reported bug and illustrate that your solution addresses the issue. + For features/enhancements, the tests should demonstrate that the feature works as intended. + In both cases, be sure to incorporate your tests into the project to protect against possible regressions. +* If applicable, documentation should be updated to reflect the introduced changes +* The code compiles and the tests pass (`./gradlew clean build`) + +For documentation contributions, mainly to respect the project code style, especially in regard +to the use of tabs - as mentioned above, code style templates are available for both IntelliJ IDEA and Eclipse +IDEs. Ideally, these contributions would also have a corresponding issue, although this +is less necessary for documentation contributions. + +## Getting Started + +If you are just getting started with Git, GitHub, and/or contributing to Hibernate via +GitHub there are a few pre-requisite steps to follow: + +* Make sure you have a [GitHub account](https://github.com/signup/free) +* [Fork](https://help.github.com/articles/fork-a-repo) the Hibernate Reactive repository. As discussed in +the linked page, this also includes: + * [set up your local git install](https://help.github.com/articles/set-up-git) + * clone your fork +* Instruct git to ignore certain commits when using `git blame`. From the directory of your local clone, run this: `git config blame.ignoreRevsFile .git-blame-ignore-revs` +* See the wiki pages for setting up your IDE, whether you use +[IntelliJ IDEA](https://hibernate.org/community/contribute/intellij-idea/) +or [Eclipse](https://hibernate.org/community/contribute/eclipse-ide/)(1). + + +## Create the working (topic) branch + +Create a [topic branch](https://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches) +on which you will work. The convention is to incorporate the JIRA issue key in the name of this branch, +although this is more of a mnemonic strategy than a hard-and-fast rule - but doing so helps: +* Remember what each branch is for +* Isolate the work from other contributions you may be working on + +_If there is not already a GitHub issue covering the work you want to do, [create one](https://github.com/hibernate/hibernate-reactive/issues/new)._ + +Assuming you will be working from the `main` branch and working +on the GitHub issue #123 : `git checkout -b 123 main` + +## Code + +Do your thing! + + +## Commit + +* Make commits of logical units +* Be sure to start each commit message using the ** GitHub issue key **. For example: + ``` + [#1234] Fix some kind of problem + ``` +* Make sure you have added the necessary tests for your changes +* Run _all_ the tests to ensure nothing else was accidentally broken + +_Before committing, if you want to pull in the latest upstream changes (highly +appreciated btw), please use rebasing rather than merging. Merging creates +"merge commits" that invariably muck up the project timeline._ + +## Submit + +* Push your changes to the topic branch in your fork of the repository +* Initiate a [pull request](https://help.github.com/articles/creating-a-pull-request) +* Adding the sentence `Fix #123`, where `#123` is the issue key, will link the pull request to the corresponding issue + and close it accordingly, when the pull request gets merged. + +It is important that this topic branch of your fork: + +* Is isolated to just the work on this one issue, or multiple issues if they are + related and also fixed/implemented by this work. The main point is to not push commits for more than + one PR to a single branch - GitHub PRs are linked to a branch rather than specific commits +* remain until the PR is closed. Once the underlying branch is deleted the corresponding PR will be closed, + if not already, and the changes will be lost. + +# Notes +(1) Gradle `eclipse` plugin is no longer supported, so the recommended way to import the project in your IDE is with the proper IDE tools/plugins. Don't try to run `./gradlew clean eclipse --refresh-dependencies` from the command line as you'll get an error because `eclipse` no longer exists From e768bb2c0ea37f23580d374a41ef05b6f88624e6 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 18 Mar 2025 08:59:17 +0100 Subject: [PATCH 090/201] [#2148] Upgrade PostgreSQL for testing to 17.4 --- .../org/hibernate/reactive/containers/PostgreSQLDatabase.java | 2 +- .../src/test/java/org/hibernate/reactive/it/BaseReactiveIT.java | 2 +- .../reactive/it/quarkus/qe/database/BaseReactiveIT.java | 2 +- .../java/org/hibernate/reactive/it/verticle/VertxServer.java | 2 +- podman.md | 2 +- tooling/jbang/Example.java | 2 +- tooling/jbang/PostgreSQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) 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 c832ae867..5b427f4e7 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 @@ -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", "16.3" ) ) + public static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>( imageName( "postgres", "17.4" ) ) .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 72ad3273d..ff9f5e601 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 @@ -53,7 +53,7 @@ public abstract class BaseReactiveIT { public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:16.3" ) + .parse( "docker.io/postgres:17.4" ) .asCompatibleSubstituteFor( "postgres" ); public static final String USERNAME = "hreact"; 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 7272f13f2..afff092e9 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 @@ -53,7 +53,7 @@ public abstract class BaseReactiveIT { public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:16.3" ) + .parse( "docker.io/postgres:17.4" ) .asCompatibleSubstituteFor( "postgres" ); public static final String USERNAME = "hreact"; 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 eab23a6ff..238e5278b 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 @@ -36,7 +36,7 @@ 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:16.3"; + public static final String IMAGE_NAME = "postgres:17.4"; public static final String USERNAME = "hreact"; public static final String PASSWORD = "hreact"; public static final String DB_NAME = "hreact"; diff --git a/podman.md b/podman.md index dc81fb828..3a35a715f 100644 --- a/podman.md +++ b/podman.md @@ -39,7 +39,7 @@ required credentials and schema to run the tests: podman run --rm --name HibernateTestingPGSQL \ -e POSTGRES_USER=hreact -e POSTGRES_PASSWORD=hreact -e POSTGRES_DB=hreact \ -e POSTGRES_INITDB_ARGS="-A password" \ - -p 5432:5432 docker.io/postgres:16.3 + -p 5432:5432 docker.io/postgres:17.4 ``` When the database has started, you can run the tests on PostgreSQL with: diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 7c0f4cef0..7f6d383fd 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -59,7 +59,7 @@ *
      *                 podman run --rm --name HibernateTestingPGSQL \
      *                      -e POSTGRES_USER=hreact -e POSTGRES_PASSWORD=hreact -e POSTGRES_DB=hreact \
    - *                      -p 5432:5432 postgres:16.3
    + *                      -p 5432:5432 postgres:17.4
      *              
    * *
    3. Run the example with JBang
    diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index b49a6433b..682ac502b 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -67,7 +67,7 @@ public class {baseName} { } @ClassRule - public final static PostgreSQLContainer database = new PostgreSQLContainer( imageName( "docker.io", "postgres", "16.3" ) ); + public final static PostgreSQLContainer database = new PostgreSQLContainer( imageName( "docker.io", "postgres", "17.4" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 51ba7f267..c1094f891 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -228,7 +228,7 @@ public String toString() { * It's a wrapper around the testcontainers classes. */ enum Database { - POSTGRESQL( () -> new PostgreSQLContainer( "postgres:16.3" ) ), + POSTGRESQL( () -> new PostgreSQLContainer( "postgres:17.4" ) ), MYSQL( () -> new MySQLContainer( "mysql:8.4.0" ) ), DB2( () -> new Db2Container( "docker.io/icr.io/db2_community/db2:12.1.0.0" ).acceptLicense() ), MARIADB( () -> new MariaDBContainer( "mariadb:11.4.2" ) ), From 27acfb2ee1b9af38966762b72bd50d8e2d7b3b72 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 19 Mar 2025 11:32:57 +0100 Subject: [PATCH 091/201] [#2150] Avoid creation of logger at runtime Quarkus doesn't like it when running in native mode --- .../temptable/ReactiveGlobalTemporaryTableStrategy.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java index 7a396753c..095230723 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/mutation/internal/temptable/ReactiveGlobalTemporaryTableStrategy.java @@ -27,6 +27,8 @@ public interface ReactiveGlobalTemporaryTableStrategy { + Log LOG = make( Log.class, lookup() ); + static String sessionIdentifier(SharedSessionContractImplementor session) { return session.getSessionIdentifier().toString(); } @@ -60,7 +62,7 @@ default void prepare(MappingModelCreationProcess mappingModelCreationProcess, Jd tableCreatedStage.complete( null ); } else { - make( Log.class, lookup() ).debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() ); + LOG.debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() ); connectionStage() .thenCompose( this::createTable ) @@ -98,7 +100,7 @@ private CompletionStage releaseConnection(ReactiveConnection connection) { private static void logConnectionClosedError(Throwable t) { if ( t != null ) { - make( Log.class, lookup() ).debugf( "Ignoring error closing the connection: %s", t.getMessage() ); + LOG.debugf( "Ignoring error closing the connection: %s", t.getMessage() ); } } @@ -146,7 +148,7 @@ default void release( setDropIdTables( false ); final TemporaryTable temporaryTable = getTemporaryTable(); - make( Log.class, lookup() ).debugf( "Dropping global-tempk ID table : %s", temporaryTable.getTableExpression() ); + LOG.debugf( "Dropping global-temp ID table : %s", temporaryTable.getTableExpression() ); connectionStage() .thenCompose( this::dropTable ) From 779a6324255acd7eba2b32f5ccbbf4e72abdb449 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 25 Mar 2025 18:09:14 +0100 Subject: [PATCH 092/201] [#2159] Remove JDK 23 testing --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d63e27906..cd4d85000 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -191,7 +191,6 @@ jobs: # and it's useful to test that. - { name: "20", java_version_numeric: 20, jvm_args: '--enable-preview' } - { name: "21", java_version_numeric: 21, jvm_args: '--enable-preview' } - - { name: "23", java_version_numeric: 23, from: 'jdk.java.net', jvm_args: '--enable-preview' } - { name: "24-ea", 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' } steps: From 08e06d11ce2b79211bed8a688d8dd55891250df2 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 25 Mar 2025 18:09:42 +0100 Subject: [PATCH 093/201] [#2159] Test with JDK 24 GA --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd4d85000..eec52d729 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -191,7 +191,7 @@ jobs: # and it's useful to test that. - { name: "20", java_version_numeric: 20, jvm_args: '--enable-preview' } - { name: "21", java_version_numeric: 21, jvm_args: '--enable-preview' } - - { name: "24-ea", java_version_numeric: 24, from: 'jdk.java.net', 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' } steps: - name: Checkout ${{ inputs.branch }} From c4fdc9a9c0bba5695a75b572a50a37bc512acde9 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 18 Feb 2025 14:34:10 +0100 Subject: [PATCH 094/201] [#2155] Upgrade Hibernate ORM to 7.0.0.Beta5 Some of the changes: * NullabilityCheck has changed * Remove ReactiveQueryImplementor#setOptional* * Replace inline calls to BatchFetchQueueHelper (removed) --- gradle.properties | 2 +- .../DefaultReactiveDeleteEventListener.java | 7 +- .../ReactiveEntityBatchLoaderArrayParam.java | 4 +- ...ReactiveMultiIdEntityLoaderArrayParam.java | 6 +- .../mutiny/impl/MutinySessionImpl.java | 2 +- .../impl/MutinyStatelessSessionImpl.java | 2 +- .../query/ReactiveQueryImplementor.java | 7 -- .../sql/internal/ReactiveNativeQueryImpl.java | 23 ++-- .../ReactiveSimpleDeleteQueryPlan.java | 39 ++++--- .../session/ReactiveQueryProducer.java | 2 + .../session/impl/ReactiveSessionImpl.java | 101 ++++++++++++++---- .../impl/ReactiveStatelessSessionImpl.java | 90 +++++++++++++--- .../reactive/stage/impl/StageSessionImpl.java | 31 +++--- .../stage/impl/StageStatelessSessionImpl.java | 2 +- 14 files changed, 218 insertions(+), 100 deletions(-) diff --git a/gradle.properties b/gradle.properties index 299ccf3b9..be7612bc5 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.Beta4 +hibernateOrmVersion = 7.0.0.Beta5 # 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 diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java index 664b09da3..f490d5366 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java @@ -436,11 +436,8 @@ protected CompletionStage deleteEntity( persister ).nullifyTransientReferences( entityEntry.getDeletedState() ) .thenAccept( vv -> { - new Nullability( session ).checkNullability( - entityEntry.getDeletedState(), - persister, - Nullability.NullabilityCheckType.DELETE - ); + new Nullability( session, Nullability.NullabilityCheckType.DELETE ) + .checkNullability( entityEntry.getDeletedState(), persister ); persistenceContext.registerNullifiableEntityKey( key ); final ReactiveActionQueue actionQueue = actionQueue( session ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java index ba69fd407..04f971ffd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveEntityBatchLoaderArrayParam.java @@ -10,7 +10,6 @@ import java.util.concurrent.CompletionStage; import org.hibernate.LockOptions; -import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -160,7 +159,8 @@ private CompletionStage initializeEntities( continue; } // found or not, remove the key from the batch-fetch queue - BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, getLoadable(), session ); + session.getPersistenceContextInternal().getBatchFetchQueue() + .removeBatchLoadableEntityKey( session.generateEntityKey( id, getLoadable().getEntityPersister() ) ); } } ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java index 8495d115b..8239a6e15 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java @@ -13,7 +13,6 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.engine.internal.BatchFetchQueueHelper; import org.hibernate.engine.spi.BatchFetchQueue; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; @@ -206,7 +205,7 @@ protected CompletionStage> performOrderedMultiLoad( // the element value at this position in the result List should be // the EntityKey for that entity - reuse it final EntityKey entityKey = (EntityKey) result.get( resultIndex ); - BatchFetchQueueHelper.removeBatchLoadableEntityKey( entityKey, session ); + session.getPersistenceContextInternal().getBatchFetchQueue().removeBatchLoadableEntityKey( entityKey ); Object entity = persistenceContext.getEntity( entityKey ); if ( entity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() ) { // make sure it is not DELETED @@ -293,7 +292,8 @@ protected CompletionStage> performUnorderedMultiLoad( continue; } // found or not, remove the key from the batch-fetch queue - BatchFetchQueueHelper.removeBatchLoadableEntityKey( id, getLoadable(), session ); + session.getPersistenceContextInternal().getBatchFetchQueue() + .removeBatchLoadableEntityKey( session.generateEntityKey( id, getLoadable().getEntityPersister() ) ); } return result; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 929987b55..87cf50bb0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -173,7 +173,7 @@ public MutationQuery createQuery(CriteriaDelete criteriaDelete) { @Override public Query createNamedQuery(String queryName) { - return new MutinyQueryImpl<>( delegate.createReactiveNamedQuery( queryName, null ), factory ); + return new MutinyQueryImpl<>( delegate.createReactiveNamedQuery( queryName ), factory ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index d6e8e984e..5605a7911 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -93,7 +93,7 @@ public Mutiny.MutationQuery createMutationQuery(String queryString) { @Override public Query createNamedQuery(String queryName) { - return new MutinyQueryImpl<>( delegate.createReactiveNamedQuery( queryName, null ), factory ); + return new MutinyQueryImpl<>( delegate.createReactiveNamedQuery( queryName ), factory ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java index 7c7588a7c..0354a59e5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.query; -import java.io.Serializable; import java.time.Instant; import java.util.Calendar; import java.util.Collection; @@ -23,12 +22,6 @@ public interface ReactiveQueryImplementor extends ReactiveQuery { - void setOptionalId(Serializable id); - - void setOptionalEntityName(String entityName); - - void setOptionalObject(Object optionalObject); - QueryParameterBindings getParameterBindings(); @Override 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 6d162882a..ac8f4e9b4 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 @@ -26,7 +26,6 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.internal.AbstractSharedSessionContract; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.query.BindableType; import org.hibernate.query.Order; @@ -65,8 +64,18 @@ public class ReactiveNativeQueryImpl extends NativeQueryImpl private final ReactiveAbstractSelectionQuery selectionQueryDelegate; - public ReactiveNativeQueryImpl(String memento, SharedSessionContractImplementor session) { - super( memento, session ); + public ReactiveNativeQueryImpl(String sql, SharedSessionContractImplementor session) { + super( sql, null, session ); + this.selectionQueryDelegate = createSelectionQueryDelegate( session ); + } + + public ReactiveNativeQueryImpl(String sql, Class resultClass, SharedSessionContractImplementor session) { + super( sql, resultClass, session ); + this.selectionQueryDelegate = createSelectionQueryDelegate( session ); + } + + public ReactiveNativeQueryImpl(String sql, NamedResultSetMappingMemento resultSetMappingMemento, Class resultClass, SharedSessionContractImplementor session) { + super( sql, resultSetMappingMemento, resultClass, session); this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } @@ -91,14 +100,6 @@ public ReactiveNativeQueryImpl( this.selectionQueryDelegate = createSelectionQueryDelegate( session ); } - public ReactiveNativeQueryImpl( - String sqlString, - NamedResultSetMappingMemento resultSetMappingMemento, - AbstractSharedSessionContract session) { - super( sqlString, resultSetMappingMemento, session ); - this.selectionQueryDelegate = createSelectionQueryDelegate( session ); - } - // Convenient for passing parameters to ReactiveAbstractSelectionQuery using method reference private T getNull() { return null; 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 a17d1eb73..eb055a38a 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 @@ -37,9 +37,7 @@ import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement; import org.hibernate.sql.ast.tree.MutationStatement; -import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcLiteral; import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; @@ -95,29 +93,28 @@ protected SqlAstTranslator createTranslato return factory.getJdbcServices() .getJdbcEnvironment() .getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, mutationStatement() ); + .buildMutationTranslator( factory, createDeleteAst() ); } - private MutationStatement mutationStatement() { + // Copy and paste from superclass + private MutationStatement createDeleteAst() { + final MutationStatement ast; if ( entityDescriptor.getSoftDeleteMapping() == null ) { - return sqmInterpretation.getSqlAst(); + ast = sqmInterpretation.getSqlAst(); } - final AbstractUpdateOrDeleteStatement sqlDeleteAst = sqmInterpretation.getSqlAst(); - final NamedTableReference targetTable = sqlDeleteAst.getTargetTable(); - final SoftDeleteMapping columnMapping = getEntityDescriptor().getSoftDeleteMapping(); - final ColumnReference columnReference = new ColumnReference( targetTable, columnMapping ); - //noinspection rawtypes,unchecked - final JdbcLiteral jdbcLiteral = new JdbcLiteral( - columnMapping.getDeletedLiteralValue(), - columnMapping.getJdbcMapping() - ); - final Assignment assignment = new Assignment( columnReference, jdbcLiteral ); - - return new UpdateStatement( - targetTable, - Collections.singletonList( assignment ), - sqlDeleteAst.getRestriction() - ); + 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 diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index 25a7402ef..a3b667fea 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -57,6 +57,8 @@ public interface ReactiveQueryProducer extends ReactiveConnectionSupplier { ReactiveQuery createReactiveQuery(String queryString, Class resultType); + ReactiveQueryImplementor createReactiveNamedQuery(String queryString); + ReactiveQueryImplementor createReactiveNamedQuery(String queryString, Class resultType); ReactiveNativeQuery createReactiveNativeQuery(String sqlString); 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 fc8ee8ae6..72157e46e 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 @@ -72,13 +72,16 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.IllegalMutationQueryException; +import org.hibernate.query.UnknownNamedQueryException; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryImplementor; +import org.hibernate.query.sql.spi.NamedNativeQueryMemento; import org.hibernate.query.sql.spi.NativeQueryImplementor; import org.hibernate.query.sqm.internal.SqmUtil; +import org.hibernate.query.sqm.spi.NamedSqmQueryMemento; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; @@ -131,7 +134,6 @@ import static org.hibernate.engine.spi.NaturalIdResolutions.INVALID_NATURAL_ID_REFERENCE; import static org.hibernate.event.spi.LoadEventListener.IMMEDIATE_LOAD; import static org.hibernate.internal.util.StringHelper.isEmpty; -import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.common.InternalStateAssertions.assertUseOnEventLoop; import static org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister.forceInitialize; @@ -443,14 +445,16 @@ public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, Cl @Override @Deprecated(forRemoval = true) public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, String resultSetMappingName) { + if ( isEmpty( resultSetMappingName ) ) { + throw new IllegalArgumentException( "Result set mapping name was not specified" ); + } + checkOpen(); pulseTransactionCoordinator(); delayedAfterCompletion(); try { - return isNotEmpty( resultSetMappingName ) - ? new ReactiveNativeQueryImpl<>( sqlString, getResultSetMappingMemento( resultSetMappingName ), this ) - : new ReactiveNativeQueryImpl<>( sqlString, this ); + return new ReactiveNativeQueryImpl<>( sqlString, getResultSetMappingMemento( resultSetMappingName ), null, this ); //TODO: why no applyQuerySettingsAndHints( query ); ??? } catch (RuntimeException he) { @@ -497,9 +501,78 @@ private ReactiveSelectionQuery createSelectionQuery(String hql, Class return query; } + @Override + public ReactiveQueryImplementor createReactiveNamedQuery(String name) { + checksBeforeQueryCreation(); + try { + return (ReactiveQueryImplementor) buildNamedQuery( + name, + this::createSqmQueryImplementor, + this::createNativeQueryImplementor + ); + } + catch (RuntimeException e) { + throw convertNamedQueryException( e ); + } + } + @Override public ReactiveQueryImplementor createReactiveNamedQuery(String name, Class resultType) { - return (ReactiveQueryImplementor) buildNamedQuery( name, resultType ); + checksBeforeQueryCreation(); + if ( resultType == null ) { + throw new IllegalArgumentException( "Result class is null" ); + } + try { + return buildNamedQuery( + name, + memento -> createReactiveSqmQueryImplementor( resultType, memento ), + memento -> createReactiveNativeQueryImplementor( resultType, memento ) + ); + } + catch (RuntimeException e) { + throw convertNamedQueryException( e ); + } + } + + private void checksBeforeQueryCreation() { + checkOpen(); + checkTransactionSynchStatus(); + } + + protected ReactiveNativeQueryImpl createReactiveNativeQueryImplementor(Class resultType, NamedNativeQueryMemento memento) { + final NativeQueryImplementor query = memento.toQuery(this, resultType ); + if ( isEmpty( query.getComment() ) ) { + query.setComment( "dynamic native SQL query" ); + } + applyQuerySettingsAndHints( query ); + return (ReactiveNativeQueryImpl) query; + } + + protected ReactiveQuerySqmImpl createReactiveSqmQueryImplementor(Class resultType, NamedSqmQueryMemento memento) { + final SqmQueryImplementor query = memento.toQuery( this, resultType ); + if ( isEmpty( query.getComment() ) ) { + query.setComment( "dynamic query" ); + } + applyQuerySettingsAndHints( query ); + if ( memento.getLockOptions() != null ) { + query.setLockOptions( memento.getLockOptions() ); + } + return (ReactiveQuerySqmImpl) query; + } + + private RuntimeException convertNamedQueryException(RuntimeException e) { + if ( e instanceof UnknownNamedQueryException ) { + // JPA expects this to mark the transaction for rollback only + getTransactionCoordinator().getTransactionDriverControl().markRollbackOnly(); + // it also expects an IllegalArgumentException, so wrap UnknownNamedQueryException + return new IllegalArgumentException( e.getMessage(), e ); + } + else if ( e instanceof IllegalArgumentException ) { + return e; + } + else { + return getExceptionConverter().convert( e ); + } } @Override @@ -584,7 +657,7 @@ public ReactiveNativeQuery createReactiveNativeQuery(String queryString, delayedAfterCompletion(); try { - final ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( queryString, this ); + final ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( queryString, null, this ); addAffectedEntities( affectedEntities, query ); if ( isEmpty( query.getComment() ) ) { query.setComment( "dynamic native SQL query" ); @@ -622,12 +695,11 @@ public ReactiveNativeQueryImpl createReactiveNativeQuery(String queryStri checkOpen(); pulseTransactionCoordinator(); delayedAfterCompletion(); - + // Should we throw an exception? + NamedResultSetMappingMemento memento = resultSetMapping == null ? null : getResultSetMappingMemento( resultSetMapping.getName() ); try { // Same approach as AbstractSharedSessionContract#createNativeQuery(String, String) - final ReactiveNativeQueryImpl nativeQuery = resultSetMapping != null - ? new ReactiveNativeQueryImpl<>( queryString, getResultSetMappingMemento( resultSetMapping.getName() ), this ) - : new ReactiveNativeQueryImpl<>( queryString, this ); + final ReactiveNativeQueryImpl nativeQuery = new ReactiveNativeQueryImpl<>( queryString, memento, null, this ); applyQuerySettingsAndHints( nativeQuery ); return nativeQuery; } @@ -652,20 +724,13 @@ public ResultSetMapping getResultSetMapping(Class resultType, String m if ( mapping == null ) { throw new IllegalArgumentException( "result set mapping does not exist: " + mappingName ); } -// -// ResultSetMappingImpl resultSetMapping = new ResultSetMappingImpl( "impl" ); -// if ( resultType != null ) { -// Class mappedResultType = resultSetMapping.; -// if ( !resultType.equals( mappedResultType ) ) { -// throw new IllegalArgumentException( "incorrect result type for result set mapping: " + mappingName + " has type " + mappedResultType.getName() ); -// } -// } return new ResultSetMapping<>() { @Override public String getName() { return mappingName; } + @Override public Class getResultType() { return resultType; 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 4941dda50..2d3efe4f5 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 @@ -22,6 +22,7 @@ import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; @@ -47,10 +48,12 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.query.IllegalMutationQueryException; +import org.hibernate.query.UnknownNamedQueryException; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.criteria.JpaCriteriaQuery; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.hql.spi.SqmQueryImplementor; +import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.spi.NativeQueryImplementor; @@ -64,7 +67,6 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; -import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.reactive.id.ReactiveIdentifierGenerator; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; @@ -905,7 +907,7 @@ public ReactiveNativeQueryImplementor createReactiveNativeQuery(String sq delayedAfterCompletion(); try { - final ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( sqlString, this ); + final ReactiveNativeQueryImpl query = new ReactiveNativeQueryImpl<>( sqlString, null, this); if ( isEmpty( query.getComment() ) ) { query.setComment( "dynamic native SQL query" ); } @@ -956,9 +958,19 @@ public ReactiveNativeQuery createReactiveNativeQuery(String sqlString, St delayedAfterCompletion(); try { - return isNotEmpty( resultSetMappingName ) - ? new ReactiveNativeQueryImpl<>( sqlString, getResultSetMappingMemento( resultSetMappingName ), this ) - : new ReactiveNativeQueryImpl<>( sqlString, this ); + if ( isNotEmpty( resultSetMappingName ) ) { + final NamedResultSetMappingMemento resultSetMappingMemento = getFactory().getQueryEngine() + .getNamedObjectRepository() + .getResultSetMappingMemento( resultSetMappingName ); + + if ( resultSetMappingMemento == null ) { + throw new HibernateException( "Could not resolve specified result-set mapping name : " + resultSetMappingName ); + } + return new ReactiveNativeQueryImpl<>( sqlString, resultSetMappingMemento, null, this ); + } + else { + return new ReactiveNativeQueryImpl<>( sqlString, this ); + } //TODO: why no applyQuerySettingsAndHints( query ); ??? } catch (RuntimeException he) { @@ -1069,11 +1081,64 @@ public ReactiveMutationQuery createNamedReactiveMutationQuery(String quer ); } + @Override + public ReactiveQueryImplementor createReactiveNamedQuery(String queryName) { + checksBeforeQueryCreation(); + try { + return (ReactiveQueryImplementor) buildNamedQuery( + queryName, + this::createSqmQueryImplementor, + this::createNativeQueryImplementor + ); + } + catch (RuntimeException e) { + throw convertNamedQueryException( e ); + } + } + + @Override + public ReactiveQueryImplementor createReactiveNamedQuery(String queryName, Class resultType) { + checksBeforeQueryCreation(); + if ( resultType == null ) { + throw new IllegalArgumentException( "Result class is null" ); + } + try { + return (ReactiveQueryImplementor) buildNamedQuery( + queryName, + memento -> createSqmQueryImplementor( resultType, memento ), + memento -> createNativeQueryImplementor( resultType, memento ) + ); + } + catch (RuntimeException e) { + throw convertNamedQueryException( e ); + } + } + + private RuntimeException convertNamedQueryException(RuntimeException e) { + if ( e instanceof UnknownNamedQueryException ) { + // JPA expects this to mark the transaction for rollback only + getTransactionCoordinator().getTransactionDriverControl().markRollbackOnly(); + // it also expects an IllegalArgumentException, so wrap UnknownNamedQueryException + return new IllegalArgumentException( e.getMessage(), e ); + } + else if ( e instanceof IllegalArgumentException ) { + return e; + } + else { + return getExceptionConverter().convert( e ); + } + } + @Override public ReactiveSelectionQuery createNamedReactiveSelectionQuery(String queryName, Class expectedResultType) { return (ReactiveSelectionQuery) createNamedSelectionQuery( queryName , expectedResultType ); } + private void checksBeforeQueryCreation() { + checkOpen(); + checkTransactionSynchStatus(); + } + @Override public ReactiveMutationQuery createNativeReactiveMutationQuery(String sqlString) { final ReactiveNativeQueryImplementor query = createReactiveNativeQuery( sqlString ); @@ -1083,11 +1148,6 @@ public ReactiveMutationQuery createNativeReactiveMutationQuery(String sql return query; } - @Override - public ReactiveQueryImplementor createReactiveNamedQuery(String queryName, Class resultType) { - return (ReactiveQueryImplementor) buildNamedQuery( queryName, resultType ); - } - @Override public ReactiveNativeQuery createReactiveNativeQuery(String queryString, AffectedEntities affectedEntities) { checkOpen(); @@ -1131,11 +1191,13 @@ public ReactiveNativeQueryImpl createReactiveNativeQuery(String queryStri pulseTransactionCoordinator(); delayedAfterCompletion(); + if ( resultSetMapping == null ) { + throw new IllegalArgumentException( "Result set mapping was not specified" ); + } + try { - // Same approach as AbstractSharedSessionContract#createNativeQuery(String, String) - final ReactiveNativeQueryImpl nativeQuery = resultSetMapping != null - ? new ReactiveNativeQueryImpl<>( queryString, getResultSetMappingMemento( resultSetMapping.getName() ), this ) - : new ReactiveNativeQueryImpl<>( queryString, this ); + final NamedResultSetMappingMemento memento = getResultSetMappingMemento( resultSetMapping.getName() ); + final ReactiveNativeQueryImpl nativeQuery = new ReactiveNativeQueryImpl<>( queryString, memento, null, this ); applyQuerySettingsAndHints( nativeQuery ); return nativeQuery; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index 78ce3c891..150c2ec96 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -5,16 +5,11 @@ */ package org.hibernate.reactive.stage.impl; -import jakarta.persistence.CacheRetrieveMode; -import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; -import jakarta.persistence.metamodel.Attribute; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; + import org.hibernate.CacheMode; import org.hibernate.Filter; import org.hibernate.FlushMode; @@ -39,10 +34,16 @@ import org.hibernate.reactive.stage.Stage.Query; import org.hibernate.reactive.stage.Stage.SelectionQuery; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Attribute; import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; @@ -556,7 +557,7 @@ public MutationQuery createQuery(CriteriaDelete criteriaDelete) { @Override public Query createNamedQuery(String queryName) { - return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName, null ) ); + return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName ) ); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index aadd76285..c48a7eab8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -326,7 +326,7 @@ public Query createNativeQuery(String queryString) { @Override public Query createNamedQuery(String queryName) { - return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName, null ) ); + return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName ) ); } @Override From f7304add53e2372038e8cf7b71bfbf3b87840d09 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 17 Mar 2025 16:28:17 +0100 Subject: [PATCH 095/201] [#2155] Move ReactiveStatelessWithBatchTest to org.hibernate.reactive --- .../{ => reactive}/ReactiveStatelessWithBatchTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename hibernate-reactive-core/src/test/java/org/hibernate/{ => reactive}/ReactiveStatelessWithBatchTest.java (99%) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java similarity index 99% rename from hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java rename to hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java index 563fad40b..a30cc1364 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/ReactiveStatelessWithBatchTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java @@ -3,11 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright: Red Hat Inc. and Hibernate Authors */ -package org.hibernate; +package org.hibernate.reactive; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; -import org.hibernate.reactive.BaseReactiveTest; import org.hibernate.reactive.annotations.EnabledFor; import org.hibernate.reactive.testing.SqlStatementTracker; From 1b52966e4767482adc970a16fc2821e985a1ebfb Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:57:12 +0100 Subject: [PATCH 096/201] [#2152] Remove experimental table support when starting CockroachDB It's not needed anymore --- .../reactive/containers/CockroachDBDatabase.java | 8 -------- 1 file changed, 8 deletions(-) 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 ae4d34d39..60dd1b997 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 @@ -51,7 +51,6 @@ private String address() { // Calling start() will start the container (if not already started) // It is required to call start() before obtaining the JDBC URL because it will contain a randomized port cockroachDb.start(); - enableTemporaryTables(); return disableSslMode( cockroachDb.getJdbcUrl() ); } @@ -62,13 +61,6 @@ private static String disableSslMode(String url) { return url + "?sslmode=disable"; } - /** - * We need temporary tables when updating entities in a hierarchy - */ - private static void enableTemporaryTables() { - runSql( "SET CLUSTER SETTING sql.defaults.experimental_temporary_tables.enabled = 'true';" ); - } - private static void runSql(String command) { Container.ExecResult execResult; try { From 8b574920acdf5df787d75d8d65eeaf0687d6f7b3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 26 Mar 2025 22:10:12 +0100 Subject: [PATCH 097/201] eliminate dependency on useless helper class --- .../StandardReactiveSelectExecutor.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java index 43b7c1554..379a83060 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java @@ -12,6 +12,7 @@ import java.util.concurrent.TimeUnit; import org.hibernate.CacheMode; +import org.hibernate.SharedSessionContract; import org.hibernate.cache.spi.QueryKey; import org.hibernate.cache.spi.QueryResultsCache; import org.hibernate.engine.spi.PersistenceContext; @@ -20,6 +21,7 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.query.TupleTransformer; import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; +import org.hibernate.query.spi.QueryOptions; import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; import org.hibernate.reactive.sql.exec.spi.ReactiveSelectExecutor; import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet; @@ -31,7 +33,6 @@ import org.hibernate.reactive.sql.results.spi.ReactiveRowReader; import org.hibernate.reactive.sql.results.spi.ReactiveValuesMappingProducer; import org.hibernate.sql.exec.SqlExecLogger; -import org.hibernate.sql.exec.internal.JdbcExecHelper; import org.hibernate.sql.exec.internal.StandardStatementCreator; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -50,6 +51,8 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; + /** * @see org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl */ @@ -286,13 +289,13 @@ public CompletionStage resolveJdbcValuesSource( final SessionFactoryImplementor factory = session.getFactory(); final boolean queryCacheEnabled = factory.getSessionFactoryOptions().isQueryCacheEnabled(); - final List cachedResults; - final CacheMode cacheMode = JdbcExecHelper.resolveCacheMode( executionContext ); + final CacheMode cacheMode = resolveCacheMode( executionContext ); final boolean cacheable = queryCacheEnabled && canBeCached && executionContext.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE; - final QueryKey queryResultsCacheKey; + final QueryKey queryResultsCacheKey; + final List cachedResults; if ( cacheable && cacheMode.isGetEnabled() ) { SqlExecLogger.SQL_EXEC_LOGGER.debugf( "Reading Query result cache data per CacheMode#isGetEnabled [%s]", cacheMode.name() ); final Set querySpaces = jdbcSelect.getAffectedTableNames(); @@ -419,6 +422,16 @@ public CompletionStage resolveJdbcValuesSource( } } + private static CacheMode resolveCacheMode(ExecutionContext executionContext) { + final QueryOptions queryOptions = executionContext.getQueryOptions(); + final SharedSessionContract session = executionContext.getSession(); + return coalesceSuppliedValues( + () -> queryOptions == null ? null : queryOptions.getCacheMode(), + session::getCacheMode, + () -> CacheMode.NORMAL + ); + } + /** * see {@link CachedJdbcValuesMetadata} */ From aea92393ac4ed165fd953c8df275e975d3dad91d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 26 Mar 2025 23:25:44 +0100 Subject: [PATCH 098/201] cleanups to StandardReactiveSelectExecutor --- .../StandardReactiveSelectExecutor.java | 112 ++++++++---------- 1 file changed, 50 insertions(+), 62 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java index 379a83060..64c5b3af3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java @@ -15,10 +15,10 @@ import org.hibernate.SharedSessionContract; import org.hibernate.cache.spi.QueryKey; import org.hibernate.cache.spi.QueryResultsCache; +import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.query.TupleTransformer; import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; import org.hibernate.query.spi.QueryOptions; @@ -43,6 +43,7 @@ import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter; import org.hibernate.sql.results.jdbc.internal.CachedJdbcValuesMetadata; import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.spi.RowTransformer; @@ -52,6 +53,7 @@ import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; +import static org.hibernate.internal.util.collections.ArrayHelper.indexOf; /** * @see org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl @@ -148,8 +150,8 @@ public CompletionStage executeQuery( ReactiveResultsConsumer resultsConsumer) { final PersistenceContext persistenceContext = executionContext.getSession().getPersistenceContext(); - boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly(); - Boolean readOnly = executionContext.getQueryOptions().isReadOnly(); + final boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly(); + final Boolean readOnly = executionContext.getQueryOptions().isReadOnly(); if ( readOnly != null ) { // The read-only/modifiable mode for the query was explicitly set. // Temporarily set the default read-only/modifiable setting to the query's setting. @@ -179,7 +181,8 @@ private CompletionStage doExecuteQuery( JdbcSelectExecutor.StatementCreator statementCreator, ReactiveResultsConsumer resultsConsumer) { - final ReactiveDeferredResultSetAccess deferredResultSetAccess = new ReactiveDeferredResultSetAccess( jdbcSelect, jdbcParameterBindings, executionContext, statementCreator, resultCountEstimate ); + final ReactiveDeferredResultSetAccess deferredResultSetAccess = + new ReactiveDeferredResultSetAccess( jdbcSelect, jdbcParameterBindings, executionContext, statementCreator, resultCountEstimate ); return resolveJdbcValuesSource( executionContext.getQueryIdentifier( deferredResultSetAccess.getFinalSql() ), @@ -216,10 +219,8 @@ public boolean shouldReturnProxies() { } }; - final JdbcValuesSourceProcessingStateStandardImpl valuesProcessingState = new JdbcValuesSourceProcessingStateStandardImpl( - executionContext, - processingOptions - ); + final JdbcValuesSourceProcessingStateStandardImpl valuesProcessingState = + new JdbcValuesSourceProcessingStateStandardImpl( executionContext, processingOptions ); final ReactiveRowReader rowReader = ReactiveResultsHelper.createRowReader( executionContext.getSession().getSessionFactory(), @@ -257,26 +258,25 @@ private static RowTransformer rowTransformer( ExecutionContext executionContext, RowTransformer transformer, ReactiveValuesResultSet jdbcValues) { - RowTransformer rowTransformer = transformer; - if ( rowTransformer == null ) { - @SuppressWarnings("unchecked") final TupleTransformer tupleTransformer = (TupleTransformer) executionContext - .getQueryOptions() - .getTupleTransformer(); - + if ( transformer == null ) { + @SuppressWarnings("unchecked") + final TupleTransformer tupleTransformer = + (TupleTransformer) executionContext.getQueryOptions().getTupleTransformer(); if ( tupleTransformer == null ) { - rowTransformer = RowTransformerStandardImpl.instance(); + return RowTransformerStandardImpl.instance(); } else { - final List> domainResults = jdbcValues.getValuesMapping() - .getDomainResults(); + final List> domainResults = jdbcValues.getValuesMapping().getDomainResults(); final String[] aliases = new String[domainResults.size()]; for ( int i = 0; i < domainResults.size(); i++ ) { aliases[i] = domainResults.get( i ).getResultVariable(); } - rowTransformer = new RowTransformerTupleTransformerAdapter<>( aliases, tupleTransformer ); + return new RowTransformerTupleTransformerAdapter<>( aliases, tupleTransformer ); } } - return rowTransformer; + else { + return transformer; + } } public CompletionStage resolveJdbcValuesSource( @@ -290,9 +290,10 @@ public CompletionStage resolveJdbcValuesSource( final boolean queryCacheEnabled = factory.getSessionFactoryOptions().isQueryCacheEnabled(); final CacheMode cacheMode = resolveCacheMode( executionContext ); + final QueryOptions queryOptions = executionContext.getQueryOptions(); final boolean cacheable = queryCacheEnabled && canBeCached - && executionContext.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE; + && queryOptions.isResultCachingEnabled() == Boolean.TRUE; final QueryKey queryResultsCacheKey; final List cachedResults; @@ -307,10 +308,10 @@ public CompletionStage resolveJdbcValuesSource( } final QueryResultsCache queryCache = factory.getCache() - .getQueryResultsCache( executionContext.getQueryOptions().getResultCacheRegionName() ); + .getQueryResultsCache( queryOptions.getResultCacheRegionName() ); queryResultsCacheKey = QueryKey - .from( jdbcSelect.getSqlString(), executionContext.getQueryOptions().getLimit(), executionContext.getQueryParameterBindings(), session ); + .from( jdbcSelect.getSqlString(), queryOptions.getLimit(), executionContext.getQueryParameterBindings(), session ); cachedResults = queryCache.get( // todo (6.0) : QueryCache#get takes the `queryResultsCacheKey` see tat discussion above @@ -345,7 +346,7 @@ public CompletionStage resolveJdbcValuesSource( if ( cacheable && cacheMode.isPutEnabled() ) { queryResultsCacheKey = QueryKey.from( jdbcSelect.getSqlString(), - executionContext.getQueryOptions().getLimit(), + queryOptions.getLimit(), executionContext.getQueryParameterBindings(), session ); @@ -355,16 +356,18 @@ public CompletionStage resolveJdbcValuesSource( } } - ReactiveValuesMappingProducer mappingProducer = (ReactiveValuesMappingProducer) jdbcSelect.getJdbcValuesMappingProducer(); + final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers(); + + final ReactiveValuesMappingProducer mappingProducer = + (ReactiveValuesMappingProducer) jdbcSelect.getJdbcValuesMappingProducer(); if ( cachedResults == null ) { if ( queryResultsCacheKey == null ) { - return mappingProducer - .reactiveResolve( resultSetAccess, session.getLoadQueryInfluencers(), factory ) + return mappingProducer.reactiveResolve( resultSetAccess, loadQueryInfluencers, factory ) .thenApply( jdbcValuesMapping -> new ReactiveValuesResultSet( resultSetAccess, null, queryIdentifier, - executionContext.getQueryOptions(), + queryOptions, resultSetAccess.usesFollowOnLocking(), jdbcValuesMapping, null, @@ -374,13 +377,12 @@ public CompletionStage resolveJdbcValuesSource( else { // If we need to put the values into the cache, we need to be able to capture the JdbcValuesMetadata final CapturingJdbcValuesMetadata capturingMetadata = new CapturingJdbcValuesMetadata( resultSetAccess ); - return mappingProducer - .reactiveResolve( resultSetAccess, session.getLoadQueryInfluencers(), factory ) + return mappingProducer.reactiveResolve( resultSetAccess, loadQueryInfluencers, factory ) .thenApply( jdbcValuesMapping -> new ReactiveValuesResultSet( resultSetAccess, queryResultsCacheKey, queryIdentifier, - executionContext.getQueryOptions(), + queryOptions, resultSetAccess.usesFollowOnLocking(), jdbcValuesMapping, capturingMetadata.resolveMetadataForCache(), @@ -392,34 +394,22 @@ public CompletionStage resolveJdbcValuesSource( // TODO: Implements JdbcValuesCacheHit for reactive, see JdbcSelectExecutorStandardImpl#resolveJdbcValuesSource // If we need to put the values into the cache, we need to be able to capture the JdbcValuesMetadata final CapturingJdbcValuesMetadata capturingMetadata = new CapturingJdbcValuesMetadata( resultSetAccess ); - if ( cachedResults.isEmpty() || !( cachedResults.get( 0 ) instanceof JdbcValuesMetadata ) ) { - return mappingProducer.reactiveResolve( resultSetAccess, session.getLoadQueryInfluencers(), factory ) - .thenApply( jdbcValuesMapping -> new ReactiveValuesResultSet( - resultSetAccess, - queryResultsCacheKey, - queryIdentifier, - executionContext.getQueryOptions(), - resultSetAccess.usesFollowOnLocking(), - jdbcValuesMapping, - capturingMetadata.resolveMetadataForCache(), - executionContext - ) ); - } - else { - return mappingProducer - .reactiveResolve( (JdbcValuesMetadata) cachedResults.get( 0 ), session.getLoadQueryInfluencers(), factory ) - .thenApply( jdbcValuesMapping -> new ReactiveValuesResultSet( - resultSetAccess, - queryResultsCacheKey, - queryIdentifier, - executionContext.getQueryOptions(), - resultSetAccess.usesFollowOnLocking(), - jdbcValuesMapping, - capturingMetadata.resolveMetadataForCache(), - executionContext - ) ); - } - } + final CompletionStage stage = + !cachedResults.isEmpty() + && cachedResults.get(0) instanceof JdbcValuesMetadata jdbcValuesMetadata + ? mappingProducer.reactiveResolve( jdbcValuesMetadata, loadQueryInfluencers, factory ) + : mappingProducer.reactiveResolve( resultSetAccess, loadQueryInfluencers, factory ); + return stage.thenApply( jdbcValuesMapping -> new ReactiveValuesResultSet( + resultSetAccess, + queryResultsCacheKey, + queryIdentifier, + queryOptions, + resultSetAccess.usesFollowOnLocking(), + jdbcValuesMapping, + capturingMetadata.resolveMetadataForCache(), + executionContext + ) ); + } } private static CacheMode resolveCacheMode(ExecutionContext executionContext) { @@ -468,7 +458,7 @@ public int resolveColumnPosition(String columnName) { position = resultSetAccess.resolveColumnPosition( columnName ); columnNames[position - 1] = columnName; } - else if ( ( position = ArrayHelper.indexOf( columnNames, columnName ) + 1 ) == 0 ) { + else if ( ( position = indexOf( columnNames, columnName ) + 1 ) == 0 ) { position = resultSetAccess.resolveColumnPosition( columnName ); columnNames[position - 1] = columnName; } @@ -556,9 +546,7 @@ public void end(JdbcOperationQuerySelect jdbcSelect, T result) { } private int getResultSize(T result) { - return result instanceof Collection - ? ( (Collection) result ).size() - : -1; + return result instanceof Collection collection ? collection.size() : -1; } } } From c3cf2f6fc09d9031d44ae7f856c4146431ca4904 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 28 Mar 2025 17:45:26 +0100 Subject: [PATCH 099/201] use @linkplain where appropriate --- .../reactive/pool/impl/ReactiveConnectionPoolInitiator.java | 6 +++--- .../pool/impl/SqlClientPoolConfigurationInitiator.java | 2 +- .../provider/service/NoJdbcConnectionProviderInitiator.java | 4 ++-- .../provider/service/NoJdbcEnvironmentInitiator.java | 4 ++-- .../NoJdbcMultiTenantConnectionProviderInitiator.java | 4 ++-- .../reactive/provider/service/NoJtaPlatformInitiator.java | 3 ++- .../provider/service/ReactiveMarkerServiceInitiator.java | 3 ++- .../service/ReactivePersisterClassResolverInitiator.java | 5 +++-- .../service/ReactiveSessionFactoryBuilderInitiator.java | 6 +++--- 9 files changed, 20 insertions(+), 17 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ReactiveConnectionPoolInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ReactiveConnectionPoolInitiator.java index 15c517acc..cf57e94f0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ReactiveConnectionPoolInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/ReactiveConnectionPoolInitiator.java @@ -19,10 +19,10 @@ import java.util.Map; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that + * A Hibernate {@linkplain StandardServiceInitiator service initiator} that * integrates our {@link ReactiveConnectionPool}. By default, the pool - * implementation is {@link DefaultSqlClientPool}. A custom implementation may - * be specified via {@link Settings#SQL_CLIENT_POOL}. + * implementation is {@link DefaultSqlClientPool}. A custom implementation + * may be specified via {@link Settings#SQL_CLIENT_POOL}. * * @see ReactiveConnectionPool * @see DefaultSqlClientPool diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientPoolConfigurationInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientPoolConfigurationInitiator.java index e23656d21..1c12cc8b6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientPoolConfigurationInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/SqlClientPoolConfigurationInitiator.java @@ -16,7 +16,7 @@ import java.util.Map; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that + * A Hibernate {@linkplain StandardServiceInitiator service initiator} that * allows the user to define their own {@link SqlClientPoolConfiguration} * strategy. */ diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProviderInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProviderInitiator.java index 6ecdffe2f..a57f72b16 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProviderInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcConnectionProviderInitiator.java @@ -14,8 +14,8 @@ import java.util.Map; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that - * wraps the Hibernate {@link ConnectionProvider} in an instance of + * A Hibernate {@linkplain StandardServiceInitiator service initiator} + * that wraps the Hibernate {@link ConnectionProvider} in an instance of * {@link NoJdbcConnectionProvider}. * * @author Gavin King diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java index 5580c99fa..953285dd4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java @@ -28,8 +28,8 @@ import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that - * provides an implementation of {@link JdbcEnvironment} that infers + * A Hibernate {@linkplain StandardServiceInitiator service initiator} + * that provides an implementation of {@link JdbcEnvironment} that infers * the Hibernate {@link org.hibernate.dialect.Dialect} from the JDBC URL. */ public class NoJdbcEnvironmentInitiator implements StandardServiceInitiator { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcMultiTenantConnectionProviderInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcMultiTenantConnectionProviderInitiator.java index 60a6cf599..50e614e2e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcMultiTenantConnectionProviderInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcMultiTenantConnectionProviderInitiator.java @@ -13,8 +13,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; /** - * A Hibernate {@link StandardServiceInitiator service initiator} for - * {@link NoJdbcMultiTenantConnectionProvider}. + * A Hibernate {@linkplain StandardServiceInitiator service initiator} + * for {@link NoJdbcMultiTenantConnectionProvider}. * * @author Gavin King */ diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJtaPlatformInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJtaPlatformInitiator.java index 30add8dae..8fd12fa10 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJtaPlatformInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJtaPlatformInitiator.java @@ -13,7 +13,8 @@ import java.util.Map; /** - * A Hibernate {@link StandardServiceInitiator service initiator} for the non-configured form of JTA platform. + * A Hibernate {@linkplain StandardServiceInitiator service initiator} + * for the non-configured form of JTA platform. * * @see NoJtaPlatform */ diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveMarkerServiceInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveMarkerServiceInitiator.java index e7e92c270..46b4549d8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveMarkerServiceInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveMarkerServiceInitiator.java @@ -11,7 +11,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; /** - * A Hibernate {@link StandardServiceInitiator service initiator} for {@link ReactiveMarkerService}. + * A Hibernate {@linkplain StandardServiceInitiator service initiator} for + * {@link ReactiveMarkerService}. */ public final class ReactiveMarkerServiceInitiator implements StandardServiceInitiator { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactivePersisterClassResolverInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactivePersisterClassResolverInitiator.java index 8e9670860..8edd7b4b1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactivePersisterClassResolverInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactivePersisterClassResolverInitiator.java @@ -12,8 +12,9 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that creates a {@link ReactivePersisterClassResolver} to register - * the persisters Hibernate Reactive needs. + * A Hibernate {@linkplain StandardServiceInitiator service initiator} + * that creates a {@link ReactivePersisterClassResolver} to register the + * persisters needed by Hibernate Reactive. */ public class ReactivePersisterClassResolverInitiator implements StandardServiceInitiator { public static final ReactivePersisterClassResolverInitiator INSTANCE = new ReactivePersisterClassResolverInitiator(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSessionFactoryBuilderInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSessionFactoryBuilderInitiator.java index 768c36ec6..14805057f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSessionFactoryBuilderInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSessionFactoryBuilderInitiator.java @@ -12,9 +12,9 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; /** - * A Hibernate {@link StandardServiceInitiator service initiator} that - * wraps the Hibernate {@link org.hibernate.engine.jdbc.connections.spi.ConnectionProvider} in an instance of - * {@link NoJdbcConnectionProvider}. + * A Hibernate {@linkplain StandardServiceInitiator service initiator} that wraps + * the Hibernate {@link org.hibernate.engine.jdbc.connections.spi.ConnectionProvider} + * in an instance of {@link NoJdbcConnectionProvider}. */ public class ReactiveSessionFactoryBuilderInitiator implements StandardServiceInitiator { From 66226f1a7be82e04ac019dc6be8dac717b0b39af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 03:16:41 +0000 Subject: [PATCH 100/201] Bump the workflow-actions group with 2 updates Bumps the workflow-actions group with 2 updates: [actions/cache](https://github.com/actions/cache) and [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `actions/cache` from 4.2.2 to 4.2.3 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/d4323d4df104b026a6aa633fdb11d772146be0bf...5a3ec84eff668545956fd18022155c47e93e2684) Updates `actions/upload-artifact` from 4.6.1 to 4.6.2 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1...ea165f8d65b6e75b540449e92b4886f43607fa02) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 4.2.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: workflow-actions - dependency-name: actions/upload-artifact dependency-version: 4.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: workflow-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eec52d729..c85d9281d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 id: cache-gradle with: path: | @@ -116,7 +116,7 @@ jobs: - name: Run examples in '${{ matrix.example }}' on ${{ matrix.db }} run: ./gradlew :${{ matrix.example }}:runAllExamplesOn${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: failure() with: name: reports-examples-${{ matrix.db }} @@ -139,7 +139,7 @@ jobs: echo "::set-output name=yearmonth::$(/bin/date -u "+%Y-%m")" shell: bash - name: Cache Gradle downloads - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 id: cache-gradle with: path: | @@ -166,7 +166,7 @@ jobs: - name: Build and Test with ${{ matrix.db }} run: ./gradlew build -PshowStandardOutput -Pdocker -Pdb=${{ matrix.db }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: failure() with: name: reports-db-${{ matrix.db }} @@ -216,7 +216,7 @@ jobs: echo "buildtool-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT - name: Cache Maven/Gradle Dependency/Dist Caches id: cache-maven - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 # if it's not a pull request, we restore and save the cache if: github.event_name != 'pull_request' with: @@ -233,7 +233,7 @@ jobs: ${{ steps.cache-key.outputs.buildtool-monthly-branch-cache-key }}- ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}- - name: Restore Maven/Gradle Dependency/Dist Caches - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 # if it's a pull request, we restore the cache, but we don't save it if: github.event_name == 'pull_request' with: @@ -291,7 +291,7 @@ jobs: -Porg.gradle.java.installations.paths=${{ steps.mainjdk-exportpath.outputs.path }},${{ steps.testjdk-exportpath.outputs.path }} \ ${{ matrix.java.jvm_args && '-Ptest.jdk.launcher.args=' }}${{ matrix.java.jvm_args }} - name: Upload reports (if build failed) - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: failure() with: name: reports-java${{ matrix.java.name }} From 147604a354c214252d5095b92c3d7b4f7e421934 Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Wed, 2 Apr 2025 15:10:32 +0200 Subject: [PATCH 101/201] [#2174] Prepare for Vert.x 5 upgrade in Vert.x Context Closes #2174 Local context data management methods are deprecated and will be removed in Vert.x 5. This change will make it easier to upgrade to Vert.x 5 (should be just a matter of changing imports) --- .../reactive/context/impl/VertxContext.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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..fef172e66 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 @@ -36,10 +36,10 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) { @Override public void put(Key key, T instance) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = ContextInternal.current(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - context.putLocal( key, instance ); + context.localContextData().put( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -49,9 +49,10 @@ public void put(Key key, T instance) { @Override public T get(Key key) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = ContextInternal.current(); if ( context != null ) { - T local = context.getLocal( key ); + @SuppressWarnings("unchecked") + T local = (T) context.localContextData().get( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -63,9 +64,9 @@ public T get(Key key) { @Override public void remove(Key key) { - final io.vertx.core.Context context = Vertx.currentContext(); + final ContextInternal context = ContextInternal.current(); if ( context != null ) { - boolean removed = context.removeLocal( key ); + boolean removed = context.localContextData().remove( key ) != null; if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed ); } else { From 7469034f7cd5c74bf0582ae27c5dc39d8b8411ea Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 6 Mar 2025 16:39:44 +0100 Subject: [PATCH 102/201] [#1867] Error when inserting in batch with joined table inheritance --- .../ReactiveMutationExecutorStandard.java | 90 +++++++++++++++++-- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java index 4f1d62325..0a09ff3a1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java @@ -5,10 +5,6 @@ */ package org.hibernate.reactive.engine.jdbc.mutation.internal; -import java.lang.invoke.MethodHandles; -import java.sql.SQLException; -import java.util.concurrent.CompletionStage; - import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.OperationResultChecker; @@ -25,6 +21,7 @@ import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.reactive.adaptor.impl.PrepareStatementDetailsAdaptor; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; +import org.hibernate.reactive.engine.jdbc.ResultsCheckerUtil; import org.hibernate.reactive.engine.jdbc.env.internal.ReactiveMutationExecutor; import org.hibernate.reactive.generator.values.ReactiveGeneratedValuesMutationDelegate; import org.hibernate.reactive.logging.impl.Log; @@ -37,9 +34,16 @@ import org.hibernate.sql.model.TableMapping; import org.hibernate.sql.model.ValuesAnalysis; +import java.lang.invoke.MethodHandles; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; + import static org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper.checkResults; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; +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.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER; @@ -73,10 +77,64 @@ private ReactiveConnection connection(SharedSessionContractImplementor session) @Override public CompletionStage performReactiveBatchedOperations( ValuesAnalysis valuesAnalysis, - TableInclusionChecker inclusionChecker, OperationResultChecker resultChecker, + TableInclusionChecker inclusionChecker, + OperationResultChecker resultChecker, SharedSessionContractImplementor session) { - return ReactiveMutationExecutor.super - .performReactiveBatchedOperations( valuesAnalysis, inclusionChecker, resultChecker, session); + final PreparedStatementGroup batchedMutationOperationGroup = getBatchedPreparedStatementGroup(); + if ( batchedMutationOperationGroup != null ) { + final List preparedStatementDetailsList = new ArrayList<>( + batchedMutationOperationGroup.getNumberOfStatements() ); + batchedMutationOperationGroup.forEachStatement( (tableName, statementDetails) -> preparedStatementDetailsList + .add( statementDetails ) ); + return loop( preparedStatementDetailsList, statementDetails -> { + if ( statementDetails == null ) { + return voidFuture(); + } + final JdbcValueBindings valueBindings = getJdbcValueBindings(); + final TableMapping tableDetails = statementDetails.getMutatingTableDetails(); + if ( inclusionChecker != null && !inclusionChecker.include( tableDetails ) ) { + if ( MODEL_MUTATION_LOGGER.isTraceEnabled() ) { + MODEL_MUTATION_LOGGER.tracef( + "Skipping execution of secondary insert : %s", + tableDetails.getTableName() + ); + } + return voidFuture(); + } + + // If we get here the statement is needed - make sure it is resolved + final Object[] paramValues = PreparedStatementAdaptor.bind( statement -> { + PreparedStatementDetails details = new PrepareStatementDetailsAdaptor( + statementDetails, + statement, + session.getJdbcServices() + ); + valueBindings.beforeStatement( details ); + } ); + + final ReactiveConnection reactiveConnection = ( (ReactiveConnectionSupplier) session ).getReactiveConnection(); + final String sql = statementDetails.getSqlString(); + return reactiveConnection.update( + sql, + paramValues, + true, + (rowCount, batchPosition, query) -> ResultsCheckerUtil.checkResults( + session, + statementDetails, + resultChecker, + rowCount, + batchPosition + ) + ).whenComplete( (o, throwable) -> { //TODO: is this part really needed? + if ( statementDetails.getStatement() != null ) { + statementDetails.releaseStatement( session ); + } + valueBindings.afterStatement( tableDetails ); + } ); + } + ); + } + return voidFuture(); } @Override @@ -159,6 +217,23 @@ public CompletionStage performReactiveNonBatchedOperations( } } + @Override + public CompletionStage performReactiveSelfExecutingOperations( + ValuesAnalysis valuesAnalysis, + TableInclusionChecker inclusionChecker, + SharedSessionContractImplementor session) { + if ( getSelfExecutingMutations() == null || getSelfExecutingMutations().isEmpty() ) { + return voidFuture(); + } + + return loop( getSelfExecutingMutations(), operation -> { + if ( inclusionChecker.include( operation.getTableDetails() ) ) { + operation.performMutation( getJdbcValueBindings(), valuesAnalysis, session ); + } + return voidFuture(); + }); + } + private class OperationsForEach { private final Object id; @@ -210,6 +285,7 @@ public CompletionStage buildLoop() { return loop; } } + @Override public CompletionStage performReactiveNonBatchedMutation( PreparedStatementDetails statementDetails, From 751ac6d90a2233612cdf8a72894ef6863eb5985e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 27 Feb 2024 12:59:51 +0100 Subject: [PATCH 103/201] [#1867] Test case --- .../reactive/JoinedInheritanceBatchTest.java | 130 ++++++++ .../ManyToManyWithCompositeIdTest.java | 293 ++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedInheritanceBatchTest.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyWithCompositeIdTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedInheritanceBatchTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedInheritanceBatchTest.java new file mode 100644 index 000000000..0c49d883e --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/JoinedInheritanceBatchTest.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; + + +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.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletionStage; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; + +@Timeout(value = 10, timeUnit = MINUTES) +public class JoinedInheritanceBatchTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( ClientA.class, Client.class ); + } + + @Override + protected CompletionStage cleanDb() { + return voidFuture(); + } + + @Test + public void test(VertxTestContext context) { + final ClientA client1 = new ClientA("Client 1", "email@c1", "123456"); + + test( context, getMutinySessionFactory().withTransaction( session -> { + session.setBatchSize( 5 ); + return session.persist( client1 ); + } ) + .chain( () -> getMutinySessionFactory().withTransaction( session -> session + .createQuery( "select c from Client c", Client.class ) + .getResultList() + .invoke( persistedClients -> assertThat( persistedClients ) + .as( "Clients has not bee persisted" ) + .isNotEmpty() ) ) ) + ); + } + + @Entity(name = "Client") + @Table(name = "`Client`") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Client { + + @Id + @SequenceGenerator(name = "seq", sequenceName = "id_seq", allocationSize = 1) + @GeneratedValue(generator = "seq", strategy = GenerationType.SEQUENCE) + private Long id; + + private String name; + + private String email; + + private String phone; + + public Client() { + } + + public Client(String name, String email, String phone) { + this.name = name; + this.email = email; + this.phone = phone; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + } + + @Entity + @Table(name = "`ClientA`") + public static class ClientA extends Client { + + public ClientA() { + } + + public ClientA(String name, String email, String phone) { + super( name, email, phone ); + } + } + +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyWithCompositeIdTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyWithCompositeIdTest.java new file mode 100644 index 000000000..8c72c38d3 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ManyToManyWithCompositeIdTest.java @@ -0,0 +1,293 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import io.vertx.junit5.Timeout; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import java.util.concurrent.CompletionStage; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; + +@Timeout(value = 10, timeUnit = MINUTES) +public class ManyToManyWithCompositeIdTest extends BaseReactiveTest { + + @Override + protected Collection> annotatedEntities() { + return List.of( CarsClients.class, ClientA.class, Client.class, Car.class ); + } + + @Override + protected CompletionStage cleanDb() { + return voidFuture(); + } + + @Test + public void test(VertxTestContext context) { + List clients = new ArrayList<>(); + for ( int i = 0; i < 5; i++ ) { + ClientA client = new ClientA(); + client.setName( "name" + i ); + client.setEmail( "email" + i ); + client.setPhone( "phone" + i ); + clients.add( client ); + } + + List cars = new ArrayList<>(); + for ( int i = 0; i < 2; i++ ) { + Car car = new Car(); + car.setBrand( "brand" + i ); + car.setModel( "model" + i ); + cars.add( car ); + } + + test( context, getMutinySessionFactory() + .withSession( session -> { + session.setBatchSize( 5 ); + return session.persistAll( cars.toArray() ) + .chain( () -> session + .persistAll( clients.toArray() ) + .chain( session::flush ) ) + .chain( () -> { + List carsClientsList = new ArrayList<>(); + for ( Client client : clients ) { + for ( Car car : cars ) { + CarsClients carsClients = new CarsClients( "location" ); + carsClientsList.add( carsClients ); + car.addClient( carsClients ); + client.addCar( carsClients ); + } + } + return session + .persistAll( carsClientsList.toArray() ) + .chain( session::flush ); + } ); + } ) + ); + } + + @Entity(name = "Car") + @Table(name = "Car_Table") + public static class Car { + + @Id + @SequenceGenerator(name = "seq_car", sequenceName = "id_seq_car", allocationSize = 1) + @GeneratedValue(generator = "seq_car", strategy = GenerationType.SEQUENCE) + private Long id; + + public String brand; + + + private String model; + + @OneToMany(mappedBy = "car") + private Set clients = new HashSet<>(); + + public Car() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public Set getClients() { + return clients; + } + + public void setClients(Set clients) { + this.clients = clients; + } + + public void addClient(CarsClients carsClients) { + carsClients.setCar( this ); + clients.add( carsClients ); + } + } + + @Entity + @Table(name = "Client_Table") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Client { + + @Id + @SequenceGenerator(name = "seq", sequenceName = "id_seq", allocationSize = 1) + @GeneratedValue(generator = "seq", strategy = GenerationType.SEQUENCE) + private Long id; + + private String name; + + private String email; + + private String phone; + + @OneToMany(mappedBy = "client") + private Set cars = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public Set getCars() { + return cars; + } + + public void setCars(Set cars) { + this.cars = cars; + } + + public void addCar(CarsClients carsClients) { + carsClients.setClient( this ); + cars.add( carsClients ); + } + } + + @Entity + @Table(name = "ClientA_Table") + public static class ClientA extends Client { + + public ClientA() { + } + } + + @Entity + @IdClass(CarsClientsId.class) + @Table(name = "cars_clients_table") + public static class CarsClients { + + @Id + @ManyToOne + private Car car; + + @Id + @ManyToOne + private Client client; + + private String location; + + public CarsClients() { + } + + public CarsClients(String location) { + this.location = location; + } + + public Car getCar() { + return car; + } + + public void setCar(Car car) { + this.car = car; + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + } + + public static class CarsClientsId { + private Car car; + + private Client client; + + public CarsClientsId() { + } + + public Car getCar() { + return car; + } + + public void setCar(Car car) { + this.car = car; + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + } +} From 4495a1f8d88656f2b2b6056f0c4194232a25b2e5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 4 Apr 2025 21:33:04 +0200 Subject: [PATCH 104/201] clean up a method --- ...SingleIdEntityLoaderProvidedQueryImpl.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java index b2ec82649..a2b3a384e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java @@ -7,17 +7,18 @@ import java.util.concurrent.CompletionStage; -import org.hibernate.FlushMode; import org.hibernate.LockOptions; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.Query; +import org.hibernate.query.QueryFlushMode; import org.hibernate.query.named.NamedQueryMemento; -import org.hibernate.query.spi.QueryImplementor; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleIdEntityLoader; import org.hibernate.reactive.query.ReactiveSelectionQuery; import jakarta.persistence.Parameter; +import org.hibernate.type.descriptor.java.JavaType; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -30,9 +31,9 @@ public class ReactiveSingleIdEntityLoaderProvidedQueryImpl implements Reactiv private static final CompletionStage EMPTY_ARRAY_STAGE = completedFuture( ArrayHelper.EMPTY_OBJECT_ARRAY ); private final EntityMappingType entityDescriptor; - private final NamedQueryMemento namedQueryMemento; + private final NamedQueryMemento namedQueryMemento; - public ReactiveSingleIdEntityLoaderProvidedQueryImpl(EntityMappingType entityDescriptor, NamedQueryMemento namedQueryMemento) { + public ReactiveSingleIdEntityLoaderProvidedQueryImpl(EntityMappingType entityDescriptor, NamedQueryMemento namedQueryMemento) { this.entityDescriptor = entityDescriptor; this.namedQueryMemento = namedQueryMemento; } @@ -42,17 +43,13 @@ public EntityMappingType getLoadable() { return entityDescriptor; } - @Override + @Override @SuppressWarnings("unchecked") public CompletionStage load(Object pkValue, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { - // noinspection unchecked - final QueryImplementor query = namedQueryMemento - .toQuery( session, entityDescriptor.getMappedJavaType().getJavaTypeClass() ); - - //noinspection unchecked + final JavaType mappedJavaType = (JavaType) entityDescriptor.getMappedJavaType(); + final Query query = namedQueryMemento.toQuery( session, mappedJavaType.getJavaTypeClass() ); query.setParameter( (Parameter) query.getParameters().iterator().next(), pkValue ); - query.setHibernateFlushMode( FlushMode.MANUAL ); - - return ( (ReactiveSelectionQuery) query ).reactiveUnique(); + query.setQueryFlushMode( QueryFlushMode.NO_FLUSH ); + return ( (ReactiveSelectionQuery) query ).reactiveUnique(); } @Override From 95ad44ca45ded244b9031dc4015089cd0064c204 Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Fri, 4 Apr 2025 15:38:04 +0200 Subject: [PATCH 105/201] [#2176] Create custom Vert.x contextual data storage Closes #2176 In Vert.x 5, the local context data map is deprecated. Hibernate Reactive can have its own Vert.x contextual data storage (requires creating a io.vertx.core.spi.context.storage.ContextLocal key). --- .../context/impl/ContextualDataStorage.java | 25 +++++++++++++++++++ .../reactive/context/impl/VertxContext.java | 18 ++++++++++--- .../io.vertx.core.spi.VertxServiceProvider | 1 + 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java create mode 100644 hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java new file mode 100644 index 000000000..295b92f4d --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java @@ -0,0 +1,25 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.context.impl; + +import java.util.concurrent.ConcurrentMap; + +import io.vertx.core.impl.VertxBuilder; +import io.vertx.core.spi.VertxServiceProvider; +import io.vertx.core.spi.context.storage.ContextLocal; + +/** + * SPI Implementation for {@link ContextLocal} storage. + */ +public class ContextualDataStorage implements VertxServiceProvider { + + @SuppressWarnings("rawtypes") + static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); + + @Override + public void init(VertxBuilder vertxBuilder) { + } +} 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 fef172e66..937dc3b1b 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 @@ -6,9 +6,12 @@ package org.hibernate.reactive.context.impl; import java.lang.invoke.MethodHandles; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import io.vertx.core.Vertx; import io.vertx.core.impl.ContextInternal; +import io.vertx.core.spi.context.storage.AccessMode; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.logging.impl.Log; @@ -39,7 +42,7 @@ public void put(Key key, T instance) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - context.localContextData().put( key, instance ); + VertxContext.contextualDataMap( context ).put( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -51,8 +54,7 @@ public void put(Key key, T instance) { public T get(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - @SuppressWarnings("unchecked") - T local = (T) context.localContextData().get( key ); + T local = VertxContext.contextualDataMap( context ).get( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -66,7 +68,7 @@ public T get(Key key) { public void remove(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - boolean removed = context.localContextData().remove( key ) != null; + boolean removed = contextualDataMap( context ).remove( key ) != null; if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed ); } else { @@ -93,4 +95,12 @@ public void execute(Runnable runnable) { } } + @SuppressWarnings({ "unchecked" }) + private static ConcurrentMap, T> contextualDataMap(ContextInternal vertxContext) { + return vertxContext.getLocal( + ContextualDataStorage.CONTEXTUAL_DATA_KEY, + AccessMode.CONCURRENT, + ConcurrentHashMap::new + ); + } } diff --git a/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider b/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider new file mode 100644 index 000000000..80ffcb31c --- /dev/null +++ b/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider @@ -0,0 +1 @@ +org.hibernate.reactive.context.impl.ContextualDataStorage \ No newline at end of file From 3d6cc0fb74c729788132ce35da948705ac03ab2d Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Thu, 27 Mar 2025 21:45:44 +0100 Subject: [PATCH 106/201] [#2166] Hibernate Reactive eager fetching the wrong record for ManyToOne GHQUARKUS-305 --- .../ReactiveEmbeddableInitializerImpl.java | 10 + .../ReactiveEntitySelectFetchInitializer.java | 10 + .../reactive/EmbeddedIdWithManyEagerTest.java | 228 ++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyEagerTest.java 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 69ae34a73..a1d4a890e 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 @@ -33,6 +33,16 @@ public ReactiveEmbeddableInitializerData( super( initializer, rowProcessingState ); } + @Override + public void setState(State state) { + super.setState( state ); + if ( State.UNINITIALIZED == state ) { + // reset instance to null as otherwise EmbeddableInitializerImpl#prepareCompositeInstance + // will never create a new instance after the "first row with a non-null instance" gets processed + setInstance( null ); + } + } + public EmbeddableMappingType.ConcreteEmbeddableType getConcreteEmbeddableType() { return super.concreteEmbeddableType; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java index b27c0cc93..8f848d01b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java @@ -56,6 +56,16 @@ public Object getEntityIdentifier() { public void setEntityIdentifier(Object entityIdentifier) { super.entityIdentifier = entityIdentifier; } + + @Override + public void setState(State state) { + super.setState( state ); + if ( State.UNINITIALIZED == state ) { + // reset instance to null as otherwise EmbeddableInitializerImpl#prepareCompositeInstance + // will never create a new instance after the "first row with a non-null instance" gets processed + setInstance( null ); + } + } } private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyEagerTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyEagerTest.java new file mode 100644 index 000000000..0ceeaef18 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EmbeddedIdWithManyEagerTest.java @@ -0,0 +1,228 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +public class EmbeddedIdWithManyEagerTest extends BaseReactiveTest { + + Fruit cherry; + Fruit apple; + Fruit banana; + + Flower sunflower; + Flower chrysanthemum; + Flower rose; + + @Override + protected Collection> annotatedEntities() { + return List.of( Flower.class, Fruit.class ); + } + + @BeforeEach + public void populateDb(VertxTestContext context) { + Seed seed1 = new Seed( 1 ); + rose = new Flower( seed1, "Rose" ); + + cherry = new Fruit( seed1, "Cherry" ); + cherry.addFriend( rose ); + + Seed seed2 = new Seed( 2 ); + sunflower = new Flower( seed2, "Sunflower" ); + + apple = new Fruit( seed2, "Apple" ); + apple.addFriend( sunflower ); + + Seed seed3 = new Seed( 3 ); + chrysanthemum = new Flower( seed3, "Chrysanthemum" ); + + banana = new Fruit( seed3, "Banana" ); + banana.addFriend( chrysanthemum ); + + test( + context, + getMutinySessionFactory().withTransaction( s -> s + .persistAll( cherry, rose, sunflower, apple, chrysanthemum, banana ) + ) + ); + } + + @Test + public void testFindWithEmbeddedId(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> s + .find( Flower.class, chrysanthemum.getSeed() ) + .invoke( flower -> assertThat( flower.getName() ).isEqualTo( chrysanthemum.getName() ) ) + ) + ); + } + + @Test + public void testSelectQueryWithEmbeddedId(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> s + .createSelectionQuery( "from Flower", Flower.class ) + .getResultList() + .invoke( list -> assertThat( list.stream().map( Flower::getName ) ) + .containsExactlyInAnyOrder( + sunflower.getName(), + chrysanthemum.getName(), + rose.getName() + ) + ) + ) + ); + } + + @Test + public void testSelectQueryWithEmbeddedIdAndEagerRelation(VertxTestContext context) { + test( + context, getMutinySessionFactory().withTransaction( s -> s + .createSelectionQuery( "from Flower f ", Flower.class ) + .getResultList() + .invoke( list -> assertThat( list.stream().map( Flower::getFriend ).map( Plant::getName ) ) + .containsExactlyInAnyOrder( + cherry.getName(), + apple.getName(), + banana.getName() + ) + ) + ) + ); + } + + @Embeddable + public static class Seed { + + @Column(nullable = false, updatable = false) + private Integer id; + + public Seed() { + } + + public Seed(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + + @MappedSuperclass + public static abstract class Plant { + + @EmbeddedId + private Seed seed; + + @Column(length = 40, unique = true) + private String name; + + protected Plant() { + } + + protected Plant(Seed seed, String name) { + this.seed = seed; + this.name = name; + } + + public Seed getSeed() { + return seed; + } + + public void setSeed(Seed seed) { + this.seed = seed; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "Fruit") + @Table(name = "known_fruits") + public static class Fruit extends Plant { + + @OneToMany(mappedBy = "friend", fetch = FetchType.LAZY) + private List friends = new ArrayList<>(); + + public Fruit() { + } + + public Fruit(Seed seed, String name) { + super( seed, name ); + } + + public void addFriend(Flower flower) { + this.friends.add( flower ); + flower.friend = this; + } + + public List getFriends() { + return friends; + } + + @Override + public String toString() { + return "Fruit{" + getSeed().getId() + "," + getName() + '}'; + } + + } + + @Entity(name = "Flower") + @Table(name = "known_flowers") + public static class Flower extends Plant { + + @ManyToOne(fetch = FetchType.EAGER, optional = false) + @JoinColumn(name = "friend", referencedColumnName = "id", nullable = false) + private Fruit friend; + + public Flower() { + } + + public Flower(Seed seed, String name) { + super( seed, name ); + } + + public Fruit getFriend() { + return friend; + } + + @Override + public String toString() { + return "Flower{" + getSeed().getId() + "," + getName() + '}'; + } + + } + +} From 45010e1f0f0012561d743fdf36c71f39444a7478 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Mon, 7 Apr 2025 15:39:07 +0200 Subject: [PATCH 107/201] [#2187] Use adjustToDefaultPrecision in UTCNormalizedZonedTest Replace `DateTimeUtils.roundToDefaultPrecision` with `DateTimeUtils.adjustToDefaultPrecision` This should prevent the failures we sometimes have on CI. --- .../reactive/timezones/UTCNormalizedZonedTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/UTCNormalizedZonedTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/UTCNormalizedZonedTest.java index 9943a0f97..474f5df7b 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/UTCNormalizedZonedTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/timezones/UTCNormalizedZonedTest.java @@ -29,7 +29,7 @@ import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; import static org.hibernate.reactive.testing.ReactiveAssertions.assertWithTruncationThat; -import static org.hibernate.type.descriptor.DateTimeUtils.roundToDefaultPrecision; +import static org.hibernate.type.descriptor.DateTimeUtils.adjustToDefaultPrecision; @Timeout(value = 10, timeUnit = MINUTES) @DisabledFor(value = DB2, reason = "Exception: IllegalStateException: Needed to have 6 in buffer but only had 0") @@ -60,10 +60,10 @@ public void test(VertxTestContext context) { .thenCompose( zid -> openSession() .thenCompose( s -> s.find( Zoned.class, zid ) .thenAccept( z -> { - assertWithTruncationThat( roundToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); - assertWithTruncationThat( roundToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) - .isEqualTo( roundToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.zonedDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowZoned.toInstant(), getDialect() ) ); + assertWithTruncationThat( adjustToDefaultPrecision( z.offsetDateTime.toInstant(), getDialect() ) ) + .isEqualTo( adjustToDefaultPrecision( nowOffset.toInstant(), getDialect() ) ); assertThat( z.offsetDateTime.getOffset() ).isEqualTo( ZoneId.of( "Z" ) ); assertThat( z.zonedDateTime.getZone() ).isEqualTo( ZoneOffset.ofHours( 0 ) ); } ) From b9fa84fa36b9cd2dcb5bec4c41ec45f8dbc70bc9 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:01:41 +0200 Subject: [PATCH 108/201] [#2190] Upgrade MariaDB to 11.7.2 --- .../java/org/hibernate/reactive/containers/MariaDatabase.java | 2 +- podman.md | 2 +- tooling/jbang/MariaDBReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 ba8e12f42..16a5f97ff 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 @@ -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.4.2" ) ) + public static final MariaDBContainer maria = new MariaDBContainer<>( imageName( "mariadb", "11.7.2" ) ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/podman.md b/podman.md index 3a35a715f..f9db3e091 100644 --- a/podman.md +++ b/podman.md @@ -67,7 +67,7 @@ and schema to run the tests: ``` podman run --rm --name HibernateTestingMariaDB \ -e MYSQL_ROOT_PASSWORD=hreact -e MYSQL_DATABASE=hreact -e MYSQL_USER=hreact -e MYSQL_PASSWORD=hreact \ - -p 3306:3306 docker.io/mariadb:11.4.2 + -p 3306:3306 docker.io/mariadb:11.7.2 ``` When the database has started, you can run the tests on MariaDB with: diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index f6b528925..42195d326 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -69,7 +69,7 @@ public class {baseName} { } @ClassRule - public final static MariaDBContainer database = new MariaDBContainer<>( imageName( "docker.io", "mariadb", "11.4.2" ) ); + public final static MariaDBContainer database = new MariaDBContainer<>( imageName( "docker.io", "mariadb", "11.7.2" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index c1094f891..5cc278087 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -231,7 +231,7 @@ enum Database { POSTGRESQL( () -> new PostgreSQLContainer( "postgres:17.4" ) ), MYSQL( () -> new MySQLContainer( "mysql:8.4.0" ) ), DB2( () -> new Db2Container( "docker.io/icr.io/db2_community/db2:12.1.0.0" ).acceptLicense() ), - MARIADB( () -> new MariaDBContainer( "mariadb:11.4.2" ) ), + MARIADB( () -> new MariaDBContainer( "mariadb:11.7.2" ) ), COCKROACHDB( () -> new CockroachContainer( "cockroachdb/cockroach:v24.1.0" ) ); private final Supplier> containerSupplier; From 741af5e89bf547e008f0f273afdf49c82fcfc493 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:07:40 +0200 Subject: [PATCH 109/201] [#2190] Upgrade MySQL to 9.2.0 --- .github/workflows/build.yml | 2 +- README.md | 2 +- .../java/org/hibernate/reactive/containers/MySQLDatabase.java | 2 +- podman.md | 2 +- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c85d9281d..00a2518dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: # Label used to access the service container mysql: # Docker Hub image - image: mysql:8.4.0 + image: mysql:9.2.0 env: MYSQL_ROOT_PASSWORD: hreact MYSQL_DATABASE: hreact diff --git a/README.md b/README.md index 72b3a7e63..e9b179491 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Hibernate Reactive has been tested with: - Java 11, 17, 21, 23 - PostgreSQL 16 -- MySQL 8 +- MySQL 9 - MariaDB 11 - Db2 11 - CockroachDB v24 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 e753da93d..e0aa9ef3a 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<>( imageName( "mysql", "8.4.0") ) + public static final MySQLContainer mysql = new MySQLContainer<>( imageName( "mysql", "9.2.0") ) .withUsername( DatabaseConfiguration.USERNAME ) .withPassword( DatabaseConfiguration.PASSWORD ) .withDatabaseName( DatabaseConfiguration.DB_NAME ) diff --git a/podman.md b/podman.md index f9db3e091..ea9280709 100644 --- a/podman.md +++ b/podman.md @@ -94,7 +94,7 @@ and schema to run the tests: ``` podman run --rm --name HibernateTestingMySQL \ -e MYSQL_ROOT_PASSWORD=hreact -e MYSQL_DATABASE=hreact -e MYSQL_USER=hreact -e MYSQL_PASSWORD=hreact \ - -p 3306:3306 docker.io/mysql:8.4.0 + -p 3306:3306 docker.io/mysql:9.2.0 ``` When the database has started, you can run the tests on MySQL with: diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index f8307495d..e568bd6b0 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", "8.4.0" ) ); + public final static MySQLContainer database = new MySQLContainer<>( imageName( "docker.io", "mysql", "9.2.0" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 5cc278087..9ae2cad49 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.4" ) ), - MYSQL( () -> new MySQLContainer( "mysql:8.4.0" ) ), + MYSQL( () -> new MySQLContainer( "mysql:9.2.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.1.0" ) ); From 59bff98400b6755cb589dad9e948ab91e28184dc Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:12:14 +0200 Subject: [PATCH 110/201] [#2190] Upgrade PostgreSQL to 17.4 The version wasn't aligned everywhere --- .github/workflows/build.yml | 2 +- podman.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00a2518dc..2718f729f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,7 +66,7 @@ jobs: - 3306:3306 postgres: # Docker Hub image - image: postgres:16.3 + image: postgres:17.4 env: POSTGRES_DB: hreact POSTGRES_USER: hreact diff --git a/podman.md b/podman.md index ea9280709..b74425d8d 100644 --- a/podman.md +++ b/podman.md @@ -14,13 +14,13 @@ If you replace `podman` with `sudo docker`, they will also work with [Docker][do Example: ``` -podman run --rm --name HibernateTestingPGSQL postgres:14.0 +podman run --rm --name HibernateTestingPGSQL postgres:17.4 ``` becomes for Docker: ``` -sudo docker run --rm --name HibernateTestingPGSQL postgres:14.0 +sudo docker run --rm --name HibernateTestingPGSQL postgres:17.4 ``` --- From 9e917ecff0f78634baf39f522d7f058aa154b1e5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:35:57 +0200 Subject: [PATCH 111/201] [#2190] Upgrade CockroachDB to 24.1.15 --- .../org/hibernate/reactive/containers/CockroachDBDatabase.java | 2 +- podman.md | 2 +- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 60dd1b997..3ec042a38 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 @@ -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.1.0" ) ) + public static final CockroachContainer cockroachDb = new CockroachContainer( imageName( "cockroachdb/cockroach", "v24.1.15" ) ) // 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/podman.md b/podman.md index b74425d8d..5d7d9476b 100644 --- a/podman.md +++ b/podman.md @@ -121,7 +121,7 @@ configured to run the tests: ``` podman run --rm --name=HibernateTestingCockroachDB \ --hostname=roachrr1 -p 26257:26257 -p 8080:8080 \ - docker.io/cockroachdb/cockroach:v24.1.0 start-single-node --insecure + docker.io/cockroachdb/cockroach:v24.1.15 start-single-node --insecure ``` Some of tests needs temporary tables and because this is an experimental feature in diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 9b713c8ea..a2437ebfe 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -70,7 +70,7 @@ public class {baseName} { } @ClassRule - public final static CockroachContainer database = new CockroachContainer( imageName( "cockroachdb", "cockroach", "v24.1.0" ) ); + public final static CockroachContainer database = new CockroachContainer( imageName( "cockroachdb", "cockroach", "v24.1.15" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 9ae2cad49..3677fe873 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -232,7 +232,7 @@ enum Database { MYSQL( () -> new MySQLContainer( "mysql:9.2.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.1.0" ) ); + COCKROACHDB( () -> new CockroachContainer( "cockroachdb/cockroach:v24.1.15" ) ); private final Supplier> containerSupplier; From a5c574038949527ed1d92c090640aa80fbffdd06 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:54:11 +0200 Subject: [PATCH 112/201] [#2190] Update Db2 version in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9b179491..01996c250 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Hibernate Reactive has been tested with: - PostgreSQL 16 - MySQL 9 - MariaDB 11 -- Db2 11 +- Db2 12 - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 From 770b58fc5af7bad8340e0e133387fadfd6f65e07 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 14:55:00 +0200 Subject: [PATCH 113/201] [#2190] Update JDKs in the README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01996c250..3dcba2ae7 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Learn more at . Hibernate Reactive has been tested with: -- Java 11, 17, 21, 23 +- Java 17, 21, 24 - PostgreSQL 16 - MySQL 9 - MariaDB 11 From f972da439c6ef1d8bc3ef6797ebf4541e095ca36 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 8 Apr 2025 15:03:24 +0200 Subject: [PATCH 114/201] [#2192] Upgrade Vert.x SQL client to 4.5.14 --- README.md | 10 +++++----- build.gradle | 2 +- gradle.properties | 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, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 3dcba2ae7..c01979d65 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.Beta4 -- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.13 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.13 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.13 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.13 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.13 +- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.14 +- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.14 +- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.14 +- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.14 +- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.14 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/build.gradle b/build.gradle index 808127db4..c7ad6918c 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.13' + vertxSqlClientVersion = '4.5.14' } testcontainersVersion = '1.20.6' diff --git a/gradle.properties b/gradle.properties index be7612bc5..3576439d6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,9 +47,9 @@ hibernateOrmVersion = 7.0.0.Beta5 #skipOrmVersionParsing = true # Override default Vert.x Sql client version -#vertxSqlClientVersion = 4.5.13-SNAPSHOT +#vertxSqlClientVersion = 4.5.14-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 4.5.13 -#vertxWebtClientVersion = 4.5.13 +#vertxWebVersion = 4.5.14 +#vertxWebtClientVersion = 4.5.14 diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index a2437ebfe..81b79ee5d 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.13} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index a13d21b6d..6aeeae23a 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.13} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-db2-client:$\{vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 7f6d383fd..893b5c80f 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -6,9 +6,9 @@ */ //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.13} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.13} -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.14} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.14} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //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 42195d326..682238370 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.13} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index e568bd6b0..b252459d1 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.13} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-mysql-client:$\{vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 682ac502b..04a0272e8 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.13} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.13} +//DEPS io.vertx:vertx-pg-client:$\{vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:$\{hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 3677fe873..c6b116321 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.13} +//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.14} //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.13} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.13} -//DEPS io.vertx:vertx-unit:${vertx.version:4.5.13} +//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.14} +//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.14} +//DEPS io.vertx:vertx-unit:${vertx.version:4.5.14} //DEPS org.hibernate.reactive:hibernate-reactive-core:${hibernate-reactive.version:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 From 2a5cbb1602a3431977879769b2410de6707f0778 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Tue, 8 Apr 2025 15:52:24 +0000 Subject: [PATCH 115/201] Update project version to : `3.0.0.Beta3` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..ebfc91836 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.Beta3 \ No newline at end of file From 8f78d1ed845fd929f45ecc5a7c354aa9e655d4bb Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Tue, 8 Apr 2025 15:53:19 +0000 Subject: [PATCH 116/201] Update project version to : `3.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 ebfc91836..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.Beta3 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From 2be13569f85570902eafccc9bcb98648abc36138 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Sat, 12 Apr 2025 08:24:15 +0200 Subject: [PATCH 117/201] [#2203] Fix error chaining stages in ReactiveStatelessWithBatchTest Ooops... --- .../hibernate/reactive/ReactiveStatelessWithBatchTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java index a30cc1364..38e386bf2 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveStatelessWithBatchTest.java @@ -364,7 +364,7 @@ public void testStageBatchingUpdate(VertxTestContext context) { .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) .getResultList() - .thenApply( pigs -> { + .thenCompose( pigs -> { pigs.get( 0 ).setName( "One updated" ); pigs.get( 1 ).setName( "Two updated" ); return s.update( 10, pigs.toArray() ); @@ -383,7 +383,7 @@ public void testStageBatchingUpdateMultiple(VertxTestContext context) { .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) .getResultList() - .thenApply( pigs -> { + .thenCompose( pigs -> { pigs.get( 0 ).setName( "One updated" ); pigs.get( 1 ).setName( "Two updated" ); return s.updateMultiple( pigs ); @@ -402,7 +402,7 @@ public void testStageBatchingUpdateNoBatchSizeParameter(VertxTestContext context .thenCompose( v -> getSessionFactory().withStatelessTransaction( s -> s .createQuery( "from GuineaPig p order by p.id", GuineaPig.class ) .getResultList() - .thenApply( pigs -> { + .thenCompose( pigs -> { pigs.get( 0 ).setName( "One updated" ); pigs.get( 1 ).setName( "Two updated" ); return s.update( pigs.get( 0 ), pigs.get( 1 ) ); From feb4c2726740247d3da7abbe279d3ffa81f1ab2e Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Fri, 11 Apr 2025 13:08:14 +0200 Subject: [PATCH 118/201] Make CONTEXTUAL_DATA_KEY constant It shouldn't be allowed to change it. --- .../hibernate/reactive/context/impl/ContextualDataStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java index 295b92f4d..39cb8fa10 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java @@ -17,7 +17,7 @@ public class ContextualDataStorage implements VertxServiceProvider { @SuppressWarnings("rawtypes") - static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); + final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); @Override public void init(VertxBuilder vertxBuilder) { From 402ce62f5632ed6203cf411a83589e43ea93747f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 21 Apr 2025 20:47:13 +0200 Subject: [PATCH 119/201] [#2209] fix cast to ReactiveSession that broke StatelesSession --- .../ReactiveEntitySelectFetchInitializer.java | 4 ++-- .../reactive/MutinyStatelessSessionTest.java | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java index 8f848d01b..6d5ea5418 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/graph/entity/internal/ReactiveEntitySelectFetchInitializer.java @@ -23,7 +23,7 @@ import org.hibernate.proxy.LazyInitializer; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.reactive.session.ReactiveSession; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -154,7 +154,7 @@ else if ( data.getInstance() == null ) { data.setState( State.INITIALIZED ); final String entityName = concreteDescriptor.getEntityName(); - return ( (ReactiveSession) session ).reactiveInternalLoad( + return ( (ReactiveQueryProducer) session ).reactiveInternalLoad( entityName, data.getEntityIdentifier(), true, diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java index cde496e57..f68bb8941 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java @@ -141,22 +141,26 @@ public void testStatelessSessionGetMultiple(VertxTestContext context) { @Test public void testStatelessSessionCriteria(VertxTestContext context) { GuineaPig pig = new GuineaPig( "Aloi" ); + GuineaPig mate = new GuineaPig("Aloina"); + pig.mate = mate; CriteriaBuilder cb = getSessionFactory().getCriteriaBuilder(); CriteriaQuery query = cb.createQuery( GuineaPig.class ); Root gp = query.from( GuineaPig.class ); query.where( cb.equal( gp.get( "name" ), cb.parameter( String.class, "n" ) ) ); + query.orderBy( cb.asc( gp.get( "name" ) ) ); CriteriaUpdate update = cb.createCriteriaUpdate( GuineaPig.class ); - update.from( GuineaPig.class ); + Root root = update.from(GuineaPig.class); update.set( "name", "Bob" ); + update.where( root.get( "mate" ).isNotNull() ); CriteriaDelete delete = cb.createCriteriaDelete( GuineaPig.class ); delete.from( GuineaPig.class ); test( context, getMutinySessionFactory().openStatelessSession() - .chain( ss -> ss.insert( pig ) + .chain( ss -> ss.insertMultiple( List.of(mate, pig) ) .chain( v -> ss.createQuery( query ) .setParameter( "n", pig.name ) .getResultList() ) @@ -168,7 +172,7 @@ public void testStatelessSessionCriteria(VertxTestContext context) { .chain( v -> ss.createQuery( update ).executeUpdate() ) .invoke( rows -> assertEquals( 1, rows ) ) .chain( v -> ss.createQuery( delete ).executeUpdate() ) - .invoke( rows -> assertEquals( 1, rows ) ) + .invoke( rows -> assertEquals( 2, rows ) ) .chain( v -> ss.close() ) ) ); } @@ -223,6 +227,9 @@ public static class GuineaPig { @Version private int version; + @ManyToOne + private GuineaPig mate; + public GuineaPig() { } From e125b9f610c7a7a127caac62775a51b663457bfa Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 21 Apr 2025 20:53:07 +0200 Subject: [PATCH 120/201] [#2209] fix more unnecessary casts to ReactiveSession at least some of these are not actually bugs, but some others look sus --- .../event/impl/DefaultReactiveDeleteEventListener.java | 3 ++- .../reactive/event/impl/DefaultReactiveLockEventListener.java | 3 ++- .../event/impl/DefaultReactiveRefreshEventListener.java | 4 ++-- .../internal/cte/ReactiveAbstractCteMutationHandler.java | 4 ++-- .../sqm/mutation/internal/cte/ReactiveCteInsertHandler.java | 4 ++-- .../graph/entity/internal/ReactiveEntityInitializerImpl.java | 4 ++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java index f490d5366..1dbf6db02 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveDeleteEventListener.java @@ -47,6 +47,7 @@ import org.hibernate.reactive.event.ReactiveDeleteEventListener; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; @@ -198,7 +199,7 @@ private CompletionStage fetchAndDelete(DeleteEvent event, DeleteContext tr } //Object entity = persistenceContext.unproxyAndReassociate( event.getObject() ); - return ( (ReactiveSession) source ) + return ( (ReactiveQueryProducer) source ) .reactiveFetch( objectEvent, true ) .thenCompose( entity -> delete( event, transientEntities, entity ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java index 2df247665..ffd0fcfde 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java @@ -33,6 +33,7 @@ import org.hibernate.reactive.event.ReactiveLockEventListener; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; import static java.lang.invoke.MethodHandles.lookup; @@ -80,7 +81,7 @@ public CompletionStage reactiveOnLock(LockEvent event) throws HibernateExc //TODO: if object was an uninitialized proxy, this is inefficient, // resulting in two SQL selects - return ( (ReactiveSession) source ).reactiveFetch( event.getObject(), true ) + return ( (ReactiveQueryProducer) source ).reactiveFetch( event.getObject(), true ) .thenCompose( entity -> reactiveOnLock( event, entity ) ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveRefreshEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveRefreshEventListener.java index f5e5eb1b4..ba3edda4a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveRefreshEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveRefreshEventListener.java @@ -37,7 +37,7 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.entity.impl.ReactiveAbstractEntityPersister; -import org.hibernate.reactive.session.ReactiveSession; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; @@ -84,7 +84,7 @@ public CompletionStage reactiveOnRefresh(RefreshEvent event, RefreshContex // Hibernate Reactive doesn't support detached instances in refresh() throw new IllegalArgumentException( "Unmanaged instance passed to refresh()" ); } - return ( (ReactiveSession) source ) + return ( (ReactiveQueryProducer) source ) .reactiveFetch( event.getObject(), true ) .thenCompose( entity -> reactiveOnRefresh( event, refreshedAlready, entity ) ); } 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 20d0e31c5..806b75718 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 @@ -30,8 +30,8 @@ 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.session.ReactiveSession; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.sql.ast.SqlAstTranslator; @@ -176,7 +176,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter StandardReactiveSelectExecutor.INSTANCE.list( select, 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 1e063f287..603563e0f 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 @@ -41,10 +41,10 @@ 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.session.ReactiveSession; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; import org.hibernate.spi.NavigablePath; @@ -554,7 +554,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter StandardReactiveSelectExecutor.INSTANCE.list( select, 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 8601d434f..a7342275d 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 @@ -24,7 +24,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.map.MapProxy; -import org.hibernate.reactive.session.ReactiveSession; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; import org.hibernate.reactive.sql.results.graph.ReactiveInitializer; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -532,7 +532,7 @@ protected CompletionStage reactiveResolveEntityInstance(ReactiveEntityIn // If this initializer owns the entity, we have to remove the entity holder, // because the subsequent loading process will claim the entity session.getPersistenceContextInternal().removeEntityHolder( data.getEntityKey() ); - return ( (ReactiveSession) session ).reactiveInternalLoad( + return ( (ReactiveQueryProducer) session ).reactiveInternalLoad( data.getConcreteDescriptor().getEntityName(), data.getEntityKey().getIdentifier(), true, From c4702b6ec2d73389b1e3885134c478bf0049d5e4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 21 Apr 2025 20:56:55 +0200 Subject: [PATCH 121/201] [#2209] fix test that failed only on Maria --- .../hibernate/reactive/MutinyStatelessSessionTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java index f68bb8941..8314179c9 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MutinyStatelessSessionTest.java @@ -152,12 +152,13 @@ public void testStatelessSessionCriteria(VertxTestContext context) { query.orderBy( cb.asc( gp.get( "name" ) ) ); CriteriaUpdate update = cb.createCriteriaUpdate( GuineaPig.class ); - Root root = update.from(GuineaPig.class); + Root updatedPig = update.from(GuineaPig.class); update.set( "name", "Bob" ); - update.where( root.get( "mate" ).isNotNull() ); + update.where( updatedPig.get( "mate" ).isNotNull() ); CriteriaDelete delete = cb.createCriteriaDelete( GuineaPig.class ); - delete.from( GuineaPig.class ); + Root deletedPig = delete.from( GuineaPig.class ); + delete.where( deletedPig.get( "mate" ).isNotNull() ); test( context, getMutinySessionFactory().openStatelessSession() .chain( ss -> ss.insertMultiple( List.of(mate, pig) ) @@ -172,7 +173,7 @@ public void testStatelessSessionCriteria(VertxTestContext context) { .chain( v -> ss.createQuery( update ).executeUpdate() ) .invoke( rows -> assertEquals( 1, rows ) ) .chain( v -> ss.createQuery( delete ).executeUpdate() ) - .invoke( rows -> assertEquals( 2, rows ) ) + .invoke( rows -> assertEquals( 1, rows ) ) .chain( v -> ss.close() ) ) ); } From 5adf1234f4b5dfb1c93cceb3d8fd07c64554b673 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 23 Apr 2025 11:16:29 +0200 Subject: [PATCH 122/201] [#2212] add getCriteriaBuilder() convenience method to session types since JPA has this, and a uniform API makes things easier for Hibernate Processor --- .../org/hibernate/reactive/mutiny/Mutiny.java | 18 ++++++++++++++++++ .../org/hibernate/reactive/stage/Stage.java | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 59edbb875..0f231ff63 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1525,6 +1525,15 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. + * + * @since 3 + */ + default CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } } /** @@ -2104,6 +2113,15 @@ default Uni refresh(Object entity, LockModeType lockModeType) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. + * + * @since 3 + */ + default CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } } /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index b490f2fb3..601b17c11 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1557,6 +1557,14 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); + /** + * Convenience method to obtain the {@link CriteriaBuilder}. + * + * @since 3 + */ + default CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } } /** @@ -2155,6 +2163,15 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. + * + * @since 3 + */ + default CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } } /** From 02db560b8edb2795828baca581b753d200584ff7 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 24 Apr 2025 10:42:04 +0200 Subject: [PATCH 123/201] [#2207] Provide access to the contextual data map We store the session in the Vert.x local context. But, Quarkus needs a way to access the same context when using Panache. See quarkus issue: https://github.com/quarkusio/quarkus/issues/47314 --- .../context/impl/ContextualDataStorage.java | 17 ++++++++++++++++- .../reactive/context/impl/VertxContext.java | 18 ++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java index 39cb8fa10..bcbfdf5f0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java @@ -5,6 +5,12 @@ */ package org.hibernate.reactive.context.impl; +import org.hibernate.reactive.context.Context; + +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.spi.context.storage.AccessMode; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import io.vertx.core.impl.VertxBuilder; @@ -17,9 +23,18 @@ public class ContextualDataStorage implements VertxServiceProvider { @SuppressWarnings("rawtypes") - final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); + private final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); @Override public void init(VertxBuilder vertxBuilder) { } + + @SuppressWarnings({ "unchecked" }) + public static Map, T> contextualDataMap(ContextInternal vertxContext) { + return vertxContext.getLocal( + ContextualDataStorage.CONTEXTUAL_DATA_KEY, + AccessMode.CONCURRENT, + ConcurrentHashMap::new + ); + } } 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 937dc3b1b..24975a87f 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 @@ -6,12 +6,9 @@ package org.hibernate.reactive.context.impl; import java.lang.invoke.MethodHandles; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import io.vertx.core.Vertx; import io.vertx.core.impl.ContextInternal; -import io.vertx.core.spi.context.storage.AccessMode; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.logging.impl.Log; @@ -20,6 +17,8 @@ import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; +import static org.hibernate.reactive.context.impl.ContextualDataStorage.contextualDataMap; + /** * An adaptor for the Vert.x {@link io.vertx.core.Context}. * @@ -42,7 +41,7 @@ public void put(Key key, T instance) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - VertxContext.contextualDataMap( context ).put( key, instance ); + ContextualDataStorage.contextualDataMap( context ).put( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -54,7 +53,7 @@ public void put(Key key, T instance) { public T get(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - T local = VertxContext.contextualDataMap( context ).get( key ); + T local = ContextualDataStorage.contextualDataMap( context ).get( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -94,13 +93,4 @@ public void execute(Runnable runnable) { runnable.run(); } } - - @SuppressWarnings({ "unchecked" }) - private static ConcurrentMap, T> contextualDataMap(ContextInternal vertxContext) { - return vertxContext.getLocal( - ContextualDataStorage.CONTEXTUAL_DATA_KEY, - AccessMode.CONCURRENT, - ConcurrentHashMap::new - ); - } } From 8b44ca04e07b587edbbefd18ee9678f3631b7abe Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 25 Apr 2025 10:45:32 +0200 Subject: [PATCH 124/201] [#2220] Upgrade Hibernate ORM to 7.0.0.CR1 --- README.md | 2 +- gradle.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c01979d65..509706206 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.Beta4 +- [Hibernate ORM][] 7.0.0.CR1 - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.14 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.14 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.14 diff --git a/gradle.properties b/gradle.properties index 3576439d6..c402bb33b 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.0.Beta5 +hibernateOrmVersion = 7.0.0.CR1 # 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.Beta4 +#hibernateOrmGradlePluginVersion = 7.0.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 From 235ba568782ca487a9349c6ddea78c3286caa0d6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 5 Apr 2025 00:44:57 +0200 Subject: [PATCH 125/201] adapt to changes in core to use SharedSessionContractImplementor instead of EventSource --- .../ast/internal/ReactiveAbstractMultiIdEntityLoader.java | 8 ++++---- .../internal/ReactiveMultiIdEntityLoaderArrayParam.java | 8 ++++---- .../ast/internal/ReactiveMultiIdEntityLoaderStandard.java | 5 ++--- .../loader/ast/spi/ReactiveMultiIdEntityLoader.java | 6 +++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java index 527d59326..71ae6e7e7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java @@ -10,7 +10,7 @@ import java.util.concurrent.CompletionStage; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.event.spi.EventSource; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -50,7 +50,7 @@ public EntityMappingType getLoadable() { } @Override - public final CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) { + public final CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session) { Objects.requireNonNull( ids ); return loadOptions.isOrderReturnEnabled() @@ -58,8 +58,8 @@ public final CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptio : performUnorderedMultiLoad( ids, loadOptions, session ); } - protected abstract CompletionStage> performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session); + protected abstract CompletionStage> performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session); - protected abstract CompletionStage> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session); + protected abstract CompletionStage> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java index 8239a6e15..612e5ad16 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java @@ -18,8 +18,8 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; -import org.hibernate.event.spi.EventSource; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.internal.MultiIdEntityLoaderArrayParam; import org.hibernate.loader.ast.internal.MultiKeyLoadLogging; @@ -82,7 +82,7 @@ public BasicEntityIdentifierMapping getIdentifierMapping() { protected CompletionStage> performOrderedMultiLoad( K[] ids, MultiIdLoadOptions loadOptions, - EventSource session) { + SharedSessionContractImplementor session) { if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) { MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( "ReactiveMultiIdEntityLoaderArrayParam#performOrderedMultiLoad - %s", @@ -227,7 +227,7 @@ protected CompletionStage> performOrderedMultiLoad( protected CompletionStage> performUnorderedMultiLoad( K[] ids, MultiIdLoadOptions loadOptions, - EventSource session) { + SharedSessionContractImplementor session) { if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) { MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( "ReactiveMultiIdEntityLoaderArrayParam#performUnorderedMultiLoad - %s", @@ -305,7 +305,7 @@ protected final K[] processResolvableEntities( MultiIdEntityLoaderArrayParam.ResolutionConsumer resolutionConsumer, MultiIdLoadOptions loadOptions, LockOptions lockOptions, - EventSource session) { + SharedSessionContractImplementor session) { if ( !loadOptions.isSessionCheckingEnabled() && !loadOptions.isSecondLevelCacheCheckingEnabled() ) { // we'll load all of them from the database diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java index 92f4e996a..743078403 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java @@ -24,7 +24,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; -import org.hibernate.event.spi.EventSource; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; @@ -75,7 +74,7 @@ public ReactiveMultiIdEntityLoaderStandard( protected CompletionStage> performOrderedMultiLoad( Object[] ids, MultiIdLoadOptions loadOptions, - EventSource session) { + SharedSessionContractImplementor session) { if ( LOG.isTraceEnabled() ) { LOG.tracef( "#performOrderedMultiLoad(`%s`, ..)", getEntityDescriptor().getEntityName() ); } @@ -289,7 +288,7 @@ private static List singletonList(Object loaded) { protected CompletionStage> performUnorderedMultiLoad( Object[] ids, MultiIdLoadOptions loadOptions, - EventSource session) { + SharedSessionContractImplementor session) { assert !loadOptions.isOrderReturnEnabled(); assert ids != null; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveMultiIdEntityLoader.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveMultiIdEntityLoader.java index 47050b139..e0236e361 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveMultiIdEntityLoader.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/spi/ReactiveMultiIdEntityLoader.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.concurrent.CompletionStage; -import org.hibernate.event.spi.EventSource; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.reactive.logging.impl.Log; @@ -22,9 +22,9 @@ public interface ReactiveMultiIdEntityLoader extends MultiIdEntityLoader { @Override - default List load(K[] ids, MultiIdLoadOptions options, EventSource session) { + default List load(K[] ids, MultiIdLoadOptions options, SharedSessionContractImplementor session) { throw make( Log.class, lookup() ).nonReactiveMethodCall( "reactiveLoad" ); } - CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptions options, EventSource session); + CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptions options, SharedSessionContractImplementor session); } From 9b6289dcb1ca36b365a848cd3607e9df9afee14c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 5 Apr 2025 08:39:18 +0200 Subject: [PATCH 126/201] use the usual entity loader from SS.getMultiple() now that refactoring to core haa made this possible --- .../ReactiveAbstractPersisterDelegate.java | 3 +- .../entity/impl/ReactiveEntityPersister.java | 3 +- ...ReactiveJoinedSubclassEntityPersister.java | 3 +- .../ReactiveSingleTableEntityPersister.java | 3 +- .../ReactiveUnionSubclassEntityPersister.java | 3 +- .../impl/ReactiveStatelessSessionImpl.java | 28 ++++++------------- 6 files changed, 14 insertions(+), 29 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java index 9a31d2bc8..966f850e4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java @@ -20,7 +20,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.id.IdentityGenerator; @@ -126,7 +125,7 @@ public DomainResult createDomainResult( /** * @see org.hibernate.persister.entity.AbstractEntityPersister#multiLoad(Object[], EventSource, MultiIdLoadOptions)` */ - public CompletionStage> multiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) { + public CompletionStage> multiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) { return multiIdEntityLoader.reactiveLoad( ids, loadOptions, session ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index 0a36d048d..f59b27e26 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -12,7 +12,6 @@ import org.hibernate.LockOptions; import org.hibernate.bytecode.BytecodeLogging; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.persister.entity.EntityPersister; @@ -92,7 +91,7 @@ CompletionStage reactiveLock( CompletionStage> reactiveMultiLoad( K[] ids, - EventSource session, + SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions); CompletionStage reactiveLoad( 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 b87ba6910..d717c7778 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 @@ -17,7 +17,6 @@ import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; @@ -239,7 +238,7 @@ public CompletionStage mergeReactive( } @Override - public CompletionStage> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) { + public CompletionStage> reactiveMultiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) { return reactiveDelegate.multiLoad( ids, session, loadOptions ); } 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 381a83b78..2dc1bdc13 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 @@ -18,7 +18,6 @@ import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.generator.values.GeneratedValuesMutationDelegate; @@ -397,7 +396,7 @@ public CompletionStage mergeReactive( } @Override - public CompletionStage> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) { + public CompletionStage> reactiveMultiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) { return reactiveDelegate.multiLoad( ids, session, loadOptions ); } 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 cb7995acb..d1c112ab7 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 @@ -19,7 +19,6 @@ import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.id.IdentityGenerator; @@ -365,7 +364,7 @@ public CompletionStage mergeReactive( } @Override - public CompletionStage> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) { + public CompletionStage> reactiveMultiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) { return reactiveDelegate.multiLoad( ids, session, loadOptions ); } 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 2d3efe4f5..c70972417 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 @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.session.impl; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -50,8 +49,6 @@ import org.hibernate.query.IllegalMutationQueryException; import org.hibernate.query.UnknownNamedQueryException; import org.hibernate.query.criteria.JpaCriteriaInsert; -import org.hibernate.query.criteria.JpaCriteriaQuery; -import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.HqlInterpretation; @@ -223,24 +220,17 @@ public CompletionStage> reactiveGet(Class entityClass, Object... } } - final EntityPersister persister = getEntityPersister( entityClass.getName() ); + Object[] sids = new Object[ids.length]; + System.arraycopy( ids, 0, sids, 0, ids.length ); - final JpaCriteriaQuery query = getCriteriaBuilder().createQuery(entityClass); - final JpaRoot from = query.from(entityClass); - query.where( from.get( persister.getIdentifierPropertyName() ).in(ids) ); - return createReactiveQuery(query).getReactiveResultList() - .thenApply( resultList -> { - final List idList = new ArrayList<>( resultList.size() ); - for (T entity : resultList) { - idList.add( persister.getIdentifier(entity, this) ); - } - final List list = new ArrayList<>( ids.length ); - for (Object id : ids) { - final int pos = idList.indexOf(id); - list.add( pos < 0 ? null : resultList.get(pos) ); + return getEntityPersister( entityClass.getName() ) + .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) + .whenComplete( (v, e) -> { + if ( getPersistenceContext().isLoadFinished() ) { + getPersistenceContext().clear(); } - return list; - }); + } ) + .thenApply( list -> (List) list ); } @Override From 7af1fc0eb366a1a9ec830067f5bb09e6415e0a6c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 5 Apr 2025 09:15:23 +0200 Subject: [PATCH 127/201] fix up some unchecked casts and other warnings --- .../ReactiveAbstractPersisterDelegate.java | 78 +++++++++++-------- .../entity/impl/ReactiveEntityPersister.java | 12 +-- ...ReactiveJoinedSubclassEntityPersister.java | 12 +-- .../ReactiveSingleTableEntityPersister.java | 14 ++-- .../ReactiveUnionSubclassEntityPersister.java | 27 ++++--- .../impl/ReactiveStatelessSessionImpl.java | 4 +- 6 files changed, 81 insertions(+), 66 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java index 966f850e4..c5d068db7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java @@ -24,6 +24,7 @@ import org.hibernate.generator.values.GeneratedValues; import org.hibernate.id.IdentityGenerator; import org.hibernate.loader.ast.spi.BatchLoaderFactory; +import org.hibernate.loader.ast.spi.EntityBatchLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; @@ -80,11 +81,11 @@ public class ReactiveAbstractPersisterDelegate { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final Supplier> singleIdEntityLoaderSupplier; - private final Supplier> multiIdEntityLoaderSupplier; + private final Supplier> singleIdEntityLoaderSupplier; + private final Supplier> multiIdEntityLoaderSupplier; - private ReactiveSingleIdEntityLoader singleIdEntityLoader; - private ReactiveMultiIdEntityLoader multiIdEntityLoader; + private ReactiveSingleIdEntityLoader singleIdEntityLoader; + private ReactiveMultiIdEntityLoader multiIdEntityLoader; private final EntityPersister entityDescriptor; @@ -100,12 +101,12 @@ public ReactiveAbstractPersisterDelegate( entityDescriptor = entityPersister; } - public ReactiveSingleIdEntityLoader buildSingleIdEntityLoader() { + public ReactiveSingleIdEntityLoader buildSingleIdEntityLoader() { singleIdEntityLoader = singleIdEntityLoaderSupplier.get(); return singleIdEntityLoader; } - public ReactiveMultiIdEntityLoader buildMultiIdEntityLoader() { + public ReactiveMultiIdEntityLoader buildMultiIdEntityLoader() { multiIdEntityLoader = multiIdEntityLoaderSupplier.get(); return multiIdEntityLoader; } @@ -134,12 +135,12 @@ private static ReactiveMultiIdEntityLoader buildMultiIdEntityLoader( PersistentClass persistentClass, SessionFactoryImplementor factory) { return entityDescriptor.getIdentifierType() instanceof BasicType - && supportsSqlArrayType( factory.getJdbcServices().getDialect() ) + && supportsSqlArrayType( factory.getJdbcServices().getDialect() ) ? new ReactiveMultiIdEntityLoaderArrayParam<>( entityDescriptor, factory ) : new ReactiveMultiIdEntityLoaderStandard<>( entityDescriptor, persistentClass, factory ); } - private static ReactiveSingleIdEntityLoader buildSingleIdEntityLoader( + private static ReactiveSingleIdEntityLoader buildSingleIdEntityLoader( EntityPersister entityDescriptor, PersistentClass bootDescriptor, RuntimeModelCreationContext creationContext, @@ -147,32 +148,45 @@ private static ReactiveSingleIdEntityLoader buildSingleIdEntityLoader( String entityName) { if ( bootDescriptor.getLoaderName() != null ) { // We must resolve the named query on-demand through the boot model because it isn't initialized yet - final NamedQueryMemento namedQueryMemento = factory.getQueryEngine().getNamedObjectRepository() - .resolve( factory, creationContext.getBootModel(), bootDescriptor.getLoaderName() ); - if ( namedQueryMemento == null ) { - throw new IllegalArgumentException( "Could not resolve named load-query [" + entityName + "] : " + bootDescriptor.getLoaderName() ); - } + final NamedQueryMemento namedQueryMemento = + getNamedQueryMemento( bootDescriptor, creationContext, factory, entityName ); return new ReactiveSingleIdEntityLoaderProvidedQueryImpl<>( entityDescriptor, namedQueryMemento ); } - - LoadQueryInfluencers loadQueryInfluencers = new LoadQueryInfluencers( factory ); - if ( loadQueryInfluencers.effectivelyBatchLoadable( entityDescriptor ) ) { - final int batchSize = loadQueryInfluencers.effectiveBatchSize( entityDescriptor ); - if ( batchSize > 1 ) { + else { + final LoadQueryInfluencers loadQueryInfluencers = new LoadQueryInfluencers( factory ); + if ( loadQueryInfluencers.effectivelyBatchLoadable( entityDescriptor ) ) { + final int batchSize = loadQueryInfluencers.effectiveBatchSize( entityDescriptor ); return createBatchingIdEntityLoader( entityDescriptor, batchSize, factory ); } + else { + return new ReactiveSingleIdEntityLoaderStandardImpl<>( entityDescriptor, loadQueryInfluencers ); + } } + } - return new ReactiveSingleIdEntityLoaderStandardImpl<>( entityDescriptor, new LoadQueryInfluencers( factory ) ); + private static NamedQueryMemento getNamedQueryMemento( + PersistentClass bootDescriptor, + RuntimeModelCreationContext creationContext, + SessionFactoryImplementor factory, + String entityName) { + final NamedQueryMemento namedQueryMemento = + factory.getQueryEngine().getNamedObjectRepository() + .resolve(factory, creationContext.getBootModel(), bootDescriptor.getLoaderName() ); + if ( namedQueryMemento == null ) { + throw new IllegalArgumentException( "Could not resolve named query '" + bootDescriptor.getLoaderName() + + "' for loading entity '" + entityName + "'" ); + } + return namedQueryMemento; } - private static ReactiveSingleIdEntityLoader createBatchingIdEntityLoader( + private static ReactiveSingleIdEntityLoader createBatchingIdEntityLoader( EntityMappingType entityDescriptor, int domainBatchSize, SessionFactoryImplementor factory) { - return (ReactiveSingleIdEntityLoader) factory.getServiceRegistry() - .getService( BatchLoaderFactory.class ) - .createEntityBatchLoader( domainBatchSize, entityDescriptor, factory ); + final EntityBatchLoader batchLoader = + factory.getServiceRegistry().requireService( BatchLoaderFactory.class ) + .createEntityBatchLoader( domainBatchSize, entityDescriptor, factory ); + return (ReactiveSingleIdEntityLoader) batchLoader; } public CompletionStage processInsertGeneratedProperties( @@ -235,7 +249,7 @@ protected ReactiveSingleUniqueKeyEntityLoader getReactiveUniqueKeyLoader ); } - public CompletionStage load( + public CompletionStage load( EntityPersister persister, Object id, Object optionalObject, @@ -254,7 +268,7 @@ public Generator reactive(Generator generator) { return generator instanceof IdentityGenerator ? new ReactiveIdentityGenerator() : generator; } - public CompletionStage loadEntityIdByNaturalId( + public CompletionStage loadEntityIdByNaturalId( Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { if ( LOG.isTraceEnabled() ) { LOG.tracef( "Resolving natural-id [%s] to id : %s ", @@ -263,7 +277,7 @@ public CompletionStage loadEntityIdByNaturalId( ); } - return ( (ReactiveNaturalIdLoader) entityDescriptor.getNaturalIdLoader() ) + return ( (ReactiveNaturalIdLoader) entityDescriptor.getNaturalIdLoader() ) .resolveNaturalIdToId( orderedNaturalIdValues, session ); } @@ -322,13 +336,15 @@ public AttributeMapping buildPluralAttributeMapping( } public EntityIdentifierMapping convertEntityIdentifierMapping(EntityIdentifierMapping entityIdentifierMapping) { - if ( entityIdentifierMapping instanceof NonAggregatedIdentifierMappingImpl ) { - return new ReactiveNonAggregatedIdentifierMappingImpl( (NonAggregatedIdentifierMappingImpl) entityIdentifierMapping ); + if ( entityIdentifierMapping instanceof NonAggregatedIdentifierMappingImpl nonAggregatedIdentifierMapping ) { + return new ReactiveNonAggregatedIdentifierMappingImpl(nonAggregatedIdentifierMapping); + } + else if ( entityIdentifierMapping instanceof EmbeddedIdentifierMappingImpl embeddedIdentifierMapping ) { + return new ReactiveEmbeddedIdentifierMappingImpl(embeddedIdentifierMapping); } - if ( entityIdentifierMapping instanceof EmbeddedIdentifierMappingImpl ) { - return new ReactiveEmbeddedIdentifierMappingImpl( (EmbeddedIdentifierMappingImpl) entityIdentifierMapping ); + else { + return entityIdentifierMapping; } - return entityIdentifierMapping; } private static class ReactiveNonAggregatedIdentifierMappingImpl extends NonAggregatedIdentifierMappingImpl { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index f59b27e26..16d6237f2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -94,37 +94,37 @@ CompletionStage> reactiveMultiLoad( SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions); - CompletionStage reactiveLoad( + CompletionStage reactiveLoad( Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session); - CompletionStage reactiveLoad( + CompletionStage reactiveLoad( Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session); - CompletionStage reactiveLoad( + CompletionStage reactiveLoad( Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly); - CompletionStage reactiveLoadByUniqueKey( + CompletionStage reactiveLoadByUniqueKey( String propertyName, Object uniqueKey, SharedSessionContractImplementor session); - CompletionStage reactiveLoadByUniqueKey( + CompletionStage reactiveLoadByUniqueKey( String propertyName, Object uniqueKey, Boolean readOnly, SharedSessionContractImplementor session); - CompletionStage reactiveLoadEntityIdByNaturalId( + CompletionStage reactiveLoadEntityIdByNaturalId( Object[] naturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session); 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 d717c7778..0182ef403 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 @@ -82,7 +82,7 @@ protected SingleIdEntityLoader buildSingleIdEntityLoader() { } @Override - protected MultiIdEntityLoader buildMultiIdLoader() { + protected MultiIdEntityLoader buildMultiIdLoader() { return reactiveDelegate.buildMultiIdEntityLoader(); } @@ -312,7 +312,7 @@ protected Object initializeLazyPropertiesFromDatastore(Object entity, Object id, } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { return reactiveLoad( id, optionalObject, new LockOptions().setLockMode( lockMode ), session ); } @@ -322,7 +322,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { return doReactiveLoad( id, optionalObject, lockOptions, null, session ); } @@ -332,11 +332,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { return doReactiveLoad( id, optionalObject, lockOptions, readOnly, session ); } - private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { + private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { return reactiveDelegate.load( this, id, optionalObject, lockOptions, readOnly, session ); } @@ -370,7 +370,7 @@ public CompletionStage reactiveLoadByUniqueKey(String propertyName, Obje * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor) */ @Override - public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { verifyHasNaturalId(); return reactiveDelegate.loadEntityIdByNaturalId( orderedNaturalIdValues, lockOptions, session ); } 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 2dc1bdc13..3006a0eae 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 @@ -69,7 +69,7 @@ public class ReactiveSingleTableEntityPersister extends SingleTableEntityPersist private static final Log LOG = make( Log.class, lookup() ); - private ReactiveAbstractPersisterDelegate reactiveDelegate; + private final ReactiveAbstractPersisterDelegate reactiveDelegate; public ReactiveSingleTableEntityPersister( final PersistentClass persistentClass, @@ -101,7 +101,7 @@ protected SingleIdEntityLoader buildSingleIdEntityLoader() { } @Override - protected MultiIdEntityLoader buildMultiIdLoader() { + protected MultiIdEntityLoader buildMultiIdLoader() { return reactiveDelegate.buildMultiIdEntityLoader(); } @@ -301,13 +301,13 @@ public CompletionStage reactiveProcessUpdateGenerated(Object id, Object en * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor) */ @Override - public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { verifyHasNaturalId(); return reactiveDelegate.loadEntityIdByNaturalId( orderedNaturalIdValues, lockOptions, session ); } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { return reactiveLoad( id, optionalObject, new LockOptions().setLockMode( lockMode ), session ); } @@ -317,7 +317,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { return doReactiveLoad( id, optionalObject, lockOptions, null, session ); } @@ -327,11 +327,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { return doReactiveLoad( id, optionalObject, lockOptions, readOnly, session ); } - private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { + private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { return reactiveDelegate.load( this, id, optionalObject, lockOptions, readOnly, session ); } 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 d1c112ab7..52871e30f 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 @@ -82,7 +82,7 @@ protected SingleIdEntityLoader buildSingleIdEntityLoader() { } @Override - protected MultiIdEntityLoader buildMultiIdLoader() { + protected MultiIdEntityLoader buildMultiIdLoader() { return reactiveDelegate.buildMultiIdEntityLoader(); } @@ -148,14 +148,6 @@ public NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess cre return ReactiveAbstractEntityPersister.super.generateNaturalIdMapping(creationProcess, bootEntityDescriptor); } - - @Override - protected void validateGenerator() { - if ( super.getGenerator() instanceof IdentityGenerator ) { - throw new MappingException( "Cannot use identity column key generation with mapping for: " + getEntityName() ); - } - } - @Override protected InsertCoordinator buildInsertCoordinator() { return ReactiveCoordinatorFactory.buildInsertCoordinator( this, getFactory() ); @@ -180,6 +172,13 @@ public DomainResult createDomainResult( return reactiveDelegate.createDomainResult( this, navigablePath, tableGroup, resultVariable, creationState ); } + @Override + protected void validateGenerator() { + if ( super.getGenerator() instanceof IdentityGenerator) { + throw new MappingException( "Cannot use identity column key generation with mapping for: " + getEntityName() ); + } + } + @Override public Generator getGenerator() throws HibernateException { return reactiveDelegate.reactive( super.getGenerator() ); @@ -279,7 +278,7 @@ protected Object initializeLazyPropertiesFromDatastore(Object entity, Object id, } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockMode lockMode, SharedSessionContractImplementor session) { return reactiveLoad( id, optionalObject, new LockOptions().setLockMode( lockMode ), session ); } @@ -289,7 +288,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) { return doReactiveLoad( id, optionalObject, lockOptions, null, session ); } @@ -299,11 +298,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh } @Override - public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { + public CompletionStage reactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session, Boolean readOnly) { return doReactiveLoad( id, optionalObject, lockOptions, readOnly, session ); } - private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { + private CompletionStage doReactiveLoad(Object id, Object optionalObject, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { return reactiveDelegate.load( this, id, optionalObject, lockOptions, readOnly, session ); } @@ -372,7 +371,7 @@ public CompletionStage> reactiveMultiLoad(K[] ids, SharedS * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor) */ @Override - public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { + public CompletionStage reactiveLoadEntityIdByNaturalId(Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) { verifyHasNaturalId(); return reactiveDelegate.loadEntityIdByNaturalId( orderedNaturalIdValues, lockOptions, session ); } 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 c70972417..30167923b 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 @@ -475,9 +475,9 @@ public CompletionStage reactiveRefresh(String entityName, Object entity, L } ); } - private CompletionStage fromInternalFetchProfile( + private CompletionStage fromInternalFetchProfile( CascadingFetchProfile cascadingFetchProfile, - Supplier> supplier) { + Supplier> supplier) { CascadingFetchProfile previous = getLoadQueryInfluencers().getEnabledCascadingFetchProfile(); return voidFuture() .thenCompose( v -> { From 79f32da481418134666ed61ef207b55c810ee89a Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 5 Apr 2025 09:26:51 +0200 Subject: [PATCH 128/201] minor changes --- .../session/impl/ReactiveSessionImpl.java | 24 +++++++------------ .../impl/ReactiveStatelessSessionImpl.java | 17 ++++++------- 2 files changed, 17 insertions(+), 24 deletions(-) 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 72157e46e..1eedcddae 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 @@ -1435,7 +1435,7 @@ protected final CompletionStage doLoad(Object id, LoadEventListener.LoadType } private class ReactiveMultiIdentifierLoadAccessImpl implements MultiIdLoadOptions { - private final EntityPersister entityPersister; + private final ReactiveEntityPersister entityPersister; private LockOptions lockOptions; private CacheMode cacheMode; @@ -1450,7 +1450,7 @@ private class ReactiveMultiIdentifierLoadAccessImpl implements MultiIdLoadOpt private boolean readOnly; public ReactiveMultiIdentifierLoadAccessImpl(EntityPersister entityPersister) { - this.entityPersister = entityPersister; + this.entityPersister = (ReactiveEntityPersister) entityPersister; } @Override @@ -1489,12 +1489,7 @@ public Integer getBatchSize() { } public ReactiveMultiIdentifierLoadAccessImpl withBatchSize(int batchSize) { - if ( batchSize < 1 ) { - this.batchSize = null; - } - else { - this.batchSize = batchSize; - } + this.batchSize = batchSize < 1 ? null : batchSize; return this; } @@ -1537,8 +1532,11 @@ public CompletionStage> multiLoad(Object... ids) { Object[] sids = new Object[ids.length]; System.arraycopy( ids, 0, sids, 0, ids.length ); - return perform( () -> (CompletionStage) ( (ReactiveEntityPersister) entityPersister ) - .reactiveMultiLoad( sids, ReactiveSessionImpl.this, this ) ); + return perform( () -> { + final CompletionStage> stage = + entityPersister.reactiveMultiLoad( sids, ReactiveSessionImpl.this, this ); + return (CompletionStage>) stage; + }); } public CompletionStage> perform(Supplier>> executor) { @@ -1572,12 +1570,6 @@ public CompletionStage> perform(Supplier>> execu } } ); } - - @SuppressWarnings("unchecked") - public CompletionStage> multiLoad(List ids) { - return perform( () -> (CompletionStage>) - entityPersister.multiLoad( ids.toArray( new Object[0] ), ReactiveSessionImpl.this, this ) ); - } } private class NaturalIdLoadAccessImpl { 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 30167923b..9bf25caa6 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 @@ -223,14 +223,15 @@ public CompletionStage> reactiveGet(Class entityClass, Object... Object[] sids = new Object[ids.length]; System.arraycopy( ids, 0, sids, 0, ids.length ); - return getEntityPersister( entityClass.getName() ) - .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) - .whenComplete( (v, e) -> { - if ( getPersistenceContext().isLoadFinished() ) { - getPersistenceContext().clear(); - } - } ) - .thenApply( list -> (List) list ); + final CompletionStage> stage = + getEntityPersister( entityClass.getName() ) + .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) + .whenComplete( (v, e) -> { + if ( getPersistenceContext().isLoadFinished() ) { + getPersistenceContext().clear(); + } + } ); + return (CompletionStage>) stage; } @Override From c8c879a2d53427468edbd5f67805325dcfc2d857 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Sun, 6 Apr 2025 21:34:27 +0200 Subject: [PATCH 129/201] [#2220] Fix errors with ORM 7.0.0.CR1 Plus, update expected Oracle default database mapping in tests: * `Double` and `double` java types changes from `FLOAT` to `BINARY_DOUBLE` * `Float` and `float` java types changes from `FLOAT` to `BINARY_FLOAT` Changes to ReactiveAbstractSelectionQuery --- .../ReactiveGeneratedValuesHelper.java | 16 ++++----- ...ctiveSingleIdEntityLoaderStandardImpl.java | 5 ++- .../ReactiveNamedObjectRepositoryImpl.java | 6 ++++ .../spi/ReactiveAbstractSelectionQuery.java | 2 +- .../session/impl/ReactiveSessionImpl.java | 8 ----- .../impl/ReactiveStatelessSessionImpl.java | 36 +++---------------- .../StandardReactiveSelectExecutor.java | 13 ++++--- .../jdbc/ReactiveJsonArrayJdbcType.java | 11 +++--- .../jdbc/ReactiveOracleArrayJdbcType.java | 5 +-- .../jdbc/ReactiveXmlArrayJdbcType.java | 12 +++---- .../descriptor/jdbc/ReactiveXmlJdbcType.java | 12 +++---- .../reactive/containers/OracleDatabase.java | 8 ++--- 12 files changed, 53 insertions(+), 81 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java index 70495ccea..e842adf11 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java @@ -5,13 +5,6 @@ */ package org.hibernate.reactive.generator.values.internal; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletionStage; - import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.dialect.Dialect; @@ -48,8 +41,15 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.type.descriptor.WrapperOptions; -import static org.hibernate.generator.internal.NaturalIdHelper.getNaturalIdPropertyNames; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; + import static org.hibernate.generator.values.internal.GeneratedValuesHelper.noCustomSql; +import static org.hibernate.internal.NaturalIdHelper.getNaturalIdPropertyNames; import static org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer.UniqueSemantic.NONE; /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderStandardImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderStandardImpl.java index 684894cf2..c23a00983 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderStandardImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderStandardImpl.java @@ -55,8 +55,7 @@ public CompletionStage load( SharedSessionContractImplementor session) { final ReactiveSingleIdLoadPlan loadPlan = (ReactiveSingleIdLoadPlan) resolveLoadPlan( lockOptions, - session.getLoadQueryInfluencers(), - session.getFactory() + session.getLoadQueryInfluencers() ); return loadPlan.load( key, readOnly, true, session ); } @@ -68,7 +67,7 @@ public CompletionStage load( LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) { - final ReactiveSingleIdLoadPlan loadPlan = (ReactiveSingleIdLoadPlan) resolveLoadPlan( lockOptions, session.getLoadQueryInfluencers(), session.getFactory() ); + final ReactiveSingleIdLoadPlan loadPlan = (ReactiveSingleIdLoadPlan) resolveLoadPlan( lockOptions, session.getLoadQueryInfluencers() ); return loadPlan.load( key, entityInstance, readOnly, false, session ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java index ef2d70afe..9fd78d104 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/internal/ReactiveNamedObjectRepositoryImpl.java @@ -5,6 +5,7 @@ */ package org.hibernate.reactive.query.internal; +import jakarta.persistence.TypedQuery; import java.util.Map; import java.util.function.Consumer; @@ -48,6 +49,11 @@ public void registerNamedQuery(String name, Query query) { delegate.registerNamedQuery( name, query ); } + @Override + public TypedQueryReference registerNamedQuery(String name, TypedQuery query) { + return delegate.registerNamedQuery( name, query ); + } + @Override public void visitSqmQueryMementos(Consumer> action) { delegate.visitSqmQueryMementos( action ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java index 1137e0a62..9c2647b8f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java @@ -30,7 +30,7 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmInterpretationsKey; -import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource; +import org.hibernate.query.sqm.spi.InterpretationsKeySource; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; 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 1eedcddae..a5489d907 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 @@ -1250,10 +1250,6 @@ private ReactiveEntityPersister entityPersister(Class entityClass) { return (ReactiveEntityPersister) getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); } - private EntityPersister requireEntityPersister(Class entityClass) { - return getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); - } - private CompletionStage fireReactiveLoad(LoadEvent event, LoadEventListener.LoadType loadType) { checkOpenOrWaitingForAutoClose(); @@ -1283,10 +1279,6 @@ public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { //no-op because we don't support transactions } - private Boolean getReadOnlyFromLoadQueryInfluencers() { - return getLoadQueryInfluencers().getReadOnly(); - } - private class ReactiveIdentifierLoadAccessImpl { private final EntityPersister entityPersister; 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 9bf25caa6..a723fe49e 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 @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.session.impl; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; - import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -28,10 +23,6 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.PostInsertEvent; -import org.hibernate.event.spi.PostInsertEventListener; -import org.hibernate.event.spi.PreInsertEvent; -import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; import org.hibernate.graph.GraphSemantic; @@ -89,6 +80,10 @@ import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; import static java.lang.Boolean.TRUE; import static java.lang.invoke.MethodHandles.lookup; @@ -340,29 +335,6 @@ private CompletionStage reactiveInsert(Object entity, ReactiveEntityPersis } } - private boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) { - if ( getFactory().getEventListenerGroups().eventListenerGroup_PRE_INSERT.isEmpty() ) { - return false; - } - else { - boolean veto = false; - final PreInsertEvent event = new PreInsertEvent( entity, id, state, persister, null ); - for ( PreInsertEventListener listener : getFactory().getEventListenerGroups().eventListenerGroup_PRE_INSERT.listeners() ) { - veto |= listener.onPreInsert( event ); - } - return veto; - } - } - - private void firePostInsert(Object entity, Object id, Object[] state, EntityPersister persister) { - if ( !getFactory().getEventListenerGroups().eventListenerGroup_POST_INSERT.isEmpty() ) { - final PostInsertEvent event = new PostInsertEvent( entity, id, state, persister, null ); - for ( PostInsertEventListener listener : getFactory().getEventListenerGroups().eventListenerGroup_POST_INSERT.listeners() ) { - listener.onPostInsert( event ); - } - } - } - private CompletionStage generateId(EntityPersister persister, Object entity, Generator generator) { if ( generator.generatesOnInsert() ) { if ( generator instanceof ReactiveIdentifierGenerator reactiveGenerator ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java index 64c5b3af3..5993d1893 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/exec/internal/StandardReactiveSelectExecutor.java @@ -412,14 +412,17 @@ public CompletionStage resolveJdbcValuesSource( } } + /** + * Copied from Hibernate ORM + */ private static CacheMode resolveCacheMode(ExecutionContext executionContext) { final QueryOptions queryOptions = executionContext.getQueryOptions(); final SharedSessionContract session = executionContext.getSession(); - return coalesceSuppliedValues( - () -> queryOptions == null ? null : queryOptions.getCacheMode(), - session::getCacheMode, - () -> CacheMode.NORMAL - ); + return coalesceSuppliedValues( + () -> queryOptions == null ? null : queryOptions.getCacheMode(), + session::getCacheMode, + () -> CacheMode.NORMAL + ); } /** 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 adc00ff79..81af936ba 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,12 +5,6 @@ */ package org.hibernate.reactive.type.descriptor.jdbc; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.hibernate.dialect.JsonHelper; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.ValueBinder; @@ -23,9 +17,14 @@ 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 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 diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveOracleArrayJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveOracleArrayJdbcType.java index 52a9d9206..09000a9e0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveOracleArrayJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveOracleArrayJdbcType.java @@ -13,7 +13,8 @@ import org.hibernate.HibernateException; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.OracleArrayJdbcType; + +import org.hibernate.dialect.type.OracleArrayJdbcType; import org.hibernate.reactive.adaptor.impl.ArrayAdaptor; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.ValueBinder; @@ -30,7 +31,7 @@ import static java.sql.Types.ARRAY; /** - * @see org.hibernate.dialect.OracleArrayJdbcType + * @see org.hibernate.dialect.type.OracleArrayJdbcType */ public class ReactiveOracleArrayJdbcType extends OracleArrayJdbcType { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java index 306e619b8..7f30d8d29 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlArrayJdbcType.java @@ -5,12 +5,6 @@ */ package org.hibernate.reactive.type.descriptor.jdbc; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.SQLXML; - -import org.hibernate.dialect.XmlHelper; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.WrapperOptions; @@ -18,6 +12,12 @@ import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.XmlArrayJdbcType; +import org.hibernate.type.descriptor.jdbc.XmlHelper; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLXML; import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java index 3c3667aa3..a81840263 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/type/descriptor/jdbc/ReactiveXmlJdbcType.java @@ -5,20 +5,20 @@ */ package org.hibernate.reactive.type.descriptor.jdbc; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.SQLXML; - -import org.hibernate.dialect.XmlHelper; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.XmlHelper; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLXML; + import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; 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 a636bc5a9..d57f9103a 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 @@ -57,10 +57,10 @@ class OracleDatabase implements TestableDatabase { expectedDBTypeForClass.put( Integer.class, "NUMBER" ); expectedDBTypeForClass.put( long.class, "NUMBER" ); expectedDBTypeForClass.put( Long.class, "NUMBER" ); - expectedDBTypeForClass.put( float.class, "FLOAT" ); - expectedDBTypeForClass.put( Float.class, "FLOAT" ); - expectedDBTypeForClass.put( double.class, "FLOAT" ); - expectedDBTypeForClass.put( Double.class, "FLOAT" ); + expectedDBTypeForClass.put( float.class, "BINARY_FLOAT" ); + expectedDBTypeForClass.put( Float.class, "BINARY_FLOAT" ); + expectedDBTypeForClass.put( double.class, "BINARY_DOUBLE" ); + expectedDBTypeForClass.put( Double.class, "BINARY_DOUBLE" ); expectedDBTypeForClass.put( byte.class, "NUMBER" ); expectedDBTypeForClass.put( Byte.class, "NUMBER" ); expectedDBTypeForClass.put( URL.class, "VARCHAR2" ); From 807f690661871f687192f8ccf2bfb3a7e30d0009 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 25 Apr 2025 16:54:40 +0200 Subject: [PATCH 130/201] [#2220] Drop setOrder methods see https://hibernate.atlassian.net/browse/HHH-19382 --- .../org/hibernate/reactive/mutiny/Mutiny.java | 41 +-- .../reactive/mutiny/impl/MutinyQueryImpl.java | 20 +- .../mutiny/impl/MutinySelectionQueryImpl.java | 21 +- .../query/ReactiveSelectionQuery.java | 22 +- .../sql/internal/ReactiveNativeQueryImpl.java | 13 - .../query/sqm/ReactiveSqmSelectionQuery.java | 8 - .../sqm/internal/ReactiveQuerySqmImpl.java | 13 - .../ReactiveSqmSelectionQueryImpl.java | 34 +- .../org/hibernate/reactive/stage/Stage.java | 43 --- .../reactive/stage/impl/StageQueryImpl.java | 13 - .../stage/impl/StageSelectionQueryImpl.java | 13 - .../org/hibernate/reactive/OrderTest.java | 345 ------------------ 12 files changed, 28 insertions(+), 558 deletions(-) delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/OrderTest.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 0f231ff63..1598f4dc8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.mutiny; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Function; - import org.hibernate.Cache; import org.hibernate.CacheMode; import org.hibernate.Filter; @@ -23,7 +18,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.proxy.HibernateProxy; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; @@ -49,6 +43,10 @@ import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; import jakarta.persistence.metamodel.Metamodel; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; @@ -360,37 +358,6 @@ default SelectionQuery setLockMode(String alias, LockModeType lockModeType) { return setLockMode( alias, convertToLockMode(lockModeType) ); } -// /** -// * Set the {@link LockOptions} to use for the whole query. -// * -// * @see org.hibernate.query.Query#setLockOptions(LockOptions) -// */ -// Query setLockOptions(LockOptions lockOptions); - - /** - * If the result type of this query is an entity class, add one or more - * {@linkplain Order rules} for ordering the query results. - * - * @param orderList one or more instances of {@link Order} - * - * @see Order - * - * @see org.hibernate.query.Query#setOrder(List) - */ - SelectionQuery setOrder(List> orderList); - - /** - * If the result type of this query is an entity class, add a - * {@linkplain Order rule} for ordering the query results. - * - * @param order an instance of {@link Order} - * - * @see Order - * - * @see org.hibernate.query.Query#setOrder(Order) - */ - SelectionQuery setOrder(Order order); - /** * Set the {@link EntityGraph} that will be used as a fetch plan for * the root entity returned by this query. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java index d0608ba5f..88243f77d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java @@ -5,16 +5,11 @@ */ package org.hibernate.reactive.mutiny.impl; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; - import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny.Query; @@ -27,6 +22,9 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; public class MutinyQueryImpl implements Query { @@ -74,18 +72,6 @@ public Query setLockMode(LockMode lockMode) { return this; } - @Override - public Mutiny.SelectionQuery setOrder(List> orders) { - delegate.setOrder( orders ); - return this; - } - - @Override - public Mutiny.SelectionQuery setOrder(Order order) { - delegate.setOrder( (List>) order ); - return this; - } - @Override public Query setPlan(EntityGraph entityGraph) { delegate.applyGraph( (RootGraphImplementor) entityGraph, GraphSemantic.FETCH ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java index b001e618a..fb4fe2ad2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java @@ -5,16 +5,11 @@ */ package org.hibernate.reactive.mutiny.impl; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; - import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.reactive.mutiny.Mutiny.SelectionQuery; import org.hibernate.reactive.query.ReactiveSelectionQuery; @@ -26,6 +21,9 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; public class MutinySelectionQueryImpl implements SelectionQuery { private final MutinySessionFactoryImpl factory; @@ -196,19 +194,6 @@ public SelectionQuery setLockMode(String alias, LockMode lockMode) { return this; } - @Override - public SelectionQuery setOrder(List> orders) { - delegate.setOrder( orders ); - return this; - } - - @Override - public SelectionQuery setOrder(Order order) { - delegate.setOrder( order ); - return this; - } - - @Override public SelectionQuery setParameter(String name, Object value) { delegate.setParameter( name, value ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java index 6c35d9260..2ee7a051e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java @@ -5,15 +5,6 @@ */ package org.hibernate.reactive.query; -import java.time.Instant; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletionStage; - import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; @@ -22,7 +13,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.query.BindableType; import org.hibernate.query.CommonQueryContract; -import org.hibernate.query.Order; import org.hibernate.query.QueryParameter; import jakarta.persistence.CacheRetrieveMode; @@ -31,6 +21,14 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import java.time.Instant; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletionStage; /** * @see org.hibernate.query.SelectionQuery @@ -123,10 +121,6 @@ default CompletionStage> getReactiveResultList() { void applyGraph(RootGraphImplementor graph, GraphSemantic semantic); - ReactiveSelectionQuery setOrder(List> orderList); - - ReactiveSelectionQuery setOrder(Order order); - ReactiveSelectionQuery enableFetchProfile(String profileName); @Override 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 ac8f4e9b4..afaf43e41 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 @@ -28,7 +28,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.query.BindableType; -import org.hibernate.query.Order; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -528,18 +527,6 @@ public ReactiveNativeQueryImpl setLockMode(String alias, LockMode lockMode) { return this; } - @Override - public ReactiveNativeQueryImpl setOrder(List> orders) { - super.setOrder( orders ); - return this; - } - - @Override - public ReactiveNativeQueryImpl setOrder(Order order) { - super.setOrder( order ); - return this; - } - @Override public ReactiveNativeQueryImpl setComment(String comment) { super.setComment( comment ); 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 1d897653f..92d00b305 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 @@ -9,13 +9,11 @@ import java.util.Calendar; import java.util.Collection; import java.util.Date; -import java.util.List; import java.util.Map; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.query.BindableType; -import org.hibernate.query.Order; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.SqmQuery; import org.hibernate.reactive.query.ReactiveSelectionQuery; @@ -169,12 +167,6 @@

    ReactiveSqmSelectionQuery setParameterList( @Override ReactiveSqmSelectionQuery setTimeout(int timeout); - @Override - ReactiveSqmSelectionQuery setOrder(List> orderList); - - @Override - ReactiveSqmSelectionQuery setOrder(Order order); - @Override ReactiveSqmSelectionQuery setFetchSize(int fetchSize); 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 096196576..61f0d39c6 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 @@ -34,7 +34,6 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.BindableType; import org.hibernate.query.IllegalQueryOperationException; -import org.hibernate.query.Order; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -478,18 +477,6 @@ public ReactiveQuerySqmImpl setLockMode(String alias, LockMode lockMode) { return this; } - @Override - public ReactiveQuerySqmImpl setOrder(List> orders) { - super.setOrder( orders ); - return this; - } - - @Override - public ReactiveQuerySqmImpl setOrder(Order order) { - super.setOrder( order ); - return this; - } - @Override public ReactiveQuerySqmImpl setTupleTransformer(TupleTransformer transformer) { throw new UnsupportedOperationException(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java index bca5e2a65..9249b0544 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java @@ -6,17 +6,6 @@ package org.hibernate.reactive.query.sqm.internal; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletionStage; -import java.util.stream.Stream; - import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -26,7 +15,6 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.query.BindableType; -import org.hibernate.query.Order; import org.hibernate.query.QueryLogging; import org.hibernate.query.QueryParameter; import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; @@ -47,6 +35,16 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletionStage; +import java.util.stream.Stream; import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions; @@ -258,18 +256,6 @@ public ReactiveSqmSelectionQueryImpl setFollowOnLocking(boolean enable) { return this; } - @Override - public ReactiveSqmSelectionQueryImpl setOrder(List> orders) { - super.setOrder( orders ); - return this; - } - - @Override - public ReactiveSqmSelectionQueryImpl setOrder(Order order) { - super.setOrder( order ); - return this; - } - @Override public ReactiveSqmSelectionQueryImpl setFetchSize(int fetchSize) { super.setFetchSize( fetchSize ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 601b17c11..39bb81dd7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -24,7 +24,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.proxy.HibernateProxy; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; @@ -360,37 +359,6 @@ default SelectionQuery setLockMode(String alias, LockModeType lockModeType) { return setLockMode( alias, convertToLockMode(lockModeType) ); } -// /** -// * Set the {@link LockOptions} to use for the whole query. -// * -// * @see org.hibernate.query.Query#setLockOptions(LockOptions) -// */ -// Query setLockOptions(LockOptions lockOptions); - - /** - * If the result type of this query is an entity class, add one or more - * {@linkplain Order rules} for ordering the query results. - * - * @param orderList one or more instances of {@link Order} - * - * @see Order - * - * @see org.hibernate.query.Query#setOrder(List) - */ - SelectionQuery setOrder(List> orderList); - - /** - * If the result type of this query is an entity class, add a - * {@linkplain Order rule} for ordering the query results. - * - * @param order an instance of {@link Order} - * - * @see Order - * - * @see org.hibernate.query.Query#setOrder(Order) - */ - SelectionQuery setOrder(Order order); - /** * Set the {@link EntityGraph} that will be used as a fetch plan for * the root entity returned by this query. @@ -885,17 +853,6 @@ default CompletionStage lock(Object entity, LockModeType lockModeType) { return lock( entity, convertToLockMode(lockModeType) ); } -// /** -// * Obtain the specified lock level upon the given object, with the given -// * {@link LockOptions}. -// * -// * @param entity a managed persistent instance -// * @param lockOptions the requested {@link LockOptions} -// * -// * @throws IllegalArgumentException if the given instance is not managed -// */ -// CompletionStage lock(Object entity, LockOptions lockOptions); - /** * Force this session to flush asynchronously. Must be called at the * end of a unit of work, before committing the transaction and closing diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java index fb022c499..df0c04c5a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java @@ -13,7 +13,6 @@ import org.hibernate.LockMode; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.stage.Stage; @@ -65,18 +64,6 @@ public Query setLockMode(LockMode lockMode) { return this; } - @Override - public Stage.SelectionQuery setOrder(List> orders) { - delegate.setOrder( orders ); - return this; - } - - @Override - public Stage.SelectionQuery setOrder(Order order) { - delegate.setOrder( (List>) order ); - return this; - } - @Override public Query setPlan(EntityGraph entityGraph) { delegate.applyGraph( (RootGraphImplementor) entityGraph, GraphSemantic.FETCH ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java index eaf1c9a53..71beb5df3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java @@ -13,7 +13,6 @@ import org.hibernate.LockMode; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.query.Order; import org.hibernate.query.Page; import org.hibernate.reactive.query.ReactiveSelectionQuery; import org.hibernate.reactive.stage.Stage.SelectionQuery; @@ -194,18 +193,6 @@ public SelectionQuery setLockMode(String alias, LockMode lockMode) { return this; } - @Override - public SelectionQuery setOrder(List> orders) { - delegate.setOrder( orders ); - return this; - } - - @Override - public SelectionQuery setOrder(Order order) { - delegate.setOrder( order ); - return this; - } - @Override public SelectionQuery setParameter(String name, Object value) { delegate.setParameter( name, value ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OrderTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OrderTest.java deleted file mode 100644 index 48b73829f..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/OrderTest.java +++ /dev/null @@ -1,345 +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; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl; - -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.metamodel.SingularAttribute; - -import static java.util.concurrent.TimeUnit.MINUTES; -import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hibernate.query.Order.asc; -import static org.hibernate.query.Order.desc; - -@Timeout(value = 10, timeUnit = MINUTES) -public class OrderTest extends BaseReactiveTest { - final Book book1 = new Book( "9781932394153", "Hibernate in Action" ); - final Book book2 = new Book( "9781617290459", "Java Persistence with Hibernate" ); - - SingularAttribute isbn; - SingularAttribute title; - - @Override - protected Collection> annotatedEntities() { - return List.of( Book.class ); - } - - @BeforeEach - public void populateDB(VertxTestContext context) { - isbn = attribute( "isbn" ); - title = attribute( "title" ); - test( context, getSessionFactory().withTransaction( session -> session.persist( book1, book2 ) ) ); - } - - @Test - public void descPositionalColumnWithStage(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - // Not sure if it's a bug, but setOrder doesn't work if we use String.class - .createSelectionQuery( "select title from Book", Object[].class ) - .setOrder( desc( 1 ) ) - .getResultList() - .thenAccept( results -> assertThat( results ) - // Keep the title - .map( row -> row[0] ) - .containsExactly( book2.title, book1.title ) ) - ) ); - } - - @Test - public void descPositionalColumnWithMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - // Not sure if it's a bug, but setOrder doesn't work if we use String.class or Object.class - .createSelectionQuery( "select title from Book", Object[].class ) - .setOrder( desc( 1 ) ) - .getResultList() - .invoke( results -> assertThat( results ) - // Keep the title - .map( row -> row[0] ) - .containsExactly( book2.title, book1.title ) - ) - ) ); - } - - @Test - public void ascAttributeWithStage(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( asc( title ) ) - .getResultList() - .thenAccept( books -> assertThat( books ).containsExactly( book1, book2 ) ) - ) ); - } - - @Test - public void ascAttributeWithMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( asc( title ) ) - .getResultList() - .invoke( books -> assertThat( books ).containsExactly( book1, book2 ) ) - ) ); - } - - @Test - public void descAttributeWithStage(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( desc( title ) ) - .getResultList() - .thenAccept( books -> assertThat( books ).containsExactly( book2, book1 ) ) - ) ); - } - - @Test - public void descAttributeWithMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( desc( title ) ) - .getResultList() - .invoke( books -> assertThat( books ).containsExactly( book2, book1 ) ) - ) ); - } - - @Test - public void ascIdWithStage(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( asc( isbn ) ) - .getResultList() - .thenAccept( books -> assertThat( books ).containsExactly( book2, book1 ) ) - ) ); - } - - @Test - public void ascIdWithMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( asc( isbn ) ) - .getResultList() - .invoke( books -> assertThat( books ).containsExactly( book2, book1 ) ) - ) ); - } - - @Test - public void descIdWithStage(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( desc( isbn ) ) - .getResultList() - .thenAccept( books -> assertThat( books ).containsExactly( book1, book2 ) ) - ) ); - } - - @Test - public void descIdWithMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( desc( isbn ) ) - .getResultList() - .invoke( books -> assertThat( books ).containsExactly( book1, book2 ) ) - ) ); - } - - @Test - public void testAscDescBySelectElement(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "select isbn, title from Book", Object[].class ) - .setOrder( asc( 2 ) ) - .getResultList() - .thenAccept( list -> assertOrderByBookArray( list, book1, book2 ) ) - .thenCompose( v -> session - .createSelectionQuery( "select isbn, title from Book", Object[].class ) - .setOrder( desc( 2 ) ) - .getResultList() - .thenAccept( list -> assertOrderByBookArray( list, book2, book1 ) ) - ) - ) ); - } - - @Test - public void testAscDescBySelectElementMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "select isbn, title from Book", Object[].class ) - .setOrder( asc( 2 ) ) - .getResultList() - .invoke( list -> assertOrderByBookArray( list, book1, book2 ) ) - .chain( v -> session - .createSelectionQuery( "select isbn, title from Book", Object[].class ) - .setOrder( desc( 2 ) ) - .getResultList() - .invoke( list -> assertOrderByBookArray( list, book2, book1 ) ) - ) - ) ); - } - - @Test - public void testOrderWithList(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( List.of( asc( isbn ), desc( title ) ) ).getResultList() - .thenAccept( isbnAsc -> assertThat( isbnAsc ).containsExactly( book2, book1 ) ) - .thenCompose( v -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( List.of( desc( isbn ), desc( title ) ) ).getResultList() - .thenAccept( isbnDesc -> assertThat( isbnDesc ).containsExactly( book1, book2 ) ) - ) - ) ); - } - - @Test - public void testOrderWithListMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( List.of( asc( isbn ), desc( title ) ) ).getResultList() - .invoke( isbnAsc -> assertThat( isbnAsc ).containsExactly( book2, book1 ) ) - .chain( v -> session - .createSelectionQuery( "from Book", Book.class ) - .setOrder( List.of( desc( isbn ), desc( title ) ) ) - .getResultList() - .invoke( isbnDesc -> assertThat( isbnDesc ).containsExactly( book1, book2 ) ) - ) - ) ); - } - - @Test - public void testAscDescWithNamedParam(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( asc( title ) ) - .getResultList() - .thenAccept( list -> assertThat( list ).containsExactly( book1, book2 ) ) - .thenCompose( v -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( desc( title ) ).getResultList() - .thenAccept( isbnDesc -> assertThat( isbnDesc ).containsExactly( book2, book1 ) ) - ) - ) ); - } - - @Test - public void testAscDescWithNamedParamMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( asc( title ) ) - .getResultList() - .invoke( list -> assertThat( list ).containsExactly( book1, book2 ) ) - .chain( v -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( desc( title ) ) - .getResultList() - .invoke( isbnDesc -> assertThat( isbnDesc ).containsExactly( book2, book1 ) ) - ) - ) ); - } - - @Test - public void testAscDescWithPositionalParam(VertxTestContext context) { - test( context, getSessionFactory().withSession( session -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( asc( title ) ) - .getResultList() - .thenAccept( list -> assertThat( list ).containsExactly( book1, book2 ) ) - .thenCompose( v -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( desc( title ) ) - .getResultList() - .thenAccept( isbnDesc -> assertThat( isbnDesc ).containsExactly( book2, book1 ) ) - ) - ) ); - } - - @Test - public void testAscDescWithPositionalParamMutiny(VertxTestContext context) { - test( context, getMutinySessionFactory().withSession( session -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( asc( title ) ) - .getResultList() - .invoke( list -> assertThat( list ).containsExactly( book1, book2 ) ) - .chain( v -> session - .createSelectionQuery( "from Book where title like :title", Book.class ) - .setParameter( "title", "%Hibernate%" ) - .setOrder( desc( title ) ) - .getResultList() - .invoke( isbnDesc -> assertThat( isbnDesc ).containsExactly( book2, book1 ) ) - ) - ) ); - } - - private void assertOrderByBookArray(List resultList, Book... expectedBooks) { - List books = resultList.stream() - .map( objects -> new Book( (String) objects[0], (String) objects[1] ) ) - .collect( toList() ); - assertThat( books ).containsExactly( expectedBooks ); - } - - private SingularAttribute attribute(String name) { - MappingMetamodelImpl metamodel = (MappingMetamodelImpl) getSessionFactory().getMetamodel(); - EntityDomainType bookType = metamodel.getJpaMetamodel().findEntityType( Book.class ); - return bookType.findSingularAttribute( name ); - } - - @Entity(name = "Book") - @Table(name = "OrderTest_Book" ) - static class Book { - @Id - String isbn; - String title; - - Book(String isbn, String title) { - this.isbn = isbn; - this.title = title; - } - - Book() { - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null || getClass() != o.getClass() ) { - return false; - } - Book book = (Book) o; - return Objects.equals( isbn, book.isbn ) && Objects.equals( - title, - book.title - ); - } - - @Override - public int hashCode() { - return Objects.hash( isbn, title ); - } - - @Override - public String toString() { - return isbn + ":" + title; - } - } -} From de80654b92f406c8cafa83ecd376f02b2cc8380d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 25 Apr 2025 17:07:21 +0200 Subject: [PATCH 131/201] [#2214] Support TypedQueryReferencea and QuerySpecification --- .../org/hibernate/reactive/mutiny/Mutiny.java | 33 +++++++++++++ .../mutiny/impl/MutinySessionImpl.java | 8 ++++ .../impl/MutinyStatelessSessionImpl.java | 8 ++++ .../session/ReactiveQueryProducer.java | 3 ++ .../session/impl/ReactiveSessionImpl.java | 31 ++++++++++++ .../impl/ReactiveStatelessSessionImpl.java | 48 +++++++++++++++---- .../org/hibernate/reactive/stage/Stage.java | 34 +++++++++++++ .../reactive/stage/impl/StageSessionImpl.java | 8 ++++ .../stage/impl/StageStatelessSessionImpl.java | 8 ++++ 9 files changed, 172 insertions(+), 9 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 1598f4dc8..c73c6b416 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -37,6 +37,7 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; @@ -961,6 +962,22 @@ default Uni lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(JpaCriteriaInsert insert); + /** + * Create a typed {@link org.hibernate.query.Query} instance for the given typed query reference. + * + * @param typedQueryReference the type query reference + * + * @return The {@link org.hibernate.query.Query} instance for execution + * + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + */ + Query createQuery(TypedQueryReference typedQueryReference); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an @@ -1616,6 +1633,22 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp @Deprecated Query createQuery(String queryString); + /** + * Create a typed {@link Query} instance for the given typed query reference. + * + * @param typedQueryReference the type query reference + * + * @return The {@link Query} instance for execution + * + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + */ + Query createQuery(TypedQueryReference typedQueryReference); + /** * Create an instance of {@link SelectionQuery} for the given HQL/JPQL * query string and query result type. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 87cf50bb0..ab57ee718 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -12,6 +12,7 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.PersistenceException; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -34,6 +35,7 @@ import org.hibernate.reactive.mutiny.Mutiny.Query; import org.hibernate.reactive.mutiny.Mutiny.SelectionQuery; import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; @@ -140,6 +142,12 @@ public MutationQuery createMutationQuery(JpaCriteriaInsert insert) { return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ), factory ); } + @Override + public Query createQuery(TypedQueryReference typedQueryReference) { + ReactiveQuery reactiveQuery = delegate.createReactiveQuery( typedQueryReference ); + return new MutinyQueryImpl<>( reactiveQuery, factory ); + } + @Override @Deprecated public Query createQuery(String queryString) { return new MutinyQueryImpl<>( delegate.createReactiveQuery( queryString ), factory ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index 5605a7911..cb7db4a8e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -7,6 +7,7 @@ import io.smallrye.mutiny.Uni; import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -17,6 +18,7 @@ import org.hibernate.reactive.mutiny.Mutiny.Query; import org.hibernate.reactive.mutiny.Mutiny.SelectionQuery; import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveStatelessSession; import java.util.List; @@ -71,6 +73,12 @@ public Uni get(EntityGraph entityGraph, Object id) { return uni( () -> delegate.reactiveGet( entityClass, id, null, entityGraph ) ); } + @Override + public Query createQuery(TypedQueryReference typedQueryReference) { + ReactiveQuery reactiveQuery = delegate.createReactiveQuery( typedQueryReference ); + return new MutinyQueryImpl<>( reactiveQuery, factory ); + } + @Override public Query createQuery(String queryString) { return new MutinyQueryImpl<>( delegate.createReactiveQuery( queryString ), factory ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index a3b667fea..c9a7cc11b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -5,6 +5,7 @@ */ package org.hibernate.reactive.session; +import jakarta.persistence.TypedQueryReference; import java.util.concurrent.CompletionStage; import org.hibernate.Incubating; @@ -53,6 +54,8 @@ public interface ReactiveQueryProducer extends ReactiveConnectionSupplier { ReactiveQuery createReactiveQuery(String queryString); + ReactiveQuery createReactiveQuery(TypedQueryReference typedQueryReference); + ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery); ReactiveQuery createReactiveQuery(String queryString, Class resultType); 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 a5489d907..f3cefc7dc 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 @@ -5,6 +5,8 @@ */ package org.hibernate.reactive.session.impl; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CommonAbstractCriteria; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Map; @@ -76,6 +78,8 @@ import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; +import org.hibernate.query.specification.internal.MutationSpecificationImpl; +import org.hibernate.query.specification.internal.SelectionSpecificationImpl; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.spi.NamedNativeQueryMemento; @@ -357,6 +361,33 @@ protected ReactiveQueryImplementor createReactiveCriteriaQuery(SqmStateme return query; } + @Override + public ReactiveQuery createReactiveQuery(TypedQueryReference typedQueryReference) { + checksBeforeQueryCreation(); + if ( typedQueryReference instanceof SelectionSpecificationImpl specification ) { + final CriteriaQuery query = specification.buildCriteria( getCriteriaBuilder() ); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, specification.getResultType(), this ); + } + if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { + final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); + // Workaround for ORM, can be remove when this issue is solved: https://hibernate.atlassian.net/browse/HHH-19386 + Class type = specification.getResultType() == Void.class + ? null + : (Class) specification.getResultType(); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, type, this ); + } + @SuppressWarnings("unchecked") + // this cast is fine because of all our impls of TypedQueryReference return Class + final Class resultType = (Class) typedQueryReference.getResultType(); + ReactiveQueryImplementor query = (ReactiveQueryImplementor) buildNamedQuery( + typedQueryReference.getName(), + memento -> createSqmQueryImplementor( resultType, memento ), + memento -> createNativeQueryImplementor( resultType, memento ) + ); + typedQueryReference.getHints().forEach( query::setHint ); + return query; + } + @Override public ReactiveQuery createReactiveQuery(String queryString) { return createReactiveQuery( queryString, null ); 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 a723fe49e..5079f0315 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 @@ -42,6 +42,8 @@ import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.named.NamedResultSetMappingMemento; +import org.hibernate.query.specification.internal.MutationSpecificationImpl; +import org.hibernate.query.specification.internal.SelectionSpecificationImpl; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.spi.NativeQueryImplementor; @@ -77,6 +79,8 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CommonAbstractCriteria; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -218,15 +222,14 @@ public CompletionStage> reactiveGet(Class entityClass, Object... Object[] sids = new Object[ids.length]; System.arraycopy( ids, 0, sids, 0, ids.length ); - final CompletionStage> stage = - getEntityPersister( entityClass.getName() ) - .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) - .whenComplete( (v, e) -> { - if ( getPersistenceContext().isLoadFinished() ) { - getPersistenceContext().clear(); - } - } ); - return (CompletionStage>) stage; + return getEntityPersister( entityClass.getName() ) + .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) + .whenComplete( (v, e) -> { + if ( getPersistenceContext().isLoadFinished() ) { + getPersistenceContext().clear(); + } + } ) + .thenApply( objects -> (List) objects ); } @Override @@ -793,6 +796,33 @@ public RootGraphImplementor getEntityGraph(Class entity, String name) return (RootGraphImplementor) entityGraph; } + @Override + public ReactiveQuery createReactiveQuery(TypedQueryReference typedQueryReference) { + checksBeforeQueryCreation(); + if ( typedQueryReference instanceof SelectionSpecificationImpl specification ) { + final CriteriaQuery query = specification.buildCriteria( getCriteriaBuilder() ); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, specification.getResultType(), this ); + } + if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { + final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); + // Workaround for ORM, can be remove when this issue is solved: https://hibernate.atlassian.net/browse/HHH-19386 + Class type = (Class) specification.getResultType() == Void.class + ? null + : (Class) specification.getResultType(); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, type, this ); + } + @SuppressWarnings("unchecked") + // this cast is fine because of all our impls of TypedQueryReference return Class + final Class resultType = (Class) typedQueryReference.getResultType(); + ReactiveQueryImplementor query = (ReactiveQueryImplementor) buildNamedQuery( + typedQueryReference.getName(), + memento -> createSqmQueryImplementor( resultType, memento ), + memento -> createNativeQueryImplementor( resultType, memento ) + ); + typedQueryReference.getHints().forEach( query::setHint ); + return query; + } + @Override public ReactiveSqmQueryImplementor createReactiveQuery(String queryString) { return createReactiveQuery( queryString, null ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 39bb81dd7..1920acc95 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -5,6 +5,7 @@ */ package org.hibernate.reactive.stage; +import jakarta.persistence.TypedQueryReference; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; @@ -25,6 +26,7 @@ import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Page; +import org.hibernate.query.Query; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; @@ -985,6 +987,22 @@ default CompletionStage lock(Object entity, LockModeType lockModeType) { */ MutationQuery createMutationQuery(JpaCriteriaInsert insert); + /** + * Create a typed {@link Query} instance for the given typed query reference. + * + * @param typedQueryReference the type query reference + * + * @return The {@link Query} instance for execution + * + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + */ + Query createQuery(TypedQueryReference typedQueryReference); + /** * Create an instance of {@link Query} for the given HQL/JPQL query * string or HQL/JPQL update or delete statement. In the case of an @@ -1637,6 +1655,22 @@ default CompletionStage get(Class entityClass, Object id, LockModeType @Deprecated Query createQuery(String queryString); + /** + * Create a typed {@link Query} instance for the given typed query reference. + * + * @param typedQueryReference the type query reference + * + * @return The {@link Query} instance for execution + * + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + */ + Query createQuery(TypedQueryReference typedQueryReference); + /** * Create an instance of {@link SelectionQuery} for the given HQL/JPQL * query string and query result type. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index 150c2ec96..dd25105f1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -5,6 +5,7 @@ */ package org.hibernate.reactive.stage.impl; +import jakarta.persistence.TypedQueryReference; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; @@ -26,6 +27,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.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; @@ -530,6 +532,12 @@ public EntityGraph createEntityGraph(Class rootType, String graphName) return delegate.createEntityGraph( rootType, graphName ); } + @Override + public Query createQuery(TypedQueryReference typedQueryReference) { + ReactiveQuery reactiveQuery = delegate.createReactiveQuery( typedQueryReference ); + return new StageQueryImpl<>( reactiveQuery ); + } + @Override @Deprecated public Query createQuery(String queryString) { return new StageQueryImpl<>( delegate.createReactiveQuery( queryString ) ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index c48a7eab8..425a0d363 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -10,6 +10,7 @@ import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveStatelessSession; import org.hibernate.reactive.stage.Stage; import org.hibernate.reactive.stage.Stage.MutationQuery; @@ -17,6 +18,7 @@ import org.hibernate.reactive.stage.Stage.SelectionQuery; import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -289,6 +291,12 @@ public Query createQuery(String queryString) { return new StageQueryImpl<>( delegate.createReactiveQuery( queryString ) ); } + @Override + public Query createQuery(TypedQueryReference typedQueryReference) { + ReactiveQuery reactiveQuery = delegate.createReactiveQuery( typedQueryReference ); + return new StageQueryImpl<>( reactiveQuery ); + } + @Override public SelectionQuery createSelectionQuery(String queryString, Class resultType) { return new StageSelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType ) ); From 66c6ab476110b7cc697d3d0d8763e79fbf902f36 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 25 Apr 2025 17:08:00 +0200 Subject: [PATCH 132/201] [#2214] Test query execution using a TypedQueryReference --- .../reactive/QuerySpecificationTest.java | 423 ++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/QuerySpecificationTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QuerySpecificationTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QuerySpecificationTest.java new file mode 100644 index 000000000..01f85f35e --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/QuerySpecificationTest.java @@ -0,0 +1,423 @@ +/* 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.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl; +import org.hibernate.query.Order; +import org.hibernate.query.restriction.Restriction; +import org.hibernate.query.specification.MutationSpecification; +import org.hibernate.query.specification.SelectionSpecification; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +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.TypedQueryReference; +import jakarta.persistence.metamodel.SingularAttribute; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.query.Order.asc; +import static org.hibernate.query.Order.desc; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Test for queries created using {@link SelectionSpecification}. + */ +@Timeout(value = 10, timeUnit = MINUTES) +public class QuerySpecificationTest extends BaseReactiveTest { + static final Book hibBook = new Book( 1L, "Hibernate in Action" ); + static final Book jpBook = new Book( 3L, "Java Persistence with Hibernate" ); + static final Book animalFarmBook = new Book( 2L, "Animal Farm" ); + + // This is only added when testing order with multiple columns + static final Book animalFarmBook2 = new Book( 4L, "Animal Farm" ); + + @Override + protected Collection> annotatedEntities() { + return List.of( Book.class ); + } + + @BeforeEach + public void populateDB(VertxTestContext context) { + test( context, getSessionFactory().withTransaction( session -> session + .persist( hibBook, jpBook, animalFarmBook ) ) + ); + } + + static Stream singleColumnOrderExpectation() { + return Stream.of( + arguments( asc( Book.class, "title" ), List.of( animalFarmBook, hibBook, jpBook ) ), + arguments( desc( Book.class, "title" ), List.of( jpBook, hibBook, animalFarmBook ) ), + arguments( asc( Book.class, "isbn" ), List.of( hibBook, animalFarmBook, jpBook ) ), + arguments( desc( Book.class, "isbn" ), List.of( jpBook, animalFarmBook, hibBook ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleColumnOrderWithStage(Order order, List expectedList, VertxTestContext context) { + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( order ) + .reference(); + + test( context, getSessionFactory() + .withSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .thenAccept( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleColumnOrderWithStageStateless(Order order, List expectedList, VertxTestContext context) { + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( order ) + .reference(); + + test( context, getSessionFactory() + .withStatelessSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .thenAccept( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleColumnOrderWithMutiny(Order order, List expectedList, VertxTestContext context) { + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( order ) + .reference(); + + test( context, getMutinySessionFactory() + .withSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .invoke( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleColumnOrderWithMutinyStateless(Order order, List expectedList, VertxTestContext context) { + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( order ) + .reference(); + + test( context, getMutinySessionFactory() + .withStatelessSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .invoke( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleAttributeOrderWithStage(Order order, List expectedList, VertxTestContext context) { + final SingularAttribute attribute = attribute( order.attributeName() ); + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( Order.by( attribute, order.direction() ) ) + .reference(); + + test( context, getSessionFactory() + .withSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .thenAccept( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleAttributeOrderWithStageStateless(Order order, List expectedList, VertxTestContext context) { + final SingularAttribute attribute = attribute( order.attributeName() ); + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( Order.by( attribute, order.direction() ) ) + .reference(); + + test( context, getSessionFactory() + .withStatelessSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .thenAccept( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleAttributeOrderWithMutiny(Order order, List expectedList, VertxTestContext context) { + final SingularAttribute attribute = attribute( order.attributeName() ); + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( Order.by( attribute, order.direction() ) ) + .reference(); + + test( context, getMutinySessionFactory() + .withSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .invoke( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("singleColumnOrderExpectation") + public void singleAttributeOrderWithMutinyStateless(Order order, List expectedList, VertxTestContext context) { + final SingularAttribute attribute = attribute( order.attributeName() ); + var bookReference = SelectionSpecification + .create( Book.class, "from Book" ) + .sort( Order.by( attribute, order.direction() ) ) + .reference(); + + test( context, getMutinySessionFactory() + .withStatelessSession( session -> session + .createQuery( bookReference ) + .getResultList() ) + .invoke( books -> assertThat( books ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("multipleColumnOrderExpectation") + public void multipleColumnsOrderWithStage(List> orders, List expectedList, VertxTestContext context) { + var columnsSpec = SelectionSpecification + .create( Book.class, "from Book" ); + for ( Order order : orders ) { + columnsSpec.sort( order ); + } + + test( context, getSessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .thenCompose( v -> getSessionFactory().withSession( session -> session + .createQuery( columnsSpec.reference() ) + .getResultList() ) ) + .thenAccept( list -> assertThat( list ).isEqualTo( expectedList ) ) + ); + } + + static Stream multipleColumnOrderExpectation() { + return Stream.of( + arguments( + List.of( asc( Book.class, "title" ), asc( Book.class, "isbn" ) ), + List.of( animalFarmBook, animalFarmBook2, hibBook, jpBook ) + ), + arguments( + List.of( asc( Book.class, "title" ), desc( Book.class, "isbn" ) ), + List.of( animalFarmBook2, animalFarmBook, hibBook, jpBook ) + ), + arguments( + List.of( desc( Book.class, "isbn" ), asc( Book.class, "title" ) ), + List.of( animalFarmBook2, jpBook, animalFarmBook, hibBook ) + ), + arguments( + List.of( desc( Book.class, "isbn" ), desc( Book.class, "title" ) ), + List.of( animalFarmBook2, jpBook, animalFarmBook, hibBook ) + ) + ); + } + + @ParameterizedTest + @MethodSource("multipleColumnOrderExpectation") + public void multipleColumnsOrderWithStageStateless(List> orders, List expectedList, VertxTestContext context) { + var columnsSpec = SelectionSpecification + .create( Book.class, "from Book" ); + for ( Order order : orders ) { + columnsSpec.sort( order ); + } + + test( context, getSessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .thenCompose( v -> getSessionFactory().withStatelessSession( session -> session + .createQuery( columnsSpec.reference() ) + .getResultList() ) ) + .thenAccept( list -> assertThat( list ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("multipleColumnOrderExpectation") + public void multipleColumnsOrderWithMutiny(List> orders, List expectedList, VertxTestContext context) { + var columnsSpec = SelectionSpecification + .create( Book.class, "from Book" ); + for ( Order order : orders ) { + columnsSpec.sort( order ); + } + + test( context, getMutinySessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .chain( v -> getMutinySessionFactory().withSession( session -> session + .createQuery( columnsSpec.reference() ) + .getResultList() ) ) + .invoke( list -> assertThat( list ).isEqualTo( expectedList ) ) + ); + } + + @ParameterizedTest + @MethodSource("multipleColumnOrderExpectation") + public void multipleColumnsOrderWithMutinyStateless(List> orders, List expectedList, VertxTestContext context) { + var columnsSpec = SelectionSpecification + .create( Book.class, "from Book" ); + for ( Order order : orders ) { + columnsSpec.sort( order ); + } + + test( context, getMutinySessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .chain( v -> getMutinySessionFactory().withStatelessSession( session -> session + .createQuery( columnsSpec.reference() ) + .getResultList() ) ) + .invoke( list -> assertThat( list ).isEqualTo( expectedList ) ) + ); + } + + @Test + public void mutationSpecificationWithStage(VertxTestContext context) { + SingularAttribute title = (SingularAttribute) attribute( "title" ); + TypedQueryReference deleteAnimalFarm = MutationSpecification + .create( Book.class, "delete Book" ) + .restrict( Restriction.equalIgnoringCase( title, animalFarmBook.title ) ) + .reference(); + + test( context, getSessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .thenCompose( v -> getSessionFactory().withTransaction( session -> session + .createQuery( deleteAnimalFarm ) + .executeUpdate() ) ) + .thenAccept( deleted -> assertThat( deleted ).isEqualTo( 2 ) ) + .thenCompose( v -> getSessionFactory().withSession( session -> session + .createQuery( "from Book", Book.class ).getResultList() ) ) + .thenAccept( list -> assertThat( list ).containsExactlyInAnyOrder( hibBook, jpBook ) ) + ); + } + + @Test + public void mutationSpecificationWithStageStateless(VertxTestContext context) { + SingularAttribute title = (SingularAttribute) attribute( "title" ); + TypedQueryReference deleteAnimalFarm = MutationSpecification + .create( Book.class, "delete Book" ) + .restrict( Restriction.equalIgnoringCase( title, animalFarmBook.title ) ) + .reference(); + + test( context, getSessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .thenCompose( v -> getSessionFactory().withStatelessTransaction( session -> session + .createQuery( deleteAnimalFarm ) + .executeUpdate() ) ) + .thenAccept( deleted -> assertThat( deleted ).isEqualTo( 2 ) ) + .thenCompose( v -> getSessionFactory().withSession( session -> session + .createQuery( "from Book", Book.class ).getResultList() ) ) + .thenAccept( list -> assertThat( list ).containsExactlyInAnyOrder( hibBook, jpBook ) ) + ); + } + + @Test + public void mutationSpecificationWithMutiny(VertxTestContext context) { + SingularAttribute title = (SingularAttribute) attribute( "title" ); + TypedQueryReference deleteAnimalFarm = MutationSpecification + .create( Book.class, "delete Book" ) + .restrict( Restriction.equalIgnoringCase( title, animalFarmBook.title ) ) + .reference(); + + test( context, getMutinySessionFactory() + .withTransaction( session -> session.persist( animalFarmBook2 ) ) + .chain( v -> getMutinySessionFactory().withTransaction( session -> session + .createQuery( deleteAnimalFarm ) + .executeUpdate() ) ) + .invoke( deleted -> assertThat( deleted ).isEqualTo( 2 ) ) + .chain( () -> getMutinySessionFactory().withSession( session -> session + .createQuery( "from Book", Book.class ).getResultList() ) ) + .invoke( list -> assertThat( list ).containsExactlyInAnyOrder( hibBook, jpBook ) ) + ); + } + + @Test + public void mutationSpecificationWithMutinyStateless(VertxTestContext context) { + SingularAttribute title = (SingularAttribute) attribute( "title" ); + TypedQueryReference deleteAnimalFarm = MutationSpecification + .create( Book.class, "delete Book" ) + .restrict( Restriction.equalIgnoringCase( title, animalFarmBook.title ) ) + .reference(); + + test( context, getMutinySessionFactory() + .withStatelessTransaction( session -> session.insert( animalFarmBook2 ) ) + .chain( v -> getMutinySessionFactory().withStatelessTransaction( session -> session + .createQuery( deleteAnimalFarm ) + .executeUpdate() ) ) + .invoke( deleted -> assertThat( deleted ).isEqualTo( 2 ) ) + .chain( () -> getMutinySessionFactory().withStatelessSession( session -> session + .createQuery( "from Book", Book.class ).getResultList() ) ) + .invoke( list -> assertThat( list ).containsExactlyInAnyOrder( hibBook, jpBook ) ) + ); + } + + private SingularAttribute attribute(String name) { + MappingMetamodelImpl metamodel = (MappingMetamodelImpl) getSessionFactory().getMetamodel(); + EntityDomainType bookType = metamodel.getJpaMetamodel().findEntityType( Book.class ); + return bookType.findSingularAttribute( name ); + } + + @Entity(name = "Book") + @Table(name = "OrderTest_Book" ) + public static class Book { + @Id + Long isbn; + String title; + + Book(Long isbn, String title) { + this.isbn = isbn; + this.title = title; + } + + Book() { + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Book book = (Book) o; + return Objects.equals( isbn, book.isbn ) && Objects.equals( + title, + book.title + ); + } + + @Override + public int hashCode() { + return Objects.hash( isbn, title ); + } + + @Override + public String toString() { + return isbn + ":" + title; + } + } +} From d9338b1637f50aaa6cc1204a125e449f6e06fe51 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 16 Apr 2025 10:13:28 +0200 Subject: [PATCH 133/201] [#2181] Add CompletionStages#supplyStage It's useful for when we need to map a try-finally block. --- .../reactive/util/impl/CompletionStages.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java index eb311aba6..397ca0e9e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java @@ -90,6 +90,26 @@ public static CompletionStage completedFuture(T value) { return CompletableFuture.completedFuture( value ); } + /** + * Useful for implementing try-finally blocks. + * For example: + *

    {@code
    +	 * return supplyStage( () -> {
    +	 *     // Any error here will get caught
    +	 *     ...
    +	 *     })
    +	 *     .whenComplete( (r,t) -> {
    +	 *         // finally block
    +	 *         ...
    +	 *     })
    +	 * }
    + */ + public static CompletionStage supplyStage(Supplier> supplier) { + // Using the voidFuture() is the simplest way I found to make sure that everything run in the correct executor + return voidFuture() + .thenCompose( v -> supplier.get() ); + } + public static CompletionStage failedFuture(Throwable t) { CompletableFuture ret = new CompletableFuture<>(); ret.completeExceptionally( t ); From 02bf6c96abc19799583db923427249e154e86f41 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 16 Apr 2025 10:17:06 +0200 Subject: [PATCH 134/201] [#2181] Add log message for JDBCExceptions I'm going to add this because ORM log this message when a JDBCException happens. I don't expect it to happen in Hibernate Reactive, but I don't have time now to check for sure. --- .../java/org/hibernate/reactive/logging/impl/Log.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 75287e5be..6bacd56db 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 @@ -12,6 +12,7 @@ import jakarta.persistence.PersistenceException; import org.hibernate.HibernateException; +import org.hibernate.JDBCException; import org.hibernate.LazyInitializationException; import org.hibernate.cache.CacheException; import org.hibernate.dialect.Dialect; @@ -24,6 +25,7 @@ import org.jboss.logging.annotations.Message; import org.jboss.logging.annotations.MessageLogger; +import static org.jboss.logging.Logger.Level.DEBUG; import static org.jboss.logging.Logger.Level.ERROR; import static org.jboss.logging.Logger.Level.INFO; import static org.jboss.logging.Logger.Level.WARN; @@ -315,4 +317,12 @@ public interface Log extends BasicLogger { @LogMessage(level = WARN) @Message( id= 494, value = "Attempt to merge an uninitialized collection with queued operations; queued operations will be ignored: %s") void ignoreQueuedOperationsOnMerge(String collectionInfoString); + + // Same method in ORM + @LogMessage(level = DEBUG) + @Message(value = "JDBCException was thrown for a transaction marked for rollback. " + + " This is probably due to an operation failing fast due to the transaction being marked for rollback.", + id = 520) + void jdbcExceptionThrownWithTransactionRolledBack(@Cause JDBCException e); + } From e1b6c184fca2d99e261ed98eb9adf18444af1e53 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 16 Apr 2025 10:19:39 +0200 Subject: [PATCH 135/201] [#2181] Align ReactiveStatelessSessionImpl to the changes in ORM 7 --- .../impl/ReactiveStatelessSessionImpl.java | 642 ++++++++++-------- 1 file changed, 372 insertions(+), 270 deletions(-) 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 5079f0315..84483afb3 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 @@ -7,22 +7,18 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.TransientObjectException; import org.hibernate.UnknownEntityTypeException; import org.hibernate.UnresolvableObjectException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; -import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; -import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.PersistentAttributeInterceptable; -import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.monitor.spi.DiagnosticEvent; +import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; import org.hibernate.graph.GraphSemantic; @@ -74,7 +70,7 @@ import org.hibernate.reactive.query.sqm.internal.ReactiveSqmSelectionQueryImpl; import org.hibernate.reactive.session.ReactiveSqmQueryImplementor; import org.hibernate.reactive.session.ReactiveStatelessSession; -import org.hibernate.reactive.util.impl.CompletionStages; +import org.hibernate.reactive.util.impl.CompletionStages.Completable; import org.hibernate.stat.spi.StatisticsImplementor; import jakarta.persistence.EntityGraph; @@ -87,19 +83,24 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; import java.util.function.Supplier; import static java.lang.Boolean.TRUE; import static java.lang.invoke.MethodHandles.lookup; +import static java.util.function.Function.identity; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.engine.internal.Versioning.incrementVersion; import static org.hibernate.engine.internal.Versioning.seedVersion; import static org.hibernate.engine.internal.Versioning.setVersion; +import static org.hibernate.event.internal.DefaultInitializeCollectionEventListener.handlePotentiallyEmptyCollection; import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.internal.util.NullnessUtil.castNonNull; import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.loader.ast.spi.CascadingFetchProfile.REFRESH; +import static org.hibernate.loader.internal.CacheLoadHelper.initializeCollectionFromCache; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.id.impl.IdentifierGeneration.castToIdentifierType; @@ -110,6 +111,7 @@ import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; 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.supplyStage; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -123,11 +125,8 @@ public class ReactiveStatelessSessionImpl extends StatelessSessionImpl implement private static final Log LOG = make( Log.class, lookup() ); private final LoadQueryInfluencers influencers; - private final ReactiveConnection reactiveConnection; - private final ReactiveStatelessSessionImpl batchingHelperSession; - private final PersistenceContext persistenceContext; public ReactiveStatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options, ReactiveConnection connection) { @@ -215,7 +214,7 @@ public CompletionStage> reactiveGet(Class entityClass, Object... checkOpen(); for (Object id : ids) { if ( id == null ) { - throw new IllegalArgumentException("Null id"); + return failedFuture( new IllegalArgumentException( "Null id" ) ); } } @@ -224,12 +223,12 @@ public CompletionStage> reactiveGet(Class entityClass, Object... return getEntityPersister( entityClass.getName() ) .reactiveMultiLoad( sids, this, StatelessSessionImpl.MULTI_ID_LOAD_OPTIONS ) - .whenComplete( (v, e) -> { + .whenComplete( (list, e) -> { if ( getPersistenceContext().isLoadFinished() ) { getPersistenceContext().clear(); } } ) - .thenApply( objects -> (List) objects ); + .thenApply( list -> (List) list ); } @Override @@ -258,7 +257,15 @@ public CompletionStage reactiveGet(String entityName, Object id, LockMode .applyGraph( (RootGraphImplementor) fetchGraph, GraphSemantic.FETCH ); } - return getEntityPersister( entityName ) + ReactiveEntityPersister persister = getEntityPersister( entityName ); + if ( persister.canReadFromCache() ) { + final Object cachedEntity = loadFromSecondLevelCache( persister, generateEntityKey( id, persister ), null, lockMode ); + if ( cachedEntity != null ) { + getPersistenceContext().clear(); + return completedFuture( (T) cachedEntity ); + } + } + return persister .reactiveLoad( id, null, getNullSafeLockMode( lockMode ), this ) .whenComplete( (v, e) -> { if ( getPersistenceContext().isLoadFinished() ) { @@ -283,8 +290,11 @@ public ReactiveEntityPersister getEntityPersister(String entityName, Object obje public CompletionStage reactiveInsert(Object entity) { checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( null, entity ); - return reactiveInsert( entity, persister ) - .thenAccept( v -> { + final Object[] state = persister.getValues( entity ); + return reactiveInsert( entity, state, persister ) + .thenCompose( id -> recreateCollections( entity, id, persister ) ) + .thenAccept( id -> { + firePostInsert( entity, id, state, persister ); final StatisticsImplementor statistics = getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { statistics.insertEntity( persister.getEntityName() ); @@ -292,71 +302,145 @@ public CompletionStage reactiveInsert(Object entity) { } ); } - private CompletionStage reactiveInsert(Object entity, ReactiveEntityPersister persister) { - final Object[] state = persister.getValues( entity ); - final Generator generator = persister.getGenerator(); - if ( !generator.generatedOnExecution() ) { - return generateId( persister, entity, generator ) - .thenCompose( generatedId -> { - final Object id = castToIdentifierType( generatedId, persister ); - if ( persister.isVersioned() ) { - if ( seedVersion( entity, state, persister, this ) ) { - persister.setValues( entity, state ); - } - } - if ( firePreInsert( entity, id, state, persister ) ) { - return voidFuture(); - } - getInterceptor() - .onInsert( - entity, - id, - state, - persister.getPropertyNames(), - persister.getPropertyTypes() - ); - return persister - .insertReactive( id, state, entity, this ) - .thenAccept( ignore -> { - persister.setIdentifier( entity, id, this ); - firePostInsert( entity, id, state, persister ); - } ); - } ); + private static class Loop { + private CompletionStage loop = voidFuture(); + + public void then(Supplier> step) { + loop = loop.thenCompose( v -> step.get() ); } - else { - if ( firePreInsert( entity, null, state, persister ) ) { - return voidFuture(); - } - getInterceptor() - .onInsert( entity, null, state, persister.getPropertyNames(), persister.getPropertyTypes() ); - return persister - .insertReactive( state, entity, this ) - .thenAccept( id -> { - persister.setIdentifier( entity, id, this ); - firePostInsert( entity, id, state, persister ); - } ); + + public void whenComplete(BiConsumer consumer) { + loop = loop.whenComplete( consumer ); } } - private CompletionStage generateId(EntityPersister persister, Object entity, Generator generator) { - if ( generator.generatesOnInsert() ) { - if ( generator instanceof ReactiveIdentifierGenerator reactiveGenerator ) { - return reactiveGenerator.generate(this, this); - } - else if ( generator instanceof BeforeExecutionGenerator beforeExecutionGenerator ) { - return completedFuture( beforeExecutionGenerator.generate(this, entity, null, INSERT) ); - } - else { - throw new IllegalArgumentException( "Unsupported generator type: " + generator.getClass().getName() ); - } - } - else { - final Object id = persister.getIdentifier( entity, this ); - if ( id == null ) { - throw new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ); + private CompletionStage recreateCollections(Object entity, Object id, EntityPersister persister) { + final Completable stage = new Completable<>(); + final Loop loop = new Loop(); + forEachOwnedCollection( + entity, id, persister, (descriptor, collection) -> { + firePreRecreate( collection, descriptor ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginCollectionRecreateEvent(); + loop.then( () -> supplyStage( () -> ( (ReactiveCollectionPersister) descriptor ) + .reactiveRecreate( collection, id, this ) ) + .whenComplete( (t, throwable) -> eventMonitor + .completeCollectionRecreateEvent( event, id, descriptor.getRole(), throwable != null, this ) + ) + .thenAccept( unused -> { + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.recreateCollection( descriptor.getRole() ); + } + firePostRecreate( collection, descriptor ); + } ) + ); + } + ); + loop.whenComplete( stage::complete ); + return stage.getStage(); + } + + private CompletionStage reactiveInsert(Object entity, Object[] state, ReactiveEntityPersister persister) { + if ( persister.isVersioned() ) { + if ( seedVersion( entity, state, persister, this ) ) { + persister.setValues( entity, state ); } + } + final Generator generator = persister.getGenerator(); + if ( generator.generatedBeforeExecution( entity, this ) ) { + return generatedIdBeforeInsert( entity, persister, generator, state ); + } + if ( generator.generatedOnExecution( entity, this ) ) { + return generateIdOnInsert( entity, persister, generator, state ); + } + return applyAssignedIdentifierInsert( entity, persister, state ); + } + + private CompletionStage applyAssignedIdentifierInsert(Object entity, ReactiveEntityPersister persister, Object[] state) { + Object id = persister.getIdentifier( entity, this ); + if ( id == null ) { + return failedFuture( new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ) ); + } + if ( firePreInsert( entity, id, state, persister ) ) { return completedFuture( id ); } + getInterceptor().onInsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityInsertEvent(); + // try-block + return supplyStage( () -> persister.insertReactive( id, state, entity, this ) ) + // finally: catches error in case insertReactive fails before returning a CompletionStage + .whenComplete( (generatedValues, throwable) -> eventMonitor + .completeEntityInsertEvent( event, id, persister.getEntityName(), throwable != null, this ) + ); + } + + private CompletionStage generateIdOnInsert( + Object entity, + ReactiveEntityPersister persister, + Generator generator, + Object[] state) { + if ( !generator.generatesOnInsert() ) { + throw new IdentifierGenerationException( "Identifier generator must generate on insert" ); + } + if ( firePreInsert( entity, null, state, persister ) ) { + return nullFuture(); + } + getInterceptor().onInsert( entity, null, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityInsertEvent(); + // try-block + return supplyStage( () -> persister + .insertReactive( state, entity, this ) + .thenApply( generatedValues -> castNonNull( generatedValues ) + .getGeneratedValue( persister.getIdentifierMapping() ) + ) ) + // finally-block: catch the exceptions from insertReactive and getGeneratedValues + .whenComplete( (id, throwable) -> eventMonitor + .completeEntityInsertEvent( event, id, persister.getEntityName(), throwable != null, this ) + ) + .thenApply( id -> { + persister.setIdentifier( entity, id, this ); + return id; + } ); + } + + private CompletionStage generatedIdBeforeInsert( + Object entity, + ReactiveEntityPersister persister, + Generator generator, + Object[] state) { + if ( !generator.generatesOnInsert() ) { + return failedFuture( new IdentifierGenerationException( "Identifier generator must generate on insert" ) ); + } + return generateIdForInsert( entity, generator, persister ) + .thenCompose( id -> { + persister.setIdentifier( entity, id, this ); + if ( firePreInsert( entity, id, state, persister ) ) { + return completedFuture( id ); + } + getInterceptor().onInsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityInsertEvent(); + // try-block + return supplyStage( () -> persister.insertReactive( id, state, entity, this ) ) + // finally: catches error in case insertReactive fails before returning a CompletionStage + .whenComplete( (generatedValues, throwable) -> eventMonitor + .completeEntityInsertEvent( event, id, persister.getEntityName(), throwable != null, this ) + ) + .thenApply( identity() ); + } ); + } + + private CompletionStage generateIdForInsert(Object entity, Generator generator, ReactiveEntityPersister persister) { + if ( generator instanceof ReactiveIdentifierGenerator reactiveGenerator ) { + return reactiveGenerator.generate( this, this ) + .thenApply( id -> castToIdentifierType( id, persister ) ); + } + + final Object currentValue = generator.allowAssignedIdentifiers() ? persister.getIdentifier( entity ) : null; + return completedFuture( ( (BeforeExecutionGenerator) generator ).generate( this, entity, currentValue, INSERT ) ); } @Override @@ -365,7 +449,57 @@ public CompletionStage reactiveDelete(Object entity) { final ReactiveEntityPersister persister = getEntityPersister( null, entity ); final Object id = persister.getIdentifier( entity, this ); final Object version = persister.getVersion( entity ); - return persister.deleteReactive( id, version, entity, this ); + if ( firePreDelete( entity, id, persister ) ) { + return voidFuture(); + } + + getInterceptor().onDelete( entity, id, persister.getPropertyNames(), persister.getPropertyTypes() ); + return removeCollections( entity, id, persister ) + .thenCompose( v -> { + final Object ck = lockCacheItem( id, version, persister ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityDeleteEvent(); + // try-block + return supplyStage( () -> persister.deleteReactive( id, version, entity, this ) ) + // finally-block + .whenComplete( (unused, throwable) -> eventMonitor + .completeEntityDeleteEvent( event, id, persister.getEntityName(), throwable != null, this ) + ) + .thenAccept( unused -> removeCacheItem( ck, persister ) ); + } ) + .thenAccept( v -> { + firePostDelete( entity, id, persister ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.deleteEntity( persister.getEntityName() ); + } + } ); + } + + 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(); } @Override @@ -390,19 +524,65 @@ private CompletionStage executeReactiveUpdate(Object entity) { final ReactiveEntityPersister persister = getEntityPersister( null, entity ); final Object id = persister.getIdentifier( entity, this ); final Object[] state = persister.getValues( entity ); - final Object oldVersion; + final Object oldVersion = persister.isVersioned() ? persister.getVersion( entity ) : null; if ( persister.isVersioned() ) { - oldVersion = persister.getVersion( entity ); final Object newVersion = incrementVersion( entity, oldVersion, persister, this ); setVersion( state, newVersion, persister ); persister.setValues( entity, state ); } - else { - oldVersion = null; + + if ( firePreUpdate(entity, id, state, persister) ) { + return voidFuture(); } - return persister - .updateReactive( id, state, null, false, null, oldVersion, entity, null, this ) - .thenCompose( CompletionStages::voidFuture ); + + getInterceptor().onUpdate( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final Object ck = lockCacheItem( id, oldVersion, persister ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityUpdateEvent(); + // try-block + return supplyStage( () -> persister + .updateReactive( id, state, null, false, null, oldVersion, entity, null, this ) ) + // finally-block + .whenComplete( (generatedValues, throwable) -> eventMonitor + .completeEntityUpdateEvent( event, id, persister.getEntityName(), throwable != null, this ) + ) + .thenAccept( generatedValues -> removeCacheItem( ck, persister ) ) + .thenCompose( v -> removeAndRecreateCollections( entity, id, persister ) ) + .thenAccept( v -> { + firePostUpdate( entity, id, state, persister ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.updateEntity( persister.getEntityName() ); + } + } ); + } + + 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(); } @Override @@ -422,6 +602,7 @@ public CompletionStage reactiveRefresh(Object entity, LockMode lockMode) { @Override public CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode) { + checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); final Object id = persister.getIdentifier( entity, this ); @@ -432,31 +613,23 @@ public CompletionStage reactiveRefresh(String entityName, Object entity, L if ( persister.canWriteToCache() ) { final EntityDataAccess cacheAccess = persister.getCacheAccessStrategy(); if ( cacheAccess != null ) { - final Object ck = cacheAccess.generateCacheKey( - id, - persister, - getFactory(), - getTenantIdentifier() - ); + final Object ck = cacheAccess.generateCacheKey( id, persister, getFactory(), getTenantIdentifier() ); cacheAccess.evict( ck ); } } return fromInternalFetchProfile( REFRESH, () -> persister.reactiveLoad( id, entity, getNullSafeLockMode( lockMode ), this ) ) .thenAccept( result -> { + UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() ); if ( getPersistenceContext().isLoadFinished() ) { getPersistenceContext().clear(); } - UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() ); } ); } - private CompletionStage fromInternalFetchProfile( - CascadingFetchProfile cascadingFetchProfile, - Supplier> supplier) { + private CompletionStage fromInternalFetchProfile(CascadingFetchProfile cascadingFetchProfile, Supplier> supplier) { CascadingFetchProfile previous = getLoadQueryInfluencers().getEnabledCascadingFetchProfile(); - return voidFuture() - .thenCompose( v -> { + return supplyStage( () -> { getLoadQueryInfluencers().setEnabledCascadingFetchProfile( cascadingFetchProfile ); return supplier.get(); } ) @@ -479,38 +652,30 @@ public CompletionStage reactiveUpsert(Object entity) { public CompletionStage reactiveUpsert(String entityName, Object entity) { checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); - Object id = persister.getIdentifier( entity, this ); - Boolean knownTransient = persister.isTransient( entity, this ); - if ( knownTransient != null && knownTransient ) { - throw new TransientObjectException( - "Object passed to upsert() has a null identifier: " - + persister.getEntityName() ); -// final Generator generator = persister.getGenerator(); -// if ( !generator.generatedOnExecution() ) { -// id = ( (BeforeExecutionGenerator) generator).generate( this, entity, null, INSERT ); -// } - } + final Object id = idToUpsert( entity, persister ); final Object[] state = persister.getValues( entity ); - final Object oldVersion; - if ( persister.isVersioned() ) { - oldVersion = persister.getVersion( entity ); - if ( oldVersion == null ) { - if ( seedVersion( entity, state, persister, this ) ) { - persister.setValues( entity, state ); - } - } - else { - final Object newVersion = incrementVersion( entity, oldVersion, persister, this ); - setVersion( state, newVersion, persister ); - persister.setValues( entity, state ); - } + if ( firePreUpsert( entity, id, state, persister ) ) { + return voidFuture(); } - else { - oldVersion = null; - } - - return persister - .mergeReactive( id, state, null, false, null, oldVersion, entity, null, this ); + getInterceptor().onUpsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final Object oldVersion = versionToUpsert( entity, persister, state ); + final Object ck = lockCacheItem( id, oldVersion, persister ); + final EventMonitor eventMonitor = getEventMonitor(); + final DiagnosticEvent event = eventMonitor.beginEntityUpsertEvent(); + return supplyStage( () -> persister + .mergeReactive( id, state, null, false, null, oldVersion, entity, null, this ) ) + .whenComplete( (v, throwable) -> eventMonitor + .completeEntityUpsertEvent( event, id, persister.getEntityName(), throwable != null, this ) + ) + .thenAccept( v -> { + removeCacheItem( ck, persister ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.upsertEntity( persister.getEntityName() ); + } + } ) + .thenCompose( v -> removeAndRecreateCollections( entity, id, persister ) ) + .thenAccept( v -> firePostUpsert( entity, id, state, persister ) ); } @Override @@ -570,7 +735,6 @@ public CompletionStage reactiveDeleteAll(int batchSize, Object... entities .whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) ); } - @Override public CompletionStage reactiveRefreshAll(Object... entities) { return loop( entities, batchingHelperSession::reactiveRefresh ) @@ -592,96 +756,26 @@ private ReactiveConnection batchingConnection(int batchSize) { .withBatchSize( batchSize ); } - private Object createProxy(EntityKey entityKey) { - final Object proxy = entityKey.getPersister().createProxy( entityKey.getIdentifier(), this ); - getPersistenceContext().addProxy( entityKey, proxy ); - return proxy; + @Override + public CompletionStage reactiveInternalLoad(String entityName, Object id, boolean eager, boolean nullable) { + Object object = super.internalLoad( entityName, id, eager, nullable ); + return object instanceof CompletionStage + ? (CompletionStage) object + : completedFuture( object ); } @Override - public CompletionStage reactiveInternalLoad( - String entityName, - Object id, - boolean eager, - boolean nullable) { - checkOpen(); - - final EntityPersister persister = getEntityPersister( entityName ); - final EntityKey entityKey = generateEntityKey( id, persister ); - - // first, try to load it from the temp PC associated to this SS - final PersistenceContext persistenceContext = getPersistenceContext(); - final Object loaded = persistenceContext.getEntity( entityKey ); - if ( loaded != null ) { - // we found it in the temp PC. Should indicate we are in the midst of processing a result set - // containing eager fetches via join fetch - return completedFuture( loaded ); - } - - if ( !eager ) { - // caller did not request forceful eager loading, see if we can create - // some form of proxy - - // first, check to see if we can use "bytecode proxies" - - final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata(); - if ( enhancementMetadata.isEnhancedForLazyLoading() ) { - - // if the entity defines a HibernateProxy factory, see if there is an - // existing proxy associated with the PC - and if so, use it - if ( persister.getRepresentationStrategy().getProxyFactory() != null ) { - final Object proxy = persistenceContext.getProxy( entityKey ); - - if ( proxy != null ) { - if ( LOG.isTraceEnabled() ) { - LOG.trace( "Entity proxy found in session cache" ); - } - if ( LOG.isDebugEnabled() && ( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUnwrap() ) { - LOG.debug( "Ignoring NO_PROXY to honor laziness" ); - } - - return completedFuture( persistenceContext.narrowProxy( proxy, persister, entityKey, null ) ); - } - - // specialized handling for entities with subclasses with a HibernateProxy factory - if ( persister.hasSubclasses() ) { - // entities with subclasses that define a ProxyFactory can create - // a HibernateProxy. -// LOG.debugf( "Creating a HibernateProxy for to-one association with subclasses to honor laziness" ); - return completedFuture( createProxy( entityKey ) ); - } - return completedFuture( enhancementMetadata.createEnhancedProxy( entityKey, false, this ) ); - } - else if ( !persister.hasSubclasses() ) { - return completedFuture( enhancementMetadata.createEnhancedProxy( entityKey, false, this ) ); - } - // If we get here, then the entity class has subclasses and there is no HibernateProxy factory. - // The entity will get loaded below. - } - else { - if ( persister.hasProxy() ) { - final Object existingProxy = persistenceContext.getProxy( entityKey ); - if ( existingProxy != null ) { - return completedFuture( persistenceContext.narrowProxy( existingProxy, persister, entityKey, null ) ); - } - else { - return completedFuture( createProxy( entityKey ) ); - } - } - } - } - + protected Object internalLoadGet(String entityName, Object id, PersistenceContext persistenceContext) { // otherwise immediately materialize it // IMPLEMENTATION NOTE: increment/decrement the load count before/after getting the value // to ensure that #get does not clear the PersistenceContext. persistenceContext.beforeLoad(); - return this.reactiveGet( persister.getEntityName(), id ) + return this.reactiveGet( entityName, id ) .whenComplete( (r, e) -> persistenceContext.afterLoad() ); } @Override - @SuppressWarnings("unchecked") public CompletionStage reactiveFetch(T association, boolean unproxy) { checkOpen(); if ( association == null ) { @@ -691,84 +785,92 @@ public CompletionStage reactiveFetch(T association, boolean unproxy) { final PersistenceContext persistenceContext = getPersistenceContext(); final LazyInitializer initializer = extractLazyInitializer( association ); if ( initializer != null ) { - if ( initializer.isUninitialized() ) { - final String entityName = initializer.getEntityName(); - final Object id = initializer.getIdentifier(); - initializer.setSession( this ); - persistenceContext.beforeLoad(); - - final ReactiveEntityPersister persister = getEntityPersister( entityName ); - - // This is hard to test because it happens on slower machines like the ones we use on CI. - // See AbstractLazyInitializer#initialize, it happens when the object is not initialized and we need to - // call session.immediateLoad - final CompletionStage stage = initializer.getImplementation() instanceof CompletionStage - ? (CompletionStage) initializer.getImplementation() - : completedFuture( initializer.getImplementation() ); - - return stage.thenCompose( implementation -> persister.reactiveLoad( id, implementation, LockOptions.NONE, this ) ) - .thenApply( entity -> { - checkEntityFound( this, entityName, id, entity ); - initializer.setImplementation( entity ); - return unproxy ? (T) entity : association; - } ) - .whenComplete( (v, e) -> { - initializer.unsetSession(); - persistenceContext.afterLoad(); - if ( persistenceContext.isLoadFinished() ) { - persistenceContext.clear(); - } - } ); - } - else { - // Initialized - return completedFuture( unproxy ? (T) initializer.getImplementation() : association ); - } - } - else if ( association instanceof PersistentCollection collection ) { - if ( collection.wasInitialized() ) { - return completedFuture( association ); - } - else { - final ReactiveCollectionPersister collectionDescriptor = - (ReactiveCollectionPersister) getFactory().getMappingMetamodel() - .getCollectionDescriptor( collection.getRole() ); - - final Object key = collection.getKey(); - persistenceContext.addUninitializedCollection( collectionDescriptor, collection, key ); - collection.setCurrentSession( this ); - return collectionDescriptor.reactiveInitialize( key, this ) - .whenComplete( (v, e) -> { - collection.unsetSession( this ); - if ( persistenceContext.isLoadFinished() ) { - persistenceContext.clear(); - } - } ) - .thenApply( v -> association ); - } + return initializer.isUninitialized() + ? fetchUninitialized( association, unproxy, initializer, persistenceContext ) + : completedFuture( unproxy ? (T) initializer.getImplementation() : association ); } else if ( isPersistentAttributeInterceptable( association ) ) { - final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( association ); - final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { - lazinessInterceptor.setSession( this ); - return forceInitialize( association, null, lazinessInterceptor.getIdentifier(), lazinessInterceptor.getEntityName(), this ) + if ( asPersistentAttributeInterceptable( association ).$$_hibernate_getInterceptor() + instanceof EnhancementAsProxyLazinessInterceptor proxyInterceptor ) { + proxyInterceptor.setSession( this ); + return forceInitialize( association, null, proxyInterceptor.getIdentifier(), proxyInterceptor.getEntityName(), this ) .whenComplete( (i,e) -> { - lazinessInterceptor.unsetSession(); + proxyInterceptor.unsetSession(); if ( persistenceContext.isLoadFinished() ) { persistenceContext.clear(); } } ) .thenApply( i -> association ); - - } - else { - return completedFuture( association ); } } - else { - return completedFuture( association ); + else if ( association instanceof PersistentCollection collection && !collection.wasInitialized() ) { + final ReactiveCollectionPersister collectionDescriptor = (ReactiveCollectionPersister) getFactory() + .getMappingMetamodel().getCollectionDescriptor( collection.getRole() ); + + final Object key = collection.getKey(); + persistenceContext.addUninitializedCollection( collectionDescriptor, collection, key ); + collection.setCurrentSession( this ); + return supplyStage( () -> { + if ( initializeCollectionFromCache( key, collectionDescriptor, collection, this ) ) { + LOG.trace( "Collection fetched from cache" ); + return completedFuture( association ); + } + else { + return collectionDescriptor + .reactiveInitialize( key, this ) + .thenApply( v -> { + handlePotentiallyEmptyCollection( collection, getPersistenceContextInternal(), key, collectionDescriptor ); + LOG.trace( "Collection fetched" ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.fetchCollection( collectionDescriptor.getRole() ); + } + return association; + } ); + } + } ).whenComplete( (t, throwable) -> { + collection.$$_hibernate_setInstanceId( 0 ); + collection.unsetSession( this ); + if ( persistenceContext.isLoadFinished() ) { + persistenceContext.clear(); + } + } ); } + + return completedFuture( association ); + } + + private CompletionStage fetchUninitialized( + T association, + boolean unproxy, + LazyInitializer initializer, + PersistenceContext persistenceContext) { + final String entityName = initializer.getEntityName(); + final Object id = initializer.getIdentifier(); + initializer.setSession( this ); + persistenceContext.beforeLoad(); + return reactiveImplementation( initializer ) + .thenApply( entity -> { + checkEntityFound( this, entityName, id, entity ); + initializer.setImplementation( entity ); + return unproxy ? (T) entity : association; + } ) + .whenComplete( (v, e) -> { + initializer.unsetSession(); + persistenceContext.afterLoad(); + if ( persistenceContext.isLoadFinished() ) { + persistenceContext.clear(); + } + } ); + } + + private static CompletionStage reactiveImplementation(LazyInitializer initializer) { + // This is hard to test because it happens on slower machines like the ones we use on CI. + // See AbstractLazyInitializer#initialize, it happens when the object is not initialized, and we need to + // call session.immediateLoad + return initializer.getImplementation() instanceof CompletionStage + ? (CompletionStage) initializer.getImplementation() + : completedFuture( initializer.getImplementation() ); } @Override From d5b75b5d2feb4c945710e51fc0c86d7c35a0ea3c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 16 Apr 2025 10:20:18 +0200 Subject: [PATCH 136/201] [#2181] Align ReactiveSessionImpl to the changes in ORM 7 --- .../reactive/session/ReactiveSession.java | 3 + .../session/impl/ReactiveSessionImpl.java | 373 +++++++++--------- 2 files changed, 180 insertions(+), 196 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index f53ed4fca..88cfe3ec6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -20,6 +20,7 @@ import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.DeleteContext; +import org.hibernate.event.spi.LoadEventListener; import org.hibernate.event.spi.MergeContext; import org.hibernate.event.spi.PersistContext; import org.hibernate.event.spi.RefreshContext; @@ -66,6 +67,8 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveMerge(Object object, MergeContext copiedAlready); + CompletionStage reactiveLoad(LoadEventListener.LoadType loadType, Object id, String entityName, LockOptions lockOptions, Boolean readOnly); + CompletionStage reactiveFlush(); CompletionStage reactiveAutoflush(); 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 f3cefc7dc..02b392a27 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 @@ -5,16 +5,6 @@ */ package org.hibernate.reactive.session.impl; -import jakarta.persistence.TypedQueryReference; -import jakarta.persistence.criteria.CommonAbstractCriteria; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; - import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -66,6 +56,7 @@ import org.hibernate.jpa.spi.NativeQueryTupleTransformer; import org.hibernate.loader.LoaderLogging; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.internal.IdentifierLoadAccessImpl; import org.hibernate.loader.internal.LoadAccessContext; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.NaturalIdMapping; @@ -127,10 +118,19 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CommonAbstractCriteria; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; import static java.lang.Boolean.TRUE; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; @@ -143,10 +143,12 @@ import static org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister.forceInitialize; import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound; 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.nullFuture; import static org.hibernate.reactive.util.impl.CompletionStages.rethrow; import static org.hibernate.reactive.util.impl.CompletionStages.returnNullorRethrow; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; +import static org.hibernate.reactive.util.impl.CompletionStages.supplyStage; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -217,8 +219,7 @@ public Object immediateLoad(String entityName, Object id) throws HibernateExcept public CompletionStage reactiveImmediateLoad(String entityName, Object id) throws HibernateException { if ( LOG.isDebugEnabled() ) { - final EntityPersister persister = getFactory().getMappingMetamodel() - .getEntityDescriptor( entityName ); + final EntityPersister persister = requireEntityPersister( entityName ); LOG.debugf( "Initializing proxy: %s", MessageHelper.infoString( persister, id, getFactory() ) ); } threadCheck(); @@ -251,10 +252,7 @@ public CompletionStage reactiveInternalLoad(String entityName, Object id } threadCheck(); - final LoadEvent event = new LoadEvent( - id, entityName, true, this, - getReadOnlyFromLoadQueryInfluencers() - ); + final LoadEvent event = makeLoadEvent( entityName, id, getReadOnlyFromLoadQueryInfluencers(), true ); return fireLoadNoChecks( event, type ) .thenApply( v -> { final Object result = event.getResult(); @@ -270,6 +268,43 @@ public CompletionStage reactiveInternalLoad(String entityName, Object id } ); } + @Override + public Object load(LoadEventListener.LoadType loadType, Object id, String entityName, LockOptions lockOptions, Boolean readOnly) { + // When the user needs a reference to the entity, we are not supposed to touche the database, and we don't return + // a CompletionStage. So it's fine to delegate to ORM. + // Everywhere else, reactiveLoad should be used. + return super.load( loadType, id, entityName, lockOptions, readOnly ); + } + + /** + * @see SessionImpl#load(LoadEventListener.LoadType, Object, String, LockOptions, Boolean) + */ + public CompletionStage reactiveLoad(LoadEventListener.LoadType loadType, Object id, String entityName, LockOptions lockOptions, Boolean readOnly) { + if ( lockOptions != null ) { + // (from ORM) TODO: I doubt that this branch is necessary, and it's probably even wrong + final LoadEvent event = makeLoadEvent( entityName, id, readOnly, lockOptions ); + return fireLoad( event, loadType ) + .thenApply( v -> { + final Object result = event.getResult(); + releaseLoadEvent( event ); + return result; + } ); + } + else { + final LoadEvent event = makeLoadEvent( entityName, id, readOnly, false ); + return supplyStage( () -> fireLoad( event, loadType ) + .thenApply( v -> { + final Object result = event.getResult(); + releaseLoadEvent( event ); + if ( !loadType.isAllowNulls() && result == null ) { + getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id ); + } + return result; + } ) + ).whenComplete( (o, throwable) -> afterOperation( throwable != null ) ); + } + } + @Override //Note: when making changes to this method, please also consider // the similar code in Mutiny.fetch() and Stage.fetch() @@ -1066,7 +1101,7 @@ public CompletionStage reactiveForceFlush(EntityEntry entry) { } if ( getPersistenceContextInternal().getCascadeLevel() > 0 ) { - return CompletionStages.failedFuture( new ObjectDeletedException( + return failedFuture( new ObjectDeletedException( "deleted object would be re-saved by cascade (remove deleted object from associations)", entry.getId(), entry.getPersister().getEntityName() @@ -1194,10 +1229,16 @@ private CompletionStage fireLock(LockEvent event) { } @Override - public CompletionStage reactiveGet( - Class entityClass, - Object id) { - return new ReactiveIdentifierLoadAccessImpl<>( entityClass ).load( id ); + public CompletionStage reactiveGet(Class entityClass, Object id) { + return reactiveById( entityClass ).load( id ); + } + + private ReactiveIdentifierLoadAccessImpl reactiveById(Class entityClass) { + return new ReactiveIdentifierLoadAccessImpl<>( this, requireEntityPersister( entityClass ) ); + } + + private ReactiveIdentifierLoadAccessImpl reactiveById(String entityName) { + return new ReactiveIdentifierLoadAccessImpl<>( this, requireEntityPersister( entityName ) ); } @Override @@ -1207,60 +1248,77 @@ public CompletionStage reactiveFind( LockOptions lockOptions, EntityGraph fetchGraph) { checkOpen(); + return supplyStage( () -> { + if ( fetchGraph != null ) { + getLoadQueryInfluencers() + .getEffectiveEntityGraph() + .applyGraph( (RootGraphImplementor) fetchGraph, GraphSemantic.FETCH ); + } + getLoadQueryInfluencers().setReadOnly( readOnlyHint( null ) ); + + return reactiveById( entityClass ) + .with( determineAppropriateLocalCacheMode( null ) ) + .with( lockOptions ) + .load( id ); + } ).handle( CompletionStages::handle ) + .thenCompose( handler -> handleReactiveFindException( entityClass, id, lockOptions, handler ) ) + .whenComplete( (v, e) -> { + getLoadQueryInfluencers().getEffectiveEntityGraph().clear(); + getLoadQueryInfluencers().setReadOnly( null ); + } ); + } - if ( fetchGraph != null ) { - getLoadQueryInfluencers() - .getEffectiveEntityGraph() - .applyGraph( (RootGraphImplementor) fetchGraph, GraphSemantic.FETCH ); - } - -// Boolean readOnly = properties == null ? null : (Boolean) properties.get( QueryHints.HINT_READONLY ); -// getLoadQueryInfluencers().setReadOnly( readOnly ); - - final ReactiveIdentifierLoadAccessImpl loadAccess = - new ReactiveIdentifierLoadAccessImpl<>( entityClass ) - .with( determineAppropriateLocalCacheMode( null ) ) - .with( lockOptions ); - - return loadAccess.load( id ) - .handle( (result, e) -> { - if ( e instanceof EntityNotFoundException ) { - // DefaultLoadEventListener.returnNarrowedProxy may throw ENFE (see HHH-7861 for details), - // which find() should not throw. Find() should return null if the entity was not found. - // if ( log.isDebugEnabled() ) { - // String entityName = entityClass != null ? entityClass.getName(): null; - // String identifierValue = id != null ? id.toString() : null ; - // log.ignoringEntityNotFound( entityName, identifierValue ); - // } - throw new UnsupportedOperationException(); - } - if ( e instanceof ObjectDeletedException ) { - //the spec is silent about people doing remove() find() on the same PC - throw new UnsupportedOperationException(); - } - if ( e instanceof ObjectNotFoundException ) { - //should not happen on the entity itself with get - throw new IllegalArgumentException( e.getMessage(), e ); - } - if ( e instanceof MappingException - || e instanceof TypeMismatchException - || e instanceof ClassCastException ) { - throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) ); - } - if ( e instanceof JDBCException ) { -// if ( accessTransaction().getRollbackOnly() ) { -// // assume this is the similar to the WildFly / IronJacamar "feature" described under HHH-12472 -// throw new UnsupportedOperationException(); -// } - throw getExceptionConverter().convert( (JDBCException) e, lockOptions ); - } - if ( e instanceof RuntimeException ) { - throw getExceptionConverter().convert( (RuntimeException) e, lockOptions ); - } + private CompletionStage handleReactiveFindException( + Class entityClass, + Object primaryKey, + LockOptions lockOptions, + CompletionStages.CompletionStageHandler handler) { + if ( !handler.hasFailed() ) { + return handler.getResultAsCompletionStage(); + } + final Throwable e = handler.getThrowable(); + if ( e instanceof EntityNotFoundException ) { + // We swallow other sorts of EntityNotFoundException and return null + // For example, DefaultLoadEventListener.proxyImplementation() throws + // EntityNotFoundException if there's an existing proxy in the session, + // but the underlying database row has been deleted (see HHH-7861) + logIgnoringEntityNotFound( entityClass, primaryKey ); + return nullFuture(); + } + if ( e instanceof ObjectDeletedException ) { + // the spec is silent about people doing remove() find() on the same PC + return null; + } + if ( e instanceof ObjectNotFoundException ) { + // should not happen on the entity itself with get + // TODO: in fact this will occur instead of EntityNotFoundException + // when using StandardEntityNotFoundDelegate, so probably we + // should return null here, as we do above + return failedFuture( new IllegalArgumentException( e.getMessage(), e ) ); + } + if ( e instanceof MappingException || e instanceof TypeMismatchException || e instanceof ClassCastException ) { + return failedFuture( getExceptionConverter().convert( new IllegalArgumentException( + e.getMessage(), + e + ) ) ); + } + if ( e instanceof JDBCException ) { + // I don't think this is ever going to happen in Hibernate Reactive + if ( accessTransaction().isActive() && accessTransaction().getRollbackOnly() ) { + // Assume situation HHH-12472 running on WildFly + // Just log the exception and return null + LOG.jdbcExceptionThrownWithTransactionRolledBack( (JDBCException) e ); + return nullFuture(); + } + else { + return failedFuture( getExceptionConverter().convert( (JDBCException) e, lockOptions ) ); + } + } + if ( e instanceof RuntimeException ) { + return failedFuture( getExceptionConverter().convert( (RuntimeException) e, lockOptions ) ); + } - return result; - } ) - .whenComplete( (v, e) -> getLoadQueryInfluencers().getEffectiveEntityGraph().clear() ); + return handler.getResultAsCompletionStage(); } @Override @@ -1281,13 +1339,18 @@ private ReactiveEntityPersister entityPersister(Class entityClass) { return (ReactiveEntityPersister) getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ); } - private CompletionStage fireReactiveLoad(LoadEvent event, LoadEventListener.LoadType loadType) { + private CompletionStage fireLoad(LoadEvent event, LoadEventListener.LoadType loadType) { checkOpenOrWaitingForAutoClose(); - return fireLoadNoChecks( event, loadType ) - .whenComplete( (v, e) -> delayedAfterCompletion() ); + .thenAccept( v -> delayedAfterCompletion() ); } + /** + * This version of {@link #load} is for use by internal methods only. + * It skips the session open check, transaction sync checks, and so on, + * which have been shown to be expensive (apparently they prevent these + * hot methods from being inlined). + */ private CompletionStage fireLoadNoChecks(LoadEvent event, LoadEventListener.LoadType loadType) { pulseTransactionCoordinator(); @@ -1310,76 +1373,38 @@ public void checkTransactionNeededForUpdateOperation(String exceptionMessage) { //no-op because we don't support transactions } - private class ReactiveIdentifierLoadAccessImpl { - - private final EntityPersister entityPersister; - - private LockOptions lockOptions; - private CacheMode cacheMode; - - //Note that entity graphs aren't supported at all - //because we're not using the EntityLoader from - //the plan package, so this stuff is useless - private RootGraphImplementor rootGraph; - private GraphSemantic graphSemantic; - - public ReactiveIdentifierLoadAccessImpl(EntityPersister entityPersister) { - this.entityPersister = entityPersister; - } - - public ReactiveIdentifierLoadAccessImpl(String entityName) { - this( getFactory().getMappingMetamodel().getEntityDescriptor( entityName ) ); - } - - public ReactiveIdentifierLoadAccessImpl(Class entityClass) { - this( getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ) ); - } - - public final ReactiveIdentifierLoadAccessImpl with(LockOptions lockOptions) { - this.lockOptions = lockOptions; - return this; - } - - public ReactiveIdentifierLoadAccessImpl with(CacheMode cacheMode) { - this.cacheMode = cacheMode; - return this; - } + private class ReactiveIdentifierLoadAccessImpl extends IdentifierLoadAccessImpl> { - public ReactiveIdentifierLoadAccessImpl with(RootGraph graph, GraphSemantic semantic) { - rootGraph = (RootGraphImplementor) graph; - graphSemantic = semantic; - return this; - } - - public final CompletionStage getReference(Object id) { - return perform( () -> doGetReference( id ) ); + public ReactiveIdentifierLoadAccessImpl(LoadAccessContext context, EntityPersister entityPersister) { + super(context, entityPersister); } + @Override protected CompletionStage perform(Supplier> executor) { - if ( graphSemantic != null ) { - if ( rootGraph == null ) { + if ( getGraphSemantic() != null ) { + if ( getRootGraph() == null ) { throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" ); } } CacheMode sessionCacheMode = getCacheMode(); boolean cacheModeChanged = false; - if ( cacheMode != null ) { + if ( getCacheMode() != null ) { // naive check for now... // todo : account for "conceptually equal" - if ( cacheMode != sessionCacheMode ) { - setCacheMode( cacheMode ); + if ( getCacheMode() != sessionCacheMode ) { + setCacheMode( getCacheMode() ); cacheModeChanged = true; } } - if ( graphSemantic != null ) { - getLoadQueryInfluencers().getEffectiveEntityGraph().applyGraph( rootGraph, graphSemantic ); + if ( getGraphSemantic() != null ) { + getLoadQueryInfluencers().getEffectiveEntityGraph().applyGraph( getRootGraph(), getGraphSemantic() ); } boolean finalCacheModeChanged = cacheModeChanged; return executor.get() .whenComplete( (v, x) -> { - if ( graphSemantic != null ) { + if ( getGraphSemantic() != null ) { getLoadQueryInfluencers().getEffectiveEntityGraph().clear(); } if ( finalCacheModeChanged ) { @@ -1389,71 +1414,39 @@ protected CompletionStage perform(Supplier> executor) { } ); } - @SuppressWarnings("unchecked") + @Override protected CompletionStage doGetReference(Object id) { - if ( lockOptions != null ) { - LoadEvent event = new LoadEvent( - id, - entityPersister.getEntityName(), - lockOptions, - ReactiveSessionImpl.this, - getReadOnlyFromLoadQueryInfluencers() - ); - return fireReactiveLoad( event, LoadEventListener.LOAD ).thenApply( v -> (T) event.getResult() ); - } - - LoadEvent event = new LoadEvent( - id, - entityPersister.getEntityName(), - false, - ReactiveSessionImpl.this, - getReadOnlyFromLoadQueryInfluencers() - ); - return fireReactiveLoad( event, LoadEventListener.LOAD ) - .thenApply( v -> { - if ( event.getResult() == null ) { - getFactory().getEntityNotFoundDelegate().handleEntityNotFound( - entityPersister.getEntityName(), - id - ); - } - return (T) event.getResult(); - } ).whenComplete( (v, x) -> afterOperation( x != null ) ); - } - - public final CompletionStage load(Object id) { - return perform( () -> doLoad( id, LoadEventListener.GET ) ); + // getReference si supposed to return T, not CompletionStage + // I can't think of a way to change the super class so that it is mapped correctly. + // So, for now, I will throw an exception and make sure that it doesn't really get called + // (the one in SessionImpl should be used) + throw new UnsupportedOperationException(); } - // public final CompletionStage fetch(Object id) { -// return perform( () -> doLoad( id, LoadEventListener.IMMEDIATE_LOAD) ); -// } -// + @Override @SuppressWarnings("unchecked") - protected final CompletionStage doLoad(Object id, LoadEventListener.LoadType loadType) { + protected CompletionStage doLoad(Object id) { if ( id == null ) { + // This is needed to make the tests with NaturalIds pass. + // It's was already part of Hibernate Reactive. + // I'm not sure why though, it doesn't seem like Hibernate ORM does it. return nullFuture(); } - if ( lockOptions != null ) { - LoadEvent event = new LoadEvent( - id, - entityPersister.getEntityName(), - lockOptions, - ReactiveSessionImpl.this, - getReadOnlyFromLoadQueryInfluencers() - ); - return fireReactiveLoad( event, loadType ).thenApply( v -> (T) event.getResult() ); - } - LoadEvent event = new LoadEvent( - id, - entityPersister.getEntityName(), - false, - ReactiveSessionImpl.this, - getReadOnlyFromLoadQueryInfluencers() - ); - return fireReactiveLoad( event, loadType ) - .whenComplete( (v, t) -> afterOperation( t != null ) ) - .thenApply( v -> (T) event.getResult() ); + final ReactiveSession session = (ReactiveSession) getContext().getSession(); + return supplyStage( () -> session + .reactiveLoad( LoadEventListener.GET, coerceId( id, session.getFactory() ), getEntityPersister().getEntityName(), getLockOptions(), isReadOnly( getContext().getSession() ) ) + ) + .handle( CompletionStages::handle ) + .thenCompose( handler -> handler.getThrowable() instanceof ObjectNotFoundException + ? nullFuture() + : handler.getResultAsCompletionStage() + ) + .thenApply( result -> { + // ORM calls + // initializeIfNecessary( result ); + // But, Hibernate Reactive doesn't support lazy initializations + return (T) result; + } ); } } @@ -1617,10 +1610,6 @@ public NaturalIdLoadAccessImpl with(LockOptions lockOptions) { return this; } - protected void synchronizationEnabled(boolean synchronizationEnabled) { - this.synchronizationEnabled = synchronizationEnabled; - } - /** * @see org.hibernate.loader.internal.BaseNaturalIdLoadAccessImpl#doGetReference(Object) */ @@ -1700,14 +1689,6 @@ protected void performAnyNeededCrossReferenceSynchronizations() { } } - protected final ReactiveIdentifierLoadAccessImpl getIdentifierLoadAccess() { - final ReactiveIdentifierLoadAccessImpl identifierLoadAccess = new ReactiveIdentifierLoadAccessImpl<>( entityPersister ); - if ( this.lockOptions != null ) { - identifierLoadAccess.with( lockOptions ); - } - return identifierLoadAccess; - } - protected ReactiveEntityPersister entityPersister() { return entityPersister; } From 5029fc98f21b8ac8794360110b739b01b5bcb51e Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 31 Mar 2025 21:56:30 +0200 Subject: [PATCH 137/201] [#2138] Use UPDATE event type for generator where applicable --- .../ReactiveUpdateCoordinatorStandard.java | 4 +- .../org/hibernate/reactive/TimestampTest.java | 69 ++++++++++++++++++- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java index 92c92bb65..9e3a6b86d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java @@ -30,7 +30,7 @@ import org.hibernate.tuple.entity.EntityMetamodel; import static org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper.identifiedResultsCheck; -import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.generator.EventType.UPDATE; import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_INT_ARRAY; import static org.hibernate.internal.util.collections.ArrayHelper.trim; import static org.hibernate.reactive.persister.entity.mutation.GeneratorValueUtil.generateValue; @@ -193,7 +193,7 @@ private CompletionStage reactivePreUpdateInMemoryValueGeneration( && generator.generatesOnUpdate() ) { final Object currentValue = currentValues[i]; final BeforeExecutionGenerator beforeGenerator = (BeforeExecutionGenerator) generator; - result = result.thenCompose( v -> generateValue( session, entity, currentValue, beforeGenerator, INSERT ) + result = result.thenCompose( v -> generateValue( session, entity, currentValue, beforeGenerator, UPDATE ) .thenAccept( generatedValue -> { currentValues[index] = generatedValue; entityPersister().setValue( entity, index, generatedValue ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TimestampTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TimestampTest.java index 8a6f631fc..2d6665982 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TimestampTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/TimestampTest.java @@ -6,6 +6,7 @@ package org.hibernate.reactive; import java.time.Instant; +import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; @@ -18,6 +19,9 @@ import io.vertx.junit5.Timeout; import io.vertx.junit5.VertxTestContext; import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; @@ -28,12 +32,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @Timeout(value = 10, timeUnit = MINUTES) - public class TimestampTest extends BaseReactiveTest { @Override protected Collection> annotatedEntities() { - return List.of( Record.class ); + return List.of( Record.class, Event.class ); } @Test @@ -56,6 +59,30 @@ public void test(VertxTestContext context) { ); } + @Test + public void testEmbedded(VertxTestContext context) { + Event event = new Event(); + History history = new History(); + event.name = "Concert"; + test( context, getMutinySessionFactory() + .withSession( session -> session.persist( event ) + .chain( session::flush ) + .invoke( () -> { + history.created = event.history.created; + history.updated = event.history.updated; + assertEquals( + event.history.created.truncatedTo( ChronoUnit.HOURS ), + event.history.updated.truncatedTo( ChronoUnit.HOURS ) + ); }) + .invoke( () -> event.name = "Conference" ) + .chain( session::flush ) + .invoke( () -> assertInstants( event, history ) ) ) + .chain( () -> getMutinySessionFactory().withSession( session -> session + .find( Record.class, event.id ) ) ) + .invoke( r -> assertInstants( event, history ) ) + ); + } + private static void assertInstants(Record r) { assertNotNull( r.created ); assertNotNull( r.updated ); @@ -66,6 +93,18 @@ private static void assertInstants(Record r) { ); } + private static void assertInstants(Event e, History h) { + assertNotNull( e.history.created ); + assertNotNull( e.history.updated ); + // Sometimes, when the test suite is fast enough, they might be the same: + assertTrue( + !e.history.updated.isBefore( e.history.created ), + "Updated instant is before created. Updated[" + e.history.updated + "], Created[" + e.history.created + "]" + ); + assertEquals( h.created, e.history.created ); + + } + @Entity(name = "Record") static class Record { @GeneratedValue @@ -78,4 +117,30 @@ static class Record { @UpdateTimestamp Instant updated; } + + @Entity(name = "Event") + static class Event { + + @Id + @GeneratedValue + public Long id; + + public String name; + + @Embedded + public History history; + + } + + @Embeddable + static class History { + @Column + @CreationTimestamp + public LocalDateTime created; + + @Column + @UpdateTimestamp + public LocalDateTime updated; + + } } From 29d84cdaeddfca3b3ea007d0baf2d8550156f53f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 25 Apr 2025 18:12:22 +0200 Subject: [PATCH 138/201] log the HR version --- .../hibernate/reactive/logging/impl/Log.java | 4 +-- .../reactive/logging/impl/Version.java | 32 +++++++++++++++++++ .../provider/impl/ReactiveIntegrator.java | 3 +- 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Version.java 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 6bacd56db..4f205492e 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 @@ -34,8 +34,8 @@ public interface Log extends BasicLogger { @LogMessage(level = INFO) - @Message(id = 1, value = "Hibernate Reactive") - void startHibernateReactive(); + @Message(id = 1, value = "Hibernate Reactive version %s") + void startHibernateReactive(String version); @LogMessage(level = INFO) @Message(id = 2, value = "Vert.x not detected, creating a new instance") diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Version.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Version.java new file mode 100644 index 000000000..2ed6d6b9d --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Version.java @@ -0,0 +1,32 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.logging.impl; + +/** + * Information about the version of Hibernate Reactive. + * + * @author Steve Ebersole + */ +public final class Version { + + private static final String VERSION; + static { + final String version = Version.class.getPackage().getImplementationVersion(); + VERSION = version != null ? version : "[WORKING]"; + } + + private Version() { + } + + /** + * Access to the Hibernate Reactive version. + * + * @return The version + */ + public static String getVersionString() { + return VERSION; + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java index c1fb0deb9..db37d7241 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java @@ -27,6 +27,7 @@ import org.hibernate.reactive.event.impl.DefaultReactiveRefreshEventListener; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; +import org.hibernate.reactive.logging.impl.Version; import org.hibernate.service.ServiceRegistry; /** @@ -48,7 +49,7 @@ public void integrate(Metadata metadata, BootstrapContext bootstrapContext, Sess private void attachEventContextManagingListenersIfRequired(ServiceRegistry serviceRegistry) { if ( ReactiveModeCheck.isReactiveRegistry( serviceRegistry ) ) { - LOG.startHibernateReactive(); + LOG.startHibernateReactive( Version.getVersionString() ); EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); eventListenerRegistry.addDuplicationStrategy( ReplacementDuplicationStrategy.INSTANCE ); From 1955d4571e9143fd2eab2c444a6c3a2b2ac58dc5 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:07:02 +0100 Subject: [PATCH 139/201] [#2129] Add DatabaseConfiguration#getMinimumVersion We use it to test the dialect selection at start up --- .../containers/DatabaseConfiguration.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java index f0d497ee2..c0a081126 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java @@ -5,6 +5,7 @@ */ package org.hibernate.reactive.containers; +import java.lang.reflect.Field; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -12,6 +13,7 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; @@ -28,15 +30,16 @@ public class DatabaseConfiguration { public static final boolean USE_DOCKER = Boolean.getBoolean("docker"); public enum DBType { - DB2( DB2Database.INSTANCE, 50000, "com.ibm.db2.jcc.DB2Driver", DB2Dialect.class ), - MYSQL( MySQLDatabase.INSTANCE, 3306, "com.mysql.cj.jdbc.Driver", MySQLDialect.class ), - MARIA( MariaDatabase.INSTANCE, 3306, "org.mariadb.jdbc.Driver", MariaDBDialect.class, "mariadb" ), - POSTGRESQL( PostgreSQLDatabase.INSTANCE, 5432, "org.postgresql.Driver", PostgreSQLDialect.class, "POSTGRES", "PG" ), - COCKROACHDB( CockroachDBDatabase.INSTANCE, 26257, "org.postgresql.Driver", CockroachDialect.class, "COCKROACH" ), - SQLSERVER( MSSQLServerDatabase.INSTANCE, 1433, "com.microsoft.sqlserver.jdbc.SQLServerDriver", SQLServerDialect.class, "MSSQL", "MSSQLSERVER" ), - ORACLE( OracleDatabase.INSTANCE, 1521, "oracle.jdbc.OracleDriver", OracleDialect.class ); + DB2( DB2Database.INSTANCE, "DB2", 50000, "com.ibm.db2.jcc.DB2Driver", DB2Dialect.class ), + MYSQL( MySQLDatabase.INSTANCE, "MySQL",3306, "com.mysql.cj.jdbc.Driver", MySQLDialect.class ), + MARIA( MariaDatabase.INSTANCE, "MariaDB",3306, "org.mariadb.jdbc.Driver", MariaDBDialect.class, "mariadb" ), + POSTGRESQL( PostgreSQLDatabase.INSTANCE, "PostgreSQL", 5432, "org.postgresql.Driver", PostgreSQLDialect.class, "POSTGRES", "PG" ), + COCKROACHDB( CockroachDBDatabase.INSTANCE, "CockroachDb", 26257, "org.postgresql.Driver", CockroachDialect.class, "COCKROACH" ), + SQLSERVER( MSSQLServerDatabase.INSTANCE, "Microsoft SQL Server", 1433, "com.microsoft.sqlserver.jdbc.SQLServerDriver", SQLServerDialect.class, "MSSQL", "MSSQLSERVER" ), + ORACLE( OracleDatabase.INSTANCE, "Oracle", 1521, "oracle.jdbc.OracleDriver", OracleDialect.class ); private final TestableDatabase configuration; + private final String productName; private final int defaultPort; // A list of alternative names that can be used to select the db @@ -47,8 +50,9 @@ public enum DBType { private final Class dialect; - DBType(TestableDatabase configuration, int defaultPort, String jdbcDriver, Class dialect, String... aliases) { + DBType(TestableDatabase configuration, String productName, int defaultPort, String jdbcDriver, Class dialect, String... aliases) { this.configuration = configuration; + this.productName = productName; this.defaultPort = defaultPort; this.aliases = aliases; this.dialect = dialect; @@ -81,6 +85,10 @@ public static DBType fromString(String dbName) { .toString( DBType.values() ) ); } + public String getProductName() { + return productName; + } + public int getDefaultPort() { return defaultPort; } @@ -92,6 +100,24 @@ public String getJdbcDriver() { public Class getDialectClass() { return dialect; } + + /** + * The minimum version of the database supported by the dialect. + *

    + * We use reflection because it's not accessible from the tests. + * Copied from MetadataAccessTests in Hibernate ORM. + *

    + */ + public DatabaseVersion getMinimumVersion() { + try { + Field field = dialect.getDeclaredField( "MINIMUM_VERSION" ); + field.setAccessible( true ); + return (DatabaseVersion) field.get( null ); + } + catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException( "Error extracting 'MINIMUM_VERSION' from '" + dialect + "'", e ); + } + } } public static final String USERNAME = "hreact"; @@ -101,7 +127,7 @@ public Class getDialectClass() { private static DBType dbType; public static DBType dbType() { - if (dbType == null) { + if ( dbType == null ) { String dbTypeString = System.getProperty( "db", DBType.POSTGRESQL.name() ); dbType = DBType.fromString( dbTypeString ); System.out.println( "Using database type: " + dbType.name() ); @@ -131,5 +157,4 @@ public static String expectedDatatype(Class dataType) { private DatabaseConfiguration() { } - } From e73ffbc34d12dc657d7b8612a14bbc39f1520af7 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:10:00 +0100 Subject: [PATCH 140/201] [#2129] Set the logging properties in a separate method This way we can reuse the method even if the tests don't extend BaseReactiveTest --- .../hibernate/reactive/BaseReactiveTest.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 1a5f51e3b..07a79379b 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 @@ -92,6 +92,18 @@ protected void setProperties(Configuration configuration) { setDefaultProperties( configuration ); } + /** + * Set the properties related to the SQL logs. + * Some tests won't extend this class, but we still want to apply the same rules for these kinds of properties. + */ + public static void setSqlLoggingProperties(Configuration configuration) { + // Use JAVA_TOOL_OPTIONS='-Dhibernate.show_sql=true' + // Keep the default to false, otherwise the log on CI becomes too big + configuration.setProperty( Settings.SHOW_SQL, System.getProperty( Settings.SHOW_SQL, "false" ) ); + configuration.setProperty( Settings.FORMAT_SQL, System.getProperty( Settings.FORMAT_SQL, "false" ) ); + configuration.setProperty( Settings.HIGHLIGHT_SQL, System.getProperty( Settings.HIGHLIGHT_SQL, "true" ) ); + } + /** * Configure default properties common to most tests. */ @@ -102,13 +114,9 @@ public static void setDefaultProperties(Configuration configuration) { configuration.setProperty( Settings.HBM2DDL_IMPORT_FILES, "/db2.sql" ); doneTablespace = true; } - // Use JAVA_TOOL_OPTIONS='-Dhibernate.show_sql=true' - // Keep the default to false, otherwise the log on CI becomes too big - configuration.setProperty( Settings.SHOW_SQL, System.getProperty( Settings.SHOW_SQL, "false" ) ); - configuration.setProperty( Settings.FORMAT_SQL, System.getProperty( Settings.FORMAT_SQL, "false" ) ); - configuration.setProperty( Settings.HIGHLIGHT_SQL, System.getProperty( Settings.HIGHLIGHT_SQL, "true" ) ); configuration.setProperty( PersistentTableStrategy.DROP_ID_TABLES, "true" ); configuration.setProperty( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + setSqlLoggingProperties( configuration ); } public static final SessionFactoryManager factoryManager = new SessionFactoryManager(); From 9a129c2e353a8222c1f84ae19b3375937718f36d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:27:57 +0100 Subject: [PATCH 141/201] [#2129] Remove StandAloneReactiveTest I'm not sure what's the purpose of this test, and why it was working before. But, it doesn't work anymore with the fix for #2129 because some properties are missing and we are not setting the right credentials. I think it was one of first tests we introduced when we have created the project and obsolete now. --- .../reactive/StandAloneReactiveTest.java | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/StandAloneReactiveTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/StandAloneReactiveTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/StandAloneReactiveTest.java deleted file mode 100644 index 29ef9de85..000000000 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/StandAloneReactiveTest.java +++ /dev/null @@ -1,36 +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; - -import org.hibernate.boot.MetadataSources; -import org.hibernate.boot.registry.StandardServiceRegistry; -import org.hibernate.dialect.PostgreSQLDialect; -import org.hibernate.reactive.provider.Settings; -import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; -import org.hibernate.reactive.stage.Stage; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class StandAloneReactiveTest { - - @Test - public void createReactiveSessionFactory() { - StandardServiceRegistry registry = new ReactiveServiceRegistryBuilder() - .applySetting( Settings.TRANSACTION_COORDINATOR_STRATEGY, "jta" ) - .applySetting( Settings.DIALECT, PostgreSQLDialect.class.getName() ) - .applySetting( Settings.URL, "jdbc:postgresql://localhost/hreact?user=none" ) - .build(); - - Stage.SessionFactory factory = new MetadataSources( registry ) - .buildMetadata() - .getSessionFactoryBuilder() - .build() - .unwrap( Stage.SessionFactory.class ); - - assertThat( factory ).isNotNull(); - } -} From 02aed1b727b5fc49faebd3e0f115f8b8db02e030 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:11:13 +0100 Subject: [PATCH 142/201] [#2129] Allow offline startup and on-demand DB version checks This feature is useful in particular for applications that start up before the DB becomes accessible. It can also be useful for Quarkus, where (part of) startup happens at build time. This achieved by setting the property: ``` hibernate.boot.allow_jdbc_metadata_access = false ``` --- .../ReactiveStandardDialectResolver.java | 10 +- .../service/NoJdbcEnvironmentInitiator.java | 200 ++++++++++++++---- 2 files changed, 167 insertions(+), 43 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java index 1b24e567d..d12791e5f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java @@ -11,24 +11,22 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolver; -import static org.hibernate.dialect.CockroachDialect.parseVersion; public class ReactiveStandardDialectResolver implements DialectResolver { @Override public Dialect resolveDialect(DialectResolutionInfo info) { - // Hibernate ORM runs an extra query to recognize CockroachDB from PostgreSQL - // We've already done it, so we are trying to skip that step + // Hibernate ORM runs an extra query to recognize CockroachDB from PostgresSQL + // We already did it when we created the DialectResolutionInfo in NoJdbcEnvironmentInitiator, + // so we can skip that step here. if ( info.getDatabaseName().startsWith( "Cockroach" ) ) { - return new CockroachDialect( parseVersion( info.getDatabaseVersion() ) ); + return new CockroachDialect( info ); } - for ( Database database : Database.values() ) { if ( database.matchesResolutionInfo( info ) ) { return database.createDialect( info ); } } - return null; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java index 953285dd4..0cf59c413 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java @@ -5,25 +5,29 @@ */ package org.hibernate.reactive.provider.service; -import java.util.Map; -import java.util.concurrent.CompletionStage; - import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectFactory; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl; +import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.pool.ReactiveConnectionPool; -import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryImplementor; import io.vertx.sqlclient.spi.DatabaseMetadata; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; +import static java.lang.Integer.parseInt; +import static java.util.Objects.requireNonNullElse; import static java.util.function.Function.identity; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -32,7 +36,8 @@ * that provides an implementation of {@link JdbcEnvironment} that infers * the Hibernate {@link org.hibernate.dialect.Dialect} from the JDBC URL. */ -public class NoJdbcEnvironmentInitiator implements StandardServiceInitiator { +public class NoJdbcEnvironmentInitiator extends JdbcEnvironmentInitiator + implements StandardServiceInitiator { public static final NoJdbcEnvironmentInitiator INSTANCE = new NoJdbcEnvironmentInitiator(); @@ -42,14 +47,59 @@ public Class getServiceInitiated() { } @Override - public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryImplementor registry) { - boolean explicitDialect = configurationValues.containsKey( Settings.DIALECT ); - if ( explicitDialect ) { - DialectFactory dialectFactory = registry.getService( DialectFactory.class ); - return new JdbcEnvironmentImpl( registry, dialectFactory.buildDialect( configurationValues, null ) ); - } + protected void logConnectionInfo(DatabaseConnectionInfo databaseConnectionInfo) { + // Nothing to do we log the connection info somewhere else + } + + @Override + protected JdbcEnvironmentImpl getJdbcEnvironmentWithExplicitConfiguration( + Map configurationValues, + ServiceRegistryImplementor registry, + DialectFactory dialectFactory, + DialectResolutionInfo dialectResolutionInfo) { + return super.getJdbcEnvironmentWithExplicitConfiguration( + configurationValues, + registry, + dialectFactory, + dialectResolutionInfo + ); + } - return new JdbcEnvironmentImpl( registry, new DialectBuilder( configurationValues, registry ).build() ); + @Override + protected JdbcEnvironmentImpl getJdbcEnvironmentWithDefaults( + Map configurationValues, + ServiceRegistryImplementor registry, + DialectFactory dialectFactory) { + return new JdbcEnvironmentImpl( registry, new DialectBuilder( configurationValues, registry ) + .build( dialectFactory ) + ); + } + + @Override + protected JdbcEnvironmentImpl getJdbcEnvironmentUsingJdbcMetadata( + Map configurationValues, + ServiceRegistryImplementor registry, + DialectFactory dialectFactory, + String explicitDatabaseName, + Integer explicitDatabaseMajorVersion, + Integer explicitDatabaseMinorVersion, + String explicitDatabaseVersion) { + try { + final Dialect dialect = new DialectBuilder( configurationValues, registry ) + .build( + dialectFactory, + new ExplicitMetadata( + explicitDatabaseName, + explicitDatabaseMajorVersion, + explicitDatabaseMinorVersion, + explicitDatabaseVersion + ) + ); + return new JdbcEnvironmentImpl( registry, dialect ); + } + catch (RuntimeException e) { + return getJdbcEnvironmentWithDefaults( configurationValues, registry, dialectFactory ); + } } private static class DialectBuilder { @@ -62,24 +112,40 @@ public DialectBuilder(Map configurationValues, ServiceRegistry r this.registry = registry; } - public Dialect build() { - DialectFactory dialectFactory = registry.getService( DialectFactory.class ); + public Dialect build(DialectFactory dialectFactory) { return dialectFactory.buildDialect( configurationValues, this::dialectResolutionInfo ); } + public Dialect build(DialectFactory dialectFactory, ExplicitMetadata explicitMetadata) { + return dialectFactory.buildDialect( configurationValues, () -> dialectResolutionInfo( explicitMetadata ) ); + } + private DialectResolutionInfo dialectResolutionInfo() { - ReactiveConnectionPool connectionPool = registry.getService( ReactiveConnectionPool.class ); - return connectionPool + return dialectResolutionInfo( DialectBuilder::buildResolutionInfo ); + } + + private DialectResolutionInfo dialectResolutionInfo(ExplicitMetadata explicitMetadata) { + return dialectResolutionInfo( reactiveConnection -> DialectBuilder + .buildResolutionInfo( reactiveConnection, explicitMetadata ) + ); + } + + private DialectResolutionInfo dialectResolutionInfo(Function> dialectResolutionFunction) { + return registry + .getService( ReactiveConnectionPool.class ) // The default SqlExceptionHelper in ORM requires the dialect, but we haven't created a dialect yet, // so we need to override it at this stage, or we will have an exception. .getConnection( new SqlExceptionHelper( true ) ) - .thenCompose( DialectBuilder::buildResolutionInfo ) + .thenCompose( dialectResolutionFunction ) .toCompletableFuture().join(); } private static CompletionStage buildResolutionInfo(ReactiveConnection connection) { - final DatabaseMetadata databaseMetadata = connection.getDatabaseMetadata(); - return resolutionInfoStage( connection, databaseMetadata ) + return buildResolutionInfo( connection, null ); + } + + private static CompletionStage buildResolutionInfo(ReactiveConnection connection, ExplicitMetadata explicitMetadata) { + return resolutionInfoStage( connection, explicitMetadata ) .handle( CompletionStages::handle ) .thenCompose( handled -> { if ( handled.hasFailed() ) { @@ -96,19 +162,27 @@ private static CompletionStage buildResolutionInf } ); } - private static CompletionStage resolutionInfoStage(ReactiveConnection connection, DatabaseMetadata databaseMetadata) { - if ( databaseMetadata.productName().equalsIgnoreCase( "PostgreSQL" ) ) { - // We need to check if the database is PostgreSQL or CockroachDB - // Hibernate ORM does it using a query, so we need to check in advance + /** + * @see org.hibernate.dialect.Database#POSTGRESQL for recognizing CockroachDB + */ + private static CompletionStage resolutionInfoStage(ReactiveConnection connection, ExplicitMetadata explicitMetadata) { + final DatabaseMetadata databaseMetadata = explicitMetadata != null + ? new ReactiveDatabaseMetadata( connection.getDatabaseMetadata(), explicitMetadata ) + : connection.getDatabaseMetadata(); + + // If the product name is explicitly set to Postgres, we are not going to override it + if ( ( explicitMetadata == null || explicitMetadata.productName == null ) + && databaseMetadata.productName().equalsIgnoreCase( "PostgreSQL" ) ) { + // CockroachDB returns "PostgreSQL" as product name in the metadata. + // So, we need to check if the database is PostgreSQL or CockroachDB + // We follow the same approach used by ORM: run a new query and check the full version metadata // See org.hibernate.dialect.Database.POSTGRESQL#createDialect return connection.select( "select version()" ) .thenApply( DialectBuilder::readFullVersion ) - .thenApply( fullversion -> { - if ( fullversion.startsWith( "Cockroach" ) ) { - return new CockroachDatabaseMetadata( fullversion ); - } - return databaseMetadata; - } ) + .thenApply( fullVersion -> fullVersion.startsWith( "Cockroach" ) + ? new ReactiveDatabaseMetadata( "Cockroach", databaseMetadata ) + : databaseMetadata + ) .thenApply( ReactiveDialectResolutionInfo::new ); } @@ -122,32 +196,62 @@ private static String readFullVersion(ReactiveConnection.Result result) { } } - private static class CockroachDatabaseMetadata implements DatabaseMetadata { + /** + * Utility class to pass around explicit metadata properties. + * It's different from {@link DatabaseMetadata} because values can be null. + */ + private static class ExplicitMetadata { + private final String productName; + private final String fullVersion; + private final Integer majorVersion; + private final Integer minorVersion; + + public ExplicitMetadata(String explicitDatabaseName, Integer explicitDatabaseMajorVersion, Integer explicitDatabaseMinorVersion, String explicitDatabaseVersion ) { + this.productName = explicitDatabaseName; + this.fullVersion = explicitDatabaseVersion; + this.majorVersion = explicitDatabaseMajorVersion; + this.minorVersion = explicitDatabaseMinorVersion; + } + } - private final String fullversion; + private static class ReactiveDatabaseMetadata implements DatabaseMetadata { + public final String productName; + public final String fullVersion; + public final int majorVersion; + public final int minorVersion; + + public ReactiveDatabaseMetadata(String productName, DatabaseMetadata databaseMetadata) { + this.productName = productName; + this.fullVersion = databaseMetadata.productName(); + this.majorVersion = databaseMetadata.majorVersion(); + this.minorVersion = databaseMetadata.minorVersion(); + } - public CockroachDatabaseMetadata(String fullversion) { - this.fullversion = fullversion; + public ReactiveDatabaseMetadata(DatabaseMetadata metadata, ExplicitMetadata explicitMetadata) { + productName = requireNonNullElse( explicitMetadata.productName, metadata.productName() ); + fullVersion = requireNonNullElse( explicitMetadata.fullVersion, metadata.fullVersion() ); + majorVersion = requireNonNullElse( explicitMetadata.majorVersion, metadata.majorVersion() ); + minorVersion = requireNonNullElse( explicitMetadata.minorVersion, metadata.minorVersion() ); } @Override public String productName() { - return "CockroachDb"; + return productName; } @Override public String fullVersion() { - return fullversion; + return fullVersion; } @Override public int majorVersion() { - return 0; + return majorVersion; } @Override public int minorVersion() { - return 0; + return minorVersion; } } @@ -179,6 +283,27 @@ public int getDatabaseMinorVersion() { return metadata.minorVersion(); } + @Override + public int getDatabaseMicroVersion() { + return databaseMicroVersion( metadata.fullVersion(), metadata.majorVersion(), metadata.minorVersion() ); + } + + // We should move this in ORM and avoid duplicated code + private static int databaseMicroVersion(String version, int major, int minor) { + final String prefix = major + "." + minor + "."; + if ( version.startsWith( prefix ) ) { + try { + final String substring = version.substring( prefix.length() ); + final String micro = new StringTokenizer( substring, " .,-:;/()[]" ).nextToken(); + return parseInt( micro ); + } + catch (NumberFormatException nfe) { + return 0; + } + } + return 0; + } + @Override public String getDriverName() { return getDatabaseName(); @@ -196,6 +321,7 @@ public int getDriverMinorVersion() { @Override public String getSQLKeywords() { + // Vert.x metadata doesn't have this info return null; } From 478294804e8c3868b9c234516fde8a401f1aba42 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 21 Mar 2025 13:16:10 +0100 Subject: [PATCH 143/201] [#2129] Test hibernate.boot.allow_jdbc_metadata_access Hibernate Reactive should be able to start even if there's no database when `hibernate.boot.allow_jdbc_metadata_access = false`. --- .../reactive/MetadataAccessTest.java | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/MetadataAccessTest.java diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MetadataAccessTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MetadataAccessTest.java new file mode 100644 index 000000000..da1578045 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/MetadataAccessTest.java @@ -0,0 +1,260 @@ +/* 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.hibernate.HibernateException; +import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl; +import org.hibernate.engine.jdbc.dialect.spi.DialectFactory; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.reactive.containers.DatabaseConfiguration; +import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; +import org.hibernate.reactive.provider.Settings; +import org.hibernate.reactive.testing.SqlStatementTracker; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Map; +import java.util.Properties; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.cfg.JdbcSettings.ALLOW_METADATA_ON_BOOT; +import static org.hibernate.cfg.JdbcSettings.DIALECT; +import static org.hibernate.cfg.JdbcSettings.JAKARTA_HBM2DDL_DB_NAME; +import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL; +import static org.hibernate.reactive.BaseReactiveTest.setSqlLoggingProperties; +import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Hibernate ORM allows starting up without access to the DB ("offline") + * when {@link Settings#ALLOW_METADATA_ON_BOOT} is set to false. + *

    + * Inspired by the test + * {@code org.hibernate.orm.test.boot.database.metadata.MetadataAccessTests} + * in Hibernate ORM. + *

    + */ +public class MetadataAccessTest { + + private static SqlStatementTracker sqlTracker; + + private static final int EXPECTED_MAJOR = 123; + private static final int EXPECTED_MINOR = 456; + private static final DatabaseVersion EXPECTED_VERSION = DatabaseVersion.make( EXPECTED_MAJOR, EXPECTED_MINOR ); + + private static Properties dialectMajorMinorProperties() { + Properties dbProperties = new Properties(); + // Major and Minor should override the full version, so we keep them different + dbProperties.setProperty( Settings.DIALECT_DB_MAJOR_VERSION, String.valueOf( EXPECTED_MAJOR ) ); + dbProperties.setProperty( Settings.DIALECT_DB_MINOR_VERSION, String.valueOf( EXPECTED_MINOR ) ); + return dbProperties; + } + + private static Properties jakartaMajorMinorProperties() { + Properties dbProperties = new Properties(); + dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION, String.valueOf( EXPECTED_MAJOR ) ); + dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MINOR_VERSION, String.valueOf( EXPECTED_MINOR ) ); + return dbProperties; + } + + private static Properties jakartaFullDbVersion() { + Properties dbProperties = new Properties(); + dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_VERSION, EXPECTED_MAJOR + "." + EXPECTED_MINOR ); + return dbProperties; + } + + private static Properties dialectFullDbVersion() { + Properties dbProperties = new Properties(); + dbProperties.setProperty( Settings.DIALECT_DB_VERSION, EXPECTED_MAJOR + "." + EXPECTED_MINOR ); + return dbProperties; + } + + static Stream explicitVersionProperties() { + return Stream.of( + arguments( "Jakarta properties", jakartaMajorMinorProperties() ), + arguments( "Deprecated dialect properties", dialectMajorMinorProperties() ), + arguments( "Jakarta db version property", jakartaFullDbVersion() ), + arguments( "Deprecated dialect db version property", dialectFullDbVersion() ) + ); + } + + @ParameterizedTest(name = "Test {0} with " + DIALECT) + @MethodSource("explicitVersionProperties") + public void testExplicitVersionWithDialect(String display, Properties dbProperties) { + dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" ); + dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() ); + + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) { + final Dialect dialect = dialect( serviceRegistry ); + assertThat( dialect ).isInstanceOf( dbType().getDialectClass() ); + assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION ); + } + + assertThat( sqlTracker.getLoggedQueries() ) + .as( "No query should be executed at start up" ) + .isEmpty(); + } + + @ParameterizedTest(name = "Test {0} with " + JAKARTA_HBM2DDL_DB_NAME) + @MethodSource("explicitVersionProperties") + public void testExplicitVersionWithJakartaDbName(String display, Properties dbProperties) { + dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" ); + dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() ); + + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) { + final Dialect dialect = dialect( serviceRegistry ); + assertThat( dialect ).isInstanceOf( dbType().getDialectClass() ); + assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION ); + } + + assertThat( sqlTracker.getLoggedQueries() ) + .as( "No query should be executed at start up" ) + .isEmpty(); + } + + @Test + public void testMinimumDatabaseVersionWithDialect() { + final Properties dbProperties = new Properties(); + dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" ); + dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() ); + + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) { + final Dialect dialect = dialect( serviceRegistry ); + assertThat( dialect ).isInstanceOf( dbType().getDialectClass() ); + assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() ); + } + + assertThat( sqlTracker.getLoggedQueries() ) + .as( "No query should be executed at start up" ) + .isEmpty(); + } + + @Test + public void testMinimumDatabaseVersionWithJakartaDbName() { + final Properties dbProperties = new Properties(); + dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" ); + dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() ); + + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) { + final Dialect dialect = dialect( serviceRegistry ); + assertThat( dialect ).isInstanceOf( dbType().getDialectClass() ); + assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() ); + } + + assertThat( sqlTracker.getLoggedQueries() ) + .as( "No query should be executed at start up" ) + .isEmpty(); + } + + @Test + public void testDeterminedVersion() { + final Properties disabledProperties = new Properties(); + disabledProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" ); + disabledProperties.setProperty( DIALECT, dbType().getDialectClass().getName() ); + // The dialect when ALLOW_METADATA_ON_BOOT si set to false + final Dialect metadataDisabledDialect; + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( disabledProperties )) { + metadataDisabledDialect = dialect( serviceRegistry ); + // We didn't set the version anywhere else, so we expect it to be the minimum version + assertThat( metadataDisabledDialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() ); + } + + assertThat( sqlTracker.getLoggedQueries() ) + .as( "No query should be executed at start up" ) + .isEmpty(); + + final Properties enabledProperties = new Properties(); + enabledProperties.setProperty( ALLOW_METADATA_ON_BOOT, "true" ); + enabledProperties.setProperty( JAKARTA_JDBC_URL, DatabaseConfiguration.getJdbcUrl() ); + try (StandardServiceRegistry serviceRegistry = createServiceRegistry( enabledProperties )) { + final Dialect metadataEnabledDialect = dialect( serviceRegistry ); + + // We expect determineDatabaseVersion(), when called on metadataAccessDisabledDialect, + // to return the version that would have been returned, + // had we booted up with auto-detection of version (metadata access allowed). + DatabaseVersion determinedDatabaseVersion = metadataDisabledDialect + .determineDatabaseVersion( dialectResolutionInfo( serviceRegistry ) ); + + // Whatever the version, we don't expect the minimum one + assertThat( determinedDatabaseVersion ).isNotEqualTo( dbType().getMinimumVersion() ); + + assertThat( determinedDatabaseVersion ).isEqualTo( metadataEnabledDialect.getVersion() ); + assertThat( determinedDatabaseVersion.getMajor() ).isEqualTo( metadataEnabledDialect.getVersion().getMajor() ); + assertThat( determinedDatabaseVersion.getMinor() ).isEqualTo( metadataEnabledDialect.getVersion().getMinor() ); + assertThat( determinedDatabaseVersion.getMicro() ).isEqualTo( metadataEnabledDialect.getVersion().getMicro() ); + } + } + + private Configuration constructConfiguration(Properties properties) { + Configuration configuration = new Configuration(); + setSqlLoggingProperties( configuration ); + configuration.addProperties( properties ); + + // Construct a tracker that collects query statements via the SqlStatementLogger framework. + // Pass in configuration properties to hand off any actual logging properties + sqlTracker = new SqlStatementTracker( s -> true, configuration.getProperties() ); + return configuration; + } + + private StandardServiceRegistry createServiceRegistry(Properties properties) { + Configuration configuration = constructConfiguration( properties ); + StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder(); + // We will set these properties when needed + assertThat( builder.getSettings() ).doesNotContainKeys( DIALECT, JAKARTA_HBM2DDL_DB_NAME ); + builder.applySettings( configuration.getProperties() ); + + builder.addInitiator( new CapturingDialectFactory.Initiator() ); + sqlTracker.registerService( builder ); + return builder.enableAutoClose().build(); + } + + private static Dialect dialect(StandardServiceRegistry registry) { + return registry.getService( JdbcEnvironment.class ).getDialect(); + } + + private static DialectResolutionInfo dialectResolutionInfo(StandardServiceRegistry registry) { + return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) ) + .capturedDialectResolutionInfoSource.getDialectResolutionInfo(); + } + + // A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM + private static class CapturingDialectFactory extends DialectFactoryImpl { + + static class Initiator implements StandardServiceInitiator { + @Override + public Class getServiceInitiated() { + return DialectFactory.class; + } + + @Override + public DialectFactory initiateService(Map configurationValues, ServiceRegistryImplementor registry) { + return new CapturingDialectFactory(); + } + } + + DialectResolutionInfoSource capturedDialectResolutionInfoSource; + + @Override + public Dialect buildDialect(Map configValues, DialectResolutionInfoSource resolutionInfoSource) + throws HibernateException { + this.capturedDialectResolutionInfoSource = resolutionInfoSource; + return super.buildDialect( configValues, resolutionInfoSource ); + } + } +} From 6cc86b119277fe5738b9129673865592a7f7254c Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Fri, 25 Apr 2025 16:45:33 +0200 Subject: [PATCH 144/201] [#2222] Add a simple JReleaser configuration --- .gitignore | 3 -- .release/.gitignore | 3 ++ build.gradle | 7 --- ci/release/Jenkinsfile | 28 ++++++---- ci/snapshot-publish.Jenkinsfile | 38 ++++++++++--- jreleaser.yml | 59 ++++++++++++++++++++ publish.gradle | 96 ++------------------------------- release/build.gradle | 1 - 8 files changed, 114 insertions(+), 121 deletions(-) create mode 100644 .release/.gitignore create mode 100644 jreleaser.yml diff --git a/.gitignore b/.gitignore index 8016117ce..750319d47 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,3 @@ bin # Vim *.swp *.swo - -# Release scripts downloaded from hibernate/hibernate-release-scripts -.release diff --git a/.release/.gitignore b/.release/.gitignore new file mode 100644 index 000000000..cdd9a17d6 --- /dev/null +++ b/.release/.gitignore @@ -0,0 +1,3 @@ +# The folder into which we checkout our release scripts into +* +!.gitignore \ No newline at end of file diff --git a/build.gradle b/build.gradle index c7ad6918c..4c5235a9a 100644 --- a/build.gradle +++ b/build.gradle @@ -30,13 +30,6 @@ ext { logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion } -// Publishing to Sonatype (Maven Central): -nexusPublishing { - repositories { - sonatype() - } -} - subprojects { apply plugin: 'java-library' apply plugin: 'com.diffplug.spotless' diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index d365436ab..4ae62e3f3 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -119,8 +119,10 @@ pipeline { 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) - def secondLastCommitter = sh(script: 'git show -s --format=\'%an\' HEAD~1', returnStdout: true) + 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' @@ -149,6 +151,7 @@ pipeline { env.DEVELOPMENT_VERSION = developmentVersion.toString() // Dry run is not supported at the moment env.SCRIPT_OPTIONS = params.RELEASE_DRY_RUN ? "-d" : "" + env.JRELEASER_DRY_RUN = true // 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) @@ -172,11 +175,10 @@ pipeline { // tags the version // changes the version to the provided development version withEnv([ - "BRANCH=${env.GIT_BRANCH}", // 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'" ]) { - sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}" + sh ".release/scripts/prepare-release.sh -b ${env.GIT_BRANCH} -d ${env.DEVELOPMENT_VERSION} ${env.PROJECT} ${env.RELEASE_VERSION}" } } } @@ -193,11 +195,17 @@ pipeline { configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts") ]) { withCredentials([ - // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh - usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername'), - file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE'), - gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default') + // 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'), + 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 @@ -217,4 +225,4 @@ pipeline { } } } -} \ No newline at end of file +} diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index 07fa844c6..59a97eeee 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -10,6 +10,14 @@ if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause')) { return } +def checkoutReleaseScripts() { + dir('.release/scripts') { + checkout scmGit(branches: [[name: '*/main']], extensions: [], + userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com', + url: 'https://github.com/hibernate/hibernate-release-scripts.git']]) + } +} + pipeline { agent { label 'Release' @@ -30,13 +38,27 @@ pipeline { } stage('Publish') { steps { - withCredentials([ - // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh - usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword'), - file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'), - string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE') - ]) { - sh "./gradlew clean publish --no-scan" + 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 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'), + gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), + string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN') + ]) { + checkoutReleaseScripts() + def version = sh( + script: ".release/scripts/determine-current-version.sh orm", + returnStdout: true + ).trim() + echo "Current version: '${version}'" + sh "bash -xe .release/scripts/snapshot-deploy.sh reactive ${version}" + } } } } @@ -48,4 +70,4 @@ pipeline { } } } -} \ No newline at end of file +} diff --git a/jreleaser.yml b/jreleaser.yml new file mode 100644 index 000000000..c3eeec649 --- /dev/null +++ b/jreleaser.yml @@ -0,0 +1,59 @@ +project: + languages: + java: + groupId: org.hibernate.reactive + +release: + github: + skipTag: true + skipRelease: true + tagName: '{{projectVersion}}' + +# File signing is always active +signing: + mode: COMMAND + active: RELEASE + armored: true + +deploy: + maven: + # TODO: Remove the entire nexus2 section when switching to maven central publishing (see below): + nexus2: + maven-central: + active: RELEASE + url: https://oss.sonatype.org/service/local + snapshotUrl: https://oss.sonatype.org/content/repositories/snapshots/ + closeRepository: true + releaseRepository: false + stagingRepositories: + - build/staging-deploy/maven + maven-central-snapshot: + active: SNAPSHOT + url: https://oss.sonatype.org/service/local + snapshotUrl: https://oss.sonatype.org/content/repositories/snapshots/ + closeRepository: true + releaseRepository: true + javadocJar: false + sign: false + stagingRepositories: + - build/staging-deploy/maven + mavenCentral: + maven-central: + # TODO: Change to RELEASE once switching to Maven-Central: + # Note, this is an untested configuration, hence might need further adjustments + active: NEVER + url: https://central.sonatype.com/api/v1/publisher + snapshotSupported: false + applyMavenCentralRules: true + stagingRepositories: + - build/staging-deploy/maven + maven-central-snapshot: + # TODO: Change to SNAPSHOT once switching to Maven-Central: + active: NEVER + url: https://central.sonatype.com/api/v1/publisher + snapshotSupported: true + applyMavenCentralRules: true + javadocJar: false + sign: false + stagingRepositories: + - build/staging-deploy/maven diff --git a/publish.gradle b/publish.gradle index 977092d9a..201b67a8a 100644 --- a/publish.gradle +++ b/publish.gradle @@ -1,6 +1,5 @@ apply plugin: 'java' apply plugin: 'maven-publish' -apply plugin: 'signing' // Java / publishing @@ -75,97 +74,10 @@ publishing { } } } -} - - -// Signing - -var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension - -var publishingExtension = project.getExtensions().getByType(PublishingExtension) as PublishingExtension -signingExtension.sign publishingExtension.publications.publishedArtifacts - -var signingKey = resolveSigningKey() -var signingPassphrase = resolveSigningPassphrase() -signingExtension.useInMemoryPgpKeys(signingKey, signingPassphrase) - -gradle.taskGraph.whenReady { TaskExecutionGraph graph -> - boolean wasPublishingRequested = false - - graph.allTasks.each {task -> - if ( task instanceof PublishToMavenRepository ) { - logger.lifecycle( "Found PublishToMavenRepository task : {}", task.path ) - wasPublishingRequested = true + repositories { + maven { + name = "staging" + url = rootProject.layout.buildDirectory.dir("staging-deploy${File.separator}maven") } } - - if ( wasPublishingRequested ) { - def publishUser = resolvePublishUser() - def publishPass = resolvePublishPass() - if ( publishUser == null || publishPass == null ) { - throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." ) - } - - logger.lifecycle "Publishing {} : {} : {}", project.group, project.name, project.version - - // require signing if publishing to OSSRH - signingExtension.required = true - } - else if ( signingKey == null || signingPassphrase == null ) { - tasks.withType( Sign ).each { t-> t.enabled = false } - } } - - -static String resolveSigningKey() { - var key = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY" ) - if ( key != null ) { - return key - } - - var keyFile = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY_PATH" ) - if ( keyFile != null ) { - return new File( keyFile ).text - } - - return null -} - -static String resolveSigningPassphrase() { - return System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) -} - -String resolvePublishUser() { - var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypeUsername" ) - if ( envVar != null ) { - return envVar - } - - def projectProp = projectPropOrNull( "sonatypeUsername" ) - if ( projectProp != null ) { - return projectProp - } - - return null -} - -String resolvePublishPass() { - var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypePassword" ) - if ( envVar != null ) { - return envVar - } - - def projectProp = projectPropOrNull( "sonatypePassword" ) - if ( projectProp != null ) { - return projectProp - } - - return null -} - -String projectPropOrNull(String name) { - if ( project.hasProperty( name ) ) { - return project.findProperty( name ) - } - return null; -} \ No newline at end of file diff --git a/release/build.gradle b/release/build.gradle index 2b507a9a2..690534750 100644 --- a/release/build.gradle +++ b/release/build.gradle @@ -236,7 +236,6 @@ void updateVersionFile(var version) { def publishReleaseArtifactsTask = tasks.register( 'publishReleaseArtifacts' ) { dependsOn uploadDocumentationTask - dependsOn ":hibernate-reactive-core:publishToSonatype" mustRunAfter gitPreparationForReleaseTask } From 160c8c343b918431f036046f8acf41c60ec9beb7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 30 Apr 2025 16:17:57 +0200 Subject: [PATCH 145/201] fix misspelling in Javadoc It should have been "proxies". But anyway I rewrote the sentence. --- .../src/main/java/org/hibernate/reactive/mutiny/Mutiny.java | 6 ++++-- .../src/main/java/org/hibernate/reactive/stage/Stage.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c73c6b416..c5b0df7a9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -846,14 +846,16 @@ default Uni lock(Object entity, LockModeType lockModeType) { Uni flush(); /** - * Asynchronously fetch an association that's configured for lazy loading. + * Asynchronously fetch an association configured for lazy loading. *

    *

     		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()));}
     		 * 
    *

    *

    - * It can also initialize proxys. For example: + * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

    *

     		 * {@code session.fetch(session.getReference(Author.class, authorId))}
     		 * 
    diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 1920acc95..6de95c154 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -870,14 +870,16 @@ default CompletionStage lock(Object entity, LockModeType lockModeType) { CompletionStage flush(); /** - * Asynchronously fetch an association that's configured for lazy loading. + * Asynchronously fetch an association configured for lazy loading. *

    *

     		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()))}
     		 * 
    *

    *

    - * It can also initialize proxys. For example: + * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

    *

     		 * {@code session.fetch(session.getReference(Author.class, authorId))}
     		 * 
    From 41506a4ddcd30aebdd4a60ffcc312c16c2e4df99 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 16:40:00 +0200 Subject: [PATCH 146/201] [#2104] Create QueryProducer interface for the sessions --- .../org/hibernate/reactive/mutiny/Mutiny.java | 1193 +++++++--------- .../mutiny/impl/MutinySessionImpl.java | 31 +- .../impl/MutinyStatelessSessionImpl.java | 76 +- .../session/ReactiveQueryProducer.java | 5 +- .../org/hibernate/reactive/stage/Stage.java | 1239 +++++++---------- .../reactive/stage/impl/StageSessionImpl.java | 22 +- .../stage/impl/StageStatelessSessionImpl.java | 27 + 7 files changed, 1106 insertions(+), 1487 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c5b0df7a9..7ced2785a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -485,715 +485,739 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query enableFetchProfile(String profileName); } - - /** - * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} - * interface, allowing a reactive style of interaction with the database. - *

    - * The semantics of operations on this interface are identical to the - * semantics of the similarly-named operations of {@code Session}, except - * that the operations are performed asynchronously, returning a {@link Uni} - * without blocking the calling thread. - *

    - * Entities associated with an {@code Session} do not support transparent - * lazy association fetching. Instead, {@link #fetch(Object)} should be used - * to explicitly request asynchronous fetching of an association, or the - * association should be fetched eagerly when the entity is first retrieved, - * for example, by: - * - *

      - *
    • {@link #enableFetchProfile(String) enabling a fetch profile}, - *
    • using an {@link EntityGraph}, or - *
    • writing a {@code join fetch} clause in a HQL query. - *
    - * - * @see org.hibernate.Session - */ - interface Session extends Closeable { - + interface QueryProducer { /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, or {@code null} if there is no such - * persistent instance. If the instance is already associated with - * the session, return the associated instance. This method never - * returns an uninitialized instance. - * - *
    -		 * {@code session.find(Book.class, id).map(book -> print(book.getTitle()));}
    -		 * 
    + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string. * - * @param entityClass The entity type - * @param id an identifier + * @param queryString The HQL/JPQL query * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#find(Class, Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - Uni find(Class entityClass, Object id); + SelectionQuery createSelectionQuery(String queryString, Class resultType); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockMode}. + * Create a typed {@link org.hibernate.query.Query} instance for the given typed query reference. * - * @param entityClass The entity type - * @param id an identifier - * @param lockMode the requested {@link LockMode} + * @param typedQueryReference the type query reference * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link org.hibernate.query.Query} instance for execution * - * @see #find(Class, Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) */ - Uni find(Class entityClass, Object id, LockMode lockMode); + Query createQuery(TypedQueryReference typedQueryReference); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockModeType}. + * Create an instance of {@link MutationQuery} for the given HQL/JPQL + * update or delete statement. * - * @param entityClass The entity type - * @param id an identifier - * @param lockModeType the requested {@link LockModeType} + * @param queryString The HQL/JPQL query, update or delete statement * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class, Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @see jakarta.persistence.EntityManager#createQuery(String) */ - default Uni find(Class entityClass, Object id, LockModeType lockModeType) { - return find( entityClass, id, convertToLockMode( lockModeType ) ); - } + MutationQuery createMutationQuery(String queryString); /** - * Asynchronously return the persistent instance with the given - * identifier of an entity class, using the given {@link EntityGraph} - * as a fetch plan. + * Create an instance of {@link MutationQuery} for the given update tree. * - * @param entityGraph an {@link EntityGraph} specifying the entity - * and associations to be fetched - * @param id an identifier + * @param updateQuery the update criteria query * - * @see #find(Class, Object) + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) */ - Uni find(EntityGraph entityGraph, Object id); + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); /** - * Asynchronously return the persistent instances of the given entity - * class with the given identifiers, or null if there is no such - * persistent instance. + * Create an instance of {@link MutationQuery} for the given delete tree. * - * @param entityClass The entity type - * @param ids the identifiers + * @param deleteQuery the delete criteria query * - * @return a list of persistent instances and nulls via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see org.hibernate.Session#findMultiple(Class, List, FindOption...) + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) */ - Uni> find(Class entityClass, Object... ids); + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); /** - * Asynchronously return the persistent instance of the given entity - * class with the given natural identifier, or null if there is no - * such persistent instance. + * Create a {@link MutationQuery} from the given insert select criteria tree * - * @param entityClass The entity type - * @param naturalId the natural identifier + * @param insert the insert select criteria query * - * @return a persistent instance or null via a {@code Uni} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) */ - @Incubating - Uni find(Class entityClass, Identifier naturalId); + MutationQuery createMutationQuery(JpaCriteriaInsert insert); /** - * Return the persistent instance of the given entity class with the - * given identifier, assuming that the instance exists. This method - * never results in access to the underlying data store, and thus - * might return a proxied instance that must be initialized explicitly - * using {@link #fetch(Object)}. - *

    - * You should not use this method to determine if an instance exists - * (use {@link #find} instead). Use this only to retrieve an instance - * which you safely assume exists, where non-existence would be an - * actual error. + * Create an instance of {@link Query} for the given HQL/JPQL query + * string or HQL/JPQL update or delete statement. In the case of an + * update or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. * - * @param entityClass a persistent class - * @param id a valid identifier of an existing persistent instance of the class + * @param queryString The HQL/JPQL query, update or delete statement * - * @return the persistent instance or proxy + * @return The {@link Query} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#getReference(Class, Object) + * @deprecated See explanation in + * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - T getReference(Class entityClass, Object id); + @Deprecated + Query createQuery(String queryString); /** - * Return the persistent instance with the same identity as the given - * instance, which might be detached, assuming that the instance is - * still persistent in the database. This method never results in - * access to the underlying data store, and thus might return a proxy - * that must be initialized explicitly using {@link #fetch(Object)}. + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string and query result type. * - * @param entity a detached persistent instance + * @param queryString The HQL/JPQL query + * @param resultType the Java type returned in each row of query results * - * @return the persistent instance or proxy + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - T getReference(T entity); + SelectionQuery createQuery(String queryString, Class resultType); /** - * Asynchronously persist the given transient instance, first assigning - * a generated identifier. (Or using the current value of the identifier - * property if the entity has assigned identifiers.) - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + * Create an instance of {@link SelectionQuery} for the given criteria + * query. * - *

    -		 * {@code session.persist(newBook).map(v -> session.flush());}
    -		 * 
    + * @param criteriaQuery The {@link CriteriaQuery} * - * @param object a transient instance of a persistent class + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#persist(Object) + * @see jakarta.persistence.EntityManager#createQuery(String) */ - Uni persist(Object object); + SelectionQuery createQuery(CriteriaQuery criteriaQuery); /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

    - * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, - * {@code persist()} ultimately results in generation of an identifier for the - * given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * Create an instance of {@link MutationQuery} for the given criteria update. * - * @param entityName the entity name - * @param object a transient instance to be made persistent - * @see #persist(Object) - */ - Uni persist(String entityName, Object object); - - /** - * Persist multiple transient entity instances at once. + * @param criteriaUpdate The {@link CriteriaUpdate} * - * @see #persist(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - Uni persistAll(Object... entities); + MutationQuery createQuery(CriteriaUpdate criteriaUpdate); /** - * Asynchronously remove a persistent instance from the datastore. The - * argument may be an instance associated with the receiving session or - * a transient instance with an identifier associated with existing - * persistent state. - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. - * - *

    -		 * {@code session.delete(book).thenAccept(v -> session.flush());}
    -		 * 
    + * Create an instance of {@link MutationQuery} for the given criteria delete. * - * @param entity the managed persistent instance to be removed + * @param criteriaDelete The {@link CriteriaDelete} * - * @throws IllegalArgumentException if the given instance is not managed - * @see jakarta.persistence.EntityManager#remove(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - Uni remove(Object entity); + MutationQuery createQuery(CriteriaDelete criteriaDelete); /** - * Remove multiple entity instances at once. + * Create an instance of {@link Query} for the named query. * - * @see #remove(Object) + * @param queryName The name of the query + * + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - Uni removeAll(Object... entities); + Query createNamedQuery(String queryName); /** - * Copy the state of the given object onto the persistent instance with - * the same identifier. If there is no such persistent instance currently - * associated with the session, it will be loaded. Return the persistent - * instance. Or, if the given instance is transient, save a copy of it - * and return the copy as a newly persistent instance. The given instance - * does not become associated with the session. - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#MERGE}. + * Create an instance of {@link SelectionQuery} for the named query. * - * @param entity a detached instance with state to be copied + * @param queryName The name of the query + * @param resultType the Java type returned in each row of query results * - * @return an updated persistent instance + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#merge(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - Uni merge(T entity); + SelectionQuery createNamedQuery(String queryName, Class resultType); /** - * Merge multiple entity instances at once. - * - * @see #merge(Object) - */ - Uni mergeAll(Object... entities); - - /** - * Re-read the state of the given instance from the underlying database. - * It is inadvisable to use this to implement long-running sessions that - * span many business tasks. This method is, however, useful in certain - * special circumstances, for example: + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * *

      - *
    • where a database trigger alters the object state after insert or - * update, or - *
    • after executing direct native SQL in the same session. + *
    • If the result set has a single column, the results will be returned + * as scalars.
    • + *
    • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
    • *
    * - * @param entity a managed persistent instance - * - * @throws IllegalArgumentException if the given instance is not managed - * @see jakarta.persistence.EntityManager#refresh(Object) + * @param queryString The SQL select, update, insert, or delete statement */ - Uni refresh(Object entity); + Query createNativeQuery(String queryString); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockMode}. + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * - * @param entity a managed persistent entity instance - * @param lockMode the requested lock mode + *
      + *
    • If the result set has a single column, the results will be returned + * as scalars.
    • + *
    • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
    • + *
    + *

    + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the statement. * - * @see #refresh(Object) + * @param queryString The SQL select, update, insert, or delete statement + * @param affectedEntities The entities which are affected by the statement */ - Uni refresh(Object entity, LockMode lockMode); + Query createNativeQuery(String queryString, AffectedEntities affectedEntities); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockModeType}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * - * @param entity a managed persistent entity instance - * @param lockModeType the requested lock mode + *

      + *
    • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar. + *
    • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned in arrays. + *
    • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity. + *
    * - * @see #refresh(Object) - */ - default Uni refresh(Object entity, LockModeType lockModeType) { - return refresh( entity, convertToLockMode( lockModeType ) ); - } - - /** - * Refresh multiple entity instances at once. + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results * - * @see #refresh(Object) + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - Uni refreshAll(Object... entities); + SelectionQuery createNativeQuery(String queryString, Class resultType); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * *
      - *
    • perform a version check with {@link LockMode#PESSIMISTIC_READ}, - *
    • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, - *
    • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, - *
    • schedule a version check just before the end of the transaction with - * {@link LockMode#OPTIMISTIC}, or - *
    • schedule a version increment just before the end of the transaction - * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. + *
    • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar. + *
    • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned in arrays. + *
    • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity. *
    *

    - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @param entity a managed persistent instance - * @param lockMode the lock level + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * @param affectedEntities The entities which are affected by the query * - * @throws IllegalArgumentException if the given instance is not managed + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - Uni lock(Object entity, LockMode lockMode); + SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. * - *

      - *
    • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, - *
    • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, - *
    • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, - *
    • schedule a version check just before the end of the transaction with - * {@link LockModeType#OPTIMISTIC}, or - *
    • schedule a version increment just before the end of the transaction - * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. - *
    - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * @param queryString The SQL query + * @param resultSetMapping the result set mapping * - * @param entity a managed persistent instance - * @param lockModeType the lock level + * @return The {@link Query} instance for manipulation and execution * - * @throws IllegalArgumentException if the given instance is not managed + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - default Uni lock(Object entity, LockModeType lockModeType) { - return lock( entity, convertToLockMode( lockModeType ) ); - } + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); /** - * Force this session to flush asynchronously. Must be called at the - * end of a unit of work, before committing the transaction and closing - * the session. Flushing is the process of synchronizing the - * underlying persistent store with state held in memory. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. + *

    + * Any {@link AffectedEntities affected entities} are synchronized with the + * database before execution of the query. * - *

    -		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
    -		 * 
    + * @param queryString The SQL query + * @param resultSetMapping the result set mapping + * @param affectedEntities The entities which are affected by the query * - * @see jakarta.persistence.EntityManager#flush() + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - Uni flush(); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); /** - * Asynchronously fetch an association configured for lazy loading. - *

    - *

    -		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    -		 * 
    - *

    - *

    - * This operation may be even be used to initialize a reference returned by - * {@link #getReference(Class, Object)}. - *

    - *

    -		 * {@code session.fetch(session.getReference(Author.class, authorId))}
    -		 * 
    - *

    - * @param association a lazy-loaded association, or a proxy - * - * @return the fetched association, via a {@code Uni} - * - * @see Mutiny#fetch(Object) - * @see #getReference(Class, Object) - * @see org.hibernate.Hibernate#initialize(Object) + * Obtain a native SQL result set mapping defined via the annotation + * {@link jakarta.persistence.SqlResultSetMapping}. */ - Uni fetch(T association); + ResultSetMapping getResultSetMapping(Class resultType, String mappingName); /** - * Fetch a lazy property of the given entity, identified by a JPA - * {@link Attribute attribute metamodel}. Note that this feature is - * only supported in conjunction with the Hibernate bytecode enhancer. + * Obtain a named {@link EntityGraph} + */ + EntityGraph getEntityGraph(Class rootType, String graphName); + + /** + * Create a new mutable {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType); + + /** + * Create a new mutable copy of a named {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType, String graphName); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. * - *
    -		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
    -		 * 
    + * @since 3 */ - Uni fetch(E entity, Attribute field); + CriteriaBuilder getCriteriaBuilder(); + } + + /** + * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} + * interface, allowing a reactive style of interaction with the database. + *

    + * The semantics of operations on this interface are identical to the + * semantics of the similarly-named operations of {@code Session}, except + * that the operations are performed asynchronously, returning a {@link Uni} + * without blocking the calling thread. + *

    + * Entities associated with an {@code Session} do not support transparent + * lazy association fetching. Instead, {@link #fetch(Object)} should be used + * to explicitly request asynchronous fetching of an association, or the + * association should be fetched eagerly when the entity is first retrieved, + * for example, by: + * + *

      + *
    • {@link #enableFetchProfile(String) enabling a fetch profile}, + *
    • using an {@link EntityGraph}, or + *
    • writing a {@code join fetch} clause in a HQL query. + *
    + * + * @see org.hibernate.Session + */ + interface Session extends QueryProducer, Closeable { /** - * Asynchronously fetch an association that's configured for lazy loading, - * and unwrap the underlying entity implementation from any proxy. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, or {@code null} if there is no such + * persistent instance. If the instance is already associated with + * the session, return the associated instance. This method never + * returns an uninitialized instance. * *
    -		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    +		 * {@code session.find(Book.class, id).map(book -> print(book.getTitle()));}
     		 * 
    * - * @param association a lazy-loaded association + * @param entityClass The entity type + * @param id an identifier * - * @return the fetched association, via a {@code Uni} + * @return a persistent instance or null via a {@code Uni} * - * @see org.hibernate.Hibernate#unproxy(Object) + * @see jakarta.persistence.EntityManager#find(Class, Object) */ - Uni unproxy(T association); + Uni find(Class entityClass, Object id); /** - * Determine the current lock mode of the given entity. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockMode}. + * + * @param entityClass The entity type + * @param id an identifier + * @param lockMode the requested {@link LockMode} + * + * @return a persistent instance or null via a {@code Uni} + * + * @see #find(Class, Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - LockMode getLockMode(Object entity); + Uni find(Class entityClass, Object id, LockMode lockMode); /** - * Determine if the given instance belongs to this persistence context. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockModeType}. + * + * @param entityClass The entity type + * @param id an identifier + * @param lockModeType the requested {@link LockModeType} + * + * @return a persistent instance or null via a {@code Uni} + * + * @see #find(Class, Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - boolean contains(Object entity); + default Uni find(Class entityClass, Object id, LockModeType lockModeType) { + return find( entityClass, id, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query + * Asynchronously return the persistent instance with the given + * identifier of an entity class, using the given {@link EntityGraph} + * as a fetch plan. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entityGraph an {@link EntityGraph} specifying the entity + * and associations to be fetched + * @param id an identifier * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #find(Class, Object) */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); + Uni find(EntityGraph entityGraph, Object id); /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. + * Asynchronously return the persistent instances of the given entity + * class with the given identifiers, or null if there is no such + * persistent instance. * - * @param queryString The HQL/JPQL query, update or delete statement + * @param entityClass The entity type + * @param ids the identifiers * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return a list of persistent instances and nulls via a {@code Uni} * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see org.hibernate.Session#findMultiple(Class, List, FindOption...) */ - MutationQuery createMutationQuery(String queryString); + Uni> find(Class entityClass, Object... ids); /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query + * Asynchronously return the persistent instance of the given entity + * class with the given natural identifier, or null if there is no + * such persistent instance. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entityClass The entity type + * @param naturalId the natural identifier * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + * @return a persistent instance or null via a {@code Uni} */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + @Incubating + Uni find(Class entityClass, Identifier naturalId); /** - * Create an instance of {@link MutationQuery} for the given delete tree. + * Return the persistent instance of the given entity class with the + * given identifier, assuming that the instance exists. This method + * never results in access to the underlying data store, and thus + * might return a proxied instance that must be initialized explicitly + * using {@link #fetch(Object)}. + *

    + * You should not use this method to determine if an instance exists + * (use {@link #find} instead). Use this only to retrieve an instance + * which you safely assume exists, where non-existence would be an + * actual error. * - * @param deleteQuery the delete criteria query + * @param entityClass a persistent class + * @param id a valid identifier of an existing persistent instance of the class * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return the persistent instance or proxy * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + * @see jakarta.persistence.EntityManager#getReference(Class, Object) */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + T getReference(Class entityClass, Object id); /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query + * Return the persistent instance with the same identity as the given + * instance, which might be detached, assuming that the instance is + * still persistent in the database. This method never results in + * access to the underlying data store, and thus might return a proxy + * that must be initialized explicitly using {@link #fetch(Object)}. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a detached persistent instance * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + * @return the persistent instance or proxy */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); + T getReference(T entity); /** - * Create a typed {@link org.hibernate.query.Query} instance for the given typed query reference. + * Asynchronously persist the given transient instance, first assigning + * a generated identifier. (Or using the current value of the identifier + * property if the entity has assigned identifiers.) + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. * - * @param typedQueryReference the type query reference + *

    +		 * {@code session.persist(newBook).map(v -> session.flush());}
    +		 * 
    * - * @return The {@link org.hibernate.query.Query} instance for execution + * @param object a transient instance of a persistent class * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference + * @see jakarta.persistence.EntityManager#persist(Object) + */ + Uni persist(Object object); + + /** + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

    + * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) */ - Query createQuery(TypedQueryReference typedQueryReference); + Uni persist(String entityName, Object object); /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. + * Persist multiple transient entity instances at once. * - * @param queryString The HQL/JPQL query, update or delete statement + * @see #persist(Object) + */ + Uni persistAll(Object... entities); + + /** + * Asynchronously remove a persistent instance from the datastore. The + * argument may be an instance associated with the receiving session or + * a transient instance with an identifier associated with existing + * persistent state. + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. * - * @return The {@link Query} instance for manipulation and execution + *

    +		 * {@code session.delete(book).thenAccept(v -> session.flush());}
    +		 * 
    * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * @param entity the managed persistent instance to be removed * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#remove(Object) */ - @Deprecated - Query createQuery(String queryString); + Uni remove(Object entity); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * Remove multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #remove(Object) */ - SelectionQuery createQuery(String queryString, Class resultType); + Uni removeAll(Object... entities); /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. + * Copy the state of the given object onto the persistent instance with + * the same identifier. If there is no such persistent instance currently + * associated with the session, it will be loaded. Return the persistent + * instance. Or, if the given instance is transient, save a copy of it + * and return the copy as a newly persistent instance. The given instance + * does not become associated with the session. + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#MERGE}. * - * @param criteriaQuery The {@link CriteriaQuery} + * @param entity a detached instance with state to be copied * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @return an updated persistent instance * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#merge(Object) */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); + Uni merge(T entity); /** - * Create an instance of {@link MutationQuery} for the given criteria update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} + * Merge multiple entity instances at once. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @see #merge(Object) */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); + Uni mergeAll(Object... entities); /** - * Create an instance of {@link MutationQuery} for the given criteria delete. + * Re-read the state of the given instance from the underlying database. + * It is inadvisable to use this to implement long-running sessions that + * span many business tasks. This method is, however, useful in certain + * special circumstances, for example: * - * @param criteriaDelete The {@link CriteriaDelete} + *

      + *
    • where a database trigger alters the object state after insert or + * update, or + *
    • after executing direct native SQL in the same session. + *
    * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * + * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#refresh(Object) */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); + Uni refresh(Object entity); /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockMode}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockMode the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see #refresh(Object) */ - Query createNamedQuery(String queryName); + Uni refresh(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockModeType}. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockModeType the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #refresh(Object) */ - SelectionQuery createNamedQuery(String queryName, Class resultType); + default Uni refresh(Object entity, LockModeType lockModeType) { + return refresh( entity, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: - * - *
      - *
    • If the result set has a single column, the results will be returned - * as scalars.
    • - *
    • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
    • - *
    + * Refresh multiple entity instances at once. * - * @param queryString The SQL select, update, insert, or delete statement + * @see #refresh(Object) */ - Query createNativeQuery(String queryString); + Uni refreshAll(Object... entities); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
      - *
    • If the result set has a single column, the results will be returned - * as scalars.
    • - *
    • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
    • + *
    • perform a version check with {@link LockMode#PESSIMISTIC_READ}, + *
    • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, + *
    • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, + *
    • schedule a version check just before the end of the transaction with + * {@link LockMode#OPTIMISTIC}, or + *
    • schedule a version increment just before the end of the transaction + * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. *
    *

    - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the statement. + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @param queryString The SQL select, update, insert, or delete statement - * @param affectedEntities The entities which are affected by the statement + * @param entity a managed persistent instance + * @param lockMode the lock level + * + * @throws IllegalArgumentException if the given instance is not managed */ - Query createNativeQuery(String queryString, AffectedEntities affectedEntities); + Uni lock(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *

      - *
    • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar. - *
    • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned in arrays. - *
    • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity. + *
    • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, + *
    • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, + *
    • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, + *
    • schedule a version check just before the end of the transaction with + * {@link LockModeType#OPTIMISTIC}, or + *
    • schedule a version increment just before the end of the transaction + * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. *
    + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockModeType the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType); + default Uni lock(Object entity, LockModeType lockModeType) { + return lock( entity, convertToLockMode( lockModeType ) ); + } /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Force this session to flush asynchronously. Must be called at the + * end of a unit of work, before committing the transaction and closing + * the session. Flushing is the process of synchronizing the + * underlying persistent store with state held in memory. * - *

      - *
    • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar. - *
    • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned in arrays. - *
    • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity. - *
    + *
    +		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
    +		 * 
    + * + * @see jakarta.persistence.EntityManager#flush() + */ + Uni flush(); + + /** + * Asynchronously fetch an association configured for lazy loading. *

    - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. + *

    +		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    +		 * 
    + *

    + *

    + * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

    + *

    +		 * {@code session.fetch(session.getReference(Author.class, authorId))}
    +		 * 
    + *

    + * @param association a lazy-loaded association, or a proxy * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * @param affectedEntities The entities which are affected by the query + * @return the fetched association, via a {@code Uni} * - * @return The {@link Query} instance for manipulation and execution + * @see Mutiny#fetch(Object) + * @see #getReference(Class, Object) + * @see org.hibernate.Hibernate#initialize(Object) + */ + Uni fetch(T association); + + /** + * Fetch a lazy property of the given entity, identified by a JPA + * {@link Attribute attribute metamodel}. Note that this feature is + * only supported in conjunction with the Hibernate bytecode enhancer. * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + *
    +		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
    +		 * 
    */ - SelectionQuery createNativeQuery( - String queryString, Class resultType, - AffectedEntities affectedEntities); + Uni fetch(E entity, Attribute field); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. + * Asynchronously fetch an association that's configured for lazy loading, + * and unwrap the underlying entity implementation from any proxy. * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping + *
    +		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    +		 * 
    * - * @return The {@link Query} instance for manipulation and execution + * @param association a lazy-loaded association * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @return the fetched association, via a {@code Uni} + * + * @see org.hibernate.Hibernate#unproxy(Object) */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); + Uni unproxy(T association); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - *

    - * Any {@link AffectedEntities affected entities} are synchronized with the - * database before execution of the query. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * @param affectedEntities The entities which are affected by the query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * Determine the current lock mode of the given entity. + */ + LockMode getLockMode(Object entity); + + /** + * Determine if the given instance belongs to this persistence context. */ - SelectionQuery createNativeQuery( - String queryString, - ResultSetMapping resultSetMapping, - AffectedEntities affectedEntities); + boolean contains(Object entity); /** * Set the {@link FlushMode flush mode} for this session. @@ -1265,27 +1289,6 @@ default Session setFlushMode(FlushModeType flushModeType) { */ Session enableFetchProfile(String name); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Disable a particular fetch profile on this session, or do nothing if * the requested fetch profile is not enabled. @@ -1511,15 +1514,6 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** @@ -1552,7 +1546,7 @@ default CriteriaBuilder getCriteriaBuilder() { * * @see org.hibernate.StatelessSession */ - interface StatelessSession extends Closeable { + interface StatelessSession extends QueryProducer, Closeable { /** * Retrieve a row. @@ -1617,169 +1611,6 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp */ Uni get(EntityGraph entityGraph, Object id); - /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link Query} instance for manipulation and execution - * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} - * - * @see Session#createQuery(String) - */ - @Deprecated - Query createQuery(String queryString); - - /** - * Create a typed {@link Query} instance for the given typed query reference. - * - * @param typedQueryReference the type query reference - * - * @return The {@link Query} instance for execution - * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference - * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) - */ - Query createQuery(TypedQueryReference typedQueryReference); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createQuery(String, Class) - */ - SelectionQuery createQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createMutationQuery(String queryString); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - Query createNamedQuery(String queryName); - - /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createNamedQuery(String queryName, Class resultType); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The SQL select, update, insert, or delete statement - * - * @see Session#createNativeQuery(String) - */ - Query createNativeQuery(String queryString); - - /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createNativeQuery(String, Class) - */ - SelectionQuery createNativeQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * - * @return The {@link Query} instance for manipulation and execution - * - * @see Session#createNativeQuery(String, ResultSetMapping) - */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); - - /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); - - /** - * Create an instance of {@link MutationQuery} for the given criteria update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} - * - * @return The {@link MutationQuery} instance for manipulation and execution - */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); - - /** - * Create an instance of {@link MutationQuery} for the given criteria delete. - * - * @param criteriaDelete The {@link CriteriaDelete} - * - * @return The {@link MutationQuery} instance for manipulation and execution - */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); - /** * Insert a row. * @@ -2046,27 +1877,6 @@ default Uni refresh(Object entity, LockModeType lockModeType) { */ Object getIdentifier(Object entity); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Performs the given work within the scope of a database transaction, * automatically flushing the session. The transaction will be rolled @@ -2115,15 +1925,6 @@ default Uni refresh(Object entity, LockModeType lockModeType) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index ab57ee718..8a4149104 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -5,18 +5,6 @@ */ package org.hibernate.reactive.mutiny.impl; -import io.smallrye.mutiny.Uni; -import jakarta.persistence.CacheRetrieveMode; -import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.TypedQueryReference; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; -import jakarta.persistence.metamodel.Attribute; import org.hibernate.CacheMode; import org.hibernate.Filter; import org.hibernate.FlushMode; @@ -40,6 +28,19 @@ import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.ReactiveSession; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Attribute; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; @@ -48,7 +49,6 @@ import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; - /** * Implements the {@link Mutiny.Session} API. This delegating class is * needed to avoid name clashes when implementing both @@ -578,6 +578,11 @@ public Mutiny.SessionFactory getFactory() { return factory; } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index cb7db4a8e..f5a6352c1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -5,14 +5,10 @@ */ package org.hibernate.reactive.mutiny.impl; -import io.smallrye.mutiny.Uni; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.TypedQueryReference; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.mutiny.Mutiny.Query; @@ -21,13 +17,19 @@ import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveStatelessSession; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.function.Supplier; - /** * Implements the {@link Mutiny.StatelessSession} API. This delegating * class is needed to avoid name clashes when implementing both @@ -47,7 +49,6 @@ public ReactiveConnection getReactiveConnection() { return delegate.getReactiveConnection(); } - Uni uni(Supplier> stageSupplier) { return factory.uni( stageSupplier ); } @@ -91,12 +92,42 @@ public SelectionQuery createQuery(String queryString, Class resultType @Override public SelectionQuery createSelectionQuery(String queryString, Class resultType) { - return new MutinySelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType), factory ); + return new MutinySelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType ), factory ); } @Override public Mutiny.MutationQuery createMutationQuery(String queryString) { - return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString), factory ); + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( queryString ), factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( updateQuery ), factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( deleteQuery ) , factory ); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return new MutinyMutationQueryImpl<>( delegate.createReactiveMutationQuery( insert ) , factory ); + } + + @Override + public Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, affectedEntities ), factory ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType, affectedEntities ), factory ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return new MutinyQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping, affectedEntities ), factory ); } @Override @@ -259,26 +290,6 @@ public Object getIdentifier(Object entity) { return delegate.getIdentifier(entity); } -// @Override -// public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { -// return delegate.getResultSetMapping( resultType, mappingName ); -// } -// -// @Override -// public EntityGraph getEntityGraph(Class entity, String name) { -// return delegate.getEntityGraph( entity, name ); -// } -// -// @Override -// public EntityGraph createEntityGraph(Class entity) { -// return delegate.createEntityGraph( entity ); -// } -// -// @Override -// public EntityGraph createEntityGraph(Class entity, String name) { -// return delegate.createEntityGraph( entity, name ); -// } - @Override public Uni withTransaction(Function> work) { return currentTransaction == null ? new Transaction().execute( work ) : work.apply( currentTransaction ); @@ -359,6 +370,11 @@ public MutinySessionFactoryImpl getFactory() { return factory; } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java index c9a7cc11b..4169d479d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveQueryProducer.java @@ -5,9 +5,6 @@ */ package org.hibernate.reactive.session; -import jakarta.persistence.TypedQueryReference; -import java.util.concurrent.CompletionStage; - import org.hibernate.Incubating; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -22,9 +19,11 @@ import org.hibernate.reactive.query.ReactiveSelectionQuery; import jakarta.persistence.EntityGraph; +import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import java.util.concurrent.CompletionStage; /** diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 6de95c154..7085ef861 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -26,7 +26,6 @@ import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.Page; -import org.hibernate.query.Query; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaInsert; import org.hibernate.reactive.common.AffectedEntities; @@ -484,740 +483,744 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query setComment(String comment); } - /** - * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} - * interface, allowing a reactive style of interaction with the database. - *

    - * The semantics of operations on this interface are identical to the - * semantics of the similarly-named operations of {@code Session}, except - * that the operations are performed asynchronously, returning a - * {@link CompletionStage} without blocking the calling thread. - *

    - * Entities associated with an {@code Session} do not support transparent - * lazy association fetching. Instead, {@link #fetch(Object)} should be used - * to explicitly request asynchronous fetching of an association, or the - * association should be fetched eagerly when the entity is first retrieved, - * for example, by: - * - *

      - *
    • {@link #enableFetchProfile(String) enabling a fetch profile}, - *
    • using an {@link EntityGraph}, or - *
    • writing a {@code join fetch} clause in a HQL query. - *
    - * - * @see org.hibernate.Session - */ - interface Session extends Closeable { - + interface QueryProducer { /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, or {@code null} if there is no such - * persistent instance. If the instance is already associated with - * the session, return the associated instance. This method never - * returns an uninitialized instance. - * - *
    -		 * {@code session.find(Book.class, id).thenAccept(book -> print(book.getTitle()));}
    -		 * 
    + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string. * - * @param entityClass The entity type - * @param id an identifier + * @param queryString The HQL/JPQL query * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#find(Class, Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage find(Class entityClass, Object id); + SelectionQuery createSelectionQuery(String queryString, Class resultType); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockMode}. + * Create an instance of {@link MutationQuery} for the given HQL/JPQL + * update or delete statement. * - * @param entityClass The entity type - * @param id an identifier - * @param lockMode the requested {@link LockMode} + * @param queryString The HQL/JPQL query, update or delete statement * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class,Object) - * @see #lock(Object, LockMode) this discussion of lock modes + * @see jakarta.persistence.EntityManager#createQuery(String) */ - CompletionStage find(Class entityClass, Object id, LockMode lockMode); + MutationQuery createMutationQuery(String queryString); /** - * Asynchronously return the persistent instance of the given entity - * class with the given identifier, requesting the given {@link LockModeType}. - * - * @param entityClass The entity type - * @param id an identifier - * @param lockModeType the requested {@link LockModeType} - * - * @return a persistent instance or null via a {@code CompletionStage} + * Create an instance of {@link MutationQuery} for the given update tree. * - * @see #find(Class,Object) - * @see #lock(Object, LockMode) this discussion of lock modes - */ - default CompletionStage find(Class entityClass, Object id, LockModeType lockModeType) { - return find( entityClass, id, convertToLockMode(lockModeType) ); - } - -// /** -// * Asynchronously return the persistent instance of the given entity -// * class with the given identifier, requesting the given {@link LockOptions}. -// * -// * @param entityClass The entity type -// * @param id an identifier -// * @param lockOptions the requested {@link LockOptions} -// * -// * @return a persistent instance or null via a {@code CompletionStage} -// * -// * @see #find(Class,Object) -// * @see #lock(Object, LockMode) this discussion of lock modes -// */ -// CompletionStage find(Class entityClass, Object id, LockOptions lockOptions); - - /** - * Asynchronously return the persistent instance with the given - * identifier of an entity class, using the given {@link EntityGraph} - * as a fetch plan. + * @param updateQuery the update criteria query * - * @param entityGraph an {@link EntityGraph} specifying the entity - * and associations to be fetched - * @param id an identifier + * @return The {@link MutationQuery} instance for manipulation and execution * - * @see #find(Class,Object) + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) */ - CompletionStage find(EntityGraph entityGraph, Object id); + MutationQuery createMutationQuery(CriteriaUpdate updateQuery); /** - * Asynchronously return the persistent instances of the given entity - * class with the given identifiers, or null if there is no such - * persistent instance. + * Create an instance of {@link MutationQuery} for the given delete tree. * - * @param entityClass The entity type - * @param ids the identifiers + * @param deleteQuery the delete criteria query * - * @return a list of persistent instances and nulls via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) */ - CompletionStage> find(Class entityClass, Object... ids); + MutationQuery createMutationQuery(CriteriaDelete deleteQuery); /** - * Asynchronously return the persistent instance of the given entity - * class with the given natural identifiers, or null if there is no - * such persistent instance. + * Create a {@link MutationQuery} from the given insert select criteria tree * - * @param entityClass The entity type - * @param naturalId the natural identifier + * @param insert the insert select criteria query * - * @return a persistent instance or null via a {@code CompletionStage} + * @return The {@link MutationQuery} instance for manipulation and execution + * + * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) */ - @Incubating - CompletionStage find(Class entityClass, Identifier naturalId); + MutationQuery createMutationQuery(JpaCriteriaInsert insert); /** - * Return the persistent instance of the given entity class with the - * given identifier, assuming that the instance exists. This method - * never results in access to the underlying data store, and thus - * might return a proxy that must be initialized explicitly using - * {@link #fetch(Object)}. - *

    - * You should not use this method to determine if an instance exists - * (use {@link #find} instead). Use this only to retrieve an instance - * which you safely assume exists, where non-existence would be an - * actual error. + * Create a typed {@link Query} instance for the given typed query reference. * - * @param entityClass a persistent class - * @param id a valid identifier of an existing persistent instance of the class + * @param typedQueryReference the type query reference * - * @return the persistent instance or proxy + * @return The {@link Query} instance for execution * - * @see jakarta.persistence.EntityManager#getReference(Class, Object) + * @throws IllegalArgumentException if a query has not been + * defined with the name of the typed query reference or if + * the query result is found to not be assignable to + * result class of the typed query reference + * + * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) */ - T getReference(Class entityClass, Object id); + Query createQuery(TypedQueryReference typedQueryReference); /** - * Return the persistent instance with the same identity as the given - * instance, which might be detached, assuming that the instance is - * still persistent in the database. This method never results in - * access to the underlying data store, and thus might return a proxy - * that must be initialized explicitly using {@link #fetch(Object)}. + * Create an instance of {@link Query} for the given HQL/JPQL query + * string or HQL/JPQL update or delete statement. In the case of an + * update or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. * - * @param entity a detached persistent instance + * @param queryString The HQL/JPQL query, update or delete statement * - * @return the persistent instance or proxy + * @return The {@link Query} instance for manipulation and execution + * + * @deprecated See explanation in + * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - T getReference(T entity); + @Deprecated + Query createQuery(String queryString); /** - * Asynchronously persist the given transient instance, first assigning - * a generated identifier. (Or using the current value of the identifier - * property if the entity has assigned identifiers.) - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + * Create an instance of {@link SelectionQuery} for the given HQL/JPQL + * query string and query result type. * - *

    -		 * {@code session.persist(newBook).thenAccept(v -> session.flush());}
    -		 * 
    + * @param queryString The HQL/JPQL query + * @param resultType the Java type returned in each row of query results * - * @param entity a transient instance of a persistent class + * @return The {@link Query} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#persist(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage persist(Object entity); + SelectionQuery createQuery(String queryString, Class resultType); /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

    - * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, - * {@code persist()} ultimately results in generation of an identifier for the - * given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. + * Create an instance of {@link MutationQuery} for the given criteria + * update. * - * @param entityName the entity name - * @param object a transient instance to be made persistent - * @see #persist(Object) - */ - CompletionStage persist(String entityName, Object object); - - /** - * Persist multiple transient entity instances at once. + * @param criteriaUpdate The {@link CriteriaUpdate} * - * @see #persist(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - CompletionStage persist(Object... entities); + MutationQuery createQuery(CriteriaUpdate criteriaUpdate); /** - * Asynchronously remove a persistent instance from the datastore. The - * argument may be an instance associated with the receiving session or - * a transient instance with an identifier associated with existing - * persistent state. - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. - * - *

    -		 * {@code session.delete(book).thenAccept(v -> session.flush());}
    -		 * 
    - * - * @param entity the managed persistent instance to be removed + * Create an instance of {@link MutationQuery} for the given criteria + * delete. * - * @throws IllegalArgumentException if the given instance is not managed + * @param criteriaDelete The {@link CriteriaDelete} * - * @see jakarta.persistence.EntityManager#remove(Object) + * @return The {@link MutationQuery} instance for manipulation and execution */ - CompletionStage remove(Object entity); + MutationQuery createQuery(CriteriaDelete criteriaDelete); /** - * Remove multiple entity instances at once. + * Create an instance of {@link Query} for the named query. * - * @see #remove(Object) + * @param queryName The name of the query + * + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createQuery(String) */ - CompletionStage remove(Object... entities); + Query createNamedQuery(String queryName); /** - * Copy the state of the given object onto the persistent instance with - * the same identifier. If there is no such persistent instance currently - * associated with the session, it will be loaded. Return the persistent - * instance. Or, if the given instance is transient, save a copy of it - * and return the copy as a newly persistent instance. The given instance - * does not become associated with the session. - *

    - * This operation cascades to associated instances if the association is - * mapped with {@link jakarta.persistence.CascadeType#MERGE}. + * Create an instance of {@link SelectionQuery} for the named query. * - * @param entity a detached instance with state to be copied + * @param queryName The name of the query + * @param resultType the Java type returned in each row of query results * - * @return an updated persistent instance + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @see jakarta.persistence.EntityManager#merge(Object) + * @see jakarta.persistence.EntityManager#createQuery(String, Class) */ - CompletionStage merge(T entity); + SelectionQuery createNamedQuery(String queryName, Class resultType); /** - * Merge multiple entity instances at once. - * - * @see #merge(Object) + * Create an instance of {@link Query} for the given SQL query string, + * using the given {@code resultType} to interpret the results. + * + *

      + *
    • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar.
    • + *
    • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned as elements of + * arrays of type {@code Object[]}.
    • + *
    • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity.
    • + *
    + * + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * + * @return The {@link SelectionQuery} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - CompletionStage merge(Object... entities); + SelectionQuery createNativeQuery(String queryString, Class resultType); /** - * Re-read the state of the given instance from the underlying database. - * It is inadvisable to use this to implement long-running sessions that - * span many business tasks. This method is, however, useful in certain - * special circumstances, for example: + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@code resultType} to interpret the results. * *
      - *
    • where a database trigger alters the object state after insert or - * update, or - *
    • after executing direct native SQL in the same session. + *
    • If the given result type is {@link Object}, or a built-in type + * such as {@link String} or {@link Integer}, the result set must + * have a single column, which will be returned as a scalar.
    • + *
    • If the given result type is {@code Object[]}, then the result set + * must have multiple columns, which will be returned as elements of + * arrays of type {@code Object[]}.
    • + *
    • Otherwise, the given result type must be an entity class, in which + * case the result set column aliases must map to the fields of the + * entity, and the query will return instances of the entity.
    • *
    * - * @param entity a managed persistent instance + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @throws IllegalArgumentException if the given instance is not managed + * @param queryString The SQL query + * @param resultType the Java type returned in each row of query results + * @param affectedEntities The entities which are affected by the query * - * @see jakarta.persistence.EntityManager#refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) */ - CompletionStage refresh(Object entity); + SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockMode}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. * - * @param entity a managed persistent entity instance - * @param lockMode the requested lock mode + * @param queryString The SQL query + * @param resultSetMapping the result set mapping * - * @see #refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - CompletionStage refresh(Object entity, LockMode lockMode); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); /** - * Re-read the state of the given instance from the underlying database, - * requesting the given {@link LockModeType}. + * Create an instance of {@link SelectionQuery} for the given SQL query + * string, using the given {@link ResultSetMapping} to interpret the + * result set. + *

    + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the query. * - * @param entity a managed persistent entity instance - * @param lockModeType the requested lock mode + * @param queryString The SQL query + * @param resultSetMapping the result set mapping + * @param affectedEntities The entities which are affected by the query * - * @see #refresh(Object) + * @return The {@link Query} instance for manipulation and execution + * + * @see #getResultSetMapping(Class, String) + * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) */ - default CompletionStage refresh(Object entity, LockModeType lockModeType) { - return refresh( entity, convertToLockMode(lockModeType) ); - } - -// /** -// * Re-read the state of the given instance from the underlying database, -// * requesting the given {@link LockOptions}. -// * -// * @param entity a managed persistent entity instance -// * @param lockOptions the requested {@link LockOptions} -// * -// * @see #refresh(Object) -// */ -// CompletionStage refresh(Object entity, LockOptions lockOptions); + SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); /** - * Refresh multiple entity instances at once. + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * - * @see #refresh(Object) + *

      + *
    • If the result set has a single column, the results will be returned + * as scalars.
    • + *
    • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
    • + *
    + * + * @param queryString The SQL select, update, insert, or delete statement */ - CompletionStage refresh(Object... entities); + Query createNativeQuery(String queryString); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: + * Create an instance of {@link Query} for the given SQL query string, + * or SQL update, insert, or delete statement. In the case of an update, + * insert, or delete, the returned {@link Query} must be executed using + * {@link Query#executeUpdate()} which returns an affected row count. + * In the case of a query: * *
      - *
    • perform a version check with {@link LockMode#PESSIMISTIC_READ}, - *
    • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, - *
    • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, - *
    • schedule a version check just before the end of the transaction with - * {@link LockMode#OPTIMISTIC}, or - *
    • schedule a version increment just before the end of the transaction - * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. + *
    • If the result set has a single column, the results will be returned + * as scalars.
    • + *
    • Otherwise, if the result set has multiple columns, the results will + * be returned as elements of arrays of type {@code Object[]}.
    • *
    * - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. - * - * @param entity a managed persistent instance - * @param lockMode the lock level + * Any {@link AffectedEntities affected entities} are synchronized with + * the database before execution of the statement. * - * @throws IllegalArgumentException if the given instance is not managed + * @param queryString The SQL select, update, insert, or delete statement + * @param affectedEntities The entities which are affected by the statement */ - CompletionStage lock(Object entity, LockMode lockMode); + Query createNativeQuery(String queryString, AffectedEntities affectedEntities); /** - * Obtain the specified lock level upon the given object. For example, - * this operation may be used to: - * - *
      - *
    • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, - *
    • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, - *
    • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, - *
    • schedule a version check just before the end of the transaction with - * {@link LockModeType#OPTIMISTIC}, or - *
    • schedule a version increment just before the end of the transaction - * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. - *
    + * Create an instance of {@link SelectionQuery} for the given criteria + * query. * - * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. + * @param criteriaQuery The {@link CriteriaQuery} * - * @param entity a managed persistent instance - * @param lockModeType the lock level + * @return The {@link SelectionQuery} instance for manipulation and execution * - * @throws IllegalArgumentException if the given instance is not managed + * @see jakarta.persistence.EntityManager#createQuery(String) */ - default CompletionStage lock(Object entity, LockModeType lockModeType) { - return lock( entity, convertToLockMode(lockModeType) ); - } + SelectionQuery createQuery(CriteriaQuery criteriaQuery); /** - * Force this session to flush asynchronously. Must be called at the - * end of a unit of work, before committing the transaction and closing - * the session. Flushing is the process of synchronizing the - * underlying persistent store with state held in memory. - * - *
    -		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
    -		 * 
    + * Obtain a native SQL result set mapping defined via the annotation + * {@link jakarta.persistence.SqlResultSetMapping}. + */ + ResultSetMapping getResultSetMapping(Class resultType, String mappingName); + + /** + * Obtain a named {@link EntityGraph} + */ + EntityGraph getEntityGraph(Class rootType, String graphName); + + /** + * Create a new mutable {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType); + + /** + * Create a new mutable copy of a named {@link EntityGraph} + */ + EntityGraph createEntityGraph(Class rootType, String graphName); + + /** + * Convenience method to obtain the {@link CriteriaBuilder}. * - * @see jakarta.persistence.EntityManager#flush() + * @since 3 */ - CompletionStage flush(); + CriteriaBuilder getCriteriaBuilder(); + } + + /** + * A non-blocking counterpart to the Hibernate {@link org.hibernate.Session} + * interface, allowing a reactive style of interaction with the database. + *

    + * The semantics of operations on this interface are identical to the + * semantics of the similarly-named operations of {@code Session}, except + * that the operations are performed asynchronously, returning a + * {@link CompletionStage} without blocking the calling thread. + *

    + * Entities associated with an {@code Session} do not support transparent + * lazy association fetching. Instead, {@link #fetch(Object)} should be used + * to explicitly request asynchronous fetching of an association, or the + * association should be fetched eagerly when the entity is first retrieved, + * for example, by: + * + *

      + *
    • {@link #enableFetchProfile(String) enabling a fetch profile}, + *
    • using an {@link EntityGraph}, or + *
    • writing a {@code join fetch} clause in a HQL query. + *
    + * + * @see org.hibernate.Session + */ + interface Session extends QueryProducer, Closeable { /** - * Asynchronously fetch an association configured for lazy loading. - *

    - *

    -		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()))}
    -		 * 
    - *

    - *

    - * This operation may be even be used to initialize a reference returned by - * {@link #getReference(Class, Object)}. - *

    + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, or {@code null} if there is no such + * persistent instance. If the instance is already associated with + * the session, return the associated instance. This method never + * returns an uninitialized instance. + * *

    -		 * {@code session.fetch(session.getReference(Author.class, authorId))}
    +		 * {@code session.find(Book.class, id).thenAccept(book -> print(book.getTitle()));}
     		 * 
    - *

    * - * @param association a lazy-loaded association, or a proxy + * @param entityClass The entity type + * @param id an identifier * - * @return the fetched association, via a {@code CompletionStage} + * @return a persistent instance or null via a {@code CompletionStage} * - * @see Stage#fetch(Object) - * @see #getReference(Class, Object) - * @see org.hibernate.Hibernate#initialize(Object) + * @see jakarta.persistence.EntityManager#find(Class, Object) */ - CompletionStage fetch(T association); + CompletionStage find(Class entityClass, Object id); /** - * Fetch a lazy property of the given entity, identified by a JPA - * {@link Attribute attribute metamodel}. Note that this feature is - * only supported in conjunction with the Hibernate bytecode enhancer. + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockMode}. * - *
    -		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
    -		 * 
    + * @param entityClass The entity type + * @param id an identifier + * @param lockMode the requested {@link LockMode} + * + * @return a persistent instance or null via a {@code CompletionStage} + * + * @see #find(Class,Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - CompletionStage fetch(E entity, Attribute field); + CompletionStage find(Class entityClass, Object id, LockMode lockMode); /** - * Asynchronously fetch an association that's configured for lazy loading, - * and unwrap the underlying entity implementation from any proxy. - * - *
    -		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    -		 * 
    + * Asynchronously return the persistent instance of the given entity + * class with the given identifier, requesting the given {@link LockModeType}. * - * @param association a lazy-loaded association + * @param entityClass The entity type + * @param id an identifier + * @param lockModeType the requested {@link LockModeType} * - * @return the fetched association, via a {@code CompletionStage} + * @return a persistent instance or null via a {@code CompletionStage} * - * @see org.hibernate.Hibernate#unproxy(Object) + * @see #find(Class,Object) + * @see #lock(Object, LockMode) this discussion of lock modes */ - CompletionStage unproxy(T association); + default CompletionStage find(Class entityClass, Object id, LockModeType lockModeType) { + return find( entityClass, id, convertToLockMode(lockModeType) ); + } - /** - * Determine the current lock mode of the given entity. + /** + * Asynchronously return the persistent instance with the given + * identifier of an entity class, using the given {@link EntityGraph} + * as a fetch plan. + * + * @param entityGraph an {@link EntityGraph} specifying the entity + * and associations to be fetched + * @param id an identifier + * + * @see #find(Class,Object) */ - LockMode getLockMode(Object entity); + CompletionStage find(EntityGraph entityGraph, Object id); /** - * Determine if the given instance belongs to this persistence context. + * Asynchronously return the persistent instances of the given entity + * class with the given identifiers, or null if there is no such + * persistent instance. + * + * @param entityClass The entity type + * @param ids the identifiers + * + * @return a list of persistent instances and nulls via a {@code CompletionStage} */ - boolean contains(Object entity); + CompletionStage> find(Class entityClass, Object... ids); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query + * Asynchronously return the persistent instance of the given entity + * class with the given natural identifiers, or null if there is no + * such persistent instance. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entityClass The entity type + * @param naturalId the natural identifier * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @return a persistent instance or null via a {@code CompletionStage} */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); + @Incubating + CompletionStage find(Class entityClass, Identifier naturalId); /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. + * Return the persistent instance of the given entity class with the + * given identifier, assuming that the instance exists. This method + * never results in access to the underlying data store, and thus + * might return a proxy that must be initialized explicitly using + * {@link #fetch(Object)}. + *

    + * You should not use this method to determine if an instance exists + * (use {@link #find} instead). Use this only to retrieve an instance + * which you safely assume exists, where non-existence would be an + * actual error. * - * @param queryString The HQL/JPQL query, update or delete statement + * @param entityClass a persistent class + * @param id a valid identifier of an existing persistent instance of the class * - * @return The {@link MutationQuery} instance for manipulation and execution + * @return the persistent instance or proxy * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#getReference(Class, Object) */ - MutationQuery createMutationQuery(String queryString); + T getReference(Class entityClass, Object id); /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query + * Return the persistent instance with the same identity as the given + * instance, which might be detached, assuming that the instance is + * still persistent in the database. This method never results in + * access to the underlying data store, and thus might return a proxy + * that must be initialized explicitly using {@link #fetch(Object)}. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a detached persistent instance * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) + * @return the persistent instance or proxy */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); + T getReference(T entity); /** - * Create an instance of {@link MutationQuery} for the given delete tree. + * Asynchronously persist the given transient instance, first assigning + * a generated identifier. (Or using the current value of the identifier + * property if the entity has assigned identifiers.) + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#PERSIST}. * - * @param deleteQuery the delete criteria query + *

    +		 * {@code session.persist(newBook).thenAccept(v -> session.flush());}
    +		 * 
    * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a transient instance of a persistent class * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) + * @see jakarta.persistence.EntityManager#persist(Object) */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); + CompletionStage persist(Object entity); /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

    + * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, + * {@code persist()} ultimately results in generation of an identifier for the + * given instance. But this may happen asynchronously, when the session is + * {@linkplain #flush() flushed}, depending on the identifier generation strategy. * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entityName the entity name + * @param object a transient instance to be made persistent + * @see #persist(Object) + */ + CompletionStage persist(String entityName, Object object); + + /** + * Persist multiple transient entity instances at once. * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) + * @see #persist(Object) */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); + CompletionStage persist(Object... entities); /** - * Create a typed {@link Query} instance for the given typed query reference. + * Asynchronously remove a persistent instance from the datastore. The + * argument may be an instance associated with the receiving session or + * a transient instance with an identifier associated with existing + * persistent state. + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#REMOVE}. * - * @param typedQueryReference the type query reference + *

    +		 * {@code session.delete(book).thenAccept(v -> session.flush());}
    +		 * 
    * - * @return The {@link Query} instance for execution + * @param entity the managed persistent instance to be removed * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference + * @throws IllegalArgumentException if the given instance is not managed + * + * @see jakarta.persistence.EntityManager#remove(Object) + */ + CompletionStage remove(Object entity); + + /** + * Remove multiple entity instances at once. * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) + * @see #remove(Object) */ - Query createQuery(TypedQueryReference typedQueryReference); + CompletionStage remove(Object... entities); /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement + * Copy the state of the given object onto the persistent instance with + * the same identifier. If there is no such persistent instance currently + * associated with the session, it will be loaded. Return the persistent + * instance. Or, if the given instance is transient, save a copy of it + * and return the copy as a newly persistent instance. The given instance + * does not become associated with the session. + *

    + * This operation cascades to associated instances if the association is + * mapped with {@link jakarta.persistence.CascadeType#MERGE}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a detached instance with state to be copied * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} + * @return an updated persistent instance * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see jakarta.persistence.EntityManager#merge(Object) */ - @Deprecated - Query createQuery(String queryString); + CompletionStage merge(T entity); /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution + * Merge multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #merge(Object) */ - SelectionQuery createQuery(String queryString, Class resultType); + CompletionStage merge(Object... entities); /** - * Create an instance of {@link MutationQuery} for the given criteria - * update. + * Re-read the state of the given instance from the underlying database. + * It is inadvisable to use this to implement long-running sessions that + * span many business tasks. This method is, however, useful in certain + * special circumstances, for example: * - * @param criteriaUpdate The {@link CriteriaUpdate} + *

      + *
    • where a database trigger alters the object state after insert or + * update, or + *
    • after executing direct native SQL in the same session. + *
    * - * @return The {@link MutationQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * + * @throws IllegalArgumentException if the given instance is not managed + * + * @see jakarta.persistence.EntityManager#refresh(Object) */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); + CompletionStage refresh(Object entity); /** - * Create an instance of {@link MutationQuery} for the given criteria - * delete. + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockMode}. * - * @param criteriaDelete The {@link CriteriaDelete} + * @param entity a managed persistent entity instance + * @param lockMode the requested lock mode * - * @return The {@link MutationQuery} instance for manipulation and execution + * @see #refresh(Object) */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); + CompletionStage refresh(Object entity, LockMode lockMode); /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query + * Re-read the state of the given instance from the underlying database, + * requesting the given {@link LockModeType}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent entity instance + * @param lockModeType the requested lock mode * - * @see jakarta.persistence.EntityManager#createQuery(String) + * @see #refresh(Object) */ - Query createNamedQuery(String queryName); + default CompletionStage refresh(Object entity, LockModeType lockModeType) { + return refresh( entity, convertToLockMode(lockModeType) ); + } /** - * Create an instance of {@link SelectionQuery} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link SelectionQuery} instance for manipulation and execution + * Refresh multiple entity instances at once. * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) + * @see #refresh(Object) */ - SelectionQuery createNamedQuery(String queryName, Class resultType); + CompletionStage refresh(Object... entities); /** - * Create an instance of {@link Query} for the given SQL query string, - * using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
      - *
    • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar.
    • - *
    • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned as elements of - * arrays of type {@code Object[]}.
    • - *
    • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity.
    • + *
    • perform a version check with {@link LockMode#PESSIMISTIC_READ}, + *
    • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_WRITE}, + *
    • force a version increment with {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}, + *
    • schedule a version check just before the end of the transaction with + * {@link LockMode#OPTIMISTIC}, or + *
    • schedule a version increment just before the end of the transaction + * with {@link LockMode#OPTIMISTIC_FORCE_INCREMENT}. *
    * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @return The {@link SelectionQuery} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockMode the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType); + CompletionStage lock(Object entity, LockMode lockMode); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@code resultType} to interpret the results. + * Obtain the specified lock level upon the given object. For example, + * this operation may be used to: * *
      - *
    • If the given result type is {@link Object}, or a built-in type - * such as {@link String} or {@link Integer}, the result set must - * have a single column, which will be returned as a scalar.
    • - *
    • If the given result type is {@code Object[]}, then the result set - * must have multiple columns, which will be returned as elements of - * arrays of type {@code Object[]}.
    • - *
    • Otherwise, the given result type must be an entity class, in which - * case the result set column aliases must map to the fields of the - * entity, and the query will return instances of the entity.
    • + *
    • perform a version check with {@link LockModeType#PESSIMISTIC_READ}, + *
    • upgrade to a pessimistic lock with {@link LockModeType#PESSIMISTIC_WRITE}, + *
    • force a version increment with {@link LockModeType#PESSIMISTIC_FORCE_INCREMENT}, + *
    • schedule a version check just before the end of the transaction with + * {@link LockModeType#OPTIMISTIC}, or + *
    • schedule a version increment just before the end of the transaction + * with {@link LockModeType#OPTIMISTIC_FORCE_INCREMENT}. *
    * - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * @param affectedEntities The entities which are affected by the query + * This operation cascades to associated instances if the association is + * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. * - * @return The {@link Query} instance for manipulation and execution + * @param entity a managed persistent instance + * @param lockModeType the lock level * - * @see jakarta.persistence.EntityManager#createNativeQuery(String, Class) + * @throws IllegalArgumentException if the given instance is not managed */ - SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities); + default CompletionStage lock(Object entity, LockModeType lockModeType) { + return lock( entity, convertToLockMode(lockModeType) ); + } /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. - * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping + * Force this session to flush asynchronously. Must be called at the + * end of a unit of work, before committing the transaction and closing + * the session. Flushing is the process of synchronizing the + * underlying persistent store with state held in memory. * - * @return The {@link Query} instance for manipulation and execution + *
    +		 * {@code session.flush().thenAccept(v -> print("done saving changes"));}
    +		 * 
    * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @see jakarta.persistence.EntityManager#flush() */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping); + CompletionStage flush(); /** - * Create an instance of {@link SelectionQuery} for the given SQL query - * string, using the given {@link ResultSetMapping} to interpret the - * result set. + * Asynchronously fetch an association configured for lazy loading. *

    - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the query. + *

    +		 * {@code session.fetch(author.getBook()).thenAccept(book -> print(book.getTitle()))}
    +		 * 
    + *

    + *

    + * This operation may be even be used to initialize a reference returned by + * {@link #getReference(Class, Object)}. + *

    + *

    +		 * {@code session.fetch(session.getReference(Author.class, authorId))}
    +		 * 
    + *

    * - * @param queryString The SQL query - * @param resultSetMapping the result set mapping - * @param affectedEntities The entities which are affected by the query + * @param association a lazy-loaded association, or a proxy * - * @return The {@link Query} instance for manipulation and execution + * @return the fetched association, via a {@code CompletionStage} * - * @see #getResultSetMapping(Class, String) - * @see jakarta.persistence.EntityManager#createNativeQuery(String, String) + * @see Stage#fetch(Object) + * @see #getReference(Class, Object) + * @see org.hibernate.Hibernate#initialize(Object) */ - SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities); + CompletionStage fetch(T association); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: - * - *
      - *
    • If the result set has a single column, the results will be returned - * as scalars.
    • - *
    • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
    • - *
    + * Fetch a lazy property of the given entity, identified by a JPA + * {@link Attribute attribute metamodel}. Note that this feature is + * only supported in conjunction with the Hibernate bytecode enhancer. * - * @param queryString The SQL select, update, insert, or delete statement + *
    +		 * {@code session.fetch(book, Book_.isbn).thenAccept(isbn -> print(isbn))}
    +		 * 
    */ - Query createNativeQuery(String queryString); + CompletionStage fetch(E entity, Attribute field); /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * In the case of a query: + * Asynchronously fetch an association that's configured for lazy loading, + * and unwrap the underlying entity implementation from any proxy. * - *
      - *
    • If the result set has a single column, the results will be returned - * as scalars.
    • - *
    • Otherwise, if the result set has multiple columns, the results will - * be returned as elements of arrays of type {@code Object[]}.
    • - *
    + *
    +		 * {@code session.unproxy(author.getBook()).thenAccept(book -> print(book.getTitle()));}
    +		 * 
    * - * Any {@link AffectedEntities affected entities} are synchronized with - * the database before execution of the statement. + * @param association a lazy-loaded association + * + * @return the fetched association, via a {@code CompletionStage} * - * @param queryString The SQL select, update, insert, or delete statement - * @param affectedEntities The entities which are affected by the statement + * @see org.hibernate.Hibernate#unproxy(Object) */ - Query createNativeQuery(String queryString, AffectedEntities affectedEntities); + CompletionStage unproxy(T association); /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) + * Determine the current lock mode of the given entity. */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); + LockMode getLockMode(Object entity); + + /** + * Determine if the given instance belongs to this persistence context. + */ + boolean contains(Object entity); /** * Set the {@link FlushMode flush mode} for this session. @@ -1289,27 +1292,6 @@ default Session setFlushMode(FlushModeType flushModeType) { */ Session enableFetchProfile(String name); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Disable a particular fetch profile on this session, or do nothing if * the requested fetch profile is not enabled. @@ -1534,14 +1516,6 @@ default Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { * The {@link SessionFactory} which created this session. */ SessionFactory getFactory(); - /** - * Convenience method to obtain the {@link CriteriaBuilder}. - * - * @since 3 - */ - default CriteriaBuilder getCriteriaBuilder() { - return getFactory().getCriteriaBuilder(); - } } /** @@ -1574,7 +1548,7 @@ default CriteriaBuilder getCriteriaBuilder() { * * @see org.hibernate.StatelessSession */ - interface StatelessSession extends Closeable { + interface StatelessSession extends QueryProducer, Closeable { /** * Retrieve a row. @@ -1639,194 +1613,6 @@ default CompletionStage get(Class entityClass, Object id, LockModeType */ CompletionStage get(EntityGraph entityGraph, Object id); - /** - * Create an instance of {@link Query} for the given HQL/JPQL query - * string or HQL/JPQL update or delete statement. In the case of an - * update or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link Query} instance for manipulation and execution - * - * @deprecated See explanation in - * {@link org.hibernate.query.QueryProducer#createSelectionQuery(String)} - * - * @see org.hibernate.Session#createQuery(String) - */ - @Deprecated - Query createQuery(String queryString); - - /** - * Create a typed {@link Query} instance for the given typed query reference. - * - * @param typedQueryReference the type query reference - * - * @return The {@link Query} instance for execution - * - * @throws IllegalArgumentException if a query has not been - * defined with the name of the typed query reference or if - * the query result is found to not be assignable to - * result class of the typed query reference - * - * @see org.hibernate.query.QueryProducer#createQuery(TypedQueryReference) - */ - Query createQuery(TypedQueryReference typedQueryReference); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string and query result type. - * - * @param queryString The HQL/JPQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see org.hibernate.Session#createQuery(String, Class) - */ - SelectionQuery createQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given HQL/JPQL - * query string. - * - * @param queryString The HQL/JPQL query - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createSelectionQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link MutationQuery} for the given HQL/JPQL - * update or delete statement. - * - * @param queryString The HQL/JPQL query, update or delete statement - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createMutationQuery(String queryString); - - /** - * Create an instance of {@link MutationQuery} for the given update tree. - * - * @param updateQuery the update criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaUpdate) - */ - MutationQuery createMutationQuery(CriteriaUpdate updateQuery); - - /** - * Create an instance of {@link MutationQuery} for the given delete tree. - * - * @param deleteQuery the delete criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(CriteriaDelete) - */ - MutationQuery createMutationQuery(CriteriaDelete deleteQuery); - - /** - * Create a {@link MutationQuery} from the given insert select criteria tree - * - * @param insert the insert select criteria query - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see org.hibernate.query.QueryProducer#createMutationQuery(JpaCriteriaInsert) - */ - MutationQuery createMutationQuery(JpaCriteriaInsert insert); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * or SQL update, insert, or delete statement. In the case of an update, - * insert, or delete, the returned {@link Query} must be executed using - * {@link Query#executeUpdate()} which returns an affected row count. - * - * @param queryString The SQL select, update, insert, or delete statement - * - * @see org.hibernate.Session#createNativeQuery(String) - */ - Query createNativeQuery(String queryString); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - Query createNamedQuery(String queryName); - - /** - * Create an instance of {@link Query} for the named query. - * - * @param queryName The name of the query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String, Class) - */ - SelectionQuery createNamedQuery(String queryName, Class resultType); - - /** - * Create an instance of {@link Query} for the given SQL query string, - * using the given {@code resultType} to interpret the results. - * - * @param queryString The SQL query - * @param resultType the Java type returned in each row of query results - * - * @return The {@link Query} instance for manipulation and execution - * - * @see org.hibernate.Session#createNativeQuery(String, Class) - */ - SelectionQuery createNativeQuery(String queryString, Class resultType); - - /** - * Create an instance of {@link SelectionQuery} for the given criteria - * query. - * - * @param criteriaQuery The {@link CriteriaQuery} - * - * @return The {@link SelectionQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - SelectionQuery createQuery(CriteriaQuery criteriaQuery); - - /** - * Create an instance of {@link MutationQuery} for the given criteria - * update. - * - * @param criteriaUpdate The {@link CriteriaUpdate} - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createQuery(CriteriaUpdate criteriaUpdate); - - /** - * Create an instance of {@link MutationQuery} for the given criteria - * delete. - * - * @param criteriaDelete The {@link CriteriaDelete} - * - * @return The {@link MutationQuery} instance for manipulation and execution - * - * @see jakarta.persistence.EntityManager#createQuery(String) - */ - MutationQuery createQuery(CriteriaDelete criteriaDelete); - /** * Insert a row. * @@ -2087,27 +1873,6 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ Object getIdentifier(Object entity); - /** - * Obtain a native SQL result set mapping defined via the annotation - * {@link jakarta.persistence.SqlResultSetMapping}. - */ - ResultSetMapping getResultSetMapping(Class resultType, String mappingName); - - /** - * Obtain a named {@link EntityGraph} - */ - EntityGraph getEntityGraph(Class rootType, String graphName); - - /** - * Create a new mutable {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType); - - /** - * Create a new mutable copy of a named {@link EntityGraph} - */ - EntityGraph createEntityGraph(Class rootType, String graphName); - /** * Performs the given work within the scope of a database transaction, * automatically flushing the session. The transaction will be rolled diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index dd25105f1..32227de7b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.stage.impl; -import jakarta.persistence.TypedQueryReference; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; import org.hibernate.CacheMode; import org.hibernate.Filter; @@ -42,10 +37,16 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.PersistenceException; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; import static org.hibernate.reactive.util.impl.CompletionStages.applyToAll; import static org.hibernate.reactive.util.impl.CompletionStages.returnOrRethrow; @@ -374,17 +375,17 @@ public boolean isFetchProfileEnabled(String name) { @Override public Filter enableFilter(String filterName) { - return delegate.enableFilter(filterName); + return delegate.enableFilter( filterName ); } @Override public void disableFilter(String filterName) { - delegate.disableFilter(filterName); + delegate.disableFilter( filterName ); } @Override public Filter getEnabledFilter(String filterName) { - return delegate.getEnabledFilter(filterName); + return delegate.getEnabledFilter( filterName ); } @Override @@ -512,6 +513,11 @@ public Stage.SessionFactory getFactory() { return delegate.getFactory().unwrap( Stage.SessionFactory.class ); } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + @Override public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { return delegate.getResultSetMapping( resultType, mappingName ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 425a0d363..4c325fdf9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -8,6 +8,7 @@ import org.hibernate.LockMode; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.pool.ReactiveConnection; import org.hibernate.reactive.query.ReactiveQuery; @@ -19,6 +20,7 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; @@ -206,6 +208,11 @@ public Stage.SessionFactory getFactory() { return delegate.getFactory().unwrap( Stage.SessionFactory.class ); } + @Override + public CriteriaBuilder getCriteriaBuilder() { + return getFactory().getCriteriaBuilder(); + } + private Transaction currentTransaction; @Override @@ -332,6 +339,11 @@ public Query createNativeQuery(String queryString) { return new StageQueryImpl<>( delegate.createReactiveNativeQuery( queryString ) ); } + @Override + public Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return new StageQueryImpl<>( delegate.createReactiveNativeQuery( queryString, affectedEntities ) ); + } + @Override public Query createNamedQuery(String queryName) { return new StageQueryImpl<>( delegate.createReactiveNamedQuery( queryName ) ); @@ -347,6 +359,21 @@ public SelectionQuery createNativeQuery(String queryString, Class resu return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType ) ); } + @Override + public SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultType, affectedEntities ) ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping ) ); + } + + @Override + public SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return new StageSelectionQueryImpl<>( delegate.createReactiveNativeQuery( queryString, resultSetMapping, affectedEntities) ); + } + @Override public SelectionQuery createQuery(CriteriaQuery criteriaQuery) { return new StageSelectionQueryImpl<>( delegate.createReactiveQuery( criteriaQuery ) ); From 7729009f781a87c60126388222bc735f84f3681d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 15:44:12 +0200 Subject: [PATCH 147/201] [#2234] Revert "[#2207] Provide access to the contextual data map" This reverts commit 02db560b8edb2795828baca581b753d200584ff7. --- .../context/impl/ContextualDataStorage.java | 17 +---------------- .../reactive/context/impl/VertxContext.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java index bcbfdf5f0..39cb8fa10 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java @@ -5,12 +5,6 @@ */ package org.hibernate.reactive.context.impl; -import org.hibernate.reactive.context.Context; - -import io.vertx.core.impl.ContextInternal; -import io.vertx.core.spi.context.storage.AccessMode; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import io.vertx.core.impl.VertxBuilder; @@ -23,18 +17,9 @@ public class ContextualDataStorage implements VertxServiceProvider { @SuppressWarnings("rawtypes") - private final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); + final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); @Override public void init(VertxBuilder vertxBuilder) { } - - @SuppressWarnings({ "unchecked" }) - public static Map, T> contextualDataMap(ContextInternal vertxContext) { - return vertxContext.getLocal( - ContextualDataStorage.CONTEXTUAL_DATA_KEY, - AccessMode.CONCURRENT, - ConcurrentHashMap::new - ); - } } 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 24975a87f..937dc3b1b 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 @@ -6,9 +6,12 @@ package org.hibernate.reactive.context.impl; import java.lang.invoke.MethodHandles; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import io.vertx.core.Vertx; import io.vertx.core.impl.ContextInternal; +import io.vertx.core.spi.context.storage.AccessMode; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.logging.impl.Log; @@ -17,8 +20,6 @@ import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; -import static org.hibernate.reactive.context.impl.ContextualDataStorage.contextualDataMap; - /** * An adaptor for the Vert.x {@link io.vertx.core.Context}. * @@ -41,7 +42,7 @@ public void put(Key key, T instance) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - ContextualDataStorage.contextualDataMap( context ).put( key, instance ); + VertxContext.contextualDataMap( context ).put( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -53,7 +54,7 @@ public void put(Key key, T instance) { public T get(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - T local = ContextualDataStorage.contextualDataMap( context ).get( key ); + T local = VertxContext.contextualDataMap( context ).get( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -93,4 +94,13 @@ public void execute(Runnable runnable) { runnable.run(); } } + + @SuppressWarnings({ "unchecked" }) + private static ConcurrentMap, T> contextualDataMap(ContextInternal vertxContext) { + return vertxContext.getLocal( + ContextualDataStorage.CONTEXTUAL_DATA_KEY, + AccessMode.CONCURRENT, + ConcurrentHashMap::new + ); + } } From 070adfa77395cb4ec4e5cdc3003faf65d186a19a Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 15:44:30 +0200 Subject: [PATCH 148/201] [#2234] Revert "Make CONTEXTUAL_DATA_KEY constant" This reverts commit feb4c2726740247d3da7abbe279d3ffa81f1ab2e. --- .../hibernate/reactive/context/impl/ContextualDataStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java index 39cb8fa10..295b92f4d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java @@ -17,7 +17,7 @@ public class ContextualDataStorage implements VertxServiceProvider { @SuppressWarnings("rawtypes") - final static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); + static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); @Override public void init(VertxBuilder vertxBuilder) { From 8ef08d5f248ccf4ba8d73b31086176ae3cd9b50c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 15:44:45 +0200 Subject: [PATCH 149/201] [#2234] Revert "[#2176] Create custom Vert.x contextual data storage" This reverts commit 95ad44ca45ded244b9031dc4015089cd0064c204. --- .../context/impl/ContextualDataStorage.java | 25 ------------------- .../reactive/context/impl/VertxContext.java | 18 +++---------- .../io.vertx.core.spi.VertxServiceProvider | 1 - 3 files changed, 4 insertions(+), 40 deletions(-) delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java delete mode 100644 hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java deleted file mode 100644 index 295b92f4d..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/context/impl/ContextualDataStorage.java +++ /dev/null @@ -1,25 +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.context.impl; - -import java.util.concurrent.ConcurrentMap; - -import io.vertx.core.impl.VertxBuilder; -import io.vertx.core.spi.VertxServiceProvider; -import io.vertx.core.spi.context.storage.ContextLocal; - -/** - * SPI Implementation for {@link ContextLocal} storage. - */ -public class ContextualDataStorage implements VertxServiceProvider { - - @SuppressWarnings("rawtypes") - static ContextLocal CONTEXTUAL_DATA_KEY = ContextLocal.registerLocal( ConcurrentMap.class ); - - @Override - public void init(VertxBuilder vertxBuilder) { - } -} 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 937dc3b1b..fef172e66 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 @@ -6,12 +6,9 @@ package org.hibernate.reactive.context.impl; import java.lang.invoke.MethodHandles; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import io.vertx.core.Vertx; import io.vertx.core.impl.ContextInternal; -import io.vertx.core.spi.context.storage.AccessMode; import org.hibernate.reactive.context.Context; import org.hibernate.reactive.logging.impl.Log; @@ -42,7 +39,7 @@ public void put(Key key, T instance) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - VertxContext.contextualDataMap( context ).put( key, instance ); + context.localContextData().put( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -54,7 +51,8 @@ public void put(Key key, T instance) { public T get(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - T local = VertxContext.contextualDataMap( context ).get( key ); + @SuppressWarnings("unchecked") + T local = (T) context.localContextData().get( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -68,7 +66,7 @@ public T get(Key key) { public void remove(Key key) { final ContextInternal context = ContextInternal.current(); if ( context != null ) { - boolean removed = contextualDataMap( context ).remove( key ) != null; + boolean removed = context.localContextData().remove( key ) != null; if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed ); } else { @@ -95,12 +93,4 @@ public void execute(Runnable runnable) { } } - @SuppressWarnings({ "unchecked" }) - private static ConcurrentMap, T> contextualDataMap(ContextInternal vertxContext) { - return vertxContext.getLocal( - ContextualDataStorage.CONTEXTUAL_DATA_KEY, - AccessMode.CONCURRENT, - ConcurrentHashMap::new - ); - } } diff --git a/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider b/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider deleted file mode 100644 index 80ffcb31c..000000000 --- a/hibernate-reactive-core/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider +++ /dev/null @@ -1 +0,0 @@ -org.hibernate.reactive.context.impl.ContextualDataStorage \ No newline at end of file From 4d5ac0006049ea448e5918cf73f7369be36e1d41 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 30 Apr 2025 15:55:26 +0200 Subject: [PATCH 150/201] [#2234] Revert "[#2174] Prepare for Vert.x 5 upgrade in Vert.x Context" This reverts commit 147604a354c214252d5095b92c3d7b4f7e421934. --- .../reactive/context/impl/VertxContext.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 fef172e66..4448c9b6c 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 @@ -36,10 +36,10 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) { @Override public void put(Key key, T instance) { - final ContextInternal context = ContextInternal.current(); + final io.vertx.core.Context context = Vertx.currentContext(); if ( context != null ) { if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance ); - context.localContextData().put( key, instance ); + context.putLocal( key, instance ); } else { if ( trace ) LOG.tracef( "Context is null for key,value: [%1$s, %2$s]", key, instance ); @@ -49,10 +49,9 @@ public void put(Key key, T instance) { @Override public T get(Key key) { - final ContextInternal context = ContextInternal.current(); + final io.vertx.core.Context context = Vertx.currentContext(); if ( context != null ) { - @SuppressWarnings("unchecked") - T local = (T) context.localContextData().get( key ); + T local = context.getLocal( key ); if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local ); return local; } @@ -64,9 +63,9 @@ public T get(Key key) { @Override public void remove(Key key) { - final ContextInternal context = ContextInternal.current(); + final io.vertx.core.Context context = Vertx.currentContext(); if ( context != null ) { - boolean removed = context.localContextData().remove( key ) != null; + boolean removed = context.removeLocal( key ); if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed ); } else { From 63cd786d49c51863e1d6c6183b1c00234615d58d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 30 Apr 2025 20:13:10 +0200 Subject: [PATCH 151/201] add javadoc to QueryProducer --- .../main/java/org/hibernate/reactive/mutiny/Mutiny.java | 7 +++++++ .../src/main/java/org/hibernate/reactive/stage/Stage.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 7ced2785a..c3608061b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -485,6 +485,13 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query enableFetchProfile(String profileName); } + /** + * Operations common to objects which act as factories for instances of + * {@link Query}. This is a common supertype of {@link Session} and + * {@link StatelessSession}. + * + * @since 3.0 + */ interface QueryProducer { /** * Create an instance of {@link SelectionQuery} for the given HQL/JPQL diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 7085ef861..981c2b703 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -483,6 +483,13 @@ default Query setLockMode(String alias, LockModeType lockModeType) { Query setComment(String comment); } + /** + * Operations common to objects which act as factories for instances of + * {@link Query}. This is a common supertype of {@link Session} and + * {@link StatelessSession}. + * + * @since 3.0 + */ interface QueryProducer { /** * Create an instance of {@link SelectionQuery} for the given HQL/JPQL From 8f2ab3e0ffc41c3be3bae6291ceb84ac039e7403 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 1 May 2025 08:08:06 +0200 Subject: [PATCH 152/201] fixes before release 1. fix some more incorrect casts to ReactiveSession or ReactiveSessionImpl 2. remove the SS.upsert() method taking an entity name (we don't need it) 3. some minor code cleanups in EntityTypes and CollectionTypes --- .../ReactivePersistenceContextAdapter.java | 7 +- .../reactive/engine/impl/CollectionTypes.java | 200 +++++++++--------- .../reactive/engine/impl/EntityTypes.java | 20 +- ...ctiveSharedSessionContractImplementor.java | 5 + ...tiveInitializeCollectionEventListener.java | 29 +-- .../org/hibernate/reactive/mutiny/Mutiny.java | 12 -- .../mutiny/impl/MutinySessionImpl.java | 38 ++-- .../impl/MutinyStatelessSessionImpl.java | 5 - .../impl/ReactiveAbstractEntityPersister.java | 5 +- .../reactive/session/ReactiveSession.java | 7 - .../session/ReactiveStatelessSession.java | 6 - .../session/impl/ReactiveSessionImpl.java | 15 +- .../impl/ReactiveStatelessSessionImpl.java | 69 ++++-- .../org/hibernate/reactive/stage/Stage.java | 9 - .../reactive/stage/impl/StageSessionImpl.java | 45 ++-- .../stage/impl/StageStatelessSessionImpl.java | 5 - .../org/hibernate/reactive/UpsertTest.java | 56 ----- 17 files changed, 206 insertions(+), 327 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java index 3dfc8dcd0..6d87416b4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/engine/internal/ReactivePersistenceContextAdapter.java @@ -37,9 +37,9 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.engine.impl.ReactiveCallbackImpl; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; -import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; @@ -79,8 +79,9 @@ private class NonLazyCollectionInitializer implements Consumer nonLazyCollection) { if ( !nonLazyCollection.wasInitialized() ) { - stage = stage.thenCompose( v -> ( (ReactiveSession) getSession() ) - .reactiveInitializeCollection( nonLazyCollection, false ) ); + stage = stage.thenCompose( v -> + ( (ReactiveSharedSessionContractImplementor) getSession() ) + .reactiveInitializeCollection( nonLazyCollection, false ) ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java index 4bc488bb6..bb72946ad 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java @@ -110,20 +110,18 @@ else if ( target instanceof Map map ) { } else { final PersistenceContext persistenceContext = session.getPersistenceContext(); - final PersistentCollection collectionHolder = persistenceContext.getCollectionHolder( target ); - if ( collectionHolder != null ) { - if ( collectionHolder instanceof PersistentArrayHolder arrayHolder ) { - persistenceContext.removeCollectionHolder( target ); - arrayHolder.beginRead(); - final PluralAttributeMapping attributeMapping = - persistenceContext.getCollectionEntry( collectionHolder ) - .getLoadedPersister().getAttributeMapping(); - arrayHolder.injectLoadedState( attributeMapping, null ); - arrayHolder.endRead(); - arrayHolder.dirty(); - persistenceContext.addCollectionHolder( collectionHolder ); - return arrayHolder.getArray(); - } + if ( persistenceContext.getCollectionHolder( target ) + instanceof PersistentArrayHolder arrayHolder ) { + persistenceContext.removeCollectionHolder( target ); + arrayHolder.beginRead(); + final PluralAttributeMapping attributeMapping = + persistenceContext.getCollectionEntry( arrayHolder ) + .getLoadedPersister().getAttributeMapping(); + arrayHolder.injectLoadedState( attributeMapping, null ); + arrayHolder.endRead(); + arrayHolder.dirty(); + persistenceContext.addCollectionHolder( arrayHolder ); + return arrayHolder.getArray(); } } return null; @@ -142,8 +140,9 @@ private static Object replaceUninitializedOriginal( // A managed entity with an uninitialized collection is being merged, // We need to replace any detached entities in the queued operations // with managed copies. - final AbstractPersistentCollection pc = (AbstractPersistentCollection) original; - pc.replaceQueuedOperationValues( + final AbstractPersistentCollection collection = + (AbstractPersistentCollection) original; + collection.replaceQueuedOperationValues( session.getFactory() .getMappingMetamodel() .getCollectionDescriptor( type.getRole() ), copyCache @@ -182,18 +181,20 @@ private static CompletionStage replaceOriginal( ).thenCompose( result -> { if ( original == target ) { // get the elements back into the target making sure to handle dirty flag - final boolean wasClean = - target instanceof PersistentCollection collection - && !collection.isDirty(); //TODO: this is a little inefficient, don't need to do a whole // deep replaceElements() call - return replaceElements( type, result, target, owner, copyCache, session ) - .thenApply( unused -> { - if ( wasClean ) { - ( (PersistentCollection) target ).clearDirty(); - } - return target; - } ); + final CompletionStage replaced = + replaceElements( type, result, target, owner, copyCache, session ); + if ( target instanceof PersistentCollection collection + && !collection.isDirty() ) { + return replaced.thenApply( unused -> { + collection.clearDirty(); + return target; + } ); + } + else { + return replaced.thenApply( unused -> target ); + } } else { return completedFuture( result ); @@ -220,7 +221,7 @@ else if ( type instanceof CustomCollectionType ) { else if ( type instanceof MapType ) { return replaceMapTypeElements( type, - (Map) original, + (Map) original, (Map) target, owner, copyCache, @@ -247,53 +248,59 @@ private static CompletionStage replaceCollectionTypeElements( Map copyCache, SessionImplementor session) { result.clear(); - // copy elements into newly empty target collection final Type elemType = type.getElementType( session.getFactory() ); - return loop( - (Collection) original, o -> getReplace( elemType, o, owner, session, copyCache ) - .thenAccept( result::add ) - ).thenCompose( unused -> { - // if the original is a PersistentCollection, and that original - // was not flagged as dirty, then reset the target's dirty flag - // here after the copy operation. - //

    - // One thing to be careful of here is a "bare" original collection - // in which case we should never ever ever reset the dirty flag - // on the target because we simply do not know... - if ( original instanceof PersistentCollection originalPersistentCollection - && result instanceof PersistentCollection resultPersistentCollection ) { - return preserveSnapshot( - originalPersistentCollection, resultPersistentCollection, - elemType, owner, copyCache, session - ).thenApply( v -> { - if ( !originalPersistentCollection.isDirty() ) { - resultPersistentCollection.clearDirty(); - } - return result; - } ); - } - else { - return completedFuture( result ); - } - } ); + return loop( (Collection) original, + o -> getReplace( elemType, o, owner, session, copyCache ) + .thenAccept( result::add ) ) + .thenCompose( v -> preserveSnapshotIfNecessary( original, result, owner, copyCache, session, elemType ) ); + } + + private static CompletionStage preserveSnapshotIfNecessary( + Object original, + Collection result, + Object owner, + Map copyCache, + SessionImplementor session, + Type elemType) { + // if the original is a PersistentCollection, and that original + // was not flagged as dirty, then reset the target's dirty flag + // here after the copy operation. + //

    + // One thing to be careful of here is a "bare" original collection + // in which case we should never ever ever reset the dirty flag + // on the target because we simply do not know... + if ( original instanceof PersistentCollection originalCollection + && result instanceof PersistentCollection resultCollection ) { + return preserveSnapshot( + originalCollection, resultCollection, + elemType, owner, copyCache, session + ).thenApply( v -> { + if ( !originalCollection.isDirty() ) { + resultCollection.clearDirty(); + } + return result; + } ); + } + else { + return completedFuture( result ); + } } private static CompletionStage replaceMapTypeElements( CollectionType type, - Map original, + Map original, Map target, Object owner, Map copyCache, SessionImplementor session) { - final CollectionPersister persister = session.getFactory().getRuntimeMetamodels() - .getMappingMetamodel().getCollectionDescriptor( type.getRole() ); - final Map result = target; - result.clear(); - + final CollectionPersister persister = + session.getFactory().getRuntimeMetamodels().getMappingMetamodel() + .getCollectionDescriptor( type.getRole() ); + target.clear(); return loop( original.entrySet(), entry -> { - final Map.Entry me = entry; + final Map.Entry me = entry; return getReplace( persister.getIndexType(), me.getKey(), owner, session, copyCache ) .thenCompose( key -> getReplace( persister.getElementType(), @@ -301,10 +308,10 @@ private static CompletionStage replaceMapTypeElements( owner, session, copyCache - ).thenAccept( value -> result.put( key, value ) ) + ).thenAccept( value -> target.put( key, value ) ) ); } - ).thenApply( unused -> result ); + ).thenApply( unused -> target); } private static CompletionStage replaceArrayTypeElements( @@ -325,8 +332,8 @@ private static CompletionStage replaceArrayTypeElements( } final Type elemType = type.getElementType( session.getFactory() ); - return loop( - 0, length, i -> getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) + return loop( 0, length, + i -> getReplace( elemType, Array.get( original, i ), owner, session, copyCache ) .thenApply( o -> { Array.set( result, i, o ); return result; @@ -345,18 +352,14 @@ private static CompletionStage getReplace( private static CompletionStage getReplace( Type elemType, - Object o, + Object object, Object target, Object owner, SessionImplementor session, Map copyCache) { - if ( elemType instanceof EntityType ) { - return EntityTypes.replace( (EntityType) elemType, o, target, session, owner, copyCache ); - } - else { - final Object replace = elemType.replace( o, target, session, owner, copyCache ); - return completedFuture( replace ); - } + return elemType instanceof EntityType entityType + ? EntityTypes.replace( entityType, object, target, session, owner, copyCache ) + : completedFuture( elemType.replace( object, target, session, owner, copyCache) ); } /** @@ -374,7 +377,9 @@ private static CompletionStage preserveSnapshot( return createSnapshot( original, result, elemType, owner, copyCache, session ) .thenAccept( serializable -> ce.resetStoredSnapshot( result, serializable ) ); } - return voidFuture(); + else { + return voidFuture(); + } } /** @@ -412,8 +417,8 @@ private static CompletionStage createArraySnapshot( Object owner, Map copyCache, SessionImplementor session) { - return loop( - 0, array.length, i -> getReplace( elemType, array[i], owner, session, copyCache ) + return loop( 0, array.length, + i -> getReplace( elemType, array[i], owner, session, copyCache ) .thenAccept( o -> array[i] = o ) ).thenApply( unused -> array ); } @@ -421,29 +426,26 @@ private static CompletionStage createArraySnapshot( /** * @see CollectionType#createMapSnapshot(Map, PersistentCollection, Type, Object, Map, SharedSessionContractImplementor) */ - private static CompletionStage createMapSnapshot( - Map map, + private static CompletionStage createMapSnapshot( + Map map, PersistentCollection result, Type elemType, Object owner, Map copyCache, SessionImplementor session) { final Map resultSnapshot = (Map) result.getStoredSnapshot(); - final Map targetMap; - if ( map instanceof SortedMap sortedMap ) { - //noinspection unchecked, rawtypes - targetMap = new TreeMap( sortedMap.comparator() ); - } - else { - targetMap = mapOfSize( map.size() ); - } - return loop( - map.entrySet(), entry -> - getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) - .thenAccept( newValue -> { - final Object key = entry.getKey(); - targetMap.put( key == entry.getValue() ? newValue : key, newValue ); - } ) + final Map targetMap = + map instanceof SortedMap sortedMap + ? new TreeMap<>( sortedMap.comparator() ) + : mapOfSize(map.size()); + return loop( map.entrySet(), + entry -> getReplace( elemType, entry.getValue(), resultSnapshot, owner, session, copyCache ) + .thenAccept( newValue -> { + final K key = entry.getKey(); + final V value = entry.getValue(); + //noinspection unchecked + targetMap.put( key == value ? (K) newValue : key, (V) newValue ); + } ) ).thenApply( v -> (Serializable) targetMap ); } @@ -457,8 +459,8 @@ private static CompletionStage createListSnapshot( Map copyCache, SessionImplementor session) { final ArrayList targetList = new ArrayList<>( list.size() ); - return loop( - list, obj -> getReplace( elemType, obj, owner, session, copyCache ) + return loop( list, + obj -> getReplace( elemType, obj, owner, session, copyCache ) .thenAccept( targetList::add ) ).thenApply( unused -> targetList ); } @@ -472,9 +474,9 @@ private static Object instantiateResultIfNecessary(CollectionType type, Object o // by default just use an unanticipated capacity since we don't // know how to extract the capacity to use from original here... return target == null - || target == original - || target == UNFETCHED_PROPERTY - || target instanceof PersistentCollection collection && collection.isWrapper( original ) + || target == original + || target == UNFETCHED_PROPERTY + || target instanceof PersistentCollection collection && collection.isWrapper( original ) ? type.instantiate( -1 ) : target; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index 0bb0c75ae..9176e853c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -22,9 +22,10 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; +import org.hibernate.reactive.session.ReactiveQueryProducer; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; -import org.hibernate.reactive.session.impl.ReactiveSessionImpl; import org.hibernate.type.CollectionType; import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; @@ -158,8 +159,8 @@ public static CompletionStage replace( final Object owner, final Map copyCache) { Object[] copied = new Object[original.length]; - return loop( - 0, types.length, i -> replace( original, target, types, session, owner, copyCache, i, copied ) + return loop( 0, types.length, + i -> replace( original, target, types, session, owner, copyCache, i, copied ) ).thenApply( v -> copied ); } @@ -175,8 +176,7 @@ public static CompletionStage replace( final Map copyCache, final ForeignKeyDirection foreignKeyDirection) { Object[] copied = new Object[original.length]; - return loop( - 0, types.length, + return loop( 0, types.length, i -> replace( original, target, types, session, owner, copyCache, foreignKeyDirection, i, copied ) ).thenApply( v -> copied ); } @@ -268,7 +268,7 @@ private static CompletionStage resolveIdOrUniqueKey( // as a ComponentType. In the case that the entity is unfetched, we need to // explicitly fetch it here before calling replace(). (Note that in Hibernate // ORM this is unnecessary due to transparent lazy fetching.) - return ( (ReactiveSessionImpl) session ) + return ( (ReactiveQueryProducer) session ) .reactiveFetch( id, true ) .thenCompose( fetched -> { Object idOrUniqueKey = entityType @@ -340,10 +340,11 @@ private static CompletionStage getIdentifierFromHibernateProxy( EntityType entityType, HibernateProxy proxy, SharedSessionContractImplementor session) { - LazyInitializer initializer = proxy.getHibernateLazyInitializer(); + final LazyInitializer initializer = proxy.getHibernateLazyInitializer(); final String entityName = initializer.getEntityName(); final Object identifier = initializer.getIdentifier(); - return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier ) + return ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveImmediateLoad( entityName, identifier ) .thenApply( entity -> { checkEntityFound( session, entityName, identifier, entity ); initializer.setSession( session ); @@ -372,7 +373,8 @@ private static CompletionStage loadHibernateProxyEntity( LazyInitializer initializer = ( (HibernateProxy) entity ).getHibernateLazyInitializer(); final String entityName = initializer.getEntityName(); final Object identifier = initializer.getIdentifier(); - return ( (ReactiveSessionImpl) session ).reactiveImmediateLoad( entityName, identifier ) + return ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveImmediateLoad( entityName, identifier ) .thenApply( result -> { checkEntityFound( session, entityName, identifier, result ); return result; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java index 1ab340380..9bdc5742a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/spi/ReactiveSharedSessionContractImplementor.java @@ -9,6 +9,7 @@ import java.util.concurrent.CompletionStage; +import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.PersistenceContext; import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture; @@ -22,5 +23,9 @@ default CompletionStage reactiveAutoFlushIfRequired(Set querySp return falseFuture(); } + CompletionStage reactiveImmediateLoad(String entityName, Object id); + + CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing); + PersistenceContext getPersistenceContext(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java index 20466abd2..5547fcc74 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveInitializeCollectionEventListener.java @@ -23,9 +23,9 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; -import org.hibernate.sql.results.internal.ResultsHelper; import org.hibernate.stat.spi.StatisticsImplementor; +import static org.hibernate.event.internal.DefaultInitializeCollectionEventListener.handlePotentiallyEmptyCollection; import static org.hibernate.pretty.MessageHelper.collectionInfoString; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; @@ -59,7 +59,8 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection final CollectionPersister loadedPersister = ce.getLoadedPersister(); final Object loadedKey = ce.getLoadedKey(); if ( LOG.isTraceEnabled() ) { - LOG.tracev( "Initializing collection {0}", collectionInfoString( loadedPersister, collection, loadedKey, source ) ); + LOG.tracev( "Initializing collection {0}", + collectionInfoString( loadedPersister, collection, loadedKey, source ) ); LOG.trace( "Checking second-level cache" ); } @@ -76,11 +77,8 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection } return ( (ReactiveCollectionPersister) loadedPersister ) .reactiveInitialize( loadedKey, source ) - .thenApply( list -> { - handlePotentiallyEmptyCollection( collection, source, ce, loadedPersister ); - return list; - } ) - .thenAccept( list -> { + .thenAccept( v -> { + handlePotentiallyEmptyCollection( collection, source.getPersistenceContext(), ce, loadedPersister ); if ( LOG.isTraceEnabled() ) { LOG.trace( "Collection initialized" ); } @@ -93,23 +91,6 @@ public CompletionStage onReactiveInitializeCollection(InitializeCollection } } - private void handlePotentiallyEmptyCollection( - PersistentCollection collection, - SessionImplementor source, - CollectionEntry ce, - CollectionPersister loadedPersister) { - if ( !collection.wasInitialized() ) { - collection.initializeEmptyCollection( loadedPersister ); - ResultsHelper.finalizeCollectionLoading( - source.getPersistenceContext(), - loadedPersister, - collection, - ce.getLoadedKey(), - true - ); - } - } - /** * Try to initialize a collection from the cache * diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index c3608061b..aab884e76 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -1745,18 +1745,6 @@ default Uni get(Class entityClass, Object id, LockModeType lockModeTyp @Incubating Uni upsert(Object entity); - /** - * Use a SQL {@code merge into} statement to perform an upsert. - * - * @param entityName The entityName for the entity to be merged - * @param entity a detached entity instance - * @throws org.hibernate.TransientObjectException is the entity is transient - * - * @see org.hibernate.StatelessSession#upsert(String, Object) - */ - @Incubating - Uni upsert(String entityName, Object entity); - /** * Use a SQL {@code merge into} statement to perform * an upsert on multiple rows using the size of the given array diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java index 8a4149104..c80be96ed 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionImpl.java @@ -332,36 +332,22 @@ public Uni lock(Object entity, LockOptions lockOptions) { @Override public FlushMode getFlushMode() { - switch ( delegate.getHibernateFlushMode() ) { - case MANUAL: - return FlushMode.MANUAL; - case COMMIT: - return FlushMode.COMMIT; - case AUTO: - return FlushMode.AUTO; - case ALWAYS: - return FlushMode.ALWAYS; - default: - throw LOG.impossibleFlushModeIllegalState(); - } + return switch (delegate.getHibernateFlushMode()) { + case MANUAL -> FlushMode.MANUAL; + case COMMIT -> FlushMode.COMMIT; + case AUTO -> FlushMode.AUTO; + case ALWAYS -> FlushMode.ALWAYS; + }; } @Override public Mutiny.Session setFlushMode(FlushMode flushMode) { - switch ( flushMode ) { - case COMMIT: - delegate.setHibernateFlushMode( FlushMode.COMMIT ); - break; - case AUTO: - delegate.setHibernateFlushMode( FlushMode.AUTO ); - break; - case MANUAL: - delegate.setHibernateFlushMode( FlushMode.MANUAL ); - break; - case ALWAYS: - delegate.setHibernateFlushMode( FlushMode.ALWAYS ); - break; - } + delegate.setHibernateFlushMode( switch ( flushMode ) { + case COMMIT -> org.hibernate.FlushMode.COMMIT; + case AUTO -> org.hibernate.FlushMode.AUTO; + case MANUAL -> org.hibernate.FlushMode.MANUAL; + case ALWAYS -> org.hibernate.FlushMode.ALWAYS; + } ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index f5a6352c1..21602c48f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -240,11 +240,6 @@ public Uni upsert(Object entity) { return uni( () -> delegate.reactiveUpsert( entity ) ); } - @Override - public Uni upsert(String entityName, Object entity) { - return uni( () -> delegate.reactiveUpsert( entityName, entity ) ); - } - @Override public Uni upsertAll(Object... entities) { return uni( () -> delegate.reactiveUpsertAll( entities.length, entities ) ); 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 c1015939c..0ebebe603 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 @@ -55,6 +55,7 @@ import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; +import org.hibernate.reactive.engine.spi.ReactiveSharedSessionContractImplementor; import org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleIdEntityLoader; @@ -62,7 +63,6 @@ import org.hibernate.reactive.metamodel.mapping.internal.ReactiveCompoundNaturalIdMapping; import org.hibernate.reactive.metamodel.mapping.internal.ReactiveSimpleNaturalIdMapping; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.reactive.session.impl.ReactiveQueryExecutorLookup; import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.Update; @@ -359,7 +359,8 @@ else if ( result instanceof PersistentCollection ) { final PersistentCollection collection = (PersistentCollection) result; return collection.wasInitialized() ? completedFuture( (T) collection ) - : ( (ReactiveSession) session ).reactiveInitializeCollection( collection, false ) + : ( (ReactiveSharedSessionContractImplementor) session ) + .reactiveInitializeCollection( collection, false ) .thenApply( v -> (T) result ); } else { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index 88cfe3ec6..dbb7a818f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -16,7 +16,6 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.UnknownProfileException; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.DeleteContext; @@ -59,8 +58,6 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveRemove(Object entity); - CompletionStage reactiveRemove(String entityName, boolean isCascadeDeleteEnabled, DeleteContext transientObjects); - CompletionStage reactiveRemove(String entityName, Object child, boolean isCascadeDeleteEnabled, DeleteContext transientEntities); CompletionStage reactiveMerge(T object); @@ -91,10 +88,6 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe CompletionStage reactiveFind(Class entityClass, Map naturalIds); - CompletionStage reactiveImmediateLoad(String entityName, Object id); - - CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing); - CompletionStage reactiveRemoveOrphanBeforeUpdates(String entityName, Object child); void setHibernateFlushMode(FlushMode flushMode); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java index e6d2aa1ee..006fbb63f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java @@ -46,18 +46,12 @@ public interface ReactiveStatelessSession extends ReactiveQueryProducer, Reactiv CompletionStage reactiveUpsert(Object entity); - CompletionStage reactiveUpsert(String entityName, Object entity); - CompletionStage reactiveUpsertAll(int batchSize, Object... entities); CompletionStage reactiveRefresh(Object entity); - CompletionStage reactiveRefresh(String entityName, Object entity); - CompletionStage reactiveRefresh(Object entity, LockMode lockMode); - CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode); - CompletionStage reactiveInsertAll(Object... entities); CompletionStage reactiveInsertAll(int batchSize, Object... entities); 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 02b392a27..d86ffd7d1 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 @@ -30,14 +30,12 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.AutoFlushEvent; import org.hibernate.event.spi.DeleteContext; import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.FlushEvent; import org.hibernate.event.spi.InitializeCollectionEvent; -import org.hibernate.event.spi.InitializeCollectionEventListener; import org.hibernate.event.spi.LoadEvent; import org.hibernate.event.spi.LoadEventListener; import org.hibernate.event.spi.LockEvent; @@ -826,8 +824,7 @@ public CompletionStage reactiveInitializeCollection(PersistentCollection eventListenerGroupInitCollection = getFactory().getEventListenerGroups().eventListenerGroup_INIT_COLLECTION; - return eventListenerGroupInitCollection + return getFactory().getEventListenerGroups().eventListenerGroup_INIT_COLLECTION .fireEventOnEachListener( event, (DefaultReactiveInitializeCollectionEventListener l) -> l::onReactiveInitializeCollection @@ -920,16 +917,6 @@ public CompletionStage reactiveRemove(Object entity) { return fireRemove( new DeleteEvent( entity, this ) ); } - @Override - public CompletionStage reactiveRemove( - String entityName, - boolean isCascadeDeleteEnabled, - DeleteContext transientEntities) - throws HibernateException { - // I'm not quite sure if we need this method - return reactiveRemove( entityName, null, isCascadeDeleteEnabled, transientEntities ); - } - @Override public CompletionStage reactiveRemove( String entityName, 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 84483afb3..be9dc87b0 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 @@ -7,6 +7,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.SessionException; import org.hibernate.UnknownEntityTypeException; import org.hibernate.UnresolvableObjectException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; @@ -14,6 +15,7 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.engine.internal.ReactivePersistenceContextAdapter; +import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -101,6 +103,7 @@ import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.loader.ast.spi.CascadingFetchProfile.REFRESH; import static org.hibernate.loader.internal.CacheLoadHelper.initializeCollectionFromCache; +import static org.hibernate.pretty.MessageHelper.collectionInfoString; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.reactive.id.impl.IdentifierGeneration.castToIdentifierType; @@ -590,18 +593,12 @@ public CompletionStage reactiveRefresh(Object entity) { return reactiveRefresh( bestGuessEntityName( entity ), entity, LockMode.NONE ); } - @Override - public CompletionStage reactiveRefresh(String entityName, Object entity) { - return reactiveRefresh( entityName, entity, LockMode.NONE ); - } - @Override public CompletionStage reactiveRefresh(Object entity, LockMode lockMode) { return reactiveRefresh( bestGuessEntityName( entity ), entity, lockMode ); } - @Override - public CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode) { + private CompletionStage reactiveRefresh(String entityName, Object entity, LockMode lockMode) { checkOpen(); final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); final Object id = persister.getIdentifier( entity, this ); @@ -642,16 +639,7 @@ private CompletionStage fromInternalFetchProfile(CascadingFetchProfile cascad @Override public CompletionStage reactiveUpsert(Object entity) { checkOpen(); - return reactiveUpsert( null, entity ); - } - - /** - * @see StatelessSessionImpl#upsert(String, Object) - */ - @Override - public CompletionStage reactiveUpsert(String entityName, Object entity) { - checkOpen(); - final ReactiveEntityPersister persister = getEntityPersister( entityName, entity ); + final ReactiveEntityPersister persister = getEntityPersister( null, entity ); final Object id = idToUpsert( entity, persister ); final Object[] state = persister.getValues( entity ); if ( firePreUpsert( entity, id, state, persister ) ) { @@ -764,6 +752,15 @@ public CompletionStage reactiveInternalLoad(String entityName, Object id : completedFuture( object ); } + @Override + public CompletionStage reactiveImmediateLoad(String entityName, Object id) { + if ( persistenceContext.isLoadFinished() ) { + throw new SessionException( "proxies cannot be fetched by a stateless session" ); + } + // unless we are still in the process of handling a top-level load + return reactiveGet( entityName, id ); + } + @Override protected Object internalLoadGet(String entityName, Object id, PersistenceContext persistenceContext) { // otherwise immediately materialize it @@ -775,6 +772,44 @@ protected Object internalLoadGet(String entityName, Object id, PersistenceContex .whenComplete( (r, e) -> persistenceContext.afterLoad() ); } + @Override + public CompletionStage reactiveInitializeCollection(PersistentCollection collection, boolean writing) { + checkOpen(); + final CollectionEntry ce = persistenceContext.getCollectionEntry( collection ); + if ( ce == null ) { + throw new HibernateException( "no entry for collection" ); + } + if ( collection.wasInitialized() ) { + return voidFuture(); + } + else { + final ReactiveCollectionPersister loadedPersister = + (ReactiveCollectionPersister) ce.getLoadedPersister(); + final Object loadedKey = ce.getLoadedKey(); + if ( LOG.isTraceEnabled() ) { + LOG.trace( "Initializing collection " + + collectionInfoString( loadedPersister, collection, loadedKey, this ) ); + } + final boolean foundInCache = + initializeCollectionFromCache( loadedKey, loadedPersister, collection, this ); + if ( foundInCache ) { + LOG.trace( "Collection initialized from cache" ); + return voidFuture(); + } + else { + return loadedPersister.reactiveInitialize( loadedKey, this ) + .thenAccept( v -> { + handlePotentiallyEmptyCollection( collection, persistenceContext, loadedKey, loadedPersister ); + LOG.trace( "Collection initialized" ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.fetchCollection( loadedPersister.getRole() ); + } + } ); + } + } + } + @Override public CompletionStage reactiveFetch(T association, boolean unproxy) { checkOpen(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 981c2b703..7219501bc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1806,15 +1806,6 @@ default CompletionStage refresh(Object entity, LockModeType lockModeType) */ CompletionStage upsert(Object entity); - /** - * - * @param entityName The entityName for the entity to be merged - * @param entity a detached entity instance - * - * @see org.hibernate.StatelessSession#upsert(String, Object) - */ - CompletionStage upsert(String entityName, Object entity); - /** * Use a SQL {@code merge into} statement to perform * an upsert on multiple rows using the size of the given array diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java index 32227de7b..da09a284b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSessionImpl.java @@ -19,8 +19,6 @@ import org.hibernate.reactive.common.ResultSetMapping; import org.hibernate.reactive.engine.ReactiveActionQueue; 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.pool.ReactiveConnection; import org.hibernate.reactive.query.ReactiveQuery; import org.hibernate.reactive.session.ReactiveConnectionSupplier; @@ -43,7 +41,6 @@ import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Attribute; -import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -59,8 +56,6 @@ */ public class StageSessionImpl implements Stage.Session { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final ReactiveSession delegate; public StageSessionImpl(ReactiveSession session) { @@ -252,38 +247,22 @@ public CompletionStage lock(Object entity, LockOptions lockOptions) { @Override public FlushMode getFlushMode() { - switch ( delegate.getHibernateFlushMode() ) { - case MANUAL: - return FlushMode.MANUAL; - case COMMIT: - return FlushMode.COMMIT; - case AUTO: - return FlushMode.AUTO; - case ALWAYS: - return FlushMode.ALWAYS; - default: - throw LOG.impossibleFlushModeIllegalState(); - } + return switch ( delegate.getHibernateFlushMode() ) { + case MANUAL -> FlushMode.MANUAL; + case COMMIT -> FlushMode.COMMIT; + case AUTO -> FlushMode.AUTO; + case ALWAYS -> FlushMode.ALWAYS; + }; } @Override public Stage.Session setFlushMode(FlushMode flushMode) { - switch ( flushMode ) { - case COMMIT: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.COMMIT ); - break; - case AUTO: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.AUTO ); - break; - case MANUAL: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.MANUAL ); - break; - case ALWAYS: - delegate.setHibernateFlushMode( org.hibernate.FlushMode.ALWAYS ); - break; - default: - throw new IllegalArgumentException( "Unsupported flushMode: " + flushMode ); - } + delegate.setHibernateFlushMode( switch ( flushMode ) { + case COMMIT -> org.hibernate.FlushMode.COMMIT; + case AUTO -> org.hibernate.FlushMode.AUTO; + case MANUAL -> org.hibernate.FlushMode.MANUAL; + case ALWAYS -> org.hibernate.FlushMode.ALWAYS; + } ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java index 4c325fdf9..07f7ef35a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageStatelessSessionImpl.java @@ -154,11 +154,6 @@ public CompletionStage upsert(Object entity) { return delegate.reactiveUpsert( entity ); } - @Override - public CompletionStage upsert(String entityName, Object entity) { - return delegate.reactiveUpsert( entityName, entity ); - } - @Override public CompletionStage upsertAll(Object... entities) { return delegate.reactiveUpsertAll( entities.length, entities ); 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 52dc52a72..8dcc07a6a 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 @@ -104,34 +104,6 @@ public void testMutinyUpsert(VertxTestContext context) { ); } - @Test - public void testMutinyUpsertWithEntityName(VertxTestContext context) { - test( context, getMutinySessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "hello earth" ) ) - .call( () -> ss.upsert( Record.class.getName(), new Record( 456L, "hello mars" ) ) ) - .invoke( this::assertQueries ) - ) - .call( v -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .invoke( results -> assertThat( results ).containsExactly( - new Record( 123L, "hello earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - .call( () -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "goodbye earth" ) ) - ) ) - .invoke( this::assertQueries ) - .call( v -> getMutinySessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .invoke( results -> assertThat( results ).containsExactly( - new Record( 123L, "goodbye earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - ); - } - @Test public void testStageUpsert(VertxTestContext context) { test( context, getSessionFactory().withStatelessTransaction( ss -> ss @@ -160,34 +132,6 @@ public void testStageUpsert(VertxTestContext context) { ); } - @Test - public void testStageUpsertWithEntityName(VertxTestContext context) { - test( context, getSessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "hello earth" ) ) - .thenCompose( v -> ss.upsert( Record.class.getName(), new Record( 456L, "hello mars" ) ) ) - ) - .thenAccept( v -> this.assertQueries() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .thenAccept( results -> assertThat( results ).containsExactly( - new Record( 123L, "hello earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .upsert( Record.class.getName(), new Record( 123L, "goodbye earth" ) ) - ) ) - .thenAccept( v -> this.assertQueries() ) - .thenCompose( v -> getSessionFactory().withStatelessTransaction( ss -> ss - .createSelectionQuery( "from Record order by id", Record.class ).getResultList() ) - .thenAccept( results -> assertThat( results ).containsExactly( - new Record( 123L, "goodbye earth" ), - new Record( 456L, "hello mars" ) - ) ) - ) - ); - } - private void assertQueries() { if ( hasMergeOperator() ) { assertThat( sqlTracker.getLoggedQueries() ).have( IS_USING_MERGE ); From 8248d08e6692c37581adce6393c4aeeedd4476e2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 1 May 2025 09:22:17 +0200 Subject: [PATCH 153/201] generalize uses of SessionImplementor to SharedSessionContractImplementor --- .../reactive/engine/impl/CollectionTypes.java | 35 +++++++++---------- .../reactive/engine/impl/EntityTypes.java | 19 +++++----- .../reactive/engine/impl/ForeignKeys.java | 13 ++++--- .../impl/ReactiveEntityInsertAction.java | 3 +- .../reactive/session/ReactiveSession.java | 1 + 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java index bb72946ad..9ce6a7c5b 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CollectionTypes.java @@ -23,7 +23,6 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; @@ -58,7 +57,7 @@ public static CompletionStage replace( CollectionType type, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache, ForeignKeyDirection foreignKeyDirection) @@ -76,7 +75,7 @@ public static CompletionStage replace( CollectionType type, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache) throws HibernateException { if ( original == null ) { @@ -96,7 +95,7 @@ else if ( !Hibernate.isInitialized( original ) ) { */ private static Object replaceNullOriginal( Object target, - SessionImplementor session) { + SharedSessionContractImplementor session) { if ( target == null ) { return null; } @@ -132,7 +131,7 @@ private static Object replaceUninitializedOriginal( CollectionType type, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Map copyCache) { final PersistentCollection persistentCollection = (PersistentCollection) original; if ( persistentCollection.hasQueuedOperations() ) { @@ -165,7 +164,7 @@ private static CompletionStage replaceOriginal( CollectionType type, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache) { @@ -211,7 +210,7 @@ private static CompletionStage replaceElements( Object target, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { if ( type instanceof ArrayType ) { return replaceArrayTypeElements( type, original, target, owner, copyCache, session ); } @@ -246,7 +245,7 @@ private static CompletionStage replaceCollectionTypeElements( final Collection result, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { result.clear(); // copy elements into newly empty target collection final Type elemType = type.getElementType( session.getFactory() ); @@ -261,7 +260,7 @@ private static CompletionStage preserveSnapshotIfNecessary( Collection result, Object owner, Map copyCache, - SessionImplementor session, + SharedSessionContractImplementor session, Type elemType) { // if the original is a PersistentCollection, and that original // was not flagged as dirty, then reset the target's dirty flag @@ -293,7 +292,7 @@ private static CompletionStage replaceMapTypeElements( Map target, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final CollectionPersister persister = session.getFactory().getRuntimeMetamodels().getMappingMetamodel() .getCollectionDescriptor( type.getRole() ); @@ -320,7 +319,7 @@ private static CompletionStage replaceArrayTypeElements( Object target, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final Object result; final int length = Array.getLength( original ); if ( length != Array.getLength( target ) ) { @@ -345,7 +344,7 @@ private static CompletionStage getReplace( Type elemType, Object o, Object owner, - SessionImplementor session, + SharedSessionContractImplementor session, Map copyCache) { return getReplace( elemType, o, null, owner, session, copyCache ); } @@ -355,7 +354,7 @@ private static CompletionStage getReplace( Object object, Object target, Object owner, - SessionImplementor session, + SharedSessionContractImplementor session, Map copyCache) { return elemType instanceof EntityType entityType ? EntityTypes.replace( entityType, object, target, session, owner, copyCache ) @@ -371,7 +370,7 @@ private static CompletionStage preserveSnapshot( Type elemType, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( result ); if ( ce != null ) { return createSnapshot( original, result, elemType, owner, copyCache, session ) @@ -391,7 +390,7 @@ private static CompletionStage createSnapshot( Type elemType, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final Serializable originalSnapshot = original.getStoredSnapshot(); if ( originalSnapshot instanceof List list ) { return createListSnapshot( list, elemType, owner, copyCache, session ); @@ -416,7 +415,7 @@ private static CompletionStage createArraySnapshot( Type elemType, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { return loop( 0, array.length, i -> getReplace( elemType, array[i], owner, session, copyCache ) .thenAccept( o -> array[i] = o ) @@ -432,7 +431,7 @@ private static CompletionStage createMapSnapshot( Type elemType, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final Map resultSnapshot = (Map) result.getStoredSnapshot(); final Map targetMap = map instanceof SortedMap sortedMap @@ -457,7 +456,7 @@ private static CompletionStage createListSnapshot( Type elemType, Object owner, Map copyCache, - SessionImplementor session) { + SharedSessionContractImplementor session) { final ArrayList targetList = new ArrayList<>( list.size() ); return loop( list, obj -> getReplace( elemType, obj, owner, session, copyCache ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java index 9176e853c..504830150 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/EntityTypes.java @@ -16,7 +16,6 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.persister.entity.EntityPersister; @@ -155,7 +154,7 @@ public static CompletionStage replace( final Object[] original, final Object[] target, final Type[] types, - final SessionImplementor session, + final SharedSessionContractImplementor session, final Object owner, final Map copyCache) { Object[] copied = new Object[original.length]; @@ -171,7 +170,7 @@ public static CompletionStage replace( final Object[] original, final Object[] target, final Type[] types, - final SessionImplementor session, + final SharedSessionContractImplementor session, final Object owner, final Map copyCache, final ForeignKeyDirection foreignKeyDirection) { @@ -188,7 +187,7 @@ private static CompletionStage replace( EntityType entityType, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache, ForeignKeyDirection foreignKeyDirection) @@ -208,7 +207,7 @@ protected static CompletionStage replace( EntityType entityType, Object original, Object target, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache) { if ( original == null ) { @@ -254,7 +253,7 @@ protected static CompletionStage replace( private static CompletionStage resolveIdOrUniqueKey( EntityType entityType, Object original, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache) { return getIdentifier( entityType, original, session ) @@ -289,7 +288,7 @@ private static CompletionStage resolveIdOrUniqueKey( private static CompletionStage getIdentifier( EntityType entityType, Object value, - SessionImplementor session) { + SharedSessionContractImplementor session) { if ( entityType.isReferenceToIdentifierProperty() ) { // tolerates nulls return getEntityIdentifierIfNotUnsaved( entityType.getAssociatedEntityName(), value, session ); @@ -358,7 +357,7 @@ private static CompletionStage getIdentifierFromHibernateProxy( // an entity type, in which case we need to resolve its identifier final AttributeMapping type = entityPersister.findAttributeMapping( uniqueKeyPropertyName ); if ( type.isEntityIdentifierMapping() ) { - propertyValue = getIdentifier( (EntityType) type, propertyValue, (SessionImplementor) session ); + propertyValue = getIdentifier( (EntityType) type, propertyValue, session ); } return propertyValue; } @@ -389,7 +388,7 @@ private static CompletionStage replace( Object[] original, Object[] target, Type[] types, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache, int i, @@ -435,7 +434,7 @@ private static CompletionStage replace( Object[] original, Object[] target, Type[] types, - SessionImplementor session, + SharedSessionContractImplementor session, Object owner, Map copyCache, ForeignKeyDirection foreignKeyDirection, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java index 087623e44..87078f87f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java @@ -15,7 +15,6 @@ import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SelfDirtinessTracker; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.persister.entity.EntityPersister; @@ -48,7 +47,7 @@ public final class ForeignKeys { public static class Nullifier { private final boolean isDelete; private final boolean isEarlyInsert; - private final SessionImplementor session; + private final SharedSessionContractImplementor session; private final Object self; private final EntityPersister persister; @@ -65,7 +64,7 @@ public Nullifier( final Object self, final boolean isDelete, final boolean isEarlyInsert, - final SessionImplementor session, + final SharedSessionContractImplementor session, final EntityPersister persister) { this.isDelete = isDelete; this.isEarlyInsert = isEarlyInsert; @@ -270,7 +269,7 @@ private CompletionStage isNullifiable(final String entityName, Object o * * @return {@code true} if the given entity is not transient (meaning it is either detached/persistent) */ - public static CompletionStage isNotTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session) { + public static CompletionStage isNotTransient(String entityName, Object entity, Boolean assumed, SharedSessionContractImplementor session) { if ( isHibernateProxy( entity ) ) { return trueFuture(); } @@ -297,7 +296,7 @@ public static CompletionStage isNotTransient(String entityName, Object * * @return {@code true} if the given entity is transient (unsaved) */ - public static CompletionStage isTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session) { + public static CompletionStage isTransient(String entityName, Object entity, Boolean assumed, SharedSessionContractImplementor session) { if ( entity == LazyPropertyInitializer.UNFETCHED_PROPERTY ) { // an unfetched association can only point to // an entity that already exists in the db @@ -350,7 +349,7 @@ public static CompletionStage isTransient(String entityName, Object ent public static CompletionStage getEntityIdentifierIfNotUnsaved( final String entityName, final Object object, - final SessionImplementor session) throws TransientObjectException { + final SharedSessionContractImplementor session) throws TransientObjectException { if ( object == null ) { return nullFuture(); } @@ -387,7 +386,7 @@ public static CompletionStage findNonNullableT final EntityPersister persister = session.getEntityPersister( entityName, entity ); final Type[] types = persister.getPropertyTypes(); - final Nullifier nullifier = new Nullifier( entity, false, isEarlyInsert, (SessionImplementor) session, persister ); + final Nullifier nullifier = new Nullifier( entity, false, isEarlyInsert, session, persister ); final String[] propertyNames = persister.getPropertyNames(); final boolean[] nullability = persister.getPropertyNullability(); final NonNullableTransientDependencies nonNullableTransientEntities = new NonNullableTransientDependencies(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityInsertAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityInsertAction.java index 4838e6971..3f40cdb86 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityInsertAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityInsertAction.java @@ -16,7 +16,6 @@ import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.persister.entity.EntityPersister; @@ -64,7 +63,7 @@ public interface ReactiveEntityInsertAction extends ReactiveExecutable, Comparab // @see org.hibernate.action.internal.AbstractEntityInsertAction#nullifyTransientReferencesIfNotAlready() default CompletionStage reactiveNullifyTransientReferencesIfNotAlready() { if ( !areTransientReferencesNullified() ) { - return new ForeignKeys.Nullifier( getInstance(), false, isEarlyInsert(), (SessionImplementor) getSession(), getPersister() ) + return new ForeignKeys.Nullifier( getInstance(), false, isEarlyInsert(), getSession(), getPersister() ) .nullifyTransientReferences( getState() ) .thenAccept( v -> { new Nullability( getSession() ).checkNullability( getState(), getPersister(), false ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java index dbb7a818f..ab2089c0d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSession.java @@ -44,6 +44,7 @@ public interface ReactiveSession extends ReactiveQueryProducer, ReactiveSharedSe ReactiveActionQueue getReactiveActionQueue(); + @Override SessionImplementor getSharedContract(); CompletionStage reactiveFetch(E entity, Attribute field); From 72106db8566eb6504a99e8ef0bf465bcadf0a690 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 30 Apr 2025 11:44:50 +0200 Subject: [PATCH 154/201] [#1780] add operations for obtaining the current sessions from the context this will make it easier to implement the necessary Quarkus integration --- .../org/hibernate/reactive/mutiny/Mutiny.java | 25 +++++++++++++++++++ .../mutiny/impl/MutinySessionFactoryImpl.java | 10 ++++++++ .../org/hibernate/reactive/stage/Stage.java | 25 +++++++++++++++++++ .../stage/impl/StageSessionFactoryImpl.java | 10 ++++++++ 4 files changed, 70 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index aab884e76..71048e5b9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -2269,6 +2269,31 @@ default Uni withStatelessTransaction(Function> w */ Statistics getStatistics(); + /** + * Return the current instance of {@link Session}, if any. + * A current session exists only when this method is called + * from within an invocation of {@link #withSession(Function)} + * or {@link #withTransaction(Function)}. + * + * @return the current instance, if any, or {@code null} + * + * @since 3.0 + */ + Session getCurrentSession(); + + /** + * Return the current instance of {@link Session}, if any. + * A current session exists only when this method is called + * from within an invocation of + * {@link #withStatelessSession(Function)} or + * {@link #withStatelessTransaction(Function)}. + * + * @return the current instance, if any, or {@code null} + * + * @since 3.0 + */ + StatelessSession getCurrentStatelessSession(); + /** * Destroy the session factory and clean up its connection pool. */ diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java index f21bed53b..46a71b0b8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySessionFactoryImpl.java @@ -148,6 +148,16 @@ private CompletionStage connection(String tenantId) { : connectionPool.getConnection( tenantId ); } + @Override + public Mutiny.Session getCurrentSession() { + return context.get( contextKeyForSession ); + } + + @Override + public Mutiny.StatelessSession getCurrentStatelessSession() { + return context.get( contextKeyForStatelessSession ); + } + @Override public Uni withSession(Function> work) { Objects.requireNonNull( work, "parameter 'work' is required" ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 7219501bc..a97b05e56 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -2273,6 +2273,31 @@ default CompletionStage withStatelessTransaction(Function connection(String tenantId) { : connectionPool.getConnection( tenantId ); } + @Override + public Stage.Session getCurrentSession() { + return context.get( contextKeyForSession ); + } + + @Override + public Stage.StatelessSession getCurrentStatelessSession() { + return context.get( contextKeyForStatelessSession ); + } + @Override public CompletionStage withSession(Function> work) { Objects.requireNonNull( work, "parameter 'work' is required" ); From 1912a5cbe14a848ec257f82ac8f193dffc20e1a2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 1 May 2025 10:01:00 +0200 Subject: [PATCH 155/201] [#1280] tests for getCurrentSession() --- .../hibernate/reactive/MutinySessionTest.java | 39 ++++++++++++++--- .../reactive/ReactiveSessionTest.java | 43 ++++++++++++++++--- 2 files changed, 70 insertions(+), 12 deletions(-) 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 fb8c7e1f1..bf46f835a 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,12 +27,7 @@ import jakarta.persistence.metamodel.EntityType; import static java.util.concurrent.TimeUnit.MINUTES; -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.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; @Timeout(value = 10, timeUnit = MINUTES) @@ -668,6 +663,38 @@ public void testForceFlushWithDelete(VertxTestContext context) { ); } + @Test + public void testCurrentSession(VertxTestContext context) { + test( context, + getMutinySessionFactory().withSession(session -> + getMutinySessionFactory().withSession(s -> { + assertEquals(session, s); + Mutiny.Session currentSession = getMutinySessionFactory().getCurrentSession(); + assertNotNull(currentSession); + assertTrue(currentSession.isOpen()); + assertEquals(session, currentSession); + return Uni.createFrom().voidItem(); + }).invoke(() -> assertNotNull(getMutinySessionFactory().getCurrentSession())) + ).invoke(() -> assertNull(getMutinySessionFactory().getCurrentSession())) + ); + } + + @Test + public void testCurrentStatelessSession(VertxTestContext context) { + test( context, + getMutinySessionFactory().withStatelessSession(session -> + getMutinySessionFactory().withStatelessSession(s -> { + assertEquals(session, s); + Mutiny.StatelessSession currentSession = getMutinySessionFactory().getCurrentStatelessSession(); + assertNotNull(currentSession); + assertTrue(currentSession.isOpen()); + assertEquals(session, currentSession); + return Uni.createFrom().voidItem(); + }).invoke(() -> assertNotNull(getMutinySessionFactory().getCurrentStatelessSession())) + ).invoke(() -> assertNull(getMutinySessionFactory().getCurrentStatelessSession())) + ); + } + private void assertThatPigsAreEqual(GuineaPig expected, GuineaPig actual) { assertNotNull( actual ); assertEquals( expected.getId(), actual.getId() ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java index 78494bd1c..ed0c724f1 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/ReactiveSessionTest.java @@ -32,12 +32,7 @@ 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.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @Timeout(value = 10, timeUnit = MINUTES) @@ -969,6 +964,42 @@ context, openSession() ); } + @Test + public void testCurrentSession(VertxTestContext context) { + test( context, + getSessionFactory().withSession(session -> + getSessionFactory().withSession(s -> { + assertEquals(session, s); + Stage.Session currentSession = getSessionFactory().getCurrentSession(); + assertNotNull(currentSession); + assertTrue(currentSession.isOpen()); + assertEquals(session, currentSession); + return CompletionStages.voidFuture(); + }) + .thenAccept(v -> assertNotNull(getSessionFactory().getCurrentSession())) + ) + .thenAccept(v -> assertNull(getSessionFactory().getCurrentSession())) + ); + } + + @Test + public void testCurrentStatelessSession(VertxTestContext context) { + test( context, + getSessionFactory().withStatelessSession(session -> + getSessionFactory().withStatelessSession(s -> { + assertEquals(session, s); + Stage.StatelessSession currentSession = getSessionFactory().getCurrentStatelessSession(); + assertNotNull(currentSession); + assertTrue(currentSession.isOpen()); + assertEquals(session, currentSession); + return CompletionStages.voidFuture(); + }) + .thenAccept(v -> assertNotNull(getSessionFactory().getCurrentStatelessSession())) + ) + .thenAccept(v -> assertNull(getSessionFactory().getCurrentStatelessSession())) + ); + } + private void assertThatPigsAreEqual(GuineaPig expected, GuineaPig actual) { assertNotNull( actual ); assertEquals( expected.getId(), actual.getId() ); From d4068786858db4ebe6b55aac039b16158bf775ca Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 30 Apr 2025 19:46:27 +0200 Subject: [PATCH 156/201] [#2238] create Delegators for use in Quarkus --- .../delegation/MutinySessionDelegator.java | 364 ++++++++++++++++++ .../MutinyStatelessSessionDelegator.java | 282 ++++++++++++++ .../delegation/ConcreteSessionDelegator.java | 17 + .../ConcreteStatelessSessionDelegator.java | 17 + 4 files changed, 680 insertions(+) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinySessionDelegator.java create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteSessionDelegator.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteStatelessSessionDelegator.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinySessionDelegator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinySessionDelegator.java new file mode 100644 index 000000000..82f5ccd18 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinySessionDelegator.java @@ -0,0 +1,364 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.mutiny.delegation; + +import io.smallrye.mutiny.Uni; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Attribute; +import org.hibernate.*; +import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; +import org.hibernate.reactive.common.Identifier; +import org.hibernate.reactive.common.ResultSetMapping; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.List; +import java.util.function.Function; + +/** + * Wraps a {@linkplain #delegate} session. + * + * @author Gavin King + */ +public abstract class MutinySessionDelegator implements Mutiny.Session { + + public abstract Mutiny.Session delegate(); + + public Uni find(Class entityClass, Object id) { + return delegate().find(entityClass, id); + } + + public Uni find(Class entityClass, Object id, LockModeType lockModeType) { + return delegate().find(entityClass, id, lockModeType); + } + + public Uni find(Class entityClass, Object id, LockMode lockMode) { + return delegate().find(entityClass, id, lockMode); + } + + public Uni find(EntityGraph entityGraph, Object id) { + return delegate().find(entityGraph, id); + } + + public Uni> find(Class entityClass, Object... ids) { + return delegate().find(entityClass, ids); + } + + public Mutiny.SelectionQuery createNamedQuery(String queryName, Class resultType) { + return delegate().createNamedQuery(queryName, resultType); + } + + public Mutiny.SelectionQuery createQuery(String queryString, Class resultType) { + return delegate().createQuery(queryString, resultType); + } + + public boolean isReadOnly(Object entityOrProxy) { + return delegate().isReadOnly(entityOrProxy); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, resultType, affectedEntities); + } + + public boolean isDefaultReadOnly() { + return delegate().isDefaultReadOnly(); + } + + public Uni unproxy(T association) { + return delegate().unproxy(association); + } + + public Mutiny.MutationQuery createMutationQuery(String queryString) { + return delegate().createMutationQuery(queryString); + } + + public Uni close() { + return delegate().close(); + } + + public Mutiny.Session disableFetchProfile(String name) { + return delegate().disableFetchProfile(name); + } + + public EntityGraph getEntityGraph(Class rootType, String graphName) { + return delegate().getEntityGraph(rootType, graphName); + } + + public Mutiny.SelectionQuery createSelectionQuery(String queryString, Class resultType) { + return delegate().createSelectionQuery(queryString, resultType); + } + + public Uni refresh(Object entity, LockModeType lockModeType) { + return delegate().refresh(entity, lockModeType); + } + + public Uni lock(Object entity, LockModeType lockModeType) { + return delegate().lock(entity, lockModeType); + } + + public Mutiny.Query createQuery(TypedQueryReference typedQueryReference) { + return delegate().createQuery(typedQueryReference); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, resultSetMapping, affectedEntities); + } + + public Uni lock(Object entity, LockMode lockMode) { + return delegate().lock(entity, lockMode); + } + + @Incubating + public Uni find(Class entityClass, Identifier naturalId) { + return delegate().find(entityClass, naturalId); + } + + public Uni withTransaction(Function> work) { + return delegate().withTransaction(work); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping) { + return delegate().createNativeQuery(queryString, resultSetMapping); + } + + public EntityGraph createEntityGraph(Class rootType, String graphName) { + return delegate().createEntityGraph(rootType, graphName); + } + + public Mutiny.Transaction currentTransaction() { + return delegate().currentTransaction(); + } + + public Mutiny.Session detach(Object entity) { + return delegate().detach(entity); + } + + public Mutiny.Session setCacheStoreMode(CacheStoreMode cacheStoreMode) { + return delegate().setCacheStoreMode(cacheStoreMode); + } + + public FlushMode getFlushMode() { + return delegate().getFlushMode(); + } + + public LockMode getLockMode(Object entity) { + return delegate().getLockMode(entity); + } + + public Mutiny.Query createNamedQuery(String queryName) { + return delegate().createNamedQuery(queryName); + } + + public CriteriaBuilder getCriteriaBuilder() { + return delegate().getCriteriaBuilder(); + } + + public Mutiny.SessionFactory getFactory() { + return delegate().getFactory(); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, Class resultType) { + return delegate().createNativeQuery(queryString, resultType); + } + + public Mutiny.Session setSubselectFetchingEnabled(boolean enabled) { + return delegate().setSubselectFetchingEnabled(enabled); + } + + public Mutiny.Session setFlushMode(FlushMode flushMode) { + return delegate().setFlushMode(flushMode); + } + + public Uni remove(Object entity) { + return delegate().remove(entity); + } + + public Mutiny.Session setCacheMode(CacheMode cacheMode) { + return delegate().setCacheMode(cacheMode); + } + + public Filter enableFilter(String filterName) { + return delegate().enableFilter(filterName); + } + + public Mutiny.MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return delegate().createMutationQuery(insert); + } + + public Mutiny.Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, affectedEntities); + } + + public Mutiny.Session setReadOnly(Object entityOrProxy, boolean readOnly) { + return delegate().setReadOnly(entityOrProxy, readOnly); + } + + public EntityGraph createEntityGraph(Class rootType) { + return delegate().createEntityGraph(rootType); + } + + public Uni refreshAll(Object... entities) { + return delegate().refreshAll(entities); + } + + public Mutiny.MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return delegate().createMutationQuery(deleteQuery); + } + + public Integer getBatchSize() { + return delegate().getBatchSize(); + } + + public Uni refresh(Object entity, LockMode lockMode) { + return delegate().refresh(entity, lockMode); + } + + public T getReference(Class entityClass, Object id) { + return delegate().getReference(entityClass, id); + } + + public T getReference(T entity) { + return delegate().getReference(entity); + } + + public Mutiny.Session setBatchSize(Integer batchSize) { + return delegate().setBatchSize(batchSize); + } + + public Uni refresh(Object entity) { + return delegate().refresh(entity); + } + + public CacheMode getCacheMode() { + return delegate().getCacheMode(); + } + + public Uni mergeAll(Object... entities) { + return delegate().mergeAll(entities); + } + + public Uni persist(Object object) { + return delegate().persist(object); + } + + public boolean contains(Object entity) { + return delegate().contains(entity); + } + + public Mutiny.MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return delegate().createMutationQuery(updateQuery); + } + + public int getFetchBatchSize() { + return delegate().getFetchBatchSize(); + } + + public Mutiny.Session setDefaultReadOnly(boolean readOnly) { + return delegate().setDefaultReadOnly(readOnly); + } + + public Mutiny.Session clear() { + return delegate().clear(); + } + + public Uni fetch(E entity, Attribute field) { + return delegate().fetch(entity, field); + } + + public Mutiny.SelectionQuery createQuery(CriteriaQuery criteriaQuery) { + return delegate().createQuery(criteriaQuery); + } + + public Mutiny.Session setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode) { + return delegate().setCacheRetrieveMode(cacheRetrieveMode); + } + + public Uni removeAll(Object... entities) { + return delegate().removeAll(entities); + } + + public Filter getEnabledFilter(String filterName) { + return delegate().getEnabledFilter(filterName); + } + + public void disableFilter(String filterName) { + delegate().disableFilter(filterName); + } + + public Mutiny.MutationQuery createQuery(CriteriaDelete criteriaDelete) { + return delegate().createQuery(criteriaDelete); + } + + public Mutiny.Session enableFetchProfile(String name) { + return delegate().enableFetchProfile(name); + } + + public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { + return delegate().getResultSetMapping(resultType, mappingName); + } + + public Uni flush() { + return delegate().flush(); + } + + public Uni persist(String entityName, Object object) { + return delegate().persist(entityName, object); + } + + public Mutiny.Query createNativeQuery(String queryString) { + return delegate().createNativeQuery(queryString); + } + + public boolean isFetchProfileEnabled(String name) { + return delegate().isFetchProfileEnabled(name); + } + + public Uni merge(T entity) { + return delegate().merge(entity); + } + + public boolean isSubselectFetchingEnabled() { + return delegate().isSubselectFetchingEnabled(); + } + + public Mutiny.MutationQuery createQuery(CriteriaUpdate criteriaUpdate) { + return delegate().createQuery(criteriaUpdate); + } + + public Mutiny.Session setFetchBatchSize(int batchSize) { + return delegate().setFetchBatchSize(batchSize); + } + + public Uni fetch(T association) { + return delegate().fetch(association); + } + + public Uni persistAll(Object... entities) { + return delegate().persistAll(entities); + } + + @Deprecated + public Mutiny.Query createQuery(String queryString) { + return delegate().createQuery(queryString); + } + + public Mutiny.Session setFlushMode(FlushModeType flushModeType) { + return delegate().setFlushMode(flushModeType); + } + + public boolean isOpen() { + return delegate().isOpen(); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java new file mode 100644 index 000000000..cb48663cf --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java @@ -0,0 +1,282 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.mutiny.delegation; + +import io.smallrye.mutiny.Uni; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.LockModeType; +import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import org.hibernate.Incubating; +import org.hibernate.LockMode; +import org.hibernate.query.criteria.JpaCriteriaInsert; +import org.hibernate.reactive.common.AffectedEntities; +import org.hibernate.reactive.common.ResultSetMapping; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.util.List; +import java.util.function.Function; + +/** + * Wraps a {@linkplain #delegate} stateless session. + * + * @author Gavin King + */ +public abstract class MutinyStatelessSessionDelegator implements Mutiny.StatelessSession { + + public abstract Mutiny.StatelessSession delegate(); + + public Uni get(Class entityClass, Object id) { + return delegate().get(entityClass, id); + } + + @Deprecated + public Mutiny.Query createQuery(String queryString) { + return delegate().createQuery(queryString); + } + + public Uni get(EntityGraph entityGraph, Object id) { + return delegate().get(entityGraph, id); + } + + public Uni> get(Class entityClass, Object... ids) { + return delegate().get(entityClass, ids); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping) { + return delegate().createNativeQuery(queryString, resultSetMapping); + } + + public Object getIdentifier(Object entity) { + return delegate().getIdentifier(entity); + } + + public Uni get(Class entityClass, Object id, LockMode lockMode) { + return delegate().get(entityClass, id, lockMode); + } + + public boolean isOpen() { + return delegate().isOpen(); + } + + public CriteriaBuilder getCriteriaBuilder() { + return delegate().getCriteriaBuilder(); + } + + public Uni insertAll(Object... entities) { + return delegate().insertAll(entities); + } + + public Uni updateAll(int batchSize, Object... entities) { + return delegate().updateAll(batchSize, entities); + } + + public EntityGraph createEntityGraph(Class rootType, String graphName) { + return delegate().createEntityGraph(rootType, graphName); + } + + public EntityGraph getEntityGraph(Class rootType, String graphName) { + return delegate().getEntityGraph(rootType, graphName); + } + + public Uni get(Class entityClass, Object id, LockModeType lockModeType) { + return delegate().get(entityClass, id, lockModeType); + } + + public Uni update(Object entity) { + return delegate().update(entity); + } + + public Uni refreshAll(int batchSize, Object... entities) { + return delegate().refreshAll(batchSize, entities); + } + + public Mutiny.Query createQuery(TypedQueryReference typedQueryReference) { + return delegate().createQuery(typedQueryReference); + } + + public Mutiny.SelectionQuery createQuery(String queryString, Class resultType) { + return delegate().createQuery(queryString, resultType); + } + + public Uni delete(Object entity) { + return delegate().delete(entity); + } + + public Uni refresh(Object entity, LockModeType lockModeType) { + return delegate().refresh(entity, lockModeType); + } + + public Mutiny.SessionFactory getFactory() { + return delegate().getFactory(); + } + + public Mutiny.SelectionQuery createNativeQuery(String queryString, Class resultType) { + return delegate().createNativeQuery(queryString, resultType); + } + + public Uni deleteMultiple(List entities) { + return delegate().deleteMultiple(entities); + } + + public Uni deleteAll(Object... entities) { + return delegate().deleteAll(entities); + } + + public Uni updateMultiple(List entities) { + return delegate().updateMultiple(entities); + } + + public Mutiny.SelectionQuery createSelectionQuery(String queryString, Class resultType) { + return delegate().createSelectionQuery(queryString, resultType); + } + + @Incubating + public Uni upsert(Object entity) { + return delegate().upsert(entity); + } + + public Mutiny.MutationQuery createMutationQuery(String queryString) { + return delegate().createMutationQuery(queryString); + } + + public Uni refresh(Object entity) { + return delegate().refresh(entity); + } + + public ResultSetMapping getResultSetMapping(Class resultType, String mappingName) { + return delegate().getResultSetMapping(resultType, mappingName); + } + + @Incubating + public Uni upsertMultiple(List entities) { + return delegate().upsertMultiple(entities); + } + + public Mutiny.MutationQuery createQuery(CriteriaUpdate criteriaUpdate) { + return delegate().createQuery(criteriaUpdate); + } + + public Uni insertAll(int batchSize, Object... entities) { + return delegate().insertAll(batchSize, entities); + } + + public Mutiny.Query createNativeQuery(String queryString) { + return delegate().createNativeQuery(queryString); + } + + public Mutiny.Query createNamedQuery(String queryName) { + return delegate().createNamedQuery(queryName); + } + + public Mutiny.SelectionQuery createNamedQuery(String queryName, Class resultType) { + return delegate().createNamedQuery(queryName, resultType); + } + + public Uni refreshAll(Object... entities) { + return delegate().refreshAll(entities); + } + + public Uni close() { + return delegate().close(); + } + + public Uni updateAll(Object... entities) { + return delegate().updateAll(entities); + } + + public Mutiny.SelectionQuery createQuery(CriteriaQuery criteriaQuery) { + return delegate().createQuery(criteriaQuery); + } + + public Mutiny.MutationQuery createQuery(CriteriaDelete criteriaDelete) { + return delegate().createQuery(criteriaDelete); + } + + public Uni withTransaction(Function> work) { + return delegate().withTransaction(work); + } + + public Uni refreshMultiple(List entities) { + return delegate().refreshMultiple(entities); + } + + public Uni fetch(T association) { + return delegate().fetch(association); + } + + @Incubating + public Uni upsert(String entityName, Object entity) { + return delegate().upsert(entityName, entity); + } + + @Incubating + public Uni upsertAll(Object... entities) { + return delegate().upsertAll(entities); + } + + public Mutiny.Transaction currentTransaction() { + return delegate().currentTransaction(); + } + + @Incubating + public Uni upsertAll(int batchSize, Object... entities) { + return delegate().upsertAll(batchSize, entities); + } + + public EntityGraph createEntityGraph(Class rootType) { + return delegate().createEntityGraph(rootType); + } + + public Uni insert(Object entity) { + return delegate().insert(entity); + } + + public Uni insertMultiple(List entities) { + return delegate().insertMultiple(entities); + } + + public Uni refresh(Object entity, LockMode lockMode) { + return delegate().refresh(entity, lockMode); + } + + public Uni deleteAll(int batchSize, Object... entities) { + return delegate().deleteAll(batchSize, entities); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaUpdate updateQuery) { + return delegate().createMutationQuery(updateQuery); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(CriteriaDelete deleteQuery) { + return delegate().createMutationQuery(deleteQuery); + } + + @Override + public Mutiny.MutationQuery createMutationQuery(JpaCriteriaInsert insert) { + return delegate().createMutationQuery(insert); + } + + @Override + public Mutiny.Query createNativeQuery(String queryString, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, affectedEntities); + } + + @Override + public Mutiny.SelectionQuery createNativeQuery(String queryString, Class resultType, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, resultType, affectedEntities); + } + + @Override + public Mutiny.SelectionQuery createNativeQuery(String queryString, ResultSetMapping resultSetMapping, AffectedEntities affectedEntities) { + return delegate().createNativeQuery(queryString, resultSetMapping, affectedEntities); + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteSessionDelegator.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteSessionDelegator.java new file mode 100644 index 000000000..2c93d9226 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteSessionDelegator.java @@ -0,0 +1,17 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.delegation; + +import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.mutiny.delegation.MutinySessionDelegator; + +@SuppressWarnings("unused") +class ConcreteSessionDelegator extends MutinySessionDelegator { + @Override + public Mutiny.Session delegate() { + throw new UnsupportedOperationException(); + } +} diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteStatelessSessionDelegator.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteStatelessSessionDelegator.java new file mode 100644 index 000000000..e275d0180 --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/delegation/ConcreteStatelessSessionDelegator.java @@ -0,0 +1,17 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.delegation; + +import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.mutiny.delegation.MutinyStatelessSessionDelegator; + +@SuppressWarnings("unused") +class ConcreteStatelessSessionDelegator extends MutinyStatelessSessionDelegator { + @Override + public Mutiny.StatelessSession delegate() { + throw new UnsupportedOperationException(); + } +} From e254a0f8f5cba33aa947e0611cc87a8adea1161a Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Mon, 5 May 2025 12:18:17 +0200 Subject: [PATCH 157/201] Remove MutinyStatelessSessionDelegator#upsert(String entityName, Object entity) method (#2246) --- .../mutiny/delegation/MutinyStatelessSessionDelegator.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java index cb48663cf..ca19b71e7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/delegation/MutinyStatelessSessionDelegator.java @@ -211,11 +211,6 @@ public Uni fetch(T association) { return delegate().fetch(association); } - @Incubating - public Uni upsert(String entityName, Object entity) { - return delegate().upsert(entityName, entity); - } - @Incubating public Uni upsertAll(Object... entities) { return delegate().upsertAll(entities); From b34d44f3c1dcb297f58007d9b4738bab4f1616f5 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 5 May 2025 11:50:46 +0200 Subject: [PATCH 158/201] [#2222] Adjust snapshot publishing to use the publish plugin instead of JReleaser --- ci/snapshot-publish.Jenkinsfile | 12 ++++-------- jreleaser.yml | 20 -------------------- publish.gradle | 7 +++++++ 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile index 59a97eeee..fea160b23 100644 --- a/ci/snapshot-publish.Jenkinsfile +++ b/ci/snapshot-publish.Jenkinsfile @@ -41,19 +41,15 @@ pipeline { 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 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'), + // 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'), gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'), string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN') ]) { checkoutReleaseScripts() def version = sh( - script: ".release/scripts/determine-current-version.sh orm", + script: ".release/scripts/determine-current-version.sh reactive", returnStdout: true ).trim() echo "Current version: '${version}'" diff --git a/jreleaser.yml b/jreleaser.yml index c3eeec649..f9428278e 100644 --- a/jreleaser.yml +++ b/jreleaser.yml @@ -27,16 +27,6 @@ deploy: releaseRepository: false stagingRepositories: - build/staging-deploy/maven - maven-central-snapshot: - active: SNAPSHOT - url: https://oss.sonatype.org/service/local - snapshotUrl: https://oss.sonatype.org/content/repositories/snapshots/ - closeRepository: true - releaseRepository: true - javadocJar: false - sign: false - stagingRepositories: - - build/staging-deploy/maven mavenCentral: maven-central: # TODO: Change to RELEASE once switching to Maven-Central: @@ -47,13 +37,3 @@ deploy: applyMavenCentralRules: true stagingRepositories: - build/staging-deploy/maven - maven-central-snapshot: - # TODO: Change to SNAPSHOT once switching to Maven-Central: - active: NEVER - url: https://central.sonatype.com/api/v1/publisher - snapshotSupported: true - applyMavenCentralRules: true - javadocJar: false - sign: false - stagingRepositories: - - build/staging-deploy/maven diff --git a/publish.gradle b/publish.gradle index 201b67a8a..f77b26959 100644 --- a/publish.gradle +++ b/publish.gradle @@ -79,5 +79,12 @@ publishing { name = "staging" url = rootProject.layout.buildDirectory.dir("staging-deploy${File.separator}maven") } + maven { + name = 'snapshots' + url = "https://oss.sonatype.org/content/repositories/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 11227f1391d46b2295ef8161aa74e73aedd5403b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 04:06:57 +0000 Subject: [PATCH 159/201] Bump actions/setup-java in the workflow-actions group Bumps the workflow-actions group with 1 update: [actions/setup-java](https://github.com/actions/setup-java). Updates `actions/setup-java` from 4.7.0 to 4.7.1 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/3a4f6e1af504cf6a31855fa899c6aa5355ba6c12...c5195efecf7bdfc987ee8bae7a71cb8b11521c00) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: workflow-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 14 +++++++------- .github/workflows/codeql.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2718f729f..63f7c7764 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,13 +101,13 @@ jobs: key: gradle-examples-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: 11 - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: 17 @@ -150,13 +150,13 @@ jobs: key: gradle-db-${{ matrix.db }}-${{ steps.get-date.outputs.yearmonth }} - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: 11 - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' @@ -255,7 +255,7 @@ jobs: release: ${{ matrix.java.java_version_numeric }} - name: Set up latest JDK ${{ matrix.java.name }} from Adoptium if: matrix.java.from == '' || matrix.java.from == 'adoptium.net' - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: ${{ matrix.java.java_version_numeric }} @@ -265,14 +265,14 @@ jobs: run: echo "::set-output name=path::${JAVA_HOME}" - name: Set up JDK 11 if: ${{ startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: 11 check-latest: true - name: Set up JDK 17 if: ${{ !startsWith( inputs.branch, 'wip/2' ) }} - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: 'temurin' java-version: 17 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 13d87abe0..c5f0ffbc8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup Java - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: distribution: temurin java-version: 17 From 38c9ea6e3713afd4f8871d631c64955de3be2743 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Mon, 5 May 2025 13:07:14 +0000 Subject: [PATCH 160/201] Update project version to : `3.0.0.CR1` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..047caa913 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.CR1 \ No newline at end of file From 562217057803069db86286c04bf2e0cc377f9b0e Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Mon, 5 May 2025 13:08:13 +0000 Subject: [PATCH 161/201] Update project version to : `3.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 047caa913..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.CR1 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From d33522c966aa033fb8eca0eaf28d39c612e5fb42 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 5 May 2025 15:39:06 +0200 Subject: [PATCH 162/201] [#2222] Set the dry run option from the parameters rather than keeping it always set to `true` --- ci/release/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 4ae62e3f3..1c18b80d2 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -151,7 +151,7 @@ pipeline { env.DEVELOPMENT_VERSION = developmentVersion.toString() // Dry run is not supported at the moment env.SCRIPT_OPTIONS = params.RELEASE_DRY_RUN ? "-d" : "" - env.JRELEASER_DRY_RUN = true + 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) From 0185f6a56fbdadc262e61978ca1bec9da895e0cb Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 5 May 2025 15:43:59 +0200 Subject: [PATCH 163/201] [#2222] Set the dry run option from the parameters rather than keeping it always set to `true` also release the staging repository automatically --- jreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jreleaser.yml b/jreleaser.yml index f9428278e..e39594d95 100644 --- a/jreleaser.yml +++ b/jreleaser.yml @@ -24,7 +24,7 @@ deploy: url: https://oss.sonatype.org/service/local snapshotUrl: https://oss.sonatype.org/content/repositories/snapshots/ closeRepository: true - releaseRepository: false + releaseRepository: true stagingRepositories: - build/staging-deploy/maven mavenCentral: From ba7e2cf48d4253053417ab6b18e19e9f67380779 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Mon, 5 May 2025 13:55:02 +0000 Subject: [PATCH 164/201] Update project version to : `3.0.0.CR1` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..047caa913 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.CR1 \ No newline at end of file From b419ec87a973c2ed5cd5dd18c7f819629a48dbbd Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Mon, 5 May 2025 13:56:00 +0000 Subject: [PATCH 165/201] Update project version to : `3.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 047caa913..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.CR1 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From 88283527105e5de8acfdc52ff6b6ec50bb1dbe9c Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 6 May 2025 16:59:00 +0200 Subject: [PATCH 166/201] Remove JReleaser config from the sources as it will be now located inside the release scripts --- ci/release/Jenkinsfile | 4 ++-- jreleaser.yml | 39 --------------------------------------- 2 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 jreleaser.yml diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 1c18b80d2..8627950a1 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -178,7 +178,7 @@ pipeline { // 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'" ]) { - sh ".release/scripts/prepare-release.sh -b ${env.GIT_BRANCH} -d ${env.DEVELOPMENT_VERSION} ${env.PROJECT} ${env.RELEASE_VERSION}" + sh ".release/scripts/prepare-release.sh -j -b ${env.GIT_BRANCH} -v ${env.DEVELOPMENT_VERSION} ${env.PROJECT} ${env.RELEASE_VERSION}" } } } @@ -210,7 +210,7 @@ pipeline { 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 ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH}" + sh ".release/scripts/publish.sh -j ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH}" } } } diff --git a/jreleaser.yml b/jreleaser.yml deleted file mode 100644 index e39594d95..000000000 --- a/jreleaser.yml +++ /dev/null @@ -1,39 +0,0 @@ -project: - languages: - java: - groupId: org.hibernate.reactive - -release: - github: - skipTag: true - skipRelease: true - tagName: '{{projectVersion}}' - -# File signing is always active -signing: - mode: COMMAND - active: RELEASE - armored: true - -deploy: - maven: - # TODO: Remove the entire nexus2 section when switching to maven central publishing (see below): - nexus2: - maven-central: - active: RELEASE - url: https://oss.sonatype.org/service/local - snapshotUrl: https://oss.sonatype.org/content/repositories/snapshots/ - closeRepository: true - releaseRepository: true - stagingRepositories: - - build/staging-deploy/maven - mavenCentral: - maven-central: - # TODO: Change to RELEASE once switching to Maven-Central: - # Note, this is an untested configuration, hence might need further adjustments - active: NEVER - url: https://central.sonatype.com/api/v1/publisher - snapshotSupported: false - applyMavenCentralRules: true - stagingRepositories: - - build/staging-deploy/maven From 3ec4ff9217b02ffdd30bce0242f749e5bd5b43e4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 14 May 2025 09:53:54 +0200 Subject: [PATCH 167/201] update to 7.0.0.CR2 adapt to changes to setParameter() signature --- gradle.properties | 2 +- .../reactive/query/ReactiveMutationQuery.java | 20 +++--- .../reactive/query/ReactiveNativeQuery.java | 20 +++--- .../reactive/query/ReactiveQuery.java | 72 +++++++++---------- .../query/ReactiveQueryImplementor.java | 20 +++--- .../query/ReactiveSelectionQuery.java | 20 +++--- .../sql/internal/ReactiveNativeQueryImpl.java | 20 +++--- .../spi/ReactiveNativeQueryImplementor.java | 20 +++--- .../query/sqm/ReactiveSqmSelectionQuery.java | 20 +++--- .../sqm/internal/ReactiveQuerySqmImpl.java | 20 +++--- .../ReactiveSqmSelectionQueryImpl.java | 21 +++--- .../session/ReactiveSqmQueryImplementor.java | 20 +++--- .../session/impl/ReactiveSessionImpl.java | 42 ++++++----- 13 files changed, 161 insertions(+), 156 deletions(-) diff --git a/gradle.properties b/gradle.properties index c402bb33b..3da2482ba 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.CR1 +hibernateOrmVersion = 7.0.0.CR2 # 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 diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveMutationQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveMutationQuery.java index ce3943f25..5833156fc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveMutationQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveMutationQuery.java @@ -13,12 +13,12 @@ import java.util.concurrent.CompletionStage; import org.hibernate.FlushMode; -import org.hibernate.query.BindableType; import org.hibernate.query.CommonQueryContract; import org.hibernate.query.QueryParameter; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; /** * @see org.hibernate.query.MutationQuery @@ -33,7 +33,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameter(String name, P value, Class

    type); @Override -

    ReactiveMutationQuery setParameter(String name, P value, BindableType

    type); +

    ReactiveMutationQuery setParameter(String name, P value, Type

    type); @Override ReactiveMutationQuery setParameter(String name, Instant value, TemporalType temporalType); @@ -51,7 +51,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameter(int position, P value, Class

    type); @Override -

    ReactiveMutationQuery setParameter(int position, P value, BindableType

    type); +

    ReactiveMutationQuery setParameter(int position, P value, Type

    type); @Override ReactiveMutationQuery setParameter(int position, Instant value, TemporalType temporalType); @@ -69,7 +69,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameter(QueryParameter

    parameter, P value, Class

    type); @Override -

    ReactiveMutationQuery setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveMutationQuery setParameter(QueryParameter

    parameter, P val, Type

    type); @Override ReactiveMutationQuery setParameter(Parameter param, T value); @@ -87,7 +87,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameterList(String name, Collection values, Class

    javaType); @Override -

    ReactiveMutationQuery setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveMutationQuery setParameterList(String name, Collection values, Type

    type); @Override ReactiveMutationQuery setParameterList(String name, Object[] values); @@ -96,7 +96,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

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

    javaType); @Override -

    ReactiveMutationQuery setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveMutationQuery setParameterList(int position, Collection values); @@ -105,7 +105,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameterList(int position, Collection values, Class

    javaType); @Override -

    ReactiveMutationQuery setParameterList(int position, Collection values, BindableType

    type); +

    ReactiveMutationQuery setParameterList(int position, Collection values, Type

    type); @Override ReactiveMutationQuery setParameterList(int position, Object[] values); @@ -114,7 +114,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

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

    javaType); @Override -

    ReactiveMutationQuery setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, Collection values); @@ -123,7 +123,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, Collection values, Class

    javaType); @Override -

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type); +

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, Collection values, Type

    type); @Override

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, P[] values); @@ -132,7 +132,7 @@ public interface ReactiveMutationQuery extends CommonQueryContract {

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveMutationQuery setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveMutationQuery setProperties(Object bean); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveNativeQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveNativeQuery.java index fb6a1f46d..708b6a486 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveNativeQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveNativeQuery.java @@ -11,12 +11,12 @@ import java.util.Date; import java.util.Map; +import jakarta.persistence.metamodel.Type; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.metamodel.model.domain.BasicDomainType; -import org.hibernate.query.BindableType; import org.hibernate.query.NativeQuery; import org.hibernate.query.NativeQuery.FetchReturn; import org.hibernate.query.QueryParameter; @@ -225,7 +225,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameter(String name, P val, Class

    type); @Override -

    ReactiveNativeQuery setParameter(String name, P val, BindableType

    type); +

    ReactiveNativeQuery setParameter(String name, P val, Type

    type); @Override ReactiveNativeQuery setParameter(String name, Instant value, TemporalType temporalType); @@ -243,7 +243,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameter(int position, P val, Class

    type); @Override -

    ReactiveNativeQuery setParameter(int position, P val, BindableType

    type); +

    ReactiveNativeQuery setParameter(int position, P val, Type

    type); @Override ReactiveNativeQuery setParameter(int position, Instant value, TemporalType temporalType); @@ -261,7 +261,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameter(QueryParameter

    parameter, P val, Class

    type); @Override -

    ReactiveNativeQuery setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveNativeQuery setParameter(QueryParameter

    parameter, P val, Type

    type); @Override

    ReactiveNativeQuery setParameter(Parameter

    param, P value); @@ -279,7 +279,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameterList(String name, Collection values, Class

    type); @Override -

    ReactiveNativeQuery setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveNativeQuery setParameterList(String name, Collection values, Type

    type); @Override ReactiveNativeQuery setParameterList(String name, Object[] values); @@ -288,7 +288,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

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

    type); @Override -

    ReactiveNativeQuery setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveNativeQuery setParameterList(int position, @SuppressWarnings("rawtypes") Collection values); @@ -297,7 +297,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameterList(int position, Collection values, Class

    type); @Override -

    ReactiveNativeQuery setParameterList(int position, Collection values, BindableType

    javaType); +

    ReactiveNativeQuery setParameterList(int position, Collection values, Type

    javaType); @Override ReactiveNativeQuery setParameterList(int position, Object[] values); @@ -306,7 +306,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

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

    javaType); @Override -

    ReactiveNativeQuery setParameterList(int position, P[] values, BindableType

    javaType); +

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

    javaType); @Override

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, Collection values); @@ -315,7 +315,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, Collection values, Class

    javaType); @Override -

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type); +

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, Collection values, Type

    type); @Override

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, P[] values); @@ -324,7 +324,7 @@ public interface ReactiveNativeQuery extends ReactiveQuery {

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveNativeQuery setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveNativeQuery setProperties(Object bean); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java index 0dae454e0..424db8b35 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java @@ -11,11 +11,11 @@ import java.util.Date; import java.util.Map; +import jakarta.persistence.metamodel.Type; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.query.BindableType; import org.hibernate.query.ParameterMetadata; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; @@ -64,7 +64,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut

    ReactiveQuery setParameter(String parameter, P argument, Class

    type); @Override -

    ReactiveQuery setParameter(String parameter, P argument, BindableType

    type); +

    ReactiveQuery setParameter(String parameter, P argument, Type

    type); /** * Bind an {@link Instant} value to the named query parameter using @@ -85,26 +85,26 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * which it occurs, use one of the forms which accepts a "type". * * @see #setParameter(int, Object, Class) - * @see #setParameter(int, Object, BindableType) + * @see #setParameter(int, Object, Type) */ @Override ReactiveQuery setParameter(int parameter, Object argument); /** * Bind the given argument to an ordinal query parameter using the given - * Class reference to attempt to determine the {@link BindableType} - * to use. If unable to determine an appropriate {@link BindableType}, + * Class reference to attempt to determine the {@link Type} + * to use. If unable to determine an appropriate {@link Type}, * {@link #setParameter(int, Object)} is used. * - * @see #setParameter(int, Object, BindableType) + * @see #setParameter(int, Object, Type) */

    ReactiveQuery setParameter(int parameter, P argument, Class

    type); /** * Bind the given argument to an ordinal query parameter using the given - * {@link BindableType}. + * {@link Type}. */ -

    ReactiveQuery setParameter(int parameter, P argument, BindableType

    type); +

    ReactiveQuery setParameter(int parameter, P argument, Type

    type); /** * Bind an {@link Instant} value to the ordinal query parameter using @@ -128,7 +128,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut

    ReactiveQuery setParameter(QueryParameter

    parameter, P argument, Class

    type); -

    ReactiveQuery setParameter(QueryParameter

    parameter, P argument, BindableType

    type); +

    ReactiveQuery setParameter(QueryParameter

    parameter, P argument, Type

    type); @Override ReactiveQuery setParameter(Parameter parameter, T argument); @@ -145,14 +145,14 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to a named query parameter using the given - * {@link BindableType}. + * {@link Type}. * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(String parameter, Collection arguments, BindableType

    type); +

    ReactiveQuery setParameterList(String parameter, Collection arguments, Type

    type); /** * Bind multiple arguments to a named query parameter. @@ -169,11 +169,11 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to a named query parameter using the given - * Class reference to attempt to determine the {@link BindableType} - * to use. If unable to determine an appropriate {@link BindableType}, + * Class reference to attempt to determine the {@link Type} + * to use. If unable to determine an appropriate {@link Type}, * {@link #setParameterList(String, Collection)} is used. * - * @see #setParameterList(java.lang.String, Object[], BindableType) + * @see #setParameterList(java.lang.String, Object[], Type) * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. @@ -185,14 +185,14 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to a named query parameter using the given - * {@link BindableType}. + * {@link Type}. * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(String parameter, P[] arguments, BindableType

    type); +

    ReactiveQuery setParameterList(String parameter, P[] arguments, Type

    type); /** * Bind multiple arguments to an ordinal query parameter. @@ -209,11 +209,11 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to an ordinal query parameter using the given - * Class reference to attempt to determine the {@link BindableType} - * to use. If unable to determine an appropriate {@link BindableType}, + * Class reference to attempt to determine the {@link Type} + * to use. If unable to determine an appropriate {@link Type}, * {@link #setParameterList(String, Collection)} is used. * - * @see #setParameterList(int, Collection, BindableType) + * @see #setParameterList(int, Collection, Type) * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. @@ -224,14 +224,14 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to an ordinal query parameter using the given - * {@link BindableType}. + * {@link Type}. * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(int parameter, Collection arguments, BindableType

    type); +

    ReactiveQuery setParameterList(int parameter, Collection arguments, Type

    type); /** * Bind multiple arguments to an ordinal query parameter. @@ -248,11 +248,11 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to an ordinal query parameter using the given - * {@link Class} reference to attempt to determine the {@link BindableType} - * to use. If unable to determine an appropriate {@link BindableType}, + * {@link Class} reference to attempt to determine the {@link Type} + * to use. If unable to determine an appropriate {@link Type}, * {@link #setParameterList(String, Collection)} is used. * - * @see #setParameterList(int, Object[], BindableType) + * @see #setParameterList(int, Object[], Type) * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. @@ -263,14 +263,14 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to an ordinal query parameter using the given - * {@link BindableType}. + * {@link Type}. * * @apiNote This is used for binding a list of values to an expression * such as {@code entity.field in (:values)}. * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(int parameter, P[] arguments, BindableType

    type); +

    ReactiveQuery setParameterList(int parameter, P[] arguments, Type

    type); /** * Bind multiple arguments to the query parameter represented by the given @@ -289,11 +289,11 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to the query parameter represented by the given * {@link QueryParameter} using the given Class reference to attempt to - * determine the {@link BindableType} to use. If unable to determine an - * appropriate {@link BindableType}, {@link #setParameterList(String, Collection)} + * determine the {@link Type} to use. If unable to determine an + * appropriate {@link Type}, {@link #setParameterList(String, Collection)} * is used. * - * @see #setParameterList(QueryParameter, java.util.Collection, BindableType) + * @see #setParameterList(QueryParameter, java.util.Collection, Type) * * @apiNote This is used for binding a list of values to an expression such * as {@code entity.field in (:values)}. @@ -304,7 +304,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to the query parameter represented by the given - * {@link QueryParameter}, inferring the {@link BindableType}. + * {@link QueryParameter}, inferring the {@link Type}. *

    * The "type mapping" for the binding is inferred from the type of the first * collection element. @@ -314,7 +314,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(QueryParameter

    parameter, Collection arguments, BindableType

    type); +

    ReactiveQuery setParameterList(QueryParameter

    parameter, Collection arguments, Type

    type); /** * Bind multiple arguments to the query parameter represented by the @@ -334,11 +334,11 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to the query parameter represented by the * given {@link QueryParameter} using the given Class reference to attempt - * to determine the {@link BindableType} to use. If unable to - * determine an appropriate {@link BindableType}, + * to determine the {@link Type} to use. If unable to + * determine an appropriate {@link Type}, * {@link #setParameterList(String, Collection)} is used * - * @see #setParameterList(QueryParameter, Object[], BindableType) + * @see #setParameterList(QueryParameter, Object[], Type) * * @apiNote This is used for binding a list of values to an expression such * as {@code entity.field in (:values)}. @@ -349,7 +349,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut /** * Bind multiple arguments to the query parameter represented by the - * given {@link QueryParameter}, inferring the {@link BindableType}. + * given {@link QueryParameter}, inferring the {@link Type}. *

    * The "type mapping" for the binding is inferred from the type of * the first collection element @@ -359,7 +359,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ -

    ReactiveQuery setParameterList(QueryParameter

    parameter, P[] arguments, BindableType

    type); +

    ReactiveQuery setParameterList(QueryParameter

    parameter, P[] arguments, Type

    type); /** * Bind the property values of the given bean to named parameters of the query, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java index 0354a59e5..6d28fbb04 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQueryImplementor.java @@ -11,7 +11,6 @@ import java.util.Date; import java.util.Map; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -19,6 +18,7 @@ import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; public interface ReactiveQueryImplementor extends ReactiveQuery { @@ -37,7 +37,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

    ReactiveQueryImplementor setParameter(String name, P value, Class

    type); @Override -

    ReactiveQueryImplementor setParameter(String name, P value, BindableType

    type); +

    ReactiveQueryImplementor setParameter(String name, P value, Type

    type); @Override ReactiveQueryImplementor setParameter(String name, Instant value, TemporalType temporalType); @@ -55,7 +55,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

    ReactiveQueryImplementor setParameter(int position, P value, Class

    type); @Override -

    ReactiveQueryImplementor setParameter(int position, P value, BindableType

    type); +

    ReactiveQueryImplementor setParameter(int position, P value, Type

    type); @Override ReactiveQueryImplementor setParameter(int position, Instant value, TemporalType temporalType); @@ -73,7 +73,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

    ReactiveQueryImplementor setParameter(QueryParameter

    parameter, P value, Class

    type); @Override -

    ReactiveQueryImplementor setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveQueryImplementor setParameter(QueryParameter

    parameter, P val, Type

    type); @Override ReactiveQueryImplementor setParameter(Parameter param, T value); @@ -91,7 +91,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

    ReactiveQueryImplementor setParameterList(String name, Collection values, Class

    javaType); @Override -

    ReactiveQueryImplementor setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveQueryImplementor setParameterList(String name, Collection values, Type

    type); @Override ReactiveQueryImplementor setParameterList(String name, Object[] values); @@ -100,7 +100,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

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

    javaType); @Override -

    ReactiveQueryImplementor setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveQueryImplementor setParameterList(int position, @SuppressWarnings("rawtypes") Collection values); @@ -112,7 +112,7 @@ public interface ReactiveQueryImplementor extends ReactiveQuery {

    ReactiveQueryImplementor setParameterList( int position, Collection values, - BindableType

    type); + Type

    type); @Override ReactiveQueryImplementor setParameterList(int position, Object[] values); @@ -121,7 +121,7 @@

    ReactiveQueryImplementor setParameterList(

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

    javaType); @Override -

    ReactiveQueryImplementor setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override

    ReactiveQueryImplementor setParameterList(QueryParameter

    parameter, Collection values); @@ -136,7 +136,7 @@

    ReactiveQueryImplementor setParameterList(

    ReactiveQueryImplementor setParameterList( QueryParameter

    parameter, Collection values, - BindableType

    type); + Type

    type); @Override

    ReactiveQueryImplementor setParameterList(QueryParameter

    parameter, P[] values); @@ -145,7 +145,7 @@

    ReactiveQueryImplementor setParameterList(

    ReactiveQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveQueryImplementor setProperties(Object bean); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java index 2ee7a051e..4fe138bca 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java @@ -5,13 +5,13 @@ */ package org.hibernate.reactive.query; +import jakarta.persistence.metamodel.Type; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.query.BindableType; import org.hibernate.query.CommonQueryContract; import org.hibernate.query.QueryParameter; @@ -130,7 +130,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameter(String name, P value, Class

    type); @Override -

    ReactiveSelectionQuery setParameter(String name, P value, BindableType

    type); +

    ReactiveSelectionQuery setParameter(String name, P value, Type

    type); @Override ReactiveSelectionQuery setParameter(String name, Instant value, TemporalType temporalType); @@ -148,7 +148,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameter(int position, P value, Class

    type); @Override -

    ReactiveSelectionQuery setParameter(int position, P value, BindableType

    type); +

    ReactiveSelectionQuery setParameter(int position, P value, Type

    type); @Override ReactiveSelectionQuery setParameter(int position, Instant value, TemporalType temporalType); @@ -166,7 +166,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameter(QueryParameter

    parameter, P value, Class

    type); @Override -

    ReactiveSelectionQuery setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveSelectionQuery setParameter(QueryParameter

    parameter, P val, Type

    type); @Override ReactiveSelectionQuery setParameter(Parameter param, T value); @@ -184,7 +184,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameterList(String name, Collection values, Class

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveSelectionQuery setParameterList(String name, Collection values, Type

    type); @Override ReactiveSelectionQuery setParameterList(String name, Object[] values); @@ -193,7 +193,7 @@ default CompletionStage> getReactiveResultList() {

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

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveSelectionQuery setParameterList(int position, @SuppressWarnings("rawtypes") Collection values); @@ -202,7 +202,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameterList(int position, Collection values, Class

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(int position, Collection values, BindableType

    type); +

    ReactiveSelectionQuery setParameterList(int position, Collection values, Type

    type); @Override ReactiveSelectionQuery setParameterList(int position, Object[] values); @@ -211,7 +211,7 @@ default CompletionStage> getReactiveResultList() {

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

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, Collection values); @@ -220,7 +220,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, Collection values, Class

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type); +

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, Collection values, Type

    type); @Override

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, P[] values); @@ -229,7 +229,7 @@ default CompletionStage> getReactiveResultList() {

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveSelectionQuery setProperties(Object bean); 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 afaf43e41..6ca5b2024 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 @@ -27,7 +27,6 @@ import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.BasicDomainType; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -54,6 +53,7 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; import jakarta.persistence.metamodel.SingularAttribute; public class ReactiveNativeQueryImpl extends NativeQueryImpl @@ -570,7 +570,7 @@ public ReactiveNativeQueryImpl setParameter(String name, Object val) { } @Override - public

    ReactiveNativeQueryImpl setParameter(String name, P val, BindableType

    type) { + public

    ReactiveNativeQueryImpl setParameter(String name, P val, Type

    type) { super.setParameter( name, val, type ); return this; } @@ -612,7 +612,7 @@ public

    ReactiveNativeQueryImpl setParameter(int position, P val, Class

    } @Override - public

    ReactiveNativeQueryImpl setParameter(int position, P val, BindableType

    type) { + public

    ReactiveNativeQueryImpl setParameter(int position, P val, Type

    type) { super.setParameter( position, val, type ); return this; } @@ -648,7 +648,7 @@ public

    ReactiveNativeQueryImpl setParameter(QueryParameter

    parameter, } @Override - public

    ReactiveNativeQueryImpl setParameter(QueryParameter

    parameter, P val, BindableType

    type) { + public

    ReactiveNativeQueryImpl setParameter(QueryParameter

    parameter, P val, Type

    type) { super.setParameter( parameter, val, type ); return this; } @@ -690,7 +690,7 @@ public

    ReactiveNativeQueryImpl setParameterList(String name, Collection ReactiveNativeQueryImpl setParameterList( String name, Collection values, - BindableType

    type) { + Type

    type) { super.setParameterList( name, values, type ); return this; } @@ -708,7 +708,7 @@ public

    ReactiveNativeQueryImpl setParameterList(String name, P[] values, } @Override - public

    ReactiveNativeQueryImpl setParameterList(String name, P[] values, BindableType

    type) { + public

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

    type) { super.setParameterList( name, values, type ); return this; } @@ -732,7 +732,7 @@ public

    ReactiveNativeQueryImpl setParameterList( public

    ReactiveNativeQueryImpl setParameterList( int position, Collection values, - BindableType

    type) { + Type

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

    ReactiveNativeQueryImpl setParameterList(int position, P[] values, } @Override - public

    ReactiveNativeQueryImpl setParameterList(int position, P[] values, BindableType

    type) { + public

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

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

    ReactiveNativeQueryImpl setParameterList( public

    ReactiveNativeQueryImpl setParameterList( QueryParameter

    parameter, Collection values, - BindableType

    type) { + Type

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

    ReactiveNativeQueryImpl setParameterList(QueryParameter

    paramet public

    ReactiveNativeQueryImpl setParameterList( QueryParameter

    parameter, P[] values, - BindableType

    type) { + Type

    type) { super.setParameterList( parameter, values, type ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java index 3c59afc3e..fd9ca555f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/spi/ReactiveNativeQueryImplementor.java @@ -11,12 +11,12 @@ import java.util.Date; import java.util.Map; +import jakarta.persistence.metamodel.Type; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.metamodel.model.domain.BasicDomainType; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -173,7 +173,7 @@ ReactiveNativeQueryImplementor addJoin( ReactiveNativeQueryImplementor setParameter(String name, Object val); @Override -

    ReactiveNativeQueryImplementor setParameter(String name, P val, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameter(String name, P val, Type

    type); @Override

    ReactiveNativeQueryImplementor setParameter(String name, P val, Class

    type); @@ -194,7 +194,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameter(int position, P val, Class

    type); @Override -

    ReactiveNativeQueryImplementor setParameter(int position, P val, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameter(int position, P val, Type

    type); @Override ReactiveNativeQueryImplementor setParameter(int position, Instant value, TemporalType temporalType); @@ -212,7 +212,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameter(QueryParameter

    parameter, P val, Class

    type); @Override -

    ReactiveNativeQueryImplementor setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameter(QueryParameter

    parameter, P val, Type

    type); @Override

    ReactiveNativeQueryImplementor setParameter(Parameter

    param, P value); @@ -230,7 +230,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameterList(String name, Collection values, Class

    type); @Override -

    ReactiveNativeQueryImplementor setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameterList(String name, Collection values, Type

    type); @Override ReactiveNativeQueryImplementor setParameterList(String name, Object[] values); @@ -239,7 +239,7 @@ ReactiveNativeQueryImplementor addJoin(

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

    type); @Override -

    ReactiveNativeQueryImplementor setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveNativeQueryImplementor setParameterList(int position, @SuppressWarnings("rawtypes") Collection values); @@ -248,7 +248,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameterList(int position, Collection values, Class

    type); @Override -

    ReactiveNativeQueryImplementor setParameterList(int position, Collection values, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameterList(int position, Collection values, Type

    type); @Override ReactiveNativeQueryImplementor setParameterList(int position, Object[] values); @@ -257,7 +257,7 @@ ReactiveNativeQueryImplementor addJoin(

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

    javaType); @Override -

    ReactiveNativeQueryImplementor setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override @@ -267,7 +267,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, Class

    javaType); @Override -

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, Type

    type); @Override

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, P[] values); @@ -276,7 +276,7 @@ ReactiveNativeQueryImplementor addJoin(

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveNativeQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveNativeQueryImplementor setProperties(Object bean); 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 92d00b305..0794ae435 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 @@ -13,13 +13,13 @@ import org.hibernate.CacheMode; import org.hibernate.FlushMode; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.SqmQuery; import org.hibernate.reactive.query.ReactiveSelectionQuery; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; /** * @see org.hibernate.query.sqm.SqmSelectionQuery @@ -33,7 +33,7 @@ public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery,

    ReactiveSqmSelectionQuery setParameter(String name, P value, Class

    type); @Override -

    ReactiveSqmSelectionQuery setParameter(String name, P value, BindableType

    type); +

    ReactiveSqmSelectionQuery setParameter(String name, P value, Type

    type); @Override ReactiveSqmSelectionQuery setParameter(String name, Instant value, TemporalType temporalType); @@ -51,7 +51,7 @@ public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery,

    ReactiveSqmSelectionQuery setParameter(int position, P value, Class

    type); @Override -

    ReactiveSqmSelectionQuery setParameter(int position, P value, BindableType

    type); +

    ReactiveSqmSelectionQuery setParameter(int position, P value, Type

    type); @Override ReactiveSqmSelectionQuery setParameter(int position, Instant value, TemporalType temporalType); @@ -69,7 +69,7 @@ public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery,

    ReactiveSqmSelectionQuery setParameter(QueryParameter

    parameter, P value, Class

    type); @Override -

    ReactiveSqmSelectionQuery setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveSqmSelectionQuery setParameter(QueryParameter

    parameter, P val, Type

    type); @Override ReactiveSqmSelectionQuery setParameter(Parameter param, T value); @@ -90,7 +90,7 @@ public interface ReactiveSqmSelectionQuery extends ReactiveSelectionQuery,

    ReactiveSqmSelectionQuery setParameterList( String name, Collection values, - BindableType

    type); + Type

    type); @Override ReactiveSqmSelectionQuery setParameterList(String name, Object[] values); @@ -99,7 +99,7 @@

    ReactiveSqmSelectionQuery setParameterList(

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

    javaType); @Override -

    ReactiveSqmSelectionQuery setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveSqmSelectionQuery setParameterList(int position, Collection values); @@ -111,7 +111,7 @@

    ReactiveSqmSelectionQuery setParameterList(

    ReactiveSqmSelectionQuery setParameterList( int position, Collection values, - BindableType

    type); + Type

    type); @Override ReactiveSqmSelectionQuery setParameterList(int position, Object[] values); @@ -120,7 +120,7 @@

    ReactiveSqmSelectionQuery setParameterList(

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

    javaType); @Override -

    ReactiveSqmSelectionQuery setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override

    ReactiveSqmSelectionQuery setParameterList(QueryParameter

    parameter, Collection values); @@ -135,7 +135,7 @@

    ReactiveSqmSelectionQuery setParameterList(

    ReactiveSqmSelectionQuery setParameterList( QueryParameter

    parameter, Collection values, - BindableType

    type); + Type

    type); @Override

    ReactiveSqmSelectionQuery setParameterList(QueryParameter

    parameter, P[] values); @@ -144,7 +144,7 @@

    ReactiveSqmSelectionQuery setParameterList(

    ReactiveSqmSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveSqmSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveSqmSelectionQuery setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveSqmSelectionQuery setProperties(Object bean); 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 61f0d39c6..5242ab747 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 @@ -32,7 +32,6 @@ import org.hibernate.id.enhanced.Optimizer; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.BindableType; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; @@ -69,6 +68,7 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; /** * A reactive {@link QuerySqmImpl} @@ -629,7 +629,7 @@ public

    ReactiveQuerySqmImpl setParameter(String name, P value, Class

    j } @Override - public

    ReactiveQuerySqmImpl setParameter(String name, P value, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameter(String name, P value, Type

    type) { super.setParameter( name, value, type ); return this; } @@ -653,7 +653,7 @@ public

    ReactiveQuerySqmImpl setParameter(int position, P value, Class

    } @Override - public

    ReactiveQuerySqmImpl setParameter(int position, P value, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameter(int position, P value, Type

    type) { super.setParameter( position, value, type ); return this; } @@ -677,7 +677,7 @@ public

    ReactiveQuerySqmImpl setParameter(QueryParameter

    parameter, P v } @Override - public

    ReactiveQuerySqmImpl setParameter(QueryParameter

    parameter, P value, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameter(QueryParameter

    parameter, P value, Type

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

    ReactiveQuerySqmImpl setParameterList(String name, Collection ReactiveQuerySqmImpl setParameterList(String name, Collection values, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameterList(String name, Collection values, Type

    type) { super.setParameterList( name, values, type ); return this; } @@ -755,7 +755,7 @@ public

    ReactiveQuerySqmImpl setParameterList(String name, P[] values, Cla } @Override - public

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

    type) { + public

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

    type) { super.setParameterList( name, values, type ); return this; } @@ -773,7 +773,7 @@ public

    ReactiveQuerySqmImpl setParameterList(int position, Collection ReactiveQuerySqmImpl setParameterList(int position, Collection values, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameterList(int position, Collection values, Type

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

    ReactiveQuerySqmImpl setParameterList(int position, P[] values, Cl } @Override - public

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

    type) { + public

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

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

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, } @Override - public

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, Collection values, Type

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

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, } @Override - public

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type) { + public

    ReactiveQuerySqmImpl setParameterList(QueryParameter

    parameter, P[] values, Type

    type) { super.setParameterList( parameter, values, type ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java index 9249b0544..26d6dadfe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java @@ -14,7 +14,6 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.util.collections.IdentitySet; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryLogging; import org.hibernate.query.QueryParameter; import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; @@ -35,6 +34,8 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; + import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; @@ -342,7 +343,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameter(String name, P value, C } @Override - public

    ReactiveSqmSelectionQueryImpl setParameter(String name, P value, BindableType

    type) { + public

    ReactiveSqmSelectionQueryImpl setParameter(String name, P value, Type

    type) { super.setParameter( name, value, type ); return this; } @@ -366,7 +367,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameter(int position, P value, } @Override - public

    ReactiveSqmSelectionQueryImpl setParameter(int position, P value, BindableType

    type) { + public

    ReactiveSqmSelectionQueryImpl setParameter(int position, P value, Type

    type) { super.setParameter( position, value, type ); return this; } @@ -393,7 +394,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameter(QueryParameter

    param public

    ReactiveSqmSelectionQueryImpl setParameter( QueryParameter

    parameter, P value, - BindableType

    type) { + Type

    type) { super.setParameter( parameter, value, type ); return this; } @@ -462,7 +463,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList( public

    ReactiveSqmSelectionQueryImpl setParameterList( String name, Collection values, - BindableType

    type) { + Type

    type) { super.setParameterList( name, values, type ); return this; } @@ -480,7 +481,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList(String name, P[] va } @Override - public

    ReactiveSqmSelectionQueryImpl setParameterList(String name, P[] values, BindableType

    type) { + public

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

    type) { super.setParameterList( name, values, type ); return this; } @@ -504,7 +505,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList( public

    ReactiveSqmSelectionQueryImpl setParameterList( int position, Collection values, - BindableType

    type) { + Type

    type) { super.setParameterList( position, values, type ); return this; } @@ -522,7 +523,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList(int position, P[] v } @Override - public

    ReactiveSqmSelectionQueryImpl setParameterList(int position, P[] values, BindableType

    type) { + public

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

    type) { super.setParameterList( position, values, type ); return this; } @@ -548,7 +549,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList( public

    ReactiveSqmSelectionQueryImpl setParameterList( QueryParameter

    parameter, Collection values, - BindableType

    type) { + Type

    type) { super.setParameterList( parameter, values, type ); return this; } @@ -572,7 +573,7 @@ public

    ReactiveSqmSelectionQueryImpl setParameterList( public

    ReactiveSqmSelectionQueryImpl setParameterList( QueryParameter

    parameter, P[] values, - BindableType

    type) { + Type

    type) { super.setParameterList( parameter, values, type ); return this; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSqmQueryImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSqmQueryImplementor.java index 090ab4748..dd19efc21 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSqmQueryImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveSqmQueryImplementor.java @@ -16,7 +16,6 @@ import org.hibernate.Incubating; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.query.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; @@ -28,6 +27,7 @@ import jakarta.persistence.LockModeType; import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; +import jakarta.persistence.metamodel.Type; /** @@ -101,7 +101,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameter(String name, P value, Class

    type); @Override -

    ReactiveSqmQueryImplementor setParameter(String name, P value, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameter(String name, P value, Type

    type); @Override ReactiveSqmQueryImplementor setParameter(String name, Instant value, TemporalType temporalType); @@ -119,7 +119,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameter(int position, P value, Class

    type); @Override -

    ReactiveSqmQueryImplementor setParameter(int position, P value, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameter(int position, P value, Type

    type); @Override ReactiveSqmQueryImplementor setParameter(int position, Instant value, TemporalType temporalType); @@ -137,7 +137,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameter(QueryParameter

    parameter, P value, Class

    type); @Override -

    ReactiveSqmQueryImplementor setParameter(QueryParameter

    parameter, P val, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameter(QueryParameter

    parameter, P val, Type

    type); @Override ReactiveSqmQueryImplementor setParameter(Parameter param, T value); @@ -155,7 +155,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameterList(String name, Collection values, Class

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(String name, Collection values, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameterList(String name, Collection values, Type

    type); @Override ReactiveSqmQueryImplementor setParameterList(String name, Object[] values); @@ -164,7 +164,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

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

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(String name, P[] values, BindableType

    type); +

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

    type); @Override ReactiveSqmQueryImplementor setParameterList(int position, @SuppressWarnings("rawtypes") Collection values); @@ -173,7 +173,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameterList(int position, Collection values, Class

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(int position, Collection values, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameterList(int position, Collection values, Type

    type); @Override ReactiveSqmQueryImplementor setParameterList(int position, Object[] values); @@ -182,7 +182,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

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

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(int position, P[] values, BindableType

    type); +

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

    type); @Override

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, Collection values); @@ -191,7 +191,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, Class

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, Collection values, Type

    type); @Override

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, P[] values); @@ -200,7 +200,7 @@ public interface ReactiveSqmQueryImplementor extends ReactiveQueryImplementor

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Class

    javaType); @Override -

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, BindableType

    type); +

    ReactiveSqmQueryImplementor setParameterList(QueryParameter

    parameter, P[] values, Type

    type); @Override ReactiveSqmQueryImplementor setProperties(Object bean); 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 d86ffd7d1..264279c90 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 @@ -80,6 +80,7 @@ import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSelectClause; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.reactive.common.AffectedEntities; @@ -311,9 +312,8 @@ public CompletionStage reactiveFetch(T association, boolean unproxy) { if ( association == null ) { return nullFuture(); } - - if ( association instanceof HibernateProxy proxy ) { - LazyInitializer initializer = proxy.getHibernateLazyInitializer(); + else if ( association instanceof HibernateProxy proxy ) { + final LazyInitializer initializer = proxy.getHibernateLazyInitializer(); if ( !initializer.isUninitialized() ) { return completedFuture( unproxy ? (T) initializer.getImplementation() : association ); } @@ -371,9 +371,10 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) final SqmSelectStatement selectStatement = (SqmSelectStatement) criteriaQuery; if ( ! ( selectStatement.getQueryPart() instanceof SqmQueryGroup ) ) { final SqmQuerySpec querySpec = selectStatement.getQuerySpec(); - if ( querySpec.getSelectClause().getSelections().isEmpty() ) { + final SqmSelectClause selectClause = querySpec.getSelectClause(); + if ( selectClause.getSelections().isEmpty() ) { if ( querySpec.getFromClause().getRoots().size() == 1 ) { - querySpec.getSelectClause().setSelection( querySpec.getFromClause().getRoots().get(0) ); + selectClause.setSelection( querySpec.getFromClause().getRoots().get(0) ); } } } @@ -401,24 +402,27 @@ public ReactiveQuery createReactiveQuery(TypedQueryReference typedQuer final CriteriaQuery query = specification.buildCriteria( getCriteriaBuilder() ); return new ReactiveQuerySqmImpl<>( (SqmStatement) query, specification.getResultType(), this ); } - if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { + else if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); // Workaround for ORM, can be remove when this issue is solved: https://hibernate.atlassian.net/browse/HHH-19386 - Class type = specification.getResultType() == Void.class - ? null - : (Class) specification.getResultType(); + final Class type = + specification.getResultType() == Void.class + ? null + : (Class) specification.getResultType(); return new ReactiveQuerySqmImpl<>( (SqmStatement) query, type, this ); } - @SuppressWarnings("unchecked") - // this cast is fine because of all our impls of TypedQueryReference return Class - final Class resultType = (Class) typedQueryReference.getResultType(); - ReactiveQueryImplementor query = (ReactiveQueryImplementor) buildNamedQuery( - typedQueryReference.getName(), - memento -> createSqmQueryImplementor( resultType, memento ), - memento -> createNativeQueryImplementor( resultType, memento ) - ); - typedQueryReference.getHints().forEach( query::setHint ); - return query; + else { + @SuppressWarnings("unchecked") + // this cast is fine because of all our impls of TypedQueryReference return Class + final Class resultType = (Class) typedQueryReference.getResultType(); + final ReactiveQueryImplementor query = (ReactiveQueryImplementor) buildNamedQuery( + typedQueryReference.getName(), + memento -> createSqmQueryImplementor(resultType, memento), + memento -> createNativeQueryImplementor(resultType, memento) + ); + typedQueryReference.getHints().forEach(query::setHint); + return query; + } } @Override From 5d1de605f8c77be73284715460e4853b5e3e3acd Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 14 May 2025 10:45:02 +0200 Subject: [PATCH 168/201] add some missing @Override annotations --- .../reactive/query/ReactiveQuery.java | 30 +++++++++++++++++++ .../query/ReactiveSelectionQuery.java | 1 + 2 files changed, 31 insertions(+) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java index 424db8b35..9c0fc5de8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveQuery.java @@ -33,10 +33,13 @@ * @see org.hibernate.query.Query */ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMutationQuery { + @Override String getQueryString(); + @Override String getComment(); + @Override ReactiveQuery setComment(String comment); ReactiveQuery addQueryHint(String hint); @@ -55,6 +58,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut QueryOptions getQueryOptions(); + @Override ParameterMetadata getParameterMetadata(); @Override @@ -98,18 +102,21 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @see #setParameter(int, Object, Type) */ + @Override

    ReactiveQuery setParameter(int parameter, P argument, Class

    type); /** * Bind the given argument to an ordinal query parameter using the given * {@link Type}. */ + @Override

    ReactiveQuery setParameter(int parameter, P argument, Type

    type); /** * Bind an {@link Instant} value to the ordinal query parameter using * just the portion indicated by the given {@link TemporalType}. */ + @Override ReactiveQuery setParameter(int parameter, Instant argument, TemporalType temporalType); /** @@ -124,10 +131,13 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut @Override ReactiveQuery setParameter(int parameter, Calendar argument, TemporalType temporalType); + @Override ReactiveQuery setParameter(QueryParameter parameter, T argument); + @Override

    ReactiveQuery setParameter(QueryParameter

    parameter, P argument, Class

    type); + @Override

    ReactiveQuery setParameter(QueryParameter

    parameter, P argument, Type

    type); @Override @@ -139,8 +149,10 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut @Override ReactiveQuery setParameter(Parameter parameter, Date argument, TemporalType temporalType); + @Override ReactiveQuery setParameterList(String parameter, @SuppressWarnings("rawtypes") Collection arguments); + @Override

    ReactiveQuery setParameterList(String parameter, Collection arguments, Class

    javaType); /** @@ -152,6 +164,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(String parameter, Collection arguments, Type

    type); /** @@ -165,6 +178,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override ReactiveQuery setParameterList(String parameter, Object[] values); /** @@ -180,6 +194,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(String parameter, P[] arguments, Class

    javaType); @@ -192,6 +207,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(String parameter, P[] arguments, Type

    type); /** @@ -205,6 +221,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override ReactiveQuery setParameterList(int parameter, @SuppressWarnings("rawtypes") Collection arguments); /** @@ -220,6 +237,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(int parameter, Collection arguments, Class

    javaType); /** @@ -231,6 +249,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(int parameter, Collection arguments, Type

    type); /** @@ -244,6 +263,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override ReactiveQuery setParameterList(int parameter, Object[] arguments); /** @@ -259,6 +279,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(int parameter, P[] arguments, Class

    javaType); /** @@ -270,6 +291,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(int parameter, P[] arguments, Type

    type); /** @@ -284,6 +306,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, Collection arguments); /** @@ -300,6 +323,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, Collection arguments, Class

    javaType); /** @@ -314,6 +338,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, Collection arguments, Type

    type); /** @@ -329,6 +354,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, P[] arguments); /** @@ -345,6 +371,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, P[] arguments, Class

    javaType); /** @@ -359,6 +386,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override

    ReactiveQuery setParameterList(QueryParameter

    parameter, P[] arguments, Type

    type); /** @@ -370,6 +398,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override ReactiveQuery setProperties(Object bean); /** @@ -381,6 +410,7 @@ public interface ReactiveQuery extends ReactiveSelectionQuery, ReactiveMut * * @return {@code this}, for method chaining */ + @Override ReactiveQuery setProperties(@SuppressWarnings("rawtypes") Map bean); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java index 4fe138bca..32aa72337 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java @@ -53,6 +53,7 @@ default CompletionStage> getReactiveResultList() { CompletionStage> reactiveUniqueResultOptional(); + @Override ReactiveSelectionQuery setHint(String hintName, Object value); // Covariant methods From dd33a58b4decad02984cf75944a8bd96ab5ae99b Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 14 May 2025 09:38:50 +0000 Subject: [PATCH 169/201] Update project version to : `3.0.0.CR2` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..a3e418b9c 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.CR2 \ No newline at end of file From 0c60e3ec3909af8efee3fdaf033b5ef6353c26a4 Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 14 May 2025 09:39:49 +0000 Subject: [PATCH 170/201] Update project version to : `3.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 a3e418b9c..603432f4e 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0.CR2 \ No newline at end of file +projectVersion=3.0.0-SNAPSHOT \ No newline at end of file From 8a2bbe5dcf3c18df16dc2b9690803eb5faf3f85a Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 14 May 2025 17:15:29 +0200 Subject: [PATCH 171/201] [#2227] Remove workaround for HHH-19386 The method getResultType doesn't return Void.class anymore. See https://hibernate.atlassian.net/browse/HHH-19386 --- .../reactive/session/impl/ReactiveSessionImpl.java | 7 +------ .../session/impl/ReactiveStatelessSessionImpl.java | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) 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 264279c90..d03822be6 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 @@ -404,12 +404,7 @@ public ReactiveQuery createReactiveQuery(TypedQueryReference typedQuer } else if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); - // Workaround for ORM, can be remove when this issue is solved: https://hibernate.atlassian.net/browse/HHH-19386 - final Class type = - specification.getResultType() == Void.class - ? null - : (Class) specification.getResultType(); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, type, this ); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); } else { @SuppressWarnings("unchecked") 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 be9dc87b0..1c8be13e6 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 @@ -942,11 +942,7 @@ public ReactiveQuery createReactiveQuery(TypedQueryReference typedQuer } if ( typedQueryReference instanceof MutationSpecificationImpl specification ) { final CommonAbstractCriteria query = specification.buildCriteria( getCriteriaBuilder() ); - // Workaround for ORM, can be remove when this issue is solved: https://hibernate.atlassian.net/browse/HHH-19386 - Class type = (Class) specification.getResultType() == Void.class - ? null - : (Class) specification.getResultType(); - return new ReactiveQuerySqmImpl<>( (SqmStatement) query, type, this ); + return new ReactiveQuerySqmImpl<>( (SqmStatement) query, (Class) specification.getResultType(), this ); } @SuppressWarnings("unchecked") // this cast is fine because of all our impls of TypedQueryReference return Class From d7f9ed9cc1e8fc4c132b329fbc6cffa27c3af359 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 15 May 2025 12:30:26 +0200 Subject: [PATCH 172/201] very minor javadoc fixes --- .../org/hibernate/reactive/mutiny/Mutiny.java | 78 ++++++++++--------- .../impl/MutinyStatelessSessionImpl.java | 13 +++- .../org/hibernate/reactive/stage/Stage.java | 61 ++++++++------- 3 files changed, 86 insertions(+), 66 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 71048e5b9..bee3a5c1f 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -2030,7 +2030,7 @@ interface SessionFactory extends AutoCloseable { /** - * Perform work using a {@link Session reactive session}. + * Perform work using a {@linkplain Session reactive session}. *

    * *

  1. If there is already a session associated with the current @@ -2049,7 +2049,7 @@ interface SessionFactory extends AutoCloseable { Uni withSession(Function> work); /** - * Perform work using a {@link Session reactive session} for + * Perform work using a {@linkplain Session reactive session} for * a specified tenant. *

    * @@ -2069,12 +2069,12 @@ interface SessionFactory extends AutoCloseable { Uni withSession(String tenantId, Function> work); /** - * Perform work using a {@link Session reactive session} within an - * associated {@link Transaction transaction}. + * Perform work using a {@linkplain Session reactive session} + * within an associated {@link Transaction transaction}. *

    * - *

  2. If there is already a session associated with the - * current reactive stream, then the work will be executed using that + *
  3. If there is already a session associated with the current + * reactive stream, then the work will be executed using that * session. *
  4. Otherwise, if there is no session associated with the * current stream, a new session will be created. @@ -2092,12 +2092,12 @@ interface SessionFactory extends AutoCloseable { Uni withTransaction(BiFunction> work); /** - * Perform work using a {@link Session reactive session} within an - * associated transaction. + * Perform work using a {@linkplain Session reactive session} + * within an associated transaction. *

    * - *

  5. If there is already a session associated with the - * current reactive stream, then the work will be executed using that + *
  6. If there is already a session associated with the current + * reactive stream, then the work will be executed using that * session. *
  7. Otherwise, if there is no session associated with the * current stream, a new session will be created. @@ -2117,8 +2117,8 @@ default Uni withTransaction(Function> work) { } /** - * Perform work using a {@link StatelessSession reactive session} within an - * associated {@link Transaction transaction}. + * Perform work using a {@linkplain StatelessSession reactive session} + * within an associated {@link Transaction transaction}. *

    * *

  8. If there is already a stateless session associated with the @@ -2128,10 +2128,11 @@ default Uni withTransaction(Function> work) { * current stream, a new stateless session will be created. * *

    - * The session will be closed automatically and the transaction committed automatically. + * The session will be closed automatically and the transaction committed + * automatically. * - * @param work a function which accepts the stateless session and returns - * the result of the work as a {@link Uni}. + * @param work a function which accepts the stateless session and + * returns the result of the work as a {@link Uni}. * * @see #withStatelessSession(Function) * @see StatelessSession#withTransaction(Function) @@ -2141,8 +2142,8 @@ default Uni withStatelessTransaction(Function> w } /** - * Perform work using a {@link StatelessSession reactive session} within an - * associated {@link Transaction transaction}. + * Perform work using a {@linkplain StatelessSession reactive session} + * within an associated {@link Transaction transaction}. *

    * *

  9. If there is already a stateless session associated with the @@ -2152,7 +2153,8 @@ default Uni withStatelessTransaction(Function> w * current stream, a new stateless session will be created. * *

    - * The session will be closed automatically and the transaction committed automatically. + * The session will be closed automatically and the transaction committed + * automatically. * * @param work a function which accepts the stateless session and returns * the result of the work as a {@link Uni}. @@ -2163,7 +2165,7 @@ default Uni withStatelessTransaction(Function> w Uni withStatelessTransaction(BiFunction> work); /** - * Perform work using a {@link StatelessSession stateless session}. + * Perform work using a {@linkplain StatelessSession stateless session}. *

    * *

  10. If there is already a stateless session associated with the @@ -2181,14 +2183,15 @@ default Uni withStatelessTransaction(Function> w Uni withStatelessSession(Function> work); /** - * Perform work using a {@link StatelessSession stateless session}. + * Perform work using a {@linkplain StatelessSession stateless session}. *

    * *

  11. If there is already a stateless session associated with the - * current reactive stream and given tenant id, then the work will be executed using that - * session. + * current reactive stream and given tenant id, then the work will be + * executed using that session. *
  12. Otherwise, if there is no stateless session associated with the - * current stream and given tenant id, a new stateless session will be created. + * current stream and given tenant id, a new stateless session will be + * created. * *

    * The session will be closed automatically. @@ -2200,15 +2203,17 @@ default Uni withStatelessTransaction(Function> w Uni withStatelessSession(String tenantId, Function> work); /** - * Perform work using a {@link Session reactive session} for a - * specified tenant within an associated {@link Transaction transaction}. + * Perform work using a {@linkplain Session reactive session} for + * the tenant with the specified tenant id within an associated + * {@link Transaction transaction}. *

    * - *

  13. If there is already a session associated with the - * current reactive stream and given tenant id, then the work will be executed using that - * session. + *
  14. If there is already a session associated with the current + * reactive stream and given tenant id, then the work will be + * executed using that session. *
  15. Otherwise, if there is no session associated with the - * current stream and given tenant id, a new stateless session will be created. + * current stream and given tenant id, a new stateless session + * will be created. * *

    * The session will be {@link Session#flush() flushed} and closed @@ -2224,18 +2229,21 @@ default Uni withStatelessTransaction(Function> w Uni withTransaction(String tenantId, BiFunction> work); /** - * Perform work using a {@link StatelessSession reactive session} for a - * specified tenant within an associated {@link Transaction transaction}. + * Perform work using a {@linkplain StatelessSession reactive session} + * for the tenant with the specified tenant id within an associated + * {@link Transaction transaction}. *

    * *

  16. If there is already a stateless session associated with the - * current reactive stream and given tenant id, then the work will be executed using that - * session. + * current reactive stream and given tenant id, then the work will be + * executed using that session. *
  17. Otherwise, if there is no stateless session associated with the - * current stream and given tenant id, a new stateless session will be created. + * current stream and given tenant id, a new stateless session will be + * created. * *

    - * The session will be closed automatically and the transaction committed automatically. + * The session will be closed automatically and the transaction committed + * automatically. * * @param tenantId the id of the tenant * @param work a function which accepts the stateless session and returns diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java index 21602c48f..1f3600deb 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java @@ -300,6 +300,10 @@ public Mutiny.Transaction currentTransaction() { private class Transaction implements Mutiny.Transaction { boolean rollback; + /** + * Execute the given work in a new transaction. Called only + * when no existing transaction was active. + */ Uni execute(Function> work) { currentTransaction = this; return begin() @@ -308,14 +312,15 @@ Uni execute(Function> work) { } /** - * Run the code assuming that a transaction has already started so that we can - * differentiate an error starting a transaction (and therefore doesn't need to rollback) - * and an error thrown by the work. + * Run the code assuming that a transaction has already started + * so that we can differentiate an error starting a transaction + * (which therefore does not need to trigger rollback) from an + * error thrown by the work (which does). */ Uni executeInTransaction(Function> work) { return Uni.createFrom().deferred( () -> work.apply( this ) ) // in the case of an exception or cancellation - // we need to rollback the transaction + // we need to roll back the transaction .onFailure().call( this::rollback ) .onCancellation().call( this::rollback ) // finally, when there was no exception, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index a97b05e56..dccb3a072 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -1980,7 +1980,7 @@ interface Transaction { interface SessionFactory extends AutoCloseable { /** - * Obtain a new {@link Session reactive session} {@link CompletionStage}, the main + * Obtain a new {@linkplain Session reactive session} {@link CompletionStage}, the main * interaction point between the user's program and Hibernate * Reactive. *

    @@ -1994,7 +1994,7 @@ interface SessionFactory extends AutoCloseable { CompletionStage openSession(); /** - * Obtain a new {@link Session reactive session} {@link CompletionStage} for a + * Obtain a new {@linkplain Session reactive session} {@link CompletionStage} for a * specified tenant. *

    * When the {@link CompletionStage} completes successfully it returns a newly created session. @@ -2033,7 +2033,7 @@ interface SessionFactory extends AutoCloseable { CompletionStage openStatelessSession(String tenantId); /** - * Perform work using a {@link Session reactive session}. + * Perform work using a {@linkplain Session reactive session}. *

    * *

  18. If there is already a session associated with the current @@ -2052,7 +2052,7 @@ interface SessionFactory extends AutoCloseable { CompletionStage withSession(Function> work); /** - * Perform work using a {@link Session reactive session} for a + * Perform work using a {@linkplain Session reactive session} for a * specified tenant. *

    * @@ -2072,7 +2072,7 @@ interface SessionFactory extends AutoCloseable { CompletionStage withSession(String tenantId, Function> work); /** - * Perform work using a {@link Session reactive session} within an + * Perform work using a {@linkplain Session reactive session} within an * associated {@link Transaction transaction}. *

    * @@ -2096,7 +2096,7 @@ interface SessionFactory extends AutoCloseable { CompletionStage withTransaction(BiFunction> work); /** - * Perform work using a {@link Session reactive session} within an + * Perform work using a {@linkplain Session reactive session} within an * associated transaction. *

    * @@ -2121,15 +2121,17 @@ default CompletionStage withTransaction(Function * - *

  19. If there is already a session associated with the - * current reactive stream and the given tenant, then the work will be executed using that - * session. - *
  20. Otherwise, if there is no stateless session associated with the - * current stream and the given tenant, a new stateless session will be created. + *
  21. If there is already a session associated with the current + * reactive stream and given tenant id, then the work will be + * executed using that session. + *
  22. Otherwise, if there is no stateless session associated with + * the current stream and given tenant id, a new stateless session + * will be created. * *

    * The session will be {@link Session#flush() flushed} and closed @@ -2145,8 +2147,8 @@ default CompletionStage withTransaction(Function CompletionStage withTransaction(String tenantId, BiFunction> work); /** - * Perform work using a {@link StatelessSession reactive session} within an - * associated {@link Transaction transaction}. + * Perform work using a {@linkplain StatelessSession reactive session} + * within an associated {@link Transaction transaction}. *

    * *

  23. If there is already a stateless session associated with the @@ -2156,7 +2158,8 @@ default CompletionStage withTransaction(Function *

    - * The session will be closed automatically, and the transaction committed automatically. + * The session will be closed automatically, and the transaction committed + * automatically. * * @param work a function which accepts the stateless session and returns * the result of the work as a {@link CompletionStage}. @@ -2169,8 +2172,8 @@ default CompletionStage withStatelessTransaction(Function * *

  24. If there is already a stateless session associated with the @@ -2180,7 +2183,8 @@ default CompletionStage withStatelessTransaction(Function *

    - * The session will be closed automatically, and the transaction committed automatically. + * The session will be closed automatically, and the transaction committed + * automatically. * * @param work a function which accepts the stateless session and returns * the result of the work as a {@link CompletionStage}. @@ -2191,18 +2195,21 @@ default CompletionStage withStatelessTransaction(Function CompletionStage withStatelessTransaction(BiFunction> work); /** - * Perform work using a {@link StatelessSession reactive session} within an - * associated {@link Transaction transaction}. + * Perform work using a {@linkplain StatelessSession reactive session} + * for the tenant with the specified tenant id within an associated + * {@link Transaction transaction}. *

    * *

  25. If there is already a stateless session associated with the - * current reactive stream and the given tenant, then the work will be executed using that - * session. + * current reactive stream and given tenant id, then the work will be + * executed using that session. *
  26. Otherwise, if there is no stateless session associated with the - * current stream, a new stateless session will be created. + * current stream and given tenant id, a new stateless session will be + * created. * *

    - * The session will be closed automatically, and the transaction committed automatically. + * The session will be closed automatically and the transaction committed + * automatically. * * @param tenantId the id of the tenant * @param work a function which accepts the stateless session and returns @@ -2214,7 +2221,7 @@ default CompletionStage withStatelessTransaction(Function CompletionStage withStatelessTransaction(String tenantId, BiFunction> work); /** - * Perform work using a {@link StatelessSession stateless session}. + * Perform work using a {@linkplain StatelessSession stateless session}. *

    * *

  27. If there is already a stateless session associated with the @@ -2232,7 +2239,7 @@ default CompletionStage withStatelessTransaction(Function CompletionStage withStatelessSession(Function> work); /** - * Perform work using a {@link StatelessSession stateless session}. + * Perform work using a {@linkplain StatelessSession stateless session}. *

    * *

  28. If there is already a stateless session associated with the From 6ccdf1b0ffb13633d1334ec18606c03881864c05 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 20 May 2025 10:22:24 +0200 Subject: [PATCH 173/201] [#2259] Upgrade Hibernate ORM to 7.0.0.Final - Replace getJpaMetamodel() with getSessionFactoryOptions() --- README.md | 2 +- gradle.properties | 4 ++-- .../ast/internal/ReactiveAbstractMultiIdEntityLoader.java | 3 +++ .../ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java | 4 ++-- .../ast/internal/ReactiveMultiIdEntityLoaderStandard.java | 4 ++-- .../hibernate/reactive/session/impl/ReactiveSessionImpl.java | 2 +- .../reactive/session/impl/ReactiveStatelessSessionImpl.java | 2 +- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 509706206..73fb60f72 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.CR1 +- [Hibernate ORM][] 7.0.0.Final - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.14 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.14 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.14 diff --git a/gradle.properties b/gradle.properties index 3da2482ba..78784fc69 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.0.CR2 +hibernateOrmVersion = 7.0.0.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.CR1 +#hibernateOrmGradlePluginVersion = 7.0.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/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java index 71ae6e7e7..9b7c16395 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java @@ -62,4 +62,7 @@ public final CompletionStage> reactiveLoad(K[] ids, MultiIdLoadOptio protected abstract CompletionStage> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session); + protected boolean isIdCoercionEnabled() { + return !getSessionFactory().getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled(); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java index 612e5ad16..ef81c828e 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderArrayParam.java @@ -90,7 +90,7 @@ protected CompletionStage> performOrderedMultiLoad( ); } - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final boolean coerce = isIdCoercionEnabled(); final LockOptions lockOptions = ( loadOptions.getLockOptions() == null ) ? new LockOptions( LockMode.NONE ) : loadOptions.getLockOptions(); @@ -312,7 +312,7 @@ protected final K[] processResolvableEntities( return ids; } - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final boolean coerce = isIdCoercionEnabled(); boolean foundAnyResolvedEntities = false; List nonResolvedIds = null; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java index 743078403..5661c3493 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveMultiIdEntityLoaderStandard.java @@ -103,7 +103,7 @@ protected CompletionStage> performOrderedMultiLoad( final List idsInBatch = new ArrayList<>(); final List elementPositionsLoadedByBatch = new ArrayList<>(); - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final boolean coerce = isIdCoercionEnabled(); return loop( 0, ids.length, i -> { final Object id = coerce ? getEntityDescriptor().getIdentifierMapping().getJavaType().coerce( ids[i], session ) @@ -311,7 +311,7 @@ protected CompletionStage> performUnorderedMultiLoad( boolean foundAnyManagedEntities = false; final List nonManagedIds = new ArrayList<>(); - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final boolean coerce = isIdCoercionEnabled(); for ( Object o : ids ) { final Object id = coerce ? getEntityDescriptor().getIdentifierMapping().getJavaType().coerce( o, session ) 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 d03822be6..795b565ff 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 @@ -382,7 +382,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) return createReactiveCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); } catch (RuntimeException e) { - if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { + if ( getSessionFactory().getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { markForRollbackOnly(); } throw getExceptionConverter().convert( e ); 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 1c8be13e6..d67cc2018 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 @@ -979,7 +979,7 @@ public ReactiveQuery createReactiveQuery(CriteriaQuery criteriaQuery) return createReactiveCriteriaQuery( selectStatement, criteriaQuery.getResultType() ); } catch (RuntimeException e) { - if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { + if ( getSessionFactory().getSessionFactoryOptions().getJpaCompliance().isJpaTransactionComplianceEnabled() ) { markForRollbackOnly(); } throw getExceptionConverter().convert( e ); From 0abb15c10edb9969c4b6b5a9319859e49bcc13ad Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 20 Feb 2025 16:00:11 +0100 Subject: [PATCH 174/201] chore: remove some warnings from tests Replace assertions --- .../JoinedSubclassInheritanceTest.java | 87 ++++++++++--------- 1 file changed, 46 insertions(+), 41 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 2b0e3f9c6..ab64e1925 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,11 +5,6 @@ */ package org.hibernate.reactive; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Objects; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -26,12 +21,13 @@ import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Objects; import static java.util.concurrent.TimeUnit.MINUTES; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @Timeout(value = 10, timeUnit = MINUTES) @@ -56,9 +52,8 @@ public void testRootClassViaAssociation(VertxTestContext context) { .thenCompose( v -> openSession() ) .thenCompose( s2 -> s2.find( Author.class, author.getId() ) ) .thenAccept( auth -> { - assertNotNull( auth ); - assertEquals( author, auth ); - assertEquals( book.getTitle(), auth.getBook().getTitle() ); + assertThat( auth ).isEqualTo( author ); + assertThat( auth.getBook().getTitle() ).isEqualTo( book.getTitle() ); } ) ) ); @@ -77,9 +72,8 @@ public void testSubclassViaAssociation(VertxTestContext context) { .thenCompose( v -> s.flush() ) .thenCompose( v -> s.find( Author.class, author.getId() ) ) .thenAccept( auth -> { - assertNotNull( auth ); - assertEquals( author, auth ); - assertEquals( book.getTitle(), auth.getBook().getTitle() ); + assertThat( auth ).isEqualTo( author ); + assertThat( auth.getBook().getTitle() ).isEqualTo( book.getTitle() ); } ) ) ); @@ -87,7 +81,6 @@ public void testSubclassViaAssociation(VertxTestContext context) { @Test public void testRootClassViaFind(VertxTestContext context) { - final Book novel = new Book( 6, "The Boy, The Mole, The Fox and The Horse", new Date()); final Author author = new Author( "Charlie Mackesy", novel ); @@ -99,9 +92,8 @@ public void testRootClassViaFind(VertxTestContext context) { .thenCompose( v -> openSession() .thenCompose( s -> s.find(Book.class, 6) ) ) .thenAccept(book -> { - assertNotNull(book); - assertFalse(book instanceof SpellBook); - assertEquals(book.getTitle(), "The Boy, The Mole, The Fox and The Horse"); + assertThat( book ).isNotInstanceOf( SpellBook.class ); + assertThat( book.getTitle() ).isEqualTo( "The Boy, The Mole, The Fox and The Horse" ); })); } @@ -117,35 +109,41 @@ public void testSubclassViaFind(VertxTestContext context) { .thenCompose( v -> s.flush() ) ) .thenCompose( v -> openSession().thenCompose( s -> s.find(Book.class, 6) ) ) .thenAccept(book -> { - assertNotNull(book); - assertTrue(book instanceof SpellBook); - assertEquals(book.getTitle(), "Necronomicon"); + assertThat( book ).isInstanceOf( SpellBook.class ); + assertThat( book.getTitle() ).isEqualTo( "Necronomicon" ); })); } @Test public void testQueryUpdate(VertxTestContext context) { final SpellBook spells = new SpellBook( 6, "Necronomicon", true, new Date() ); -// final Author author = new Author( "Abdul Alhazred", spells ); - test( context, + test( + context, openSession() - .thenCompose( s -> s.persist(spells).thenCompose( v -> s.flush() ) ) + .thenCompose( s -> s.persist( spells ).thenCompose( v -> s.flush() ) ) .thenCompose( vv -> openSession() - .thenCompose( s -> s.withTransaction( t -> s.createMutationQuery("update SpellBook set title='x' where forbidden=false").executeUpdate() ) - .thenCompose( v -> s.withTransaction( t -> s.createMutationQuery("update SpellBook set forbidden=false where title='Necronomicon'").executeUpdate() ) ) - .thenCompose( v -> s.withTransaction( t -> s.createMutationQuery("update Book set title=title||' II' where title='Necronomicon'").executeUpdate() ) ) - .thenCompose( v -> s.find(Book.class, 6) ) + .thenCompose( s -> s.withTransaction( t -> s + .createMutationQuery( "update SpellBook set title='x' where forbidden=false" ) + .executeUpdate() ) + .thenCompose( v -> s.withTransaction( t -> s + .createMutationQuery( "update SpellBook set forbidden=false where title='Necronomicon'" ) + .executeUpdate() ) ) + .thenCompose( v -> s.withTransaction( t -> s + .createMutationQuery( "update Book set title=title||' II' where title='Necronomicon'" ) + .executeUpdate() ) ) + .thenCompose( v -> s.find( Book.class, 6 ) ) .thenAccept( book -> { - assertNotNull(book); - assertTrue(book instanceof SpellBook); - assertEquals(book.getTitle(), "Necronomicon II"); + assertThat( book ).isInstanceOf( SpellBook.class ); + assertThat( book.getTitle() ).isEqualTo( "Necronomicon II" ); } ) - ) ) + ) ) .thenCompose( vv -> openSession() - .thenCompose( s -> s.withTransaction( t -> s.createMutationQuery("delete Book where title='Necronomicon II'").executeUpdate() ) ) + .thenCompose( s -> s.withTransaction( t -> s + .createMutationQuery( "delete Book where title='Necronomicon II'" ) + .executeUpdate() ) ) .thenCompose( v -> openSession() ) - .thenCompose( s -> s.find(Book.class, 6) ) + .thenCompose( s -> s.find( Book.class, 6 ) ) .thenAccept( Assertions::assertNull ) ) ); @@ -154,7 +152,6 @@ public void testQueryUpdate(VertxTestContext context) { @Test public void testQueryUpdateWithParameters(VertxTestContext context) { final SpellBook spells = new SpellBook( 6, "Necronomicon", true, new Date() ); -// final Author author = new Author( "Abdul Alhazred", spells ); test( context, openSession() @@ -174,11 +171,9 @@ public void testQueryUpdateWithParameters(VertxTestContext context) { .thenCompose( v -> openSession() .thenCompose( s -> s.find( Book.class, 6) ) .thenAccept( book -> { - assertNotNull(book); - assertTrue(book instanceof SpellBook); - assertEquals(book.getTitle(), "Necronomicon II"); - } - ) + assertThat( book ).isInstanceOf( SpellBook.class ); + assertThat( book.getTitle() ).isEqualTo( "Necronomicon II" ); + } ) ) .thenCompose( v -> openSession() .thenCompose( s -> s.withTransaction( t -> s.createMutationQuery("delete Book where title=:tit") @@ -207,6 +202,11 @@ public SpellBook(Integer id, String title, boolean forbidden, Date published) { public boolean getForbidden() { return forbidden; } + + @Override + public String toString() { + return "SpellBook{" + super.toString() + ",forbidden=" + forbidden + '}'; + } } @Entity(name="Book") @@ -268,6 +268,11 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash( title ); } + + @Override + public String toString() { + return id + ", " + title + ", " + published; + } } @Entity(name = "Author") From f8a13de5b7f64f0fecdda21a29f034d24cb96d30 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 10:03:24 +0200 Subject: [PATCH 175/201] [#2261] Upgrade Vert.x to 4.5.15 (from 4.5.14) --- README.md | 10 +++++----- build.gradle | 2 +- gradle.properties | 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, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 73fb60f72..6047808fb 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.14 -- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.14 -- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.14 -- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.14 -- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.14 +- [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 - [Quarkus][Quarkus] via the Hibernate Reactive extension [PostgreSQL]: https://www.postgresql.org diff --git a/build.gradle b/build.gradle index 4c5235a9a..407bad966 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.14' + vertxSqlClientVersion = '4.5.15' } testcontainersVersion = '1.20.6' diff --git a/gradle.properties b/gradle.properties index 78784fc69..5cd937337 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.14-SNAPSHOT +#vertxSqlClientVersion = 4.5.15-SNAPSHOT # Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion -#vertxWebVersion = 4.5.14 -#vertxWebtClientVersion = 4.5.14 +#vertxWebVersion = 4.5.15 +#vertxWebtClientVersion = 4.5.15 diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 81b79ee5d..adc3a32be 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.14} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 6aeeae23a..201e77e7e 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.14} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index 893b5c80f..d56a86368 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -6,9 +6,9 @@ */ //DEPS com.ongres.scram:client:2.1 -//DEPS io.vertx:vertx-pg-client:${vertx.version:4.5.14} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.14} -//DEPS io.vertx:vertx-db2-client:${vertx.version:4.5.14} +//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.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 682238370..03f8b4892 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.14} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index b252459d1..22489fa8f 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.14} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index 04a0272e8..f0d3cc1af 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.14} -//DEPS io.vertx:vertx-unit:$\{vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index c6b116321..99bc8c1f0 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.14} +//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.14} -//DEPS io.vertx:vertx-mysql-client:${vertx.version:4.5.14} -//DEPS io.vertx:vertx-unit:${vertx.version:4.5.14} +//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.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 From 5f1b5348936baec86a50dcc6db2331dc0b3068ac Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 10:34:04 +0200 Subject: [PATCH 176/201] test: Upgrade Testcontainers to 1.21.0 --- build.gradle | 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/build.gradle b/build.gradle index 407bad966..452f7ea2c 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ ext { vertxSqlClientVersion = '4.5.15' } - testcontainersVersion = '1.20.6' + testcontainersVersion = '1.21.0' logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion } diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index adc3a32be..4154f5ba8 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:cockroachdb:1.20.6 +//DEPS org.testcontainers:cockroachdb:1.21.0 //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 201e77e7e..971180741 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:db2:1.20.6 +//DEPS org.testcontainers:db2:1.21.0 //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 03f8b4892..bc5e7ee27 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mariadb:1.20.6 +//DEPS org.testcontainers:mariadb:1.21.0 //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 22489fa8f..8e9a0c579 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:mysql:1.20.6 +//DEPS org.testcontainers:mysql:1.21.0 //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 f0d3cc1af..efc331af0 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.20.6 +//DEPS org.testcontainers:postgresql:1.21.0 //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 99bc8c1f0..bffc7b05b 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:3.0.0.Beta1} //DEPS org.assertj:assertj-core:3.26.3 //DEPS junit:junit:4.13.2 -//DEPS org.testcontainers:postgresql:1.20.6 -//DEPS org.testcontainers:mysql:1.20.6 -//DEPS org.testcontainers:db2:1.20.6 -//DEPS org.testcontainers:mariadb:1.20.6 -//DEPS org.testcontainers:cockroachdb:1.20.6 +//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 // //// Testcontainer needs the JDBC drivers to start the containers //// Hibernate Reactive doesn't use them From 454f8e50a635cfdff8d228a24b93b1b3bea3e411 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 10:52:17 +0200 Subject: [PATCH 177/201] test: Upgrade AssertJ to 3.27.3 --- hibernate-reactive-core/build.gradle | 2 +- integration-tests/bytecode-enhancements-it/build.gradle | 2 +- integration-tests/hibernate-validator-postgres-it/build.gradle | 2 +- integration-tests/techempower-postgres-it/build.gradle | 2 +- integration-tests/verticle-postgres-it/build.gradle | 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 | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index fca12935f..999f70cd6 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -25,7 +25,7 @@ dependencies { implementation "io.vertx:vertx-sql-client:${vertxSqlClientVersion}" // Testing - testImplementation 'org.assertj:assertj-core:3.26.3' + testImplementation 'org.assertj:assertj-core:3.27.3' testImplementation "io.vertx:vertx-junit5:${vertxSqlClientVersion}" // Drivers diff --git a/integration-tests/bytecode-enhancements-it/build.gradle b/integration-tests/bytecode-enhancements-it/build.gradle index 177deae9c..17ffdf10e 100644 --- a/integration-tests/bytecode-enhancements-it/build.gradle +++ b/integration-tests/bytecode-enhancements-it/build.gradle @@ -17,7 +17,7 @@ description = 'Bytecode enhancements integration tests' ext { log4jVersion = '2.20.0' - assertjVersion = '3.26.3' + assertjVersion = '3.27.3' } dependencies { diff --git a/integration-tests/hibernate-validator-postgres-it/build.gradle b/integration-tests/hibernate-validator-postgres-it/build.gradle index 8236f6509..27689b24d 100644 --- a/integration-tests/hibernate-validator-postgres-it/build.gradle +++ b/integration-tests/hibernate-validator-postgres-it/build.gradle @@ -17,7 +17,7 @@ description = 'Quarkus QE integration tests' ext { log4jVersion = '2.20.0' - assertjVersion = '3.26.3' + assertjVersion = '3.27.3' } dependencies { diff --git a/integration-tests/techempower-postgres-it/build.gradle b/integration-tests/techempower-postgres-it/build.gradle index 925aedfd8..5c4ecda47 100644 --- a/integration-tests/techempower-postgres-it/build.gradle +++ b/integration-tests/techempower-postgres-it/build.gradle @@ -14,7 +14,7 @@ description = 'TechEmpower integration tests' ext { jacksonDatabindVersion = '2.15.2' jbossLoggingVersion = '3.5.0.Final' - assertjVersion = '3.26.3' + assertjVersion = '3.27.3' vertxWebVersion = project.hasProperty( 'vertxWebVersion' ) ? project.property( 'vertxWebVersion' ) : vertxSqlClientVersion diff --git a/integration-tests/verticle-postgres-it/build.gradle b/integration-tests/verticle-postgres-it/build.gradle index 4b48e87aa..0be234620 100644 --- a/integration-tests/verticle-postgres-it/build.gradle +++ b/integration-tests/verticle-postgres-it/build.gradle @@ -14,7 +14,7 @@ description = 'Bytecode enhancements integration tests' ext { jacksonDatabindVersion = '2.15.2' jbossLoggingVersion = '3.5.0.Final' - assertjVersion = '3.26.3' + assertjVersion = '3.27.3' vertxWebVersion = project.hasProperty( 'vertxWebVersion' ) ? project.property( 'vertxWebVersion' ) : vertxSqlClientVersion diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 4154f5ba8..28215e088 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -8,7 +8,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.assertj:assertj-core:3.26.3 +//DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:cockroachdb:1.21.0 //DEPS org.slf4j:slf4j-simple:2.0.7 diff --git a/tooling/jbang/Db2ReactiveTest.java.qute b/tooling/jbang/Db2ReactiveTest.java.qute index 971180741..0bb807c65 100755 --- a/tooling/jbang/Db2ReactiveTest.java.qute +++ b/tooling/jbang/Db2ReactiveTest.java.qute @@ -8,7 +8,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.assertj:assertj-core:3.26.3 +//DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:db2:1.21.0 //DEPS org.slf4j:slf4j-simple:2.0.7 diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index bc5e7ee27..cd035314b 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -8,7 +8,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.assertj:assertj-core:3.26.3 +//DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mariadb:1.21.0 //DEPS org.slf4j:slf4j-simple:2.0.7 diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 8e9a0c579..7b502c7f8 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -8,7 +8,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.assertj:assertj-core:3.26.3 +//DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:mysql:1.21.0 //DEPS org.slf4j:slf4j-simple:2.0.7 diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index efc331af0..f12e26c0b 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -8,7 +8,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.assertj:assertj-core:3.26.3 +//DEPS org.assertj:assertj-core:3.27.3 //DEPS junit:junit:4.13.2 //DEPS org.testcontainers:postgresql:1.21.0 //DEPS org.slf4j:slf4j-simple:2.0.7 diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index bffc7b05b..28ac6d365 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -11,7 +11,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.assertj:assertj-core:3.26.3 +//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 From 21ba4471e2479da884bbba551a82c17f38c3f020 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 11:05:30 +0200 Subject: [PATCH 178/201] test: Upgrade PostgresSQL JDBC driver to 42.7.5 --- hibernate-reactive-core/build.gradle | 2 +- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 999f70cd6..5ad4fa9f6 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -46,7 +46,7 @@ dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.3' // JDBC driver to test with ORM and PostgreSQL - testRuntimeOnly "org.postgresql:postgresql:42.7.4" + testRuntimeOnly "org.postgresql:postgresql:42.7.5" // JDBC driver for Testcontainers with MS SQL Server testRuntimeOnly "com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11" diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 28215e088..91ed2143f 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -15,7 +15,7 @@ //// Testcontainer needs the JDBC drivers to start the container //// Hibernate Reactive doesn't need it -//DEPS org.postgresql:postgresql:42.7.4 +//DEPS org.postgresql:postgresql:42.7.5 import java.io.IOException; import jakarta.persistence.Entity; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 28ac6d365..91f060a94 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -21,7 +21,7 @@ // //// Testcontainer needs the JDBC drivers to start the containers //// Hibernate Reactive doesn't use them -//DEPS org.postgresql:postgresql:42.7.4 +//DEPS org.postgresql:postgresql:42.7.5 //DEPS com.mysql:mysql-connector-j:9.1.0 //DEPS org.mariadb.jdbc:mariadb-java-client:3.5.1 // From bc76188dcabe5bc18c75da3dc2c95049d5098995 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 11:06:59 +0200 Subject: [PATCH 179/201] test: Upgrade MSSQL JDBC driver to 12.10.0.jre11 --- hibernate-reactive-core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 5ad4fa9f6..2291ec295 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -49,7 +49,7 @@ dependencies { testRuntimeOnly "org.postgresql:postgresql:42.7.5" // JDBC driver for Testcontainers with MS SQL Server - testRuntimeOnly "com.microsoft.sqlserver:mssql-jdbc:12.8.1.jre11" + testRuntimeOnly "com.microsoft.sqlserver:mssql-jdbc:12.10.0.jre11" // JDBC driver for Testcontainers with MariaDB Server testRuntimeOnly "org.mariadb.jdbc:mariadb-java-client:3.5.1" From 327e5af0501329c4a36905ae503613d04508eda7 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 11:08:54 +0200 Subject: [PATCH 180/201] test: Upgrade MariaDB JDBC driver to 3.5.3 --- hibernate-reactive-core/build.gradle | 2 +- tooling/jbang/MariaDBReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 2291ec295..c4ce458d9 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -52,7 +52,7 @@ dependencies { testRuntimeOnly "com.microsoft.sqlserver:mssql-jdbc:12.10.0.jre11" // JDBC driver for Testcontainers with MariaDB Server - testRuntimeOnly "org.mariadb.jdbc:mariadb-java-client:3.5.1" + testRuntimeOnly "org.mariadb.jdbc:mariadb-java-client:3.5.3" // JDBC driver for Testcontainers with MYSQL Server testRuntimeOnly "com.mysql:mysql-connector-j:9.1.0" diff --git a/tooling/jbang/MariaDBReactiveTest.java.qute b/tooling/jbang/MariaDBReactiveTest.java.qute index cd035314b..ea4e87521 100755 --- a/tooling/jbang/MariaDBReactiveTest.java.qute +++ b/tooling/jbang/MariaDBReactiveTest.java.qute @@ -15,7 +15,7 @@ //// Testcontainer needs the JDBC drivers to start the container //// Hibernate Reactive doesn't need it -//DEPS org.mariadb.jdbc:mariadb-java-client:3.5.1 +//DEPS org.mariadb.jdbc:mariadb-java-client:3.5.3 import jakarta.persistence.Entity; import jakarta.persistence.Id; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 91f060a94..61d09fa4b 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -23,7 +23,7 @@ //// Hibernate Reactive doesn't use them //DEPS org.postgresql:postgresql:42.7.5 //DEPS com.mysql:mysql-connector-j:9.1.0 -//DEPS org.mariadb.jdbc:mariadb-java-client:3.5.1 +//DEPS org.mariadb.jdbc:mariadb-java-client:3.5.3 // import java.util.function.Supplier; From b9265b831b31c4c674ba75ce4001dcf36528871c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 11:10:35 +0200 Subject: [PATCH 181/201] test: Upgrade MySQL JDBC driver to 9.3.0 --- hibernate-reactive-core/build.gradle | 2 +- tooling/jbang/MySQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index c4ce458d9..dc442dd2d 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -55,7 +55,7 @@ dependencies { testRuntimeOnly "org.mariadb.jdbc:mariadb-java-client:3.5.3" // JDBC driver for Testcontainers with MYSQL Server - testRuntimeOnly "com.mysql:mysql-connector-j:9.1.0" + testRuntimeOnly "com.mysql:mysql-connector-j:9.3.0" // JDBC driver for Db2 server, for testing testRuntimeOnly "com.ibm.db2:jcc:12.1.0.0" diff --git a/tooling/jbang/MySQLReactiveTest.java.qute b/tooling/jbang/MySQLReactiveTest.java.qute index 7b502c7f8..51e028782 100755 --- a/tooling/jbang/MySQLReactiveTest.java.qute +++ b/tooling/jbang/MySQLReactiveTest.java.qute @@ -15,7 +15,7 @@ //// Testcontainer needs the JDBC drivers to start the container //// Hibernate Reactive doesn't need it -//DEPS com.mysql:mysql-connector-j:9.1.0 +//DEPS com.mysql:mysql-connector-j:9.3.0 import jakarta.persistence.Entity; import jakarta.persistence.Id; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 61d09fa4b..e919271b1 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -22,7 +22,7 @@ //// Testcontainer needs the JDBC drivers to start the containers //// Hibernate Reactive doesn't use them //DEPS org.postgresql:postgresql:42.7.5 -//DEPS com.mysql:mysql-connector-j:9.1.0 +//DEPS com.mysql:mysql-connector-j:9.3.0 //DEPS org.mariadb.jdbc:mariadb-java-client:3.5.3 // From a8e7e23ed471e2e0be3ec693f94efbbbc2f9d9f7 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 11:14:02 +0200 Subject: [PATCH 182/201] Upgrade JBoss Logging Annotations and Processor to 3.0.4 --- hibernate-reactive-core/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index dc442dd2d..0ebf8ca5c 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -16,9 +16,9 @@ dependencies { implementation 'org.jboss.logging:jboss-logging:3.6.1.Final' annotationProcessor 'org.jboss.logging:jboss-logging:3.6.1.Final' - compileOnly 'org.jboss.logging:jboss-logging-annotations:3.0.3.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-annotations:3.0.3.Final' - annotationProcessor 'org.jboss.logging:jboss-logging-processor:3.0.3.Final' + 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' //Specific implementation details of Hibernate Reactive: From e73e0b0acb17132ca8ad2c449d28f7e10b14ad6c Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 09:39:45 +0200 Subject: [PATCH 183/201] test: Upgrade PostgreSQL to 17.5 --- .../hibernate/reactive/containers/PostgreSQLDatabase.java | 2 +- .../test/java/org/hibernate/reactive/it/BaseReactiveIT.java | 2 +- .../reactive/it/quarkus/qe/database/BaseReactiveIT.java | 2 +- .../org/hibernate/reactive/it/verticle/VertxServer.java | 2 +- podman.md | 6 +++--- tooling/jbang/Example.java | 2 +- tooling/jbang/PostgreSQLReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) 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 5b427f4e7..56ef4f878 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 @@ -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.4" ) ) + public static final PostgreSQLContainer postgresql = new PostgreSQLContainer<>( imageName( "postgres", "17.5" ) ) .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 ff9f5e601..2388cb421 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 @@ -53,7 +53,7 @@ public abstract class BaseReactiveIT { public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:17.4" ) + .parse( "docker.io/postgres:17.5" ) .asCompatibleSubstituteFor( "postgres" ); public static final String USERNAME = "hreact"; 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 afff092e9..7debdbb2c 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 @@ -53,7 +53,7 @@ public abstract class BaseReactiveIT { public static final boolean USE_DOCKER = Boolean.getBoolean( "docker" ); public static final DockerImageName IMAGE_NAME = DockerImageName - .parse( "docker.io/postgres:17.4" ) + .parse( "docker.io/postgres:17.5" ) .asCompatibleSubstituteFor( "postgres" ); public static final String USERNAME = "hreact"; 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 238e5278b..990fef754 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 @@ -36,7 +36,7 @@ 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.4"; + public static final String IMAGE_NAME = "postgres:17.5"; public static final String USERNAME = "hreact"; public static final String PASSWORD = "hreact"; public static final String DB_NAME = "hreact"; diff --git a/podman.md b/podman.md index 5d7d9476b..7d651353e 100644 --- a/podman.md +++ b/podman.md @@ -14,13 +14,13 @@ If you replace `podman` with `sudo docker`, they will also work with [Docker][do Example: ``` -podman run --rm --name HibernateTestingPGSQL postgres:17.4 +podman run --rm --name HibernateTestingPGSQL postgres:17.5 ``` becomes for Docker: ``` -sudo docker run --rm --name HibernateTestingPGSQL postgres:17.4 +sudo docker run --rm --name HibernateTestingPGSQL postgres:17.5 ``` --- @@ -39,7 +39,7 @@ required credentials and schema to run the tests: podman run --rm --name HibernateTestingPGSQL \ -e POSTGRES_USER=hreact -e POSTGRES_PASSWORD=hreact -e POSTGRES_DB=hreact \ -e POSTGRES_INITDB_ARGS="-A password" \ - -p 5432:5432 docker.io/postgres:17.4 + -p 5432:5432 docker.io/postgres:17.5 ``` When the database has started, you can run the tests on PostgreSQL with: diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java index d56a86368..3a287c0b5 100644 --- a/tooling/jbang/Example.java +++ b/tooling/jbang/Example.java @@ -59,7 +59,7 @@ *
      *                 podman run --rm --name HibernateTestingPGSQL \
      *                      -e POSTGRES_USER=hreact -e POSTGRES_PASSWORD=hreact -e POSTGRES_DB=hreact \
    - *                      -p 5432:5432 postgres:17.4
    + *                      -p 5432:5432 postgres:17.5
      *              
    * *
    3. Run the example with JBang
    diff --git a/tooling/jbang/PostgreSQLReactiveTest.java.qute b/tooling/jbang/PostgreSQLReactiveTest.java.qute index f12e26c0b..e75486fff 100755 --- a/tooling/jbang/PostgreSQLReactiveTest.java.qute +++ b/tooling/jbang/PostgreSQLReactiveTest.java.qute @@ -67,7 +67,7 @@ public class {baseName} { } @ClassRule - public final static PostgreSQLContainer database = new PostgreSQLContainer( imageName( "docker.io", "postgres", "17.4" ) ); + public final static PostgreSQLContainer database = new PostgreSQLContainer( imageName( "docker.io", "postgres", "17.5" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index e919271b1..5bb53a42c 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -228,7 +228,7 @@ public String toString() { * It's a wrapper around the testcontainers classes. */ enum Database { - POSTGRESQL( () -> new PostgreSQLContainer( "postgres:17.4" ) ), + POSTGRESQL( () -> new PostgreSQLContainer( "postgres:17.5" ) ), MYSQL( () -> new MySQLContainer( "mysql:9.2.0" ) ), DB2( () -> new Db2Container( "docker.io/icr.io/db2_community/db2:12.1.0.0" ).acceptLicense() ), MARIADB( () -> new MariaDBContainer( "mariadb:11.7.2" ) ), From 5caf95239b53c5e69a4a8882c316ff419b6c0963 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 09:45:38 +0200 Subject: [PATCH 184/201] test: Upgrade CockroachDB to v24.3.13 --- .../org/hibernate/reactive/containers/CockroachDBDatabase.java | 2 +- podman.md | 2 +- tooling/jbang/CockroachDBReactiveTest.java.qute | 2 +- tooling/jbang/ReactiveTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 3ec042a38..e1a0928d1 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 @@ -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.1.15" ) ) + public static final CockroachContainer cockroachDb = new CockroachContainer( imageName( "cockroachdb/cockroach", "v24.3.13" ) ) // 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/podman.md b/podman.md index 7d651353e..bc4d96ba7 100644 --- a/podman.md +++ b/podman.md @@ -121,7 +121,7 @@ configured to run the tests: ``` podman run --rm --name=HibernateTestingCockroachDB \ --hostname=roachrr1 -p 26257:26257 -p 8080:8080 \ - docker.io/cockroachdb/cockroach:v24.1.15 start-single-node --insecure + docker.io/cockroachdb/cockroach:v24.3.13 start-single-node --insecure ``` Some of tests needs temporary tables and because this is an experimental feature in diff --git a/tooling/jbang/CockroachDBReactiveTest.java.qute b/tooling/jbang/CockroachDBReactiveTest.java.qute index 91ed2143f..174bf6858 100755 --- a/tooling/jbang/CockroachDBReactiveTest.java.qute +++ b/tooling/jbang/CockroachDBReactiveTest.java.qute @@ -70,7 +70,7 @@ public class {baseName} { } @ClassRule - public final static CockroachContainer database = new CockroachContainer( imageName( "cockroachdb", "cockroach", "v24.1.15" ) ); + public final static CockroachContainer database = new CockroachContainer( imageName( "cockroachdb", "cockroach", "v24.3.13" ) ); private Mutiny.SessionFactory sessionFactory; diff --git a/tooling/jbang/ReactiveTest.java b/tooling/jbang/ReactiveTest.java index 5bb53a42c..9d3759fce 100755 --- a/tooling/jbang/ReactiveTest.java +++ b/tooling/jbang/ReactiveTest.java @@ -232,7 +232,7 @@ enum Database { MYSQL( () -> new MySQLContainer( "mysql:9.2.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.1.15" ) ); + COCKROACHDB( () -> new CockroachContainer( "cockroachdb/cockroach:v24.3.13" ) ); private final Supplier> containerSupplier; From 362566ca0a2916d45231328092fc06c337d40d33 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 21 May 2025 10:37:50 +0200 Subject: [PATCH 185/201] [#2266] Upgrade Mutiny to 2.9.0 --- hibernate-reactive-core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 0ebf8ca5c..01f593d80 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -10,7 +10,7 @@ dependencies { api "org.hibernate.orm:hibernate-core:${hibernateOrmVersion}" - api 'io.smallrye.reactive:mutiny:2.7.0' + api 'io.smallrye.reactive:mutiny:2.9.0' //Logging implementation 'org.jboss.logging:jboss-logging:3.6.1.Final' From 4d4a7332599f4ca0ef7da87c41bc7b6bed37a37d Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 22 May 2025 09:06:36 +0000 Subject: [PATCH 186/201] Update project version to : `3.0.0.Final` --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 603432f4e..e3e58d698 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -projectVersion=3.0.0-SNAPSHOT \ No newline at end of file +projectVersion=3.0.0.Final \ No newline at end of file From 17511458f90fca98e37c0f72f61a42f3e7f2543b Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Thu, 22 May 2025 09:07:32 +0000 Subject: [PATCH 187/201] 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 188/201] 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 189/201] [#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 190/201] [#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 191/201] 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 192/201] 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 193/201] [#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 194/201] 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 195/201] [#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 196/201] [#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 197/201] [#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 198/201] 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 199/201] [#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 200/201] [#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 201/201] [#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