diff --git a/README.md b/README.md index a44faa31b..7280fce86 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Hibernate Reactive has been tested with: - CockroachDB v24 - MS SQL Server 2022 - Oracle 23 -- [Hibernate ORM][] 6.6.3.Final +- [Hibernate ORM][] 7.0.0.Beta3 - [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.11 - [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.11 - [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.11 diff --git a/documentation/src/main/asciidoc/reference/introduction.adoc b/documentation/src/main/asciidoc/reference/introduction.adoc index 40b4847bc..4f2237edb 100644 --- a/documentation/src/main/asciidoc/reference/introduction.adoc +++ b/documentation/src/main/asciidoc/reference/introduction.adoc @@ -578,8 +578,8 @@ custom reactive identifier generator. === JSON Mapping -:orm-json-basic-mapping: https://docs.jboss.org/hibernate/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-json -:orm-json-embeddable-mapping: https://docs.jboss.org/hibernate/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#_jsonxml_aggregate_embeddable_mapping +:orm-json-basic-mapping: https://docs.jboss.org/hibernate/orm/7.0/userguide/html_single/Hibernate_User_Guide.html#basic-mapping-json +:orm-json-embeddable-mapping: https://docs.jboss.org/hibernate/orm/7.0/userguide/html_single/Hibernate_User_Guide.html#_jsonxml_aggregate_embeddable_mapping :string-to-json-converter: https://github.com/hibernate/hibernate-reactive/blob/main/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/StringToJsonConverter.java Like in Hibernate ORM, it's possible to map a JSON field using the {orm-json-basic-mapping}[SqlTypes.JSON] diff --git a/gradle.properties b/gradle.properties index 55ec51fc8..0ee0c1936 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,12 +35,12 @@ org.gradle.java.installations.auto-download=false #enableMavenLocalRepo = true # The default Hibernate ORM version (override using `-PhibernateOrmVersion=the.version.you.want`) -hibernateOrmVersion = 6.6.3.Final +hibernateOrmVersion = 7.0.0.Beta3 # Override default Hibernate ORM Gradle plugin version # Using the stable version because I don't know how to configure the build to download the snapshot version from # a remote repository -#hibernateOrmGradlePluginVersion = 6.6.3.Final +hibernateOrmGradlePluginVersion = 7.0.0.Beta2 # If set to true, skip Hibernate ORM version parsing (default is true, if set to null) # this is required when using intervals or weird versions or the build will fail diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java index 8ff274857..066ec9de6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/PreparedStatementAdaptor.java @@ -5,11 +5,6 @@ */ package org.hibernate.reactive.adaptor.impl; -import io.vertx.core.buffer.Buffer; - -import org.hibernate.AssertionFailure; -import org.hibernate.HibernateException; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -39,12 +34,23 @@ import java.util.Calendar; import java.util.function.Function; +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; +import org.hibernate.reactive.logging.impl.Log; + +import io.vertx.core.buffer.Buffer; + +import static java.lang.invoke.MethodHandles.lookup; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; + /** * Collects parameter bindings from Hibernate core code * that expects a JDBC {@link PreparedStatement}. */ public class PreparedStatementAdaptor implements PreparedStatement { + private static final Log LOG = make( Log.class, lookup() ); + @FunctionalInterface public interface Binder { void bind(PreparedStatement statement) throws SQLException; @@ -315,7 +321,7 @@ public void setNClob(int parameterIndex, Reader reader, long length) { @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override @@ -540,7 +546,7 @@ public int[] executeBatch() { @Override public Connection getConnection() { - throw new UnsupportedOperationException(); + throw LOG.unexpectedConnectionRequest(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java index 0bfcd89d5..b75133699 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java @@ -36,8 +36,9 @@ import java.util.NoSuchElementException; import java.util.function.Function; -import org.hibernate.engine.jdbc.BlobProxy; -import org.hibernate.engine.jdbc.ClobProxy; +import org.hibernate.engine.jdbc.proxy.BlobProxy; +import org.hibernate.engine.jdbc.proxy.ClobProxy; +import org.hibernate.reactive.logging.impl.Log; import org.hibernate.type.descriptor.jdbc.JdbcType; import io.vertx.core.buffer.Buffer; @@ -48,8 +49,10 @@ import io.vertx.sqlclient.desc.ColumnDescriptor; import io.vertx.sqlclient.impl.RowBase; +import static java.lang.invoke.MethodHandles.lookup; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; /** * An adaptor that allows Hibernate core code which expects a JDBC @@ -57,6 +60,8 @@ */ public class ResultSetAdaptor implements ResultSet { + private static final Log LOG = make( Log.class, lookup() ); + private final Iterator iterator; private final List columnDescriptors; @@ -866,12 +871,12 @@ public NClob getNClob(String columnLabel) { @Override public SQLXML getSQLXML(int columnIndex) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override public SQLXML getSQLXML(String columnLabel) { - throw new UnsupportedOperationException(); + throw LOG.unsupportedXmlType(); } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java index fb72b2624..e63d3215d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/boot/spi/ReactiveMetadataImplementor.java @@ -7,7 +7,6 @@ import org.hibernate.boot.spi.AbstractDelegatingMetadata; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.reactive.query.internal.ReactiveNamedObjectRepositoryImpl; @@ -18,7 +17,7 @@ public ReactiveMetadataImplementor(MetadataImplementor delegate) { } @Override - public NamedObjectRepository buildNamedQueryRepository(SessionFactoryImplementor sessionFactory) { - return new ReactiveNamedObjectRepositoryImpl( delegate().buildNamedQueryRepository( sessionFactory ) ); + public NamedObjectRepository buildNamedQueryRepository() { + return new ReactiveNamedObjectRepositoryImpl( super.buildNamedQueryRepository() ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java deleted file mode 100644 index 0773ab033..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/dialect/ReactiveOracleSqlAstTranslator.java +++ /dev/null @@ -1,43 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.dialect; - -import org.hibernate.dialect.OracleSqlAstTranslator; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.entity.mutation.EntityTableMapping; -import org.hibernate.reactive.sql.model.ReactiveDeleteOrUpsertOperation; -import org.hibernate.sql.ast.tree.Statement; -import org.hibernate.sql.exec.spi.JdbcOperation; -import org.hibernate.sql.model.MutationOperation; -import org.hibernate.sql.model.internal.OptionalTableUpdate; -import org.hibernate.sql.model.jdbc.UpsertOperation; - -public class ReactiveOracleSqlAstTranslator extends OracleSqlAstTranslator { - public ReactiveOracleSqlAstTranslator( - SessionFactoryImplementor sessionFactory, - Statement statement) { - super( sessionFactory, statement ); - } - - @Override - public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableUpdate) { - renderUpsertStatement( optionalTableUpdate ); - - final UpsertOperation upsertOperation = new UpsertOperation( - optionalTableUpdate.getMutatingTable().getTableMapping(), - optionalTableUpdate.getMutationTarget(), - getSql(), - getParameterBinders() - ); - - return new ReactiveDeleteOrUpsertOperation( - optionalTableUpdate.getMutationTarget(), - (EntityTableMapping) optionalTableUpdate.getMutatingTable().getTableMapping(), - upsertOperation, - optionalTableUpdate - ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java index 034c9f583..3c815b2a8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java @@ -21,6 +21,8 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.PropertyValueException; +import org.hibernate.TransientObjectException; +import org.hibernate.action.internal.AbstractEntityInsertAction; import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.action.internal.EntityDeleteAction; import org.hibernate.action.internal.UnresolvedEntityInsertActions; @@ -28,13 +30,13 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess; import org.hibernate.action.spi.Executable; import org.hibernate.cache.CacheException; +import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.ComparableExecutable; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.ExecutableList; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metadata.ClassMetadata; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; @@ -59,7 +61,6 @@ import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; -import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; @@ -520,11 +521,21 @@ public CompletionStage executeInserts() { */ public CompletionStage executeActions() { if ( hasUnresolvedEntityInsertActions() ) { - return failedFuture( new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." ) ); + final AbstractEntityInsertAction insertAction = unresolvedInsertions + .getDependentEntityInsertActions() + .iterator() + .next(); + final NonNullableTransientDependencies transientEntities = insertAction.findNonNullableTransientEntities(); + final Object transientEntity = transientEntities.getNonNullableTransientEntities().iterator().next(); + final String path = transientEntities.getNonNullableTransientPropertyPaths(transientEntity).iterator().next(); + //TODO: should be TransientPropertyValueException + throw new TransientObjectException( "Persistent instance of '" + insertAction.getEntityName() + + "' with id '" + insertAction.getId() + + "' references an unsaved transient instance via attribute '" + path + + "' (save the transient instance before flushing)" ); } CompletionStage ret = voidFuture(); - for ( OrderedActions action : ORDERED_OPERATIONS ) { ret = ret.thenCompose( v -> executeActions( action.getActions( this ) ) ); } @@ -738,26 +749,6 @@ public int numberOfInsertions() { return insertions.size(); } -// public TransactionCompletionProcesses getTransactionCompletionProcesses() { -// return new TransactionCompletionProcesses( beforeTransactionProcesses(), afterTransactionProcesses() ); -// } -// -// /** -// * Bind transaction completion processes to make them shared between primary and secondary session. -// * Transaction completion processes are always executed by transaction owner (primary session), -// * but can be registered using secondary session too. -// * -// * @param processes Transaction completion processes. -// * @param isTransactionCoordinatorShared Flag indicating shared transaction context. -// */ -// public void setTransactionCompletionProcesses( -// TransactionCompletionProcesses processes, -// boolean isTransactionCoordinatorShared) { -// this.isTransactionCoordinatorShared = isTransactionCoordinatorShared; -// this.beforeTransactionProcesses = processes.beforeTransactionCompletionProcesses; -// this.afterTransactionProcesses = processes.afterTransactionCompletionProcesses; -// } - public void sortCollectionActions() { if ( isOrderUpdatesEnabled() ) { // sort the updates by fk @@ -864,32 +855,6 @@ public void unScheduleDeletion(EntityEntry entry, Object rescuedEntity) { throw new AssertionFailure( "Unable to perform un-delete for instance " + entry.getEntityName() ); } -// /** -// * Used by the owning session to explicitly control serialization of the action queue -// * -// * @param oos The stream to which the action queue should get written -// * -// * @throws IOException Indicates an error writing to the stream -// */ -// public void serialize(ObjectOutputStream oos) throws IOException { -// LOG.trace( "Serializing action-queue" ); -// if ( unresolvedInsertions == null ) { -// unresolvedInsertions = new UnresolvedEntityInsertActions(); -// } -// unresolvedInsertions.serialize( oos ); -// -// for ( ListProvider p : EXECUTABLE_LISTS_MAP.values() ) { -// ExecutableList l = p.get( this ); -// if ( l == null ) { -// oos.writeBoolean( false ); -// } -// else { -// oos.writeBoolean( true ); -// l.writeExternal( oos ); -// } -// } -// } - private abstract static class AbstractTransactionCompletionProcessQueue { final ReactiveSession session; @@ -994,21 +959,6 @@ public CompletionStage afterTransactionCompletion(boolean success) { } } -// /** -// * Wrapper class allowing to bind the same transaction completion process queues in different sessions. -// */ -// public static class TransactionCompletionProcesses { -// private final BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcesses; -// private final AfterTransactionCompletionProcessQueue afterTransactionCompletionProcesses; -// -// private TransactionCompletionProcesses( -// BeforeTransactionCompletionProcessQueue beforeTransactionCompletionProcessQueue, -// AfterTransactionCompletionProcessQueue afterTransactionCompletionProcessQueue) { -// this.beforeTransactionCompletionProcesses = beforeTransactionCompletionProcessQueue; -// this.afterTransactionCompletionProcesses = afterTransactionCompletionProcessQueue; -// } -// } - /** * Order the {@link #insertions} queue such that we group inserts against the same entity together (without * violating constraints). The original order is generated by cascade order, which in turn is based on the @@ -1152,26 +1102,23 @@ public void sort(List insertions) { */ private void addParentChildEntityNames(ReactiveEntityInsertAction action, BatchIdentifier batchIdentifier) { Object[] propertyValues = action.getState(); - ClassMetadata classMetadata = action.getPersister().getClassMetadata(); - if ( classMetadata != null ) { - Type[] propertyTypes = classMetadata.getPropertyTypes(); - Type identifierType = classMetadata.getIdentifierType(); - - for ( int i = 0; i < propertyValues.length; i++ ) { - Object value = propertyValues[i]; - if (value!=null) { - Type type = propertyTypes[i]; - addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, value ); - } + Type[] propertyTypes = action.getPersister().getPropertyTypes(); + Type identifierType = action.getPersister().getIdentifierType(); + + for ( int i = 0; i < propertyValues.length; i++ ) { + Object value = propertyValues[i]; + if (value!=null) { + Type type = propertyTypes[i]; + addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, value ); } + } - if ( identifierType.isComponentType() ) { - CompositeType compositeType = (CompositeType) identifierType; - Type[] compositeIdentifierTypes = compositeType.getSubtypes(); + if ( identifierType.isComponentType() ) { + CompositeType compositeType = (CompositeType) identifierType; + Type[] compositeIdentifierTypes = compositeType.getSubtypes(); - for ( Type type : compositeIdentifierTypes ) { - addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, null ); - } + for ( Type type : compositeIdentifierTypes ) { + addParentChildEntityNameByPropertyAndValue( action, batchIdentifier, type, null ); } } } @@ -1275,10 +1222,9 @@ public boolean equals(Object o) { if ( this == o ) { return true; } - if ( !( o instanceof BatchIdentifier ) ) { + if ( !( o instanceof BatchIdentifier that ) ) { return false; } - BatchIdentifier that = (BatchIdentifier) o; return Objects.equals( entityName, that.entityName ); } @@ -1315,9 +1261,7 @@ boolean hasAnyChildEntityNames(BatchIdentifier batchIdentifier) { /** * Check if this {@link BatchIdentifier} has a parent or grandparent * matching the given {@link BatchIdentifier reference. - * * @param batchIdentifier {@link BatchIdentifier} reference - * * @return this {@link BatchIdentifier} has a parent matching the given {@link BatchIdentifier reference */ boolean hasParent(BatchIdentifier batchIdentifier) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java index 9964ffe93..1b24e567d 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/dialect/internal/ReactiveStandardDialectResolver.java @@ -8,19 +8,8 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Database; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.dialect.spi.DialectResolver; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.persister.entity.mutation.EntityMutationTarget; -import org.hibernate.reactive.dialect.ReactiveOracleSqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslator; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.Statement; -import org.hibernate.sql.exec.spi.JdbcOperation; -import org.hibernate.sql.model.MutationOperation; -import org.hibernate.sql.model.internal.OptionalTableUpdate; import static org.hibernate.dialect.CockroachDialect.parseVersion; @@ -36,30 +25,7 @@ public Dialect resolveDialect(DialectResolutionInfo info) { for ( Database database : Database.values() ) { if ( database.matchesResolutionInfo( info ) ) { - Dialect dialect = database.createDialect( info ); - if ( info.getDatabaseName().toUpperCase().startsWith( "ORACLE" ) ) { - return new DialectDelegateWrapper( dialect ) { - @Override - public MutationOperation createOptionalTableUpdateOperation( - EntityMutationTarget mutationTarget, - OptionalTableUpdate optionalTableUpdate, - SessionFactoryImplementor factory) { - return new ReactiveOracleSqlAstTranslator<>( factory, optionalTableUpdate ) - .createMergeOperation( optionalTableUpdate ); - } - - @Override - public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { - return new StandardSqlAstTranslatorFactory() { - @Override - protected SqlAstTranslator buildTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { - return new ReactiveOracleSqlAstTranslator<>( sessionFactory, statement ); - } - }; - } - }; - } - return dialect; + return database.createDialect( info ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java deleted file mode 100644 index a8fd73a6a..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveJdbcEnvironment.java +++ /dev/null @@ -1,31 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.engine.jdbc.env.internal; - -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl; -import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.hibernate.sql.ast.SqlAstTranslatorFactory; - -public class ReactiveJdbcEnvironment extends JdbcEnvironmentImpl { - - public ReactiveJdbcEnvironment(ServiceRegistryImplementor registry, Dialect dialect) { - super( registry, dialect ); - } - - @Deprecated - public ReactiveJdbcEnvironment(ServiceRegistryImplementor registry, Dialect dialect, DatabaseMetaData metaData) throws SQLException { - super( registry, dialect, metaData ); - } - - @Override - public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { - return super.getSqlAstTranslatorFactory(); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java index fe9873791..3253be9dd 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/env/internal/ReactiveMutationExecutor.java @@ -10,7 +10,6 @@ import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SQLServerDialect; @@ -104,15 +103,14 @@ default CompletionStage performReactiveBatchedOperations( private static String createInsert(String insertSql, String identifierColumnName, Dialect dialect) { String sql = insertSql; final String sqlEnd = " returning " + identifierColumnName; - Dialect realDialect = DialectDelegateWrapper.extractRealDialect( dialect ); - if ( realDialect instanceof MySQLDialect ) { + if ( dialect instanceof MySQLDialect ) { // For some reason ORM generates a query with an invalid syntax int index = sql.lastIndexOf( sqlEnd ); return index > -1 ? sql.substring( 0, index ) : sql; } - if ( realDialect instanceof SQLServerDialect ) { + if ( dialect instanceof SQLServerDialect ) { int index = sql.lastIndexOf( sqlEnd ); // FIXME: this is a hack for HHH-16365 if ( index > -1 ) { @@ -128,12 +126,12 @@ private static String createInsert(String insertSql, String identifierColumnName } return sql; } - if ( realDialect instanceof DB2Dialect ) { + if ( dialect instanceof DB2Dialect ) { // ORM query: select id from new table ( insert into IntegerTypeEntity values ( )) // Correct : select id from new table ( insert into LongTypeEntity (id) values (default)) return sql.replace( " values ( ))", " (" + identifierColumnName + ") values (default))" ); } - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { final String valuesStr = " values ( )"; int index = sql.lastIndexOf( sqlEnd ); // remove "returning id" since it's added via diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java index 80e24fac7..4f1d62325 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorStandard.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.concurrent.CompletionStage; +import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.OperationResultChecker; import org.hibernate.engine.jdbc.mutation.ParameterUsage; @@ -99,7 +100,8 @@ protected void performSelfExecutingOperations( @Override protected void performBatchedOperations( ValuesAnalysis valuesAnalysis, - TableInclusionChecker inclusionChecker) { + TableInclusionChecker inclusionChecker, + Batch.StaleStateMapper staleStateMapper) { throw LOG.nonReactiveMethodCall( "performReactiveBatchedOperations" ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java index e9d68b00e..0887dfff6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java @@ -25,7 +25,7 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; -import org.hibernate.id.Assigned; +import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; @@ -134,7 +134,7 @@ protected CompletionStage reactiveSaveWithGeneratedId( // and is not yet available generatedId = null; } - else if ( generator instanceof Assigned ) { + else if ( !generator.generatesOnInsert() ) { // get it from the entity later, since we need // the @PrePersist callback to happen first generatedId = null; @@ -144,55 +144,58 @@ else if ( generator instanceof Assigned ) { // the entity instance, so it will be available // to the entity in the @PrePersist callback if ( generator instanceof ReactiveIdentifierGenerator ) { - return ( (ReactiveIdentifierGenerator) generator ) - .generate( ( ReactiveConnectionSupplier ) source, entity ) - .thenApply( id -> castToIdentifierType( id, persister ) ) - .thenCompose( gid -> performSaveWithId( - entity, - context, - source, - persister, - generator, - gid, - requiresImmediateIdAccess, - false - ) ); + return generateId( entity, source, (ReactiveIdentifierGenerator) generator, persister ) + .thenCompose( gid -> { + if ( gid == SHORT_CIRCUIT_INDICATOR ) { + source.getIdentifier( entity ); + return voidFuture(); + } + persister.setIdentifier( entity, gid, source ); + return reactivePerformSave( + entity, + gid, + persister, + generatedOnExecution, + context, + source, + false + ); + } ); } generatedId = ( (BeforeExecutionGenerator) generator ).generate( source, entity, null, INSERT ); + if ( generatedId == SHORT_CIRCUIT_INDICATOR ) { + source.getIdentifier( entity ); + return voidFuture(); + } + persister.setIdentifier( entity, generatedId, source ); } final Object id = castToIdentifierType( generatedId, persister ); - return reactivePerformSave( entity, id, persister, generatedOnExecution, context, source, requiresImmediateIdAccess ); + final boolean delayIdentityInserts = !source.isTransactionInProgress() && !requiresImmediateIdAccess && generatedOnExecution; + return reactivePerformSave( entity, id, persister, generatedOnExecution, context, source, delayIdentityInserts ); } - private CompletionStage performSaveWithId( + private CompletionStage generateId( Object entity, - C context, EventSource source, - EntityPersister persister, - Generator generator, - Object generatedId, - boolean requiresImmediateIdAccess, - boolean generatedOnExecution) { - if ( generatedId == null ) { - throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); - } - if ( generatedId == SHORT_CIRCUIT_INDICATOR ) { - source.getIdentifier( entity ); - return voidFuture(); - } - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Generated identifier: %s, using strategy: %s", - persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), - generator.getClass().getName() - ); - } - final boolean delayIdentityInserts = - !source.isTransactionInProgress() - && !requiresImmediateIdAccess - && generatedOnExecution; - return reactivePerformSave( entity, generatedId, persister, false, context, source, delayIdentityInserts ); + ReactiveIdentifierGenerator generator, + EntityPersister persister) { + return generator + .generate( (ReactiveConnectionSupplier) source, entity ) + .thenApply( id -> castToIdentifierType( id, persister ) ) + .thenCompose( generatedId -> { + if ( generatedId == null ) { + return failedFuture( new IdentifierGenerationException( "null id generated for: " + entity.getClass() ) ); + } + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Generated identifier: %s, using strategy: %s", + persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), + generator.getClass().getName() + ); + } + return completedFuture( generatedId ); + } ); } /** @@ -229,13 +232,11 @@ protected CompletionStage reactivePerformSave( processIfSelfDirtinessTracker( entity, SelfDirtinessTracker::$$_hibernate_clearDirtyAttributes ); processIfManagedEntity( entity, managedEntity -> managedEntity.$$_hibernate_setUseTracker( true ) ); - if ( persister.getGenerator() instanceof Assigned ) { + final Generator generator = persister.getGenerator(); + if ( !generator.generatesOnInsert() || generator instanceof CompositeNestedGeneratedValueGenerator ) { id = persister.getIdentifier( entity, source ); if ( id == null ) { - throw new IdentifierGenerationException( - "Identifier of entity '" + persister.getEntityName() - + "' must be manually assigned before calling 'persist()'" - ); + return failedFuture( new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'persist()'" ) ); } } @@ -420,7 +421,7 @@ private CompletionStage addInsertAction( boolean useIdentityColumn, EventSource source, boolean shouldDelayIdentityInserts) { - final ReactiveActionQueue actionQueue = source.unwrap( ReactiveSession.class ).getReactiveActionQueue(); + final ReactiveActionQueue actionQueue = source.unwrap(ReactiveSession.class).getReactiveActionQueue(); if ( useIdentityColumn ) { final ReactiveEntityIdentityInsertAction insert = new ReactiveEntityIdentityInsertAction( values, entity, persister, false, source, shouldDelayIdentityInserts diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java index 7832298e0..2df247665 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveLockEventListener.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.event.impl; -import java.lang.invoke.MethodHandles; import java.util.concurrent.CompletionStage; import org.hibernate.HibernateException; @@ -20,7 +19,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; -import org.hibernate.event.internal.AbstractReassociateEventListener; +import org.hibernate.event.internal.DefaultLockEventListener; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LockEvent; import org.hibernate.event.spi.LockEventListener; @@ -33,19 +32,24 @@ import org.hibernate.reactive.engine.impl.ReactiveEntityVerifyVersionProcess; import org.hibernate.reactive.event.ReactiveLockEventListener; import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.reactive.session.ReactiveSession; +import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.pretty.MessageHelper.infoString; +import static org.hibernate.reactive.logging.impl.LoggerFactory.make; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; -public class DefaultReactiveLockEventListener extends AbstractReassociateEventListener - implements LockEventListener, ReactiveLockEventListener { +public class DefaultReactiveLockEventListener extends DefaultLockEventListener implements LockEventListener, ReactiveLockEventListener { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + private static final Log LOG = make( Log.class, lookup() ); + + @Override + public void onLock(LockEvent event) throws HibernateException { + throw LOG.nonReactiveMethodCall( "reactiveOnLock" ); + } @Override public CompletionStage reactiveOnLock(LockEvent event) throws HibernateException { @@ -96,11 +100,13 @@ private CompletionStage lockEntry( if ( entry == null ) { final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity ); final Object id = persister.getIdentifier( entity, source ); - return ForeignKeys.isNotTransient( event.getEntityName(), entity, Boolean.FALSE, source ) + return ForeignKeys + .isNotTransient( event.getEntityName(), entity, Boolean.FALSE, source ) .thenCompose( trans -> { if ( !trans ) { return failedFuture( new TransientObjectException( - "cannot lock an unsaved transient instance: " + persister.getEntityName() ) ); + "Cannot lock unsaved transient instance of entity '" + persister.getEntityName() + "'" + ) ); } final EntityEntry e = reassociate( event, entity, id, persister ); @@ -227,9 +233,4 @@ private CompletionStage doUpgradeLock( throw he; } } - - @Override - public void onLock(LockEvent event) throws HibernateException { - throw new UnsupportedOperationException(); - } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java index 1d80f3634..783c02ec8 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveResolveNaturalIdEventListener.java @@ -10,7 +10,6 @@ import org.hibernate.HibernateException; import org.hibernate.engine.spi.NaturalIdResolutions; -import org.hibernate.event.internal.AbstractLockUpgradeEventListener; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.ResolveNaturalIdEvent; import org.hibernate.event.spi.ResolveNaturalIdEventListener; @@ -27,10 +26,9 @@ import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; /** - * A reactific {@link org.hibernate.event.internal.DefaultResolveNaturalIdEventListener}. + * A reactive {@link org.hibernate.event.internal.DefaultResolveNaturalIdEventListener}. */ -public class DefaultReactiveResolveNaturalIdEventListener extends AbstractLockUpgradeEventListener - implements ReactiveResolveNaturalIdEventListener, ResolveNaturalIdEventListener { +public class DefaultReactiveResolveNaturalIdEventListener implements ReactiveResolveNaturalIdEventListener, ResolveNaturalIdEventListener { private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java deleted file mode 100644 index 36d85f353..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/ReactiveInsertGeneratedIdentifierDelegate.java +++ /dev/null @@ -1,116 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.generator.values; - -import java.sql.PreparedStatement; -import java.util.concurrent.CompletionStage; - -import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; -import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.generator.EventType; -import org.hibernate.generator.values.GeneratedValues; -import org.hibernate.id.insert.Binder; -import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; -import org.hibernate.jdbc.Expectation; -import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; -import org.hibernate.sql.model.ast.builder.TableInsertBuilder; -import org.hibernate.sql.model.ast.builder.TableMutationBuilder; -import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; - -public class ReactiveInsertGeneratedIdentifierDelegate implements InsertGeneratedIdentifierDelegate, ReactiveGeneratedValuesMutationDelegate { - private final InsertGeneratedIdentifierDelegate delegate; - - public ReactiveInsertGeneratedIdentifierDelegate(InsertGeneratedIdentifierDelegate delegate) { - this.delegate = delegate; - } - - @Override - public TableInsertBuilder createTableInsertBuilder( - BasicEntityIdentifierMapping identifierMapping, - Expectation expectation, - SessionFactoryImplementor sessionFactory) { - return delegate.createTableInsertBuilder( identifierMapping, expectation, sessionFactory ); - } - - @Override - public PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session) { - return delegate.prepareStatement( insertSql, session ); - } - - @Override - public Object performInsert( - PreparedStatementDetails insertStatementDetails, - JdbcValueBindings valueBindings, - Object entity, - SharedSessionContractImplementor session) { - return delegate.performInsert( insertStatementDetails, valueBindings, entity, session ); - } - - @Override - public String prepareIdentifierGeneratingInsert(String insertSQL) { - return delegate.prepareIdentifierGeneratingInsert( insertSQL ); - } - - @Override - public Object performInsert(String insertSQL, SharedSessionContractImplementor session, Binder binder) { - return delegate.performInsert( insertSQL, session, binder ); - } - - @Override - public GeneratedValues performInsertReturning( - String insertSQL, - SharedSessionContractImplementor session, - Binder binder) { - return delegate.performInsertReturning( insertSQL, session, binder ); - } - - @Override - public TableMutationBuilder createTableMutationBuilder( - Expectation expectation, - SessionFactoryImplementor sessionFactory) { - return delegate.createTableMutationBuilder( expectation, sessionFactory ); - } - - @Override - public GeneratedValues performMutation( - PreparedStatementDetails statementDetails, - JdbcValueBindings valueBindings, - Object entity, - SharedSessionContractImplementor session) { - return delegate.performMutation( statementDetails, valueBindings, entity, session ); - } - - @Override - public EventType getTiming() { - return delegate.getTiming(); - } - - @Override - public boolean supportsArbitraryValues() { - return delegate.supportsArbitraryValues(); - } - - @Override - public boolean supportsRowId() { - return delegate.supportsRowId(); - } - - @Override - public JdbcValuesMappingProducer getGeneratedValuesMappingProducer() { - return delegate.getGeneratedValuesMappingProducer(); - } - - @Override - public CompletionStage reactivePerformMutation( - PreparedStatementDetails singleStatementDetails, - JdbcValueBindings jdbcValueBindings, - Object modelReference, - SharedSessionContractImplementor session) { - return null; - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java index 8854f22d8..70495ccea 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java @@ -63,14 +63,14 @@ public class ReactiveGeneratedValuesHelper { * * @see GeneratedValuesHelper#getGeneratedValuesDelegate(EntityPersister, EventType) */ - public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate( - EntityPersister persister, - EventType timing) { + public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate(EntityPersister persister, EventType timing) { final boolean hasGeneratedProperties = !persister.getGeneratedProperties( timing ).isEmpty(); final boolean hasRowId = timing == EventType.INSERT && persister.getRowIdMapping() != null; final Dialect dialect = persister.getFactory().getJdbcServices().getDialect(); - if ( hasRowId && dialect.supportsInsertReturning() && dialect.supportsInsertReturningRowId() + if ( hasRowId + && dialect.supportsInsertReturning() + && dialect.supportsInsertReturningRowId() && noCustomSql( persister, timing ) ) { // Special case for RowId on INSERT, since GetGeneratedKeysDelegate doesn't support it // make InsertReturningDelegate the preferred method if the dialect supports it diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java index 4e44eb7d0..7121ea5c4 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java @@ -6,6 +6,7 @@ package org.hibernate.reactive.id; import org.hibernate.Incubating; +import org.hibernate.generator.Generator; import org.hibernate.id.IdentifierGenerator; import org.hibernate.reactive.session.ReactiveConnectionSupplier; @@ -24,7 +25,7 @@ * @see IdentifierGenerator */ @Incubating -public interface ReactiveIdentifierGenerator { +public interface ReactiveIdentifierGenerator extends Generator { /** * Returns a generated identifier, via a {@link CompletionStage}. diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java deleted file mode 100644 index 78eed431d..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/factory/spi/ReactiveIdentifierGeneratorFactoryInitiator.java +++ /dev/null @@ -1,27 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.id.factory.spi; - -import java.util.Map; - -import org.hibernate.boot.registry.StandardServiceInitiator; -import org.hibernate.id.factory.IdentifierGeneratorFactory; -import org.hibernate.reactive.id.impl.ReactiveIdentifierGeneratorFactory; -import org.hibernate.service.spi.ServiceRegistryImplementor; - -public class ReactiveIdentifierGeneratorFactoryInitiator implements StandardServiceInitiator { - public static final ReactiveIdentifierGeneratorFactoryInitiator INSTANCE = new ReactiveIdentifierGeneratorFactoryInitiator(); - - @Override - public Class getServiceInitiated() { - return IdentifierGeneratorFactory.class; - } - - @Override - public IdentifierGeneratorFactory initiateService(Map configurationValues, ServiceRegistryImplementor registry) { - return new ReactiveIdentifierGeneratorFactory( registry ); - } -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java index 889f3d69a..dd9702c69 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/EmulatedSequenceReactiveIdentifierGenerator.java @@ -5,8 +5,15 @@ */ package org.hibernate.reactive.id.impl; +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.id.enhanced.TableStructure; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; @@ -25,6 +32,10 @@ */ public class EmulatedSequenceReactiveIdentifierGenerator extends TableReactiveIdentifierGenerator { + public EmulatedSequenceReactiveIdentifierGenerator(TableStructure structure, RuntimeModelCreationContext runtimeModelCreationContext) { + super( structure, runtimeModelCreationContext ); + } + @Override public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { super.configure( type, params, serviceRegistry ); @@ -86,20 +97,44 @@ protected Object[] selectParameters() { } @Override - protected String buildSelectQuery() { + protected String buildSelectQuery(Dialect dialect) { return "select tbl." + valueColumnName + " from " + renderedTableName + " tbl"; } @Override - protected String buildUpdateQuery() { + protected String buildUpdateQuery(Dialect dialect) { + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=$1" + + " where " + valueColumnName + "=$2"; + + } + if ( dialect instanceof SQLServerDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=@P1" + + " where " + valueColumnName + "=@P2"; + + } + if ( dialect instanceof OracleDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=:1" + + " where " + valueColumnName + "=:2"; + + } return "update " + renderedTableName + " set " + valueColumnName + "=?" + " where " + valueColumnName + "=?"; } @Override - protected String buildInsertQuery() { - return "insert into " + renderedTableName + " (" + valueColumnName + ") " - + " values (?)"; + protected String buildInsertQuery(Dialect dialect) { + final String sql = "insert into " + renderedTableName + " (" + valueColumnName + ") values "; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + "($1)"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + "(@P1)"; + } + if ( dialect instanceof OracleDialect ) { + return sql + "(:1)"; + } + return sql + "(?)"; } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java deleted file mode 100644 index 1e3596294..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveIdentifierGeneratorFactory.java +++ /dev/null @@ -1,143 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.id.impl; - -import java.lang.invoke.MethodHandles; -import java.util.Properties; - -import org.hibernate.MappingException; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; -import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; -import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; -import org.hibernate.generator.BeforeExecutionGenerator; -import org.hibernate.generator.Generator; -import org.hibernate.generator.GeneratorCreationContext; -import org.hibernate.generator.OnExecutionGenerator; -import org.hibernate.id.Configurable; -import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.PersistentIdentifierGenerator; -import org.hibernate.id.SelectGenerator; -import org.hibernate.id.enhanced.DatabaseStructure; -import org.hibernate.id.enhanced.SequenceStructure; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.id.enhanced.TableGenerator; -import org.hibernate.id.enhanced.TableStructure; -import org.hibernate.id.factory.internal.StandardIdentifierGeneratorFactory; -import org.hibernate.reactive.id.ReactiveIdentifierGenerator; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; -import org.hibernate.reactive.provider.Settings; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.Type; - -public class ReactiveIdentifierGeneratorFactory extends StandardIdentifierGeneratorFactory { - - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - - private final ServiceRegistry serviceRegistry; - - public ReactiveIdentifierGeneratorFactory(ServiceRegistry serviceRegistry) { - super( serviceRegistry ); - this.serviceRegistry = serviceRegistry; - } - - @Override - public Generator createIdentifierGenerator(String strategy, Type type, GeneratorCreationContext creationContext, Properties config) { - Object generator; - try { - generator = super.createIdentifierGenerator( strategy, type, creationContext, config ); - } - catch ( MappingException ignored ) { - try { - final Class clazz = generatorClassForName( strategy ); - generator = clazz.getConstructor().newInstance(); - if ( generator instanceof Configurable ) { - ( (Configurable) generator ).configure( type, config, serviceRegistry ); - } - } - catch ( Exception e ) { - final String entityName = config.getProperty( IdentifierGenerator.ENTITY_NAME ); - throw new MappingException( String.format( "Could not instantiate id generator [entity-name=%s]", entityName ), e ); - } - } - - //FIXME: Not sure why we need all these instanceof - if ( generator instanceof BeforeExecutionGenerator ) { - return augmentWithReactiveGenerator( (BeforeExecutionGenerator) generator, type, config ); - } - - if ( generator instanceof OnExecutionGenerator ) { - return augmentWithReactiveGenerator( (OnExecutionGenerator) generator, type, config ); - } - - if ( generator instanceof ReactiveIdentifierGenerator ) { - return new ReactiveGeneratorWrapper( (ReactiveIdentifierGenerator) generator ); - } - - final String entityName = config.getProperty( IdentifierGenerator.ENTITY_NAME ); - throw new MappingException( String.format( "Not an id generator [entity-name=%s]", entityName ) ); - } - - protected Class generatorClassForName(String strategy) { - try { - return serviceRegistry.getService( ClassLoaderService.class ).classForName( strategy ); - } - catch ( ClassLoadingException e ) { - throw new MappingException( String.format( "Could not interpret id generator strategy [%s]", strategy ) ); - } - } - - public Generator augmentWithReactiveGenerator(Generator generator, Type type, Properties params) { - return augmentWithReactiveGenerator( serviceRegistry, generator, type, params ); - } - - public static Generator augmentWithReactiveGenerator(ServiceRegistry serviceRegistry, Generator generator, Type type, Properties params) { - final ReactiveIdentifierGenerator reactiveGenerator; - if ( generator instanceof SequenceStyleGenerator ) { - final DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); - if ( structure instanceof TableStructure ) { - reactiveGenerator = new EmulatedSequenceReactiveIdentifierGenerator(); - } - else if ( structure instanceof SequenceStructure ) { - reactiveGenerator = new ReactiveSequenceIdentifierGenerator(); - } - else { - throw LOG.unknownStructureType(); - } - } - else if ( generator instanceof TableGenerator ) { - reactiveGenerator = new TableReactiveIdentifierGenerator(); - } - else if ( generator instanceof SelectGenerator ) { - throw LOG.selectGeneratorIsNotSupportedInHibernateReactive(); - } - else { - //nothing to do - return generator; - } - - //this is not the way ORM does this: instead it passes a - //SqlStringGenerationContext to IdentifierGenerator.initialize() - final ConfigurationService cs = serviceRegistry.getService( ConfigurationService.class ); - if ( !params.containsKey( PersistentIdentifierGenerator.SCHEMA ) ) { - final String schema = cs.getSetting( Settings.DEFAULT_SCHEMA, StandardConverters.STRING ); - if ( schema != null ) { - params.put( PersistentIdentifierGenerator.SCHEMA, schema ); - } - } - if ( !params.containsKey( PersistentIdentifierGenerator.CATALOG ) ) { - final String catalog = cs.getSetting( Settings.DEFAULT_CATALOG, StandardConverters.STRING ); - if ( catalog != null ) { - params.put( PersistentIdentifierGenerator.CATALOG, catalog ); - } - } - - ( (Configurable) reactiveGenerator ).configure( type, params, serviceRegistry ); - return new ReactiveGeneratorWrapper( reactiveGenerator, (IdentifierGenerator) generator ); - } - -} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java index 6331d178b..325a5b662 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveSequenceIdentifierGenerator.java @@ -19,11 +19,13 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.enhanced.DatabaseStructure; import org.hibernate.id.enhanced.ImplicitDatabaseObjectNamingStrategy; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.StandardNamingStrategy; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; @@ -51,6 +53,16 @@ public class ReactiveSequenceIdentifierGenerator extends BlockingIdentifierGener private String sql; private int increment; + + public ReactiveSequenceIdentifierGenerator() { + } + + public ReactiveSequenceIdentifierGenerator(DatabaseStructure structure, RuntimeModelCreationContext creationContext) { + qualifiedName = structure.getPhysicalName(); + increment = structure.getIncrementSize(); + dialect = creationContext.getDialect(); + } + @Override protected int getBlockSize() { return increment; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java index a49a04289..79fb63aa6 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/TableReactiveIdentifierGenerator.java @@ -14,17 +14,22 @@ import org.hibernate.LockOptions; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.id.enhanced.TableStructure; import org.hibernate.internal.util.StringHelper; import org.hibernate.jdbc.TooManyRowsAffectedException; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.reactive.pool.ReactiveConnection; -import org.hibernate.reactive.pool.impl.Parameters; import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.session.ReactiveConnectionSupplier; import org.hibernate.service.ServiceRegistry; @@ -65,6 +70,60 @@ public class TableReactiveIdentifierGenerator extends BlockingIdentifierGenerato private String insertQuery; private String updateQuery; + public TableReactiveIdentifierGenerator(TableGenerator generator, RuntimeModelCreationContext runtimeModelCreationContext) { + ServiceRegistry serviceRegistry = runtimeModelCreationContext.getServiceRegistry(); + segmentColumnName = generator.getSegmentColumnName(); + valueColumnName = generator.getValueColumnName(); + segmentValue = generator.getSegmentValue(); + initialValue = generator.getInitialValue(); + increment = generator.getIncrementSize(); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = generator.getTableName(); + + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + Dialect dialect = jdbcEnvironment.getDialect(); + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + + public TableReactiveIdentifierGenerator( + TableStructure structure, + RuntimeModelCreationContext runtimeModelCreationContext) { + ServiceRegistry serviceRegistry = runtimeModelCreationContext.getServiceRegistry(); + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + Dialect dialect = jdbcEnvironment.getDialect(); + + valueColumnName = structure.getLogicalValueColumnNameIdentifier().render( dialect ); + initialValue = structure.getInitialValue(); + increment = structure.getIncrementSize(); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = structure.getPhysicalName().render(); + segmentColumnName = null; + segmentValue = null; + + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + + @Override + public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); + valueColumnName = determineValueColumnNameForTable( params, jdbcEnvironment ); + segmentValue = determineSegmentValue( params ); + initialValue = determineInitialValue( params ); + increment = determineIncrement( params ); + storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); + renderedTableName = determineTableName( type, params, serviceRegistry ); + + Dialect dialect = jdbcEnvironment.getDialect(); + selectQuery = applyLocksToSelect( dialect, "tbl", buildSelectQuery( dialect ) ); + updateQuery = buildUpdateQuery( dialect ); + insertQuery = buildInsertQuery( dialect ); + } + @Override protected int getBlockSize() { return increment; @@ -130,24 +189,6 @@ protected CompletionStage nextHiValue(ReactiveConnectionSupplier session) } ); } - @Override - public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) { - JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); - segmentColumnName = determineSegmentColumnName( params, jdbcEnvironment ); - valueColumnName = determineValueColumnNameForTable( params, jdbcEnvironment ); - segmentValue = determineSegmentValue( params ); - initialValue = determineInitialValue( params ); - increment = determineIncrement( params ); - storeLastUsedValue = determineStoreLastUsedValue( serviceRegistry ); - renderedTableName = determineTableName( type, params, serviceRegistry ); - - Dialect dialect = jdbcEnvironment.getDialect(); - Parameters parameters = Parameters.instance( dialect ); - selectQuery = parameters.process( applyLocksToSelect( dialect, "tbl", buildSelectQuery() ) ); - updateQuery = parameters.process( buildUpdateQuery() ); - insertQuery = parameters.process( buildInsertQuery() ); - } - @Override public void registerExportables(Database database) { } @@ -224,19 +265,48 @@ protected Object[] selectParameters() { return new Object[]{ segmentValue }; } - protected String buildSelectQuery() { - return "select tbl." + valueColumnName + " from " + renderedTableName + " tbl" - + " where tbl." + segmentColumnName + "=?"; + protected String buildSelectQuery(Dialect dialect) { + final String sql = "select tbl." + valueColumnName + " from " + renderedTableName + " tbl where tbl." + segmentColumnName; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + "=$1"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + "=@P1"; + } + if ( dialect instanceof OracleDialect ) { + return sql + "=:1"; + } + return sql + "=?"; } - protected String buildUpdateQuery() { + protected String buildUpdateQuery(Dialect dialect) { + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=$1" + + " where " + valueColumnName + "=$2 and " + segmentColumnName + "=$3"; + } + if ( dialect instanceof SQLServerDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=@P1" + + " where " + valueColumnName + "=@P2 and " + segmentColumnName + "=@P3"; + } + if ( dialect instanceof OracleDialect ) { + return "update " + renderedTableName + " set " + valueColumnName + "=:1" + + " where " + valueColumnName + "=:2 and " + segmentColumnName + "=:3"; + } return "update " + renderedTableName + " set " + valueColumnName + "=?" + " where " + valueColumnName + "=? and " + segmentColumnName + "=?"; } - protected String buildInsertQuery() { - return "insert into " + renderedTableName + " (" + segmentColumnName + ", " + valueColumnName + ") " - + " values (?, ?)"; + protected String buildInsertQuery(Dialect dialect) { + final String sql = "insert into " + renderedTableName + " (" + segmentColumnName + ", " + valueColumnName + ") "; + if ( dialect instanceof PostgreSQLDialect || dialect instanceof CockroachDialect ) { + return sql + " values ($1, $2)"; + } + if ( dialect instanceof SQLServerDialect ) { + return sql + " values (@P1, @P2)"; + } + if ( dialect instanceof OracleDialect ) { + return sql + " values (:1, :2)"; + } + return sql + " values (?, ?)"; } - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java index 42dfddef0..aed2555fe 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java @@ -12,7 +12,6 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.DialectDelegateWrapper; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SQLServerDialect; @@ -21,8 +20,8 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.generator.values.GeneratedValues; -import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.Binder; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.reactive.adaptor.impl.PrepareStatementDetailsAdaptor; import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor; import org.hibernate.reactive.logging.impl.Log; @@ -35,7 +34,7 @@ public interface ReactiveAbstractReturningDelegate extends ReactiveInsertGenerat @Override PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session); - PostInsertIdentityPersister getPersister(); + EntityPersister getPersister(); @Override default CompletionStage reactivePerformInsertReturning(String sql, SharedSessionContractImplementor session, Binder binder) { @@ -89,15 +88,14 @@ && getPersister().getFactory().getJdbcServices().getDialect() instanceof Cockroa private static String createInsert(String insertSql, String identifierColumnName, Dialect dialect) { String sql = insertSql; final String sqlEnd = " returning " + identifierColumnName; - Dialect realDialect = DialectDelegateWrapper.extractRealDialect( dialect ); - if ( realDialect instanceof MySQLDialect ) { + if ( dialect instanceof MySQLDialect ) { // For some reason ORM generates a query with an invalid syntax int index = sql.lastIndexOf( sqlEnd ); return index > -1 ? sql.substring( 0, index ) : sql; } - if ( realDialect instanceof SQLServerDialect ) { + if ( dialect instanceof SQLServerDialect ) { int index = sql.lastIndexOf( sqlEnd ); // FIXME: this is a hack for HHH-16365 if ( index > -1 ) { @@ -113,12 +111,12 @@ private static String createInsert(String insertSql, String identifierColumnName } return sql; } - if ( realDialect instanceof DB2Dialect ) { + if ( dialect instanceof DB2Dialect ) { // ORM query: select id from new table ( insert into IntegerTypeEntity values ( )) // Correct : select id from new table ( insert into LongTypeEntity (id) values (default)) return sql.replace( " values ( ))", " (" + identifierColumnName + ") values (default))" ); } - if ( realDialect instanceof OracleDialect ) { + if ( dialect instanceof OracleDialect ) { final String valuesStr = " values ( )"; int index = sql.lastIndexOf( sqlEnd ); // remove "returning id" since it's added via diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java index 79460c546..2b4063173 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveInsertReturningDelegate.java @@ -21,7 +21,6 @@ import org.hibernate.generator.values.GeneratedValueBasicResultBuilder; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.generator.values.internal.TableUpdateReturningBuilder; -import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.AbstractReturningDelegate; import org.hibernate.id.insert.InsertReturningDelegate; import org.hibernate.id.insert.TableInsertReturningBuilder; @@ -36,6 +35,7 @@ import org.hibernate.sql.model.ast.builder.TableMutationBuilder; import static java.sql.Statement.NO_GENERATED_KEYS; +import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart; import static org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper.getGeneratedValues; @@ -46,27 +46,26 @@ public class ReactiveInsertReturningDelegate extends AbstractReturningDelegate i private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private final PostInsertIdentityPersister persister; + private final EntityPersister persister; private final MutatingTableReference tableReference; private final List generatedColumns; public ReactiveInsertReturningDelegate(EntityPersister persister, EventType timing) { - this( (PostInsertIdentityPersister) persister, timing, false ); - + this( persister, timing, false ); } - public ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, Dialect dialect) { + public ReactiveInsertReturningDelegate(EntityPersister persister, Dialect dialect) { // With JDBC it's possible to enabled GetGeneratedKeys for identity generation. // Vert.x doesn't have this option, so we always use the same strategy for all database. // But MySQL requires setting supportsArbitraryValues to false or it's not going to work. - this( persister, EventType.INSERT, supportsArbitraryValues( dialect ) ); + this( persister, INSERT, supportsArbitraryValues( dialect ) ); } private static boolean supportsArbitraryValues( Dialect dialect) { return !( dialect instanceof MySQLDialect ); } - private ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, EventType timing, boolean supportsArbitraryValues) { + private ReactiveInsertReturningDelegate(EntityPersister persister, EventType timing, boolean supportsArbitraryValues) { super( persister, timing, @@ -88,7 +87,7 @@ private ReactiveInsertReturningDelegate(PostInsertIdentityPersister persister, E public TableMutationBuilder createTableMutationBuilder( Expectation expectation, SessionFactoryImplementor sessionFactory) { - if ( getTiming() == EventType.INSERT ) { + if ( getTiming() == INSERT ) { return new TableInsertReturningBuilder( persister, tableReference, generatedColumns, sessionFactory ); } else { @@ -110,7 +109,7 @@ public PreparedStatement prepareStatement(String sql, SharedSessionContractImple } @Override - public PostInsertIdentityPersister getPersister() { + public EntityPersister getPersister() { return persister; } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java index 72760a26b..9c6c126cc 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/logging/impl/Log.java @@ -258,6 +258,12 @@ public interface Log extends BasicLogger { @Message(id = 80, value = "No results were returned by the query (you can try running it with '.executeUpdate()'): %1$s") HibernateException noResultException(String sql); + @Message(id = 81, value = "The Vert.x SQL client doesn't support the SQL XML data type. If it's the mapping of an array, you can also try setting the property `hibernate.type.preferred_array_jdbc_type`") + HibernateException unsupportedXmlType(); + + @Message(id = 83, value = "Unexpected request of a non reactive connection") + HibernateException unexpectedConnectionRequest(); + // Same method that exists in CoreMessageLogger @LogMessage(level = WARN) @Message(id = 104, value = "firstResult/maxResults specified with collection fetch; applying in memory!" ) @@ -278,6 +284,11 @@ public interface Log extends BasicLogger { @Message(id = 245, value = "Manipulation query [%s] resulted in [%s] split queries" ) void splitQueries(String sourceQuery, int length); + // Same method that exists in CoreMessageLogger + @LogMessage(level = INFO) + @Message(value = "Could not find any META-INF/persistence.xml file in the classpath", id = 318) + void unableToFindPersistenceXmlInClasspath(); + // Same method that exists in CoreMessageLogger @LogMessage(level = INFO) @Message(id = 327, value = "Error performing load command") diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java new file mode 100644 index 000000000..876312b47 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/metamodel/mapping/internal/ReactiveRuntimeModelCreationContext.java @@ -0,0 +1,128 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.metamodel.mapping.internal; + +import java.util.Map; + +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.cache.spi.CacheImplementor; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.generator.Generator; +import org.hibernate.mapping.GeneratorSettings; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.metamodel.spi.MappingMetamodelImplementor; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.sqm.function.SqmFunctionRegistry; +import org.hibernate.reactive.tuple.entity.ReactiveEntityMetamodel; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +public class ReactiveRuntimeModelCreationContext implements RuntimeModelCreationContext { + + private final RuntimeModelCreationContext delegate; + + public ReactiveRuntimeModelCreationContext(RuntimeModelCreationContext delegate) { + this.delegate = delegate; + } + + @Override + public EntityMetamodel createEntityMetamodel(PersistentClass persistentClass, EntityPersister persister) { + return new ReactiveEntityMetamodel( persistentClass, persister, delegate ); + } + + @Override + public SessionFactoryImplementor getSessionFactory() { + return delegate.getSessionFactory(); + } + + @Override + public BootstrapContext getBootstrapContext() { + return delegate.getBootstrapContext(); + } + + @Override + public MetadataImplementor getBootModel() { + return delegate.getBootModel(); + } + + @Override + public MappingMetamodelImplementor getDomainModel() { + return delegate.getDomainModel(); + } + + @Override + public TypeConfiguration getTypeConfiguration() { + return delegate.getTypeConfiguration(); + } + + @Override + public JavaTypeRegistry getJavaTypeRegistry() { + return delegate.getJavaTypeRegistry(); + } + + @Override + public MetadataImplementor getMetadata() { + return delegate.getMetadata(); + } + + @Override + public SqmFunctionRegistry getFunctionRegistry() { + return delegate.getFunctionRegistry(); + } + + @Override + public Map getSettings() { + return delegate.getSettings(); + } + + @Override + public Dialect getDialect() { + return delegate.getDialect(); + } + + @Override + public CacheImplementor getCache() { + return delegate.getCache(); + } + + @Override + public SessionFactoryOptions getSessionFactoryOptions() { + return delegate.getSessionFactoryOptions(); + } + + @Override + public JdbcServices getJdbcServices() { + return delegate.getJdbcServices(); + } + + @Override + public SqlStringGenerationContext getSqlStringGenerationContext() { + return delegate.getSqlStringGenerationContext(); + } + + @Override + public ServiceRegistry getServiceRegistry() { + return delegate.getServiceRegistry(); + } + + @Override + public Map getGenerators() { + return delegate.getGenerators(); + } + + @Override + public GeneratorSettings getGeneratorSettings() { + return delegate.getGeneratorSettings(); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 65b0ad906..9bdf215f7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -677,11 +677,27 @@ default Uni find(Class entityClass, Object id, LockModeType lockModeTy * {@code session.persist(newBook).map(v -> session.flush());} * * - * @param entity a transient instance of a persistent class + * @param object a transient instance of a persistent class * * @see jakarta.persistence.EntityManager#persist(Object) */ - Uni persist(Object entity); + Uni persist(Object object); + + /** + * Make a transient instance persistent and mark it for later insertion in the + * database. This operation cascades to associated instances if the association + * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. + *

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

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

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

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

type) { + public

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

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

ReactiveNativeQueryImpl setParameterList(int position, Collection values, Class

type) { + public

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

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

ReactiveNativeQueryImpl setParameterList(int position, Collection values, BindableType

type) { + public

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

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

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values, Class

javaType) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + Collection values, + Class

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, Collection values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + Collection values, + BindableType

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

ReactiveNativeQueryImpl setParameterList(QueryParameter

paramet } @Override - public

ReactiveNativeQueryImpl setParameterList(QueryParameter

parameter, P[] values, BindableType

type) { + public

ReactiveNativeQueryImpl setParameterList( + QueryParameter

parameter, + P[] values, + BindableType

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

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

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

+ * For example: + *

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

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

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

- */ + */ private static Predicate postgresPredicate(String columnName, Integer arrayLength, Integer columnLength) { StringBuilder regexBuilder = new StringBuilder(); regexBuilder.append( ".*" ); @@ -468,6 +499,15 @@ private static Predicate postgresPredicate(String columnName, Integer ar return s -> s.matches( regexBuilder.toString() ); } + private static Predicate arrayAsJsonPredicate(String columnName) { + return s -> s.contains( columnName + " json" ); + } + + private static Predicate arrayAsXmlPredicate(String columnName) { + // Example of correct query definition: columnName xml + return s -> s.contains( columnName + " xml" ); + } + private static Predicate arrayAsVarbinaryPredicate(String columnName, Integer columnLength) { StringBuilder regexBuilder = new StringBuilder(); // Example of correct query definition: columnName varbinary(255) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java index 7af5d42c7..f97f354fd 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/types/JsonQueryTest.java @@ -150,6 +150,30 @@ public void nativeQueryWithJson(VertxTestContext context) { ); } + @Test + @Disabled("https://github.com/hibernate/hibernate-reactive/issues/1999") + public void nativeQueryWithEscapedQuestionMark(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createNativeQuery( "select * from BookWithJson where author -> 'name' \\? 'Jo'", Book.class ) + .getSingleResult() + ) + .invoke( result -> assertThat( result ).isEqualTo( fakeHistory ) ) + ); + } + + @Test + @Disabled("https://github.com/hibernate/hibernate-reactive/issues/2012") + public void nativeQuerySelectScalarWithEscapedQuestionMark(VertxTestContext context) { + test( context, getMutinySessionFactory() + .withTransaction( s -> s + .createNativeQuery( "select 123 from BookWithJson where author -> 'name' \\? 'Jo'", Object.class ) + .getSingleResult() + ) + .invoke( result -> assertThat( result ).isEqualTo( 123 ) ) + ); + } + @Entity(name = "Book") @Table(name = "BookWithJson") public static class Book { diff --git a/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java b/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java index 60bf2a278..11ed5a200 100644 --- a/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java +++ b/integration-tests/bytecode-enhancements-it/src/main/java/org/hibernate/reactive/it/lazytoone/Ship.java @@ -15,9 +15,6 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; -import org.hibernate.annotations.LazyToOne; -import org.hibernate.annotations.LazyToOneOption; - @Entity(name = "Ship") @Table(name = "Ship") public class Ship { @@ -30,7 +27,6 @@ public class Ship { @Basic(fetch = FetchType.LAZY) private byte[] picture; - @LazyToOne(LazyToOneOption.NO_PROXY) @OneToOne(fetch = FetchType.LAZY, mappedBy = "ship", cascade = CascadeType.ALL) private Captain captain;