Repository: hibernate/hibernate-reactive
Pull request #2223 (2025-04-25 15:06:57)

Add a simple JReleaser configuration
Fixes https://github.com/hibernate/hibernate-reactive/issues/2222

This one is to match the ORM updates: https://github.com/hibernate/hibernate-orm/commit/ec026fc7fbd9a9ec45b4602e6294234f47f9d649


Patch:
diff --git a/.gitignore b/.gitignore
index 8016117c..750319d4 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 00000000..cdd9a17d
--- /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/README.md b/README.md
index c01979d6..50970620 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/build.gradle b/build.gradle
index c7ad6918..4c5235a9 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 d365436a..4ae62e3f 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 07fa844c..59a97eee 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/gradle.properties b/gradle.properties
index 3576439d..c402bb33 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
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 1b24e567..d12791e5 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/generator/values/internal/ReactiveGeneratedValuesHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java
index 70495cce..e842adf1 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/ReactiveAbstractMultiIdEntityLoader.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveAbstractMultiIdEntityLoader.java
index 527d5932..71ae6e7e 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 <K> CompletionStage<List<T>> reactiveLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) {
+	public final <K> CompletionStage<List<T>> reactiveLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session) {
 		Objects.requireNonNull( ids );
 
 		return loadOptions.isOrderReturnEnabled()
@@ -58,8 +58,8 @@ public final <K> CompletionStage<List<T>> reactiveLoad(K[] ids, MultiIdLoadOptio
 				: performUnorderedMultiLoad( ids, loadOptions, session );
 	}
 
-	protected abstract <K> CompletionStage<List<T>> performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session);
+	protected abstract <K> CompletionStage<List<T>> performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session);
 
-	protected abstract <K> CompletionStage<List<T>> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session);
+	protected abstract <K> CompletionStage<List<T>> 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 8239a6e1..612e5ad1 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 <K> CompletionStage<List<E>> 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 <K> CompletionStage<List<E>> performOrderedMultiLoad(
 	protected <K> CompletionStage<List<E>> 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 <R, K> K[] processResolvableEntities(
 			MultiIdEntityLoaderArrayParam.ResolutionConsumer<R> 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 92f4e996..74307840 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<List<T>> performOrderedMultiLoad(
 			Object[] ids,
 			MultiIdLoadOptions loadOptions,
-			EventSource session) {
+			SharedSessionContractImplementor session) {
 		if ( LOG.isTraceEnabled() ) {
 			LOG.tracef( "#performOrderedMultiLoad(`%s`, ..)", getEntityDescriptor().getEntityName() );
 		}
@@ -289,7 +288,7 @@ private static <T> List<T> singletonList(Object loaded) {
 	protected CompletionStage<List<T>> 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/internal/ReactiveSingleIdEntityLoaderStandardImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderStandardImpl.java
index 684894cf..c23a0098 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<T> load(
 			SharedSessionContractImplementor session) {
 		final ReactiveSingleIdLoadPlan<T> loadPlan = (ReactiveSingleIdLoadPlan<T>) resolveLoadPlan(
 				lockOptions,
-				session.getLoadQueryInfluencers(),
-				session.getFactory()
+				session.getLoadQueryInfluencers()
 		);
 		return loadPlan.load( key, readOnly, true, session );
 	}
@@ -68,7 +67,7 @@ public CompletionStage<T> load(
 			LockOptions lockOptions,
 			Boolean readOnly,
 			SharedSessionContractImplementor session) {
-		final ReactiveSingleIdLoadPlan<T> loadPlan = (ReactiveSingleIdLoadPlan<T>) resolveLoadPlan( lockOptions, session.getLoadQueryInfluencers(), session.getFactory() );
+		final ReactiveSingleIdLoadPlan<T> loadPlan = (ReactiveSingleIdLoadPlan<T>) resolveLoadPlan( lockOptions, session.getLoadQueryInfluencers() );
 		return loadPlan.load( key, entityInstance, readOnly, false, session );
 	}
 
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 47050b13..e0236e36 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<T> extends MultiIdEntityLoader<T> {
 
 	@Override
-	default <K> List<T> load(K[] ids, MultiIdLoadOptions options, EventSource session) {
+	default <K> List<T> load(K[] ids, MultiIdLoadOptions options, SharedSessionContractImplementor session) {
 		throw make( Log.class, lookup() ).nonReactiveMethodCall( "reactiveLoad" );
 	}
 
-	<K> CompletionStage<List<T>> reactiveLoad(K[] ids, MultiIdLoadOptions options, EventSource session);
+	<K> CompletionStage<List<T>> reactiveLoad(K[] ids, MultiIdLoadOptions options, SharedSessionContractImplementor session);
 }
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 75287e5b..4f205492 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;
@@ -32,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")
@@ -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);
+
 }
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 00000000..2ed6d6b9
--- /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/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java
index 0f231ff6..c73c6b41 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;
@@ -43,12 +37,17 @@
 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;
 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 +359,6 @@ default SelectionQuery<R> 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<R> 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<R> setOrder(List<Order<? super R>> 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<R> setOrder(Order<? super R> order);
-
 		/**
 		 * Set the {@link EntityGraph} that will be used as a fetch plan for
 		 * the root entity returned by this query.
@@ -994,6 +962,22 @@ default Uni<Void> 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)
+		 */
+		<R> Query<R> createQuery(TypedQueryReference<R> 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
@@ -1649,6 +1633,22 @@ default <T> Uni<T> get(Class<T> entityClass, Object id, LockModeType lockModeTyp
 		@Deprecated
 		<R> Query<R> 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)
+		 */
+		<R> Query<R> createQuery(TypedQueryReference<R> 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/MutinyQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java
index d0608ba5..88243f77 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<R> implements Query<R> {
 
@@ -74,18 +72,6 @@ public Query<R> setLockMode(LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public Mutiny.SelectionQuery<R> setOrder(List<Order<? super R>> orders) {
-		delegate.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public Mutiny.SelectionQuery<R> setOrder(Order<? super R> order) {
-		delegate.setOrder( (List<Order<? super R>>) order );
-		return this;
-	}
-
 	@Override
 	public Query<R> setPlan(EntityGraph<R> 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 b001e618..fb4fe2ad 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<R> implements SelectionQuery<R> {
 	private final MutinySessionFactoryImpl factory;
@@ -196,19 +194,6 @@ public SelectionQuery<R> setLockMode(String alias, LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public SelectionQuery<R> setOrder(List<Order<? super R>> orders) {
-		delegate.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public SelectionQuery<R> setOrder(Order<? super R> order) {
-		delegate.setOrder( order );
-		return this;
-	}
-
-
 	@Override
 	public SelectionQuery<R> setParameter(String name, Object value) {
 		delegate.setParameter( name, value );
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 87cf50bb..ab57ee71 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 <R> Query<R> createQuery(TypedQueryReference<R> typedQueryReference) {
+		ReactiveQuery<R> reactiveQuery = delegate.createReactiveQuery( typedQueryReference );
+		return new MutinyQueryImpl<>( reactiveQuery, factory );
+	}
+
 	@Override @Deprecated
 	public <R> Query<R> 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 5605a791..cb7db4a8 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 <T> Uni<T> get(EntityGraph<T> entityGraph, Object id) {
 		return uni( () -> delegate.reactiveGet( entityClass, id, null, entityGraph ) );
 	}
 
+	@Override
+	public <R> Query<R> createQuery(TypedQueryReference<R> typedQueryReference) {
+		ReactiveQuery<R> reactiveQuery = delegate.createReactiveQuery( typedQueryReference );
+		return new MutinyQueryImpl<>( reactiveQuery, factory );
+	}
+
 	@Override
 	public <R> Query<R> createQuery(String queryString) {
 		return new MutinyQueryImpl<>( delegate.createReactiveQuery( queryString ), factory );
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 9a31d2bc..c5d068db 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,11 +20,11 @@
 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;
 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;
@@ -81,11 +81,11 @@ public class ReactiveAbstractPersisterDelegate {
 
 	private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
 
-	private final Supplier<ReactiveSingleIdEntityLoader<Object>> singleIdEntityLoaderSupplier;
-	private final Supplier<ReactiveMultiIdEntityLoader<Object>> multiIdEntityLoaderSupplier;
+	private final Supplier<ReactiveSingleIdEntityLoader<?>> singleIdEntityLoaderSupplier;
+	private final Supplier<ReactiveMultiIdEntityLoader<?>> multiIdEntityLoaderSupplier;
 
-	private ReactiveSingleIdEntityLoader<Object> singleIdEntityLoader;
-	private ReactiveMultiIdEntityLoader<Object> multiIdEntityLoader;
+	private ReactiveSingleIdEntityLoader<?> singleIdEntityLoader;
+	private ReactiveMultiIdEntityLoader<?> multiIdEntityLoader;
 
 	private final EntityPersister entityDescriptor;
 
@@ -101,12 +101,12 @@ public ReactiveAbstractPersisterDelegate(
 		entityDescriptor = entityPersister;
 	}
 
-	public ReactiveSingleIdEntityLoader<Object> buildSingleIdEntityLoader() {
+	public ReactiveSingleIdEntityLoader<?> buildSingleIdEntityLoader() {
 		singleIdEntityLoader = singleIdEntityLoaderSupplier.get();
 		return singleIdEntityLoader;
 	}
 
-	public ReactiveMultiIdEntityLoader<Object> buildMultiIdEntityLoader() {
+	public ReactiveMultiIdEntityLoader<?> buildMultiIdEntityLoader() {
 		multiIdEntityLoader = multiIdEntityLoaderSupplier.get();
 		return multiIdEntityLoader;
 	}
@@ -126,7 +126,7 @@ public <T> DomainResult<T> createDomainResult(
 	/**
 	 * @see org.hibernate.persister.entity.AbstractEntityPersister#multiLoad(Object[], EventSource, MultiIdLoadOptions)`
 	 */
-	public <K> CompletionStage<? extends List<?>> multiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
+	public <K> CompletionStage<? extends List<?>> multiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) {
 		return multiIdEntityLoader.reactiveLoad( ids, loadOptions, session );
 	}
 
@@ -135,12 +135,12 @@ private static ReactiveMultiIdEntityLoader<Object> 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<Object> buildSingleIdEntityLoader(
+	private static ReactiveSingleIdEntityLoader<?> buildSingleIdEntityLoader(
 			EntityPersister entityDescriptor,
 			PersistentClass bootDescriptor,
 			RuntimeModelCreationContext creationContext,
@@ -148,32 +148,45 @@ private static ReactiveSingleIdEntityLoader<Object> 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<Object> createBatchingIdEntityLoader(
+	private static <T> 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<Void> processInsertGeneratedProperties(
@@ -236,7 +249,7 @@ protected ReactiveSingleUniqueKeyEntityLoader<Object> getReactiveUniqueKeyLoader
 		);
 	}
 
-	public CompletionStage<Object> load(
+	public CompletionStage<?> load(
 			EntityPersister persister,
 			Object id,
 			Object optionalObject,
@@ -255,7 +268,7 @@ public Generator reactive(Generator generator) {
 		return generator instanceof IdentityGenerator ? new ReactiveIdentityGenerator() : generator;
 	}
 
-	public CompletionStage<Object> loadEntityIdByNaturalId(
+	public CompletionStage<?> loadEntityIdByNaturalId(
 			Object[] orderedNaturalIdValues, LockOptions lockOptions, SharedSessionContractImplementor session) {
 		if ( LOG.isTraceEnabled() ) {
 			LOG.tracef( "Resolving natural-id [%s] to id : %s ",
@@ -264,7 +277,7 @@ public CompletionStage<Object> loadEntityIdByNaturalId(
 			);
 		}
 
-		return ( (ReactiveNaturalIdLoader) entityDescriptor.getNaturalIdLoader() )
+		return ( (ReactiveNaturalIdLoader<?>) entityDescriptor.getNaturalIdLoader() )
 				.resolveNaturalIdToId( orderedNaturalIdValues, session );
 	}
 
@@ -323,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 0a36d048..16d6237f 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,40 +91,40 @@ CompletionStage<Void> reactiveLock(
 
 	<K> CompletionStage<? extends List<?>> reactiveMultiLoad(
 			K[] ids,
-			EventSource session,
+			SharedSessionContractImplementor session,
 			MultiIdLoadOptions loadOptions);
 
-	CompletionStage<Object> reactiveLoad(
+	CompletionStage<?> reactiveLoad(
 			Object id,
 			Object optionalObject,
 			LockMode lockMode,
 			SharedSessionContractImplementor session);
 
-	CompletionStage<Object> reactiveLoad(
+	CompletionStage<?> reactiveLoad(
 			Object id,
 			Object optionalObject,
 			LockOptions lockOptions,
 			SharedSessionContractImplementor session);
 
-	CompletionStage<Object> reactiveLoad(
+	CompletionStage<?> reactiveLoad(
 			Object id,
 			Object optionalObject,
 			LockOptions lockOptions,
 			SharedSessionContractImplementor session,
 			Boolean readOnly);
 
-	CompletionStage<Object> reactiveLoadByUniqueKey(
+	CompletionStage<?> reactiveLoadByUniqueKey(
 			String propertyName,
 			Object uniqueKey,
 			SharedSessionContractImplementor session);
 
-	CompletionStage<Object> reactiveLoadByUniqueKey(
+	CompletionStage<?> reactiveLoadByUniqueKey(
 			String propertyName,
 			Object uniqueKey,
 			Boolean readOnly,
 			SharedSessionContractImplementor session);
 
-	CompletionStage<Object> 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 b87ba691..0182ef40 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;
@@ -83,7 +82,7 @@ protected SingleIdEntityLoader<?> buildSingleIdEntityLoader() {
 	}
 
 	@Override
-	protected MultiIdEntityLoader<Object> buildMultiIdLoader() {
+	protected MultiIdEntityLoader<?> buildMultiIdLoader() {
 		return reactiveDelegate.buildMultiIdEntityLoader();
 	}
 
@@ -239,7 +238,7 @@ public CompletionStage<Void> mergeReactive(
 	}
 
 	@Override
-	public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
+	public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) {
 		return reactiveDelegate.multiLoad( ids, session, loadOptions );
 	}
 
@@ -313,7 +312,7 @@ protected Object initializeLazyPropertiesFromDatastore(Object entity, Object id,
 	}
 
 	@Override
-	public CompletionStage<Object> 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 );
 	}
 
@@ -323,7 +322,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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 );
 	}
 
@@ -333,11 +332,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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<Object> 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 );
 	}
 
@@ -371,7 +370,7 @@ public CompletionStage<Object> reactiveLoadByUniqueKey(String propertyName, Obje
 	 * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor)
 	 */
 	@Override
-	public CompletionStage<Object> 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 381a83b7..3006a0ea 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;
@@ -70,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,
@@ -102,7 +101,7 @@ protected SingleIdEntityLoader<?> buildSingleIdEntityLoader() {
 	}
 
 	@Override
-	protected MultiIdEntityLoader<Object> buildMultiIdLoader() {
+	protected MultiIdEntityLoader<?> buildMultiIdLoader() {
 		return reactiveDelegate.buildMultiIdEntityLoader();
 	}
 
@@ -302,13 +301,13 @@ public CompletionStage<Void> reactiveProcessUpdateGenerated(Object id, Object en
 	 * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor)
 	 */
 	@Override
-	public CompletionStage<Object> 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<Object> 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 );
 	}
 
@@ -318,7 +317,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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 );
 	}
 
@@ -328,11 +327,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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<Object> 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 );
 	}
 
@@ -397,7 +396,7 @@ public CompletionStage<Void> mergeReactive(
 	}
 
 	@Override
-	public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
+	public <K> CompletionStage<? extends List<?>> 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 cb7995ac..52871e30 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;
@@ -83,7 +82,7 @@ protected SingleIdEntityLoader<?> buildSingleIdEntityLoader() {
 	}
 
 	@Override
-	protected MultiIdEntityLoader<Object> buildMultiIdLoader() {
+	protected MultiIdEntityLoader<?> buildMultiIdLoader() {
 		return reactiveDelegate.buildMultiIdEntityLoader();
 	}
 
@@ -149,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 <union-subclass> mapping for: " + getEntityName() );
-		}
-	}
-
 	@Override
 	protected InsertCoordinator buildInsertCoordinator() {
 		return ReactiveCoordinatorFactory.buildInsertCoordinator( this, getFactory() );
@@ -181,6 +172,13 @@ public <T> DomainResult<T> 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 <union-subclass> mapping for: " + getEntityName() );
+		}
+	}
+
 	@Override
 	public Generator getGenerator() throws HibernateException {
 		return reactiveDelegate.reactive( super.getGenerator() );
@@ -280,7 +278,7 @@ protected Object initializeLazyPropertiesFromDatastore(Object entity, Object id,
 	}
 
 	@Override
-	public CompletionStage<Object> 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 );
 	}
 
@@ -290,7 +288,7 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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 );
 	}
 
@@ -300,11 +298,11 @@ public Object load(Object id, Object optionalObject, LockOptions lockOptions, Sh
 	}
 
 	@Override
-	public CompletionStage<Object> 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<Object> 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 );
 	}
 
@@ -365,7 +363,7 @@ public CompletionStage<Void> mergeReactive(
 	}
 
 	@Override
-	public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
+	public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, SharedSessionContractImplementor session, MultiIdLoadOptions loadOptions) {
 		return reactiveDelegate.multiLoad( ids, session, loadOptions );
 	}
 
@@ -373,7 +371,7 @@ public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSo
 	 * @see AbstractEntityPersister#loadEntityIdByNaturalId(Object[], LockOptions, SharedSessionContractImplementor)
 	 */
 	@Override
-	public CompletionStage<Object> 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/mutation/ReactiveUpdateCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java
index 92c92bb6..9e3a6b86 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<int[]> 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/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/impl/ReactiveIntegrator.java
index c1fb0deb..db37d724 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 );
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 953285dd..0cf59c41 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<JdbcEnvironment> {
+public class NoJdbcEnvironmentInitiator extends JdbcEnvironmentInitiator
+		implements StandardServiceInitiator<JdbcEnvironment> {
 
 	public static final NoJdbcEnvironmentInitiator INSTANCE = new NoJdbcEnvironmentInitiator();
 
@@ -42,14 +47,59 @@ public Class<JdbcEnvironment> getServiceInitiated() {
 	}
 
 	@Override
-	public JdbcEnvironment initiateService(Map<String, Object> 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<String, Object> 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<String, Object> configurationValues,
+			ServiceRegistryImplementor registry,
+			DialectFactory dialectFactory) {
+		return new JdbcEnvironmentImpl( registry, new DialectBuilder( configurationValues, registry )
+				.build( dialectFactory )
+		);
+	}
+
+	@Override
+	protected JdbcEnvironmentImpl getJdbcEnvironmentUsingJdbcMetadata(
+			Map<String, Object> 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<String, Object> 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<ReactiveConnection, CompletionStage<ReactiveDialectResolutionInfo>> 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<ReactiveDialectResolutionInfo> buildResolutionInfo(ReactiveConnection connection) {
-			final DatabaseMetadata databaseMetadata = connection.getDatabaseMetadata();
-			return resolutionInfoStage( connection, databaseMetadata )
+			return buildResolutionInfo( connection, null );
+		}
+
+		private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInfo(ReactiveConnection connection, ExplicitMetadata explicitMetadata) {
+			return resolutionInfoStage( connection, explicitMetadata )
 					.handle( CompletionStages::handle )
 					.thenCompose( handled -> {
 						if ( handled.hasFailed() ) {
@@ -96,19 +162,27 @@ private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInf
 					} );
 		}
 
-		private static CompletionStage<ReactiveDialectResolutionInfo> 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<ReactiveDialectResolutionInfo> 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;
 		}
 
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 6c35d926..2ee7a051 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<List<R>> getReactiveResultList() {
 
 	void applyGraph(RootGraphImplementor<?> graph, GraphSemantic semantic);
 
-	ReactiveSelectionQuery<R> setOrder(List<? extends Order<? super R>> orderList);
-
-	ReactiveSelectionQuery<R> setOrder(Order<? super R> order);
-
 	ReactiveSelectionQuery<R> enableFetchProfile(String profileName);
 
 	@Override
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 ef2d70af..9fd78d10 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 <R> TypedQueryReference<R> registerNamedQuery(String name, TypedQuery<R> query) {
+		return delegate.registerNamedQuery( name, query );
+	}
+
 	@Override
 	public void visitSqmQueryMementos(Consumer<NamedSqmQueryMemento<?>> 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 1137e0a6..9c2647b8 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/query/sql/internal/ReactiveNativeQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java
index ac8f4e9b..afaf43e4 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<R> setLockMode(String alias, LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public ReactiveNativeQueryImpl<R> setOrder(List<? extends Order<? super R>> orders) {
-		super.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public ReactiveNativeQueryImpl<R> setOrder(Order<? super R> order) {
-		super.setOrder( order );
-		return this;
-	}
-
 	@Override
 	public ReactiveNativeQueryImpl<R> 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 1d897653..92d00b30 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 @@ <P> ReactiveSqmSelectionQuery<R> setParameterList(
 	@Override
 	ReactiveSqmSelectionQuery<R> setTimeout(int timeout);
 
-	@Override
-	ReactiveSqmSelectionQuery<R> setOrder(List<? extends Order<? super R>> orderList);
-
-	@Override
-	ReactiveSqmSelectionQuery<R> setOrder(Order<? super R> order);
-
 	@Override
 	ReactiveSqmSelectionQuery<R> 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 09619657..61f0d39c 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<R> setLockMode(String alias, LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public ReactiveQuerySqmImpl<R> setOrder(List<? extends Order<? super R>> orders) {
-		super.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public ReactiveQuerySqmImpl<R> setOrder(Order<? super R> order) {
-		super.setOrder( order );
-		return this;
-	}
-
 	@Override
 	public <T> ReactiveQuerySqmImpl<T> setTupleTransformer(TupleTransformer<T> 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 bca5e2a6..9249b054 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<R> setFollowOnLocking(boolean enable) {
 		return this;
 	}
 
-	@Override
-	public ReactiveSqmSelectionQueryImpl<R> setOrder(List<? extends Order<? super R>> orders) {
-		super.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public ReactiveSqmSelectionQueryImpl<R> setOrder(Order<? super R> order) {
-		super.setOrder( order );
-		return this;
-	}
-
 	@Override
 	public ReactiveSqmSelectionQueryImpl<R> setFetchSize(int fetchSize) {
 		super.setFetchSize( fetchSize );
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 a3b667fe..c9a7cc11 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 {
 
 	<R> ReactiveQuery<R> createReactiveQuery(String queryString);
 
+	<R> ReactiveQuery<R> createReactiveQuery(TypedQueryReference<R> typedQueryReference);
+
 	<R> ReactiveQuery<R> createReactiveQuery(CriteriaQuery<R> criteriaQuery);
 
 	<R> ReactiveQuery<R> createReactiveQuery(String queryString, Class<R> resultType);
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 f53ed4fc..88cfe3ec 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<Void> reactiveMerge(Object object, MergeContext copiedAlready);
 
+	CompletionStage<Object> reactiveLoad(LoadEventListener.LoadType loadType, Object id, String entityName, LockOptions lockOptions, Boolean readOnly);
+
 	CompletionStage<Void> reactiveFlush();
 
 	CompletionStage<Void> 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 72157e46..02b392a2 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,14 +5,6 @@
  */
 package org.hibernate.reactive.session.impl;
 
-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;
@@ -64,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;
@@ -76,6 +69,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;
@@ -123,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;
@@ -139,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;
 
 /**
@@ -213,8 +219,7 @@ public Object immediateLoad(String entityName, Object id) throws HibernateExcept
 	public CompletionStage<Object> 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();
@@ -247,10 +252,7 @@ public CompletionStage<Object> 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();
@@ -266,6 +268,43 @@ public CompletionStage<Object> 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<Object> 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()
@@ -357,6 +396,33 @@ protected <T> ReactiveQueryImplementor<T> createReactiveCriteriaQuery(SqmStateme
 		return query;
 	}
 
+	@Override
+	public <R> ReactiveQuery<R> createReactiveQuery(TypedQueryReference<R> typedQueryReference) {
+		checksBeforeQueryCreation();
+		if ( typedQueryReference instanceof SelectionSpecificationImpl<R> specification ) {
+			final CriteriaQuery<R> query = specification.buildCriteria( getCriteriaBuilder() );
+			return new ReactiveQuerySqmImpl<>( (SqmStatement<R>) 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<R> type = specification.getResultType() == Void.class
+					? null
+					: (Class<R>) specification.getResultType();
+			return new ReactiveQuerySqmImpl<>( (SqmStatement<R>) query, type, this );
+		}
+		@SuppressWarnings("unchecked")
+		// this cast is fine because of all our impls of TypedQueryReference return Class<R>
+		final Class<R> resultType = (Class<R>) typedQueryReference.getResultType();
+		ReactiveQueryImplementor<R> query = (ReactiveQueryImplementor<R>) buildNamedQuery(
+				typedQueryReference.getName(),
+				memento -> createSqmQueryImplementor( resultType, memento ),
+				memento -> createNativeQueryImplementor( resultType, memento )
+		);
+		typedQueryReference.getHints().forEach( query::setHint );
+		return query;
+	}
+
 	@Override
 	public <R> ReactiveQuery<R> createReactiveQuery(String queryString) {
 		return createReactiveQuery( queryString, null );
@@ -1035,7 +1101,7 @@ public CompletionStage<Void> 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()
@@ -1163,10 +1229,16 @@ private CompletionStage<Void> fireLock(LockEvent event) {
 	}
 
 	@Override
-	public <T> CompletionStage<T> reactiveGet(
-			Class<T> entityClass,
-			Object id) {
-		return new ReactiveIdentifierLoadAccessImpl<>( entityClass ).load( id );
+	public <T> CompletionStage<T> reactiveGet(Class<T> entityClass, Object id) {
+		return reactiveById( entityClass ).load( id );
+	}
+
+	private <T> ReactiveIdentifierLoadAccessImpl<T> reactiveById(Class<T> entityClass) {
+		return new ReactiveIdentifierLoadAccessImpl<>( this, requireEntityPersister( entityClass ) );
+	}
+
+	private <T> ReactiveIdentifierLoadAccessImpl<T> reactiveById(String entityName) {
+		return new ReactiveIdentifierLoadAccessImpl<>( this, requireEntityPersister( entityName ) );
 	}
 
 	@Override
@@ -1176,60 +1248,77 @@ public <T> CompletionStage<T> reactiveFind(
 			LockOptions lockOptions,
 			EntityGraph<T> fetchGraph) {
 		checkOpen();
+		return supplyStage( () -> {
+			if ( fetchGraph != null ) {
+				getLoadQueryInfluencers()
+						.getEffectiveEntityGraph()
+						.applyGraph( (RootGraphImplementor<T>) 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<T>) fetchGraph, GraphSemantic.FETCH );
-		}
-
-//		Boolean readOnly = properties == null ? null : (Boolean) properties.get( QueryHints.HINT_READONLY );
-//		getLoadQueryInfluencers().setReadOnly( readOnly );
-
-		final ReactiveIdentifierLoadAccessImpl<T> 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 <T> CompletionStage<T> handleReactiveFindException(
+			Class<T> entityClass,
+			Object primaryKey,
+			LockOptions lockOptions,
+			CompletionStages.CompletionStageHandler<T, Throwable> 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
@@ -1250,17 +1339,18 @@ private <T> ReactiveEntityPersister entityPersister(Class<T> entityClass) {
 		return (ReactiveEntityPersister) getFactory().getMappingMetamodel().getEntityDescriptor( entityClass );
 	}
 
-	private EntityPersister requireEntityPersister(Class<?> entityClass) {
-		return getFactory().getMappingMetamodel().getEntityDescriptor( entityClass );
-	}
-
-	private CompletionStage<Void> fireReactiveLoad(LoadEvent event, LoadEventListener.LoadType loadType) {
+	private CompletionStage<Void> 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<Void> fireLoadNoChecks(LoadEvent event, LoadEventListener.LoadType loadType) {
 		pulseTransactionCoordinator();
 
@@ -1283,80 +1373,38 @@ public void checkTransactionNeededForUpdateOperation(String exceptionMessage) {
 		//no-op because we don't support transactions
 	}
 
-	private Boolean getReadOnlyFromLoadQueryInfluencers() {
-		return getLoadQueryInfluencers().getReadOnly();
-	}
-
-	private class ReactiveIdentifierLoadAccessImpl<T> {
-
-		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<T> rootGraph;
-		private GraphSemantic graphSemantic;
-
-		public ReactiveIdentifierLoadAccessImpl(EntityPersister entityPersister) {
-			this.entityPersister = entityPersister;
-		}
-
-		public ReactiveIdentifierLoadAccessImpl(String entityName) {
-			this( getFactory().getMappingMetamodel().getEntityDescriptor( entityName ) );
-		}
-
-		public ReactiveIdentifierLoadAccessImpl(Class<T> entityClass) {
-			this( getFactory().getMappingMetamodel().getEntityDescriptor( entityClass ) );
-		}
-
-		public final ReactiveIdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
-			this.lockOptions = lockOptions;
-			return this;
-		}
-
-		public ReactiveIdentifierLoadAccessImpl<T> with(CacheMode cacheMode) {
-			this.cacheMode = cacheMode;
-			return this;
-		}
+	private class ReactiveIdentifierLoadAccessImpl<T> extends IdentifierLoadAccessImpl<CompletionStage<T>> {
 
-		public ReactiveIdentifierLoadAccessImpl<T> with(RootGraph<T> graph, GraphSemantic semantic) {
-			rootGraph = (RootGraphImplementor<T>) graph;
-			graphSemantic = semantic;
-			return this;
-		}
-
-		public final CompletionStage<T> getReference(Object id) {
-			return perform( () -> doGetReference( id ) );
+		public ReactiveIdentifierLoadAccessImpl(LoadAccessContext context, EntityPersister entityPersister) {
+			super(context, entityPersister);
 		}
 
+		@Override
 		protected CompletionStage<T> perform(Supplier<CompletionStage<T>> 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 ) {
@@ -1366,76 +1414,44 @@ protected CompletionStage<T> perform(Supplier<CompletionStage<T>> executor) {
 					} );
 		}
 
-		@SuppressWarnings("unchecked")
+		@Override
 		protected CompletionStage<T> 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 ) );
+			// getReference si supposed to return T, not CompletionStage<T>
+			// 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<T> load(Object id) {
-			return perform( () -> doLoad( id, LoadEventListener.GET ) );
-		}
-
-		//		public final CompletionStage<T> fetch(Object id) {
-//			return perform( () -> doLoad( id, LoadEventListener.IMMEDIATE_LOAD) );
-//		}
-//
+		@Override
 		@SuppressWarnings("unchecked")
-		protected final CompletionStage<T> doLoad(Object id, LoadEventListener.LoadType loadType) {
+		protected CompletionStage<T> 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;
+					} );
 		}
 	}
 
 	private class ReactiveMultiIdentifierLoadAccessImpl<T> implements MultiIdLoadOptions {
-		private final EntityPersister entityPersister;
+		private final ReactiveEntityPersister entityPersister;
 
 		private LockOptions lockOptions;
 		private CacheMode cacheMode;
@@ -1450,7 +1466,7 @@ protected final CompletionStage<T> doLoad(Object id, LoadEventListener.LoadType
 		private boolean readOnly;
 
 		public ReactiveMultiIdentifierLoadAccessImpl(EntityPersister entityPersister) {
-			this.entityPersister = entityPersister;
+			this.entityPersister = (ReactiveEntityPersister) entityPersister;
 		}
 
 		@Override
@@ -1489,12 +1505,7 @@ public Integer getBatchSize() {
 		}
 
 		public ReactiveMultiIdentifierLoadAccessImpl<T> withBatchSize(int batchSize) {
-			if ( batchSize < 1 ) {
-				this.batchSize = null;
-			}
-			else {
-				this.batchSize = batchSize;
-			}
+            this.batchSize = batchSize < 1 ? null : batchSize;
 			return this;
 		}
 
@@ -1537,8 +1548,11 @@ public CompletionStage<List<T>> 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<? extends List<?>> stage =
+						entityPersister.reactiveMultiLoad( sids, ReactiveSessionImpl.this, this );
+				return (CompletionStage<List<T>>) stage;
+            });
 		}
 
 		public CompletionStage<List<T>> perform(Supplier<CompletionStage<List<T>>> executor) {
@@ -1572,12 +1586,6 @@ public CompletionStage<List<T>> perform(Supplier<CompletionStage<List<T>>> execu
 						}
 					} );
 		}
-
-		@SuppressWarnings("unchecked")
-		public <K extends Object> CompletionStage<List<T>> multiLoad(List<K> ids) {
-			return perform( () -> (CompletionStage<List<T>>)
-					entityPersister.multiLoad( ids.toArray( new Object[0] ), ReactiveSessionImpl.this, this ) );
-		}
 	}
 
 	private class NaturalIdLoadAccessImpl<T> {
@@ -1602,10 +1610,6 @@ public NaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
 			return this;
 		}
 
-		protected void synchronizationEnabled(boolean synchronizationEnabled) {
-			this.synchronizationEnabled = synchronizationEnabled;
-		}
-
 		/**
 		 * @see org.hibernate.loader.internal.BaseNaturalIdLoadAccessImpl#doGetReference(Object)
 		 */
@@ -1685,14 +1689,6 @@ protected void performAnyNeededCrossReferenceSynchronizations() {
 			}
 		}
 
-		protected final ReactiveIdentifierLoadAccessImpl<T> getIdentifierLoadAccess() {
-			final ReactiveIdentifierLoadAccessImpl<T> identifierLoadAccess = new ReactiveIdentifierLoadAccessImpl<>( entityPersister );
-			if ( this.lockOptions != null ) {
-				identifierLoadAccess.with( lockOptions );
-			}
-			return identifierLoadAccess;
-		}
-
 		protected ReactiveEntityPersister entityPersister() {
 			return 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 2d3efe4f..84483afb 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,34 +5,20 @@
  */
 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;
-
 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.spi.PostInsertEvent;
-import org.hibernate.event.spi.PostInsertEventListener;
-import org.hibernate.event.spi.PreInsertEvent;
-import org.hibernate.event.spi.PreInsertEventListener;
+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;
@@ -50,10 +36,10 @@
 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.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;
@@ -84,26 +70,37 @@
 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;
 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 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;
@@ -114,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;
 
 /**
@@ -127,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) {
@@ -219,28 +214,21 @@ public <T> CompletionStage<List<T>> reactiveGet(Class<T> entityClass, Object...
 		checkOpen();
 		for (Object id : ids) {
 			if ( id == null ) {
-				throw new IllegalArgumentException("Null id");
+				return failedFuture( new IllegalArgumentException( "Null id" ) );
 			}
 		}
 
-		final EntityPersister persister = getEntityPersister( entityClass.getName() );
+		Object[] sids = new Object[ids.length];
+		System.arraycopy( ids, 0, sids, 0, ids.length );
 
-		final JpaCriteriaQuery<T> query = getCriteriaBuilder().createQuery(entityClass);
-		final JpaRoot<T> from = query.from(entityClass);
-		query.where( from.get( persister.getIdentifierPropertyName() ).in(ids) );
-		return createReactiveQuery(query).getReactiveResultList()
-				.thenApply( resultList -> {
-					final List<Object> idList = new ArrayList<>( resultList.size() );
-					for (T entity : resultList) {
-						idList.add( persister.getIdentifier(entity, this) );
-					}
-					final List<T> 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( (list, e) -> {
+					if ( getPersistenceContext().isLoadFinished() ) {
+						getPersistenceContext().clear();
 					}
-					return list;
-				});
+				} )
+				.thenApply( list -> (List<T>) list );
 	}
 
 	@Override
@@ -269,7 +257,15 @@ public <T> CompletionStage<T> reactiveGet(String entityName, Object id, LockMode
 					.applyGraph( (RootGraphImplementor<T>) 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() ) {
@@ -294,8 +290,11 @@ public ReactiveEntityPersister getEntityPersister(String entityName, Object obje
 	public CompletionStage<Void> 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() );
@@ -303,94 +302,145 @@ public CompletionStage<Void> reactiveInsert(Object entity) {
 				} );
 	}
 
-	private CompletionStage<Void> 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<Void> loop = voidFuture();
+
+		public void then(Supplier<CompletionStage<Void>> step) {
+			loop = loop.thenCompose( v -> step.get() );
 		}
-		else {
-			if ( firePreInsert( entity, null, state, persister ) ) {
-				return voidFuture();
+
+		public void whenComplete(BiConsumer<Void, Throwable> consumer) {
+			loop = loop.whenComplete( consumer );
+		}
+	}
+
+	private CompletionStage<Void> recreateCollections(Object entity, Object id, EntityPersister persister) {
+		final Completable<Void> 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 );
 			}
-			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 );
-					} );
 		}
+		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 boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) {
-		if ( getFactory().getEventListenerGroups().eventListenerGroup_PRE_INSERT.isEmpty() ) {
-			return false;
+	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()'" ) );
 		}
-		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;
+		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 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<?> 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<?> 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()'" );
-			}
-			return completedFuture( 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
@@ -399,7 +449,57 @@ public CompletionStage<Void> 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<Void> removeCollections(Object entity, Object id, EntityPersister persister) {
+		final Completable<Void> 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
@@ -424,19 +524,65 @@ private CompletionStage<Void> 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<Void> removeAndRecreateCollections(Object entity, Object id, EntityPersister persister) {
+		final Completable<Void> 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
@@ -456,6 +602,7 @@ public CompletionStage<Void> reactiveRefresh(Object entity, LockMode lockMode) {
 
 	@Override
 	public CompletionStage<Void> reactiveRefresh(String entityName, Object entity, LockMode lockMode) {
+		checkOpen();
 		final ReactiveEntityPersister persister = getEntityPersister( entityName, entity );
 		final Object id = persister.getIdentifier( entity, this );
 
@@ -466,31 +613,23 @@ public CompletionStage<Void> 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<Object> fromInternalFetchProfile(
-			CascadingFetchProfile cascadingFetchProfile,
-			Supplier<CompletionStage<Object>> supplier) {
+	private CompletionStage<?> fromInternalFetchProfile(CascadingFetchProfile cascadingFetchProfile, Supplier<CompletionStage<?>> supplier) {
 		CascadingFetchProfile previous = getLoadQueryInfluencers().getEnabledCascadingFetchProfile();
-		return voidFuture()
-				.thenCompose( v -> {
+		return supplyStage( () -> {
 					getLoadQueryInfluencers().setEnabledCascadingFetchProfile( cascadingFetchProfile );
 					return supplier.get();
 				} )
@@ -513,38 +652,30 @@ public CompletionStage<Void> reactiveUpsert(Object entity) {
 	public CompletionStage<Void> 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
@@ -604,7 +735,6 @@ public CompletionStage<Void> reactiveDeleteAll(int batchSize, Object... entities
 				.whenComplete( (v, throwable) -> batchingHelperSession.setJdbcBatchSize( jdbcBatchSize ) );
 	}
 
-
 	@Override
 	public CompletionStage<Void> reactiveRefreshAll(Object... entities) {
 		return loop( entities, batchingHelperSession::reactiveRefresh )
@@ -626,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<Object> reactiveInternalLoad(String entityName, Object id, boolean eager, boolean nullable) {
+		Object object = super.internalLoad( entityName, id, eager, nullable );
+		return object instanceof CompletionStage<?>
+				? (CompletionStage<Object>) object
+				: completedFuture( object );
 	}
 
 	@Override
-	public CompletionStage<Object> 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 <T> CompletionStage<T> reactiveFetch(T association, boolean unproxy) {
 		checkOpen();
 		if ( association == null ) {
@@ -725,84 +785,92 @@ public <T> CompletionStage<T> 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 <T> CompletionStage<T> 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
@@ -830,6 +898,33 @@ public <T> RootGraphImplementor<T> getEntityGraph(Class<T> entity, String name)
 		return (RootGraphImplementor<T>) entityGraph;
 	}
 
+	@Override
+	public <R> ReactiveQuery<R> createReactiveQuery(TypedQueryReference<R> typedQueryReference) {
+		checksBeforeQueryCreation();
+		if ( typedQueryReference instanceof SelectionSpecificationImpl<R> specification ) {
+			final CriteriaQuery<R> query = specification.buildCriteria( getCriteriaBuilder() );
+			return new ReactiveQuerySqmImpl<>( (SqmStatement<R>) 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<R> type = (Class<R>) specification.getResultType() == Void.class
+					? null
+					: (Class<R>) specification.getResultType();
+			return new ReactiveQuerySqmImpl<>( (SqmStatement<R>) query, type, this );
+		}
+		@SuppressWarnings("unchecked")
+		// this cast is fine because of all our impls of TypedQueryReference return Class<R>
+		final Class<R> resultType = (Class<R>) typedQueryReference.getResultType();
+		ReactiveQueryImplementor<R> query = (ReactiveQueryImplementor<R>) buildNamedQuery(
+				typedQueryReference.getName(),
+				memento -> createSqmQueryImplementor( resultType, memento ),
+				memento -> createNativeQueryImplementor( resultType, memento )
+		);
+		typedQueryReference.getHints().forEach( query::setHint );
+		return query;
+	}
+
 	@Override
 	public <R> ReactiveSqmQueryImplementor<R> createReactiveQuery(String queryString) {
 		return createReactiveQuery( queryString, null );
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 64c5b3af..5993d189 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<ReactiveValuesResultSet> 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/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java
index 601b17c1..1920acc9 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;
@@ -24,8 +25,8 @@
 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.Query;
 import org.hibernate.query.criteria.HibernateCriteriaBuilder;
 import org.hibernate.query.criteria.JpaCriteriaInsert;
 import org.hibernate.reactive.common.AffectedEntities;
@@ -360,37 +361,6 @@ default SelectionQuery<R> 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<R> 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<R> setOrder(List<Order<? super R>> 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<R> setOrder(Order<? super R> order);
-
 		/**
 		 * Set the {@link EntityGraph} that will be used as a fetch plan for
 		 * the root entity returned by this query.
@@ -885,17 +855,6 @@ default CompletionStage<Void> 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<Void> 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
@@ -1028,6 +987,22 @@ default CompletionStage<Void> 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)
+		 */
+		<R> Query<R> createQuery(TypedQueryReference<R> 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
@@ -1680,6 +1655,22 @@ default <T> CompletionStage<T> get(Class<T> entityClass, Object id, LockModeType
 		@Deprecated
 		<R> Query<R> 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)
+		 */
+		<R> Query<R> createQuery(TypedQueryReference<R> 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/StageQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java
index fb022c49..df0c04c5 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<R> setLockMode(LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public Stage.SelectionQuery<R> setOrder(List<Order<? super R>> orders) {
-		delegate.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public Stage.SelectionQuery<R> setOrder(Order<? super R> order) {
-		delegate.setOrder( (List<Order<? super R>>) order );
-		return this;
-	}
-
 	@Override
 	public Query<R> setPlan(EntityGraph<R> 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 eaf1c9a5..71beb5df 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<T> setLockMode(String alias, LockMode lockMode) {
 		return this;
 	}
 
-	@Override
-	public SelectionQuery<T> setOrder(List<Order<? super T>> orders) {
-		delegate.setOrder( orders );
-		return this;
-	}
-
-	@Override
-	public SelectionQuery<T> setOrder(Order<? super T> order) {
-		delegate.setOrder( order );
-		return this;
-	}
-
 	@Override
 	public SelectionQuery<T> setParameter(String name, Object value) {
 		delegate.setParameter( name, value );
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 150c2ec9..dd25105f 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 <T> EntityGraph<T> createEntityGraph(Class<T> rootType, String graphName)
 		return delegate.createEntityGraph( rootType, graphName );
 	}
 
+	@Override
+	public <R> Query<R> createQuery(TypedQueryReference<R> typedQueryReference) {
+		ReactiveQuery<R> reactiveQuery = delegate.createReactiveQuery( typedQueryReference );
+		return new StageQueryImpl<>( reactiveQuery );
+	}
+
 	@Override @Deprecated
 	public <R> Query<R> 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 c48a7eab..425a0d36 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 <R> Query<R> createQuery(String queryString) {
 		return new StageQueryImpl<>( delegate.createReactiveQuery( queryString ) );
 	}
 
+	@Override
+	public <R> Query<R> createQuery(TypedQueryReference<R> typedQueryReference) {
+		ReactiveQuery<R> reactiveQuery = delegate.createReactiveQuery( typedQueryReference );
+		return new StageQueryImpl<>( reactiveQuery );
+	}
+
 	@Override
 	public <R> SelectionQuery<R> createSelectionQuery(String queryString, Class<R> resultType) {
 		return new StageSelectionQueryImpl<>( delegate.createReactiveSelectionQuery( queryString, resultType ) );
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 adc00ff7..81af936b 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 52a9d920..09000a9e 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 306e619b..7f30d8d2 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 3c3667aa..a8184026 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/main/java/org/hibernate/reactive/util/impl/CompletionStages.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/util/impl/CompletionStages.java
index eb311aba..397ca0e9 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 <T> CompletionStage<T> completedFuture(T value) {
 		return CompletableFuture.completedFuture( value );
 	}
 
+	/**
+	 * Useful for implementing try-finally blocks.
+	 * For example:
+	 * <pre>{@code
+	 * return supplyStage( () -> {
+	 *     // Any error here will get caught
+	 *     ...
+	 *     })
+	 *     .whenComplete( (r,t) -> {
+	 *         // finally block
+	 *         ...
+	 *     })
+	 * }</pre>
+	 */
+	public static <T> CompletionStage<T> supplyStage(Supplier<CompletionStage<T>> 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 <T> CompletionStage<T> failedFuture(Throwable t) {
 		CompletableFuture<T> ret = new CompletableFuture<>();
 		ret.completeExceptionally( t );
diff --git a/jreleaser.yml b/jreleaser.yml
new file mode 100644
index 00000000..c3eeec64
--- /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 977092d9..201b67a8 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 2b507a9a..69053475 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
 }


Test patch:
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 1a5f51e3..07a79379 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();
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 00000000..da157804
--- /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.
+ * <p>
+ * Inspired by the test
+ * {@code org.hibernate.orm.test.boot.database.metadata.MetadataAccessTests}
+ * in Hibernate ORM.
+ * </p>
+ */
+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<Arguments> 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<DialectFactory> {
+			@Override
+			public Class<DialectFactory> getServiceInitiated() {
+				return DialectFactory.class;
+			}
+
+			@Override
+			public DialectFactory initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
+				return new CapturingDialectFactory();
+			}
+		}
+
+		DialectResolutionInfoSource capturedDialectResolutionInfoSource;
+
+		@Override
+		public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
+				throws HibernateException {
+			this.capturedDialectResolutionInfoSource = resolutionInfoSource;
+			return super.buildDialect( configValues, resolutionInfoSource );
+		}
+	}
+}
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 48b73829..00000000
--- 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<? super Book, ?> isbn;
-	SingularAttribute<? super Book, ?> title;
-
-	@Override
-	protected Collection<Class<?>> 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<Object[]> resultList, Book... expectedBooks) {
-		List<Book> books = resultList.stream()
-				.map( objects -> new Book( (String) objects[0], (String) objects[1] ) )
-				.collect( toList() );
-		assertThat( books ).containsExactly( expectedBooks );
-	}
-
-	private SingularAttribute<? super Book, ?> attribute(String name) {
-		MappingMetamodelImpl metamodel = (MappingMetamodelImpl) getSessionFactory().getMetamodel();
-		EntityDomainType<Book> 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;
-		}
-	}
-}
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 00000000..01f85f35
--- /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<Class<?>> annotatedEntities() {
+		return List.of( Book.class );
+	}
+
+	@BeforeEach
+	public void populateDB(VertxTestContext context) {
+		test( context, getSessionFactory().withTransaction( session -> session
+				.persist( hibBook, jpBook, animalFarmBook ) )
+		);
+	}
+
+	static Stream<Arguments> 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<Book> order, List<Book> 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<Book> order, List<Book> 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<Book> order, List<Book> 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<Book> order, List<Book> 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<Book> order, List<Book> expectedList, VertxTestContext context) {
+		final SingularAttribute<? super Book, ?> 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<Book> order, List<Book> expectedList, VertxTestContext context) {
+		final SingularAttribute<? super Book, ?> 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<Book> order, List<Book> expectedList, VertxTestContext context) {
+		final SingularAttribute<? super Book, ?> 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<Book> order, List<Book> expectedList, VertxTestContext context) {
+		final SingularAttribute<? super Book, ?> 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<Order<Book>> orders, List<Book> expectedList, VertxTestContext context) {
+		var columnsSpec = SelectionSpecification
+				.create( Book.class, "from Book" );
+		for ( Order<Book> 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<Arguments> 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<Order<Book>> orders, List<Book> expectedList, VertxTestContext context) {
+		var columnsSpec = SelectionSpecification
+				.create( Book.class, "from Book" );
+		for ( Order<Book> 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<Order<Book>> orders, List<Book> expectedList, VertxTestContext context) {
+		var columnsSpec = SelectionSpecification
+				.create( Book.class, "from Book" );
+		for ( Order<Book> 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<Order<Book>> orders, List<Book> expectedList, VertxTestContext context) {
+		var columnsSpec = SelectionSpecification
+				.create( Book.class, "from Book" );
+		for ( Order<Book> 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<Book, String> title = (SingularAttribute<Book, String>) attribute( "title" );
+		TypedQueryReference<Void> 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<Book, String> title = (SingularAttribute<Book, String>) attribute( "title" );
+		TypedQueryReference<Void> 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<Book, String> title = (SingularAttribute<Book, String>) attribute( "title" );
+		TypedQueryReference<Void> 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<Book, String> title = (SingularAttribute<Book, String>) attribute( "title" );
+		TypedQueryReference<Void> 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<? super Book, ?> attribute(String name) {
+		MappingMetamodelImpl metamodel = (MappingMetamodelImpl) getSessionFactory().getMetamodel();
+		EntityDomainType<Book> 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;
+		}
+	}
+}
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 29ef9de8..00000000
--- 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();
-	}
-}
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 8a6f631f..2d666598 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<Class<?>> 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;
+
+	}
 }
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 f0d497ee..c0a08112 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<? extends Dialect> dialect;
 
-		DBType(TestableDatabase configuration, int defaultPort, String jdbcDriver, Class<? extends Dialect> dialect, String... aliases) {
+		DBType(TestableDatabase configuration, String productName, int defaultPort, String jdbcDriver, Class<? extends Dialect> 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<? extends Dialect> getDialectClass() {
 			return dialect;
 		}
+
+		/**
+		 * The minimum version of the database supported by the dialect.
+		 * <p>
+		 * We use reflection because it's not accessible from the tests.
+		 * Copied from MetadataAccessTests in Hibernate ORM.
+		 * </p>
+		 */
+		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<? extends Dialect> 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() {
 	}
-
 }
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 a636bc5a..d57f9103 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" );