From 0e94d1e75050ac9c916030b5e2ad04022bf84ea3 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 13 Apr 2023 16:57:33 +0200 Subject: [PATCH 1/5] cleanups to id generation stuff --- .../AbstractReactiveSaveEventListener.java | 5 +- .../id/impl/IdentifierGeneration.java | 11 ++- .../id/impl/ReactiveGeneratorWrapper.java | 50 +++--------- .../ReactiveIdentifierGeneratorFactory.java | 80 +++++++------------ ...SingleIdEntityLoaderProvidedQueryImpl.java | 3 - .../mutation/ReactiveInsertCoordinator.java | 13 +-- .../ReactiveUpdateCoordinatorStandard.java | 24 +++--- .../impl/ReactiveStatelessSessionImpl.java | 4 +- .../reactive/testing/ReactiveAssertions.java | 1 - 9 files changed, 71 insertions(+), 120 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveSaveEventListener.java index 2bf325e26..44b235d5b 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 @@ -48,6 +48,7 @@ import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.id.IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR; import static org.hibernate.pretty.MessageHelper.infoString; +import static org.hibernate.reactive.id.impl.IdentifierGeneration.castToIdentifierType; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture; @@ -131,11 +132,13 @@ protected CompletionStage reactiveSaveWithGeneratedId( if ( generator instanceof ReactiveIdentifierGenerator ) { return ( (ReactiveIdentifierGenerator) generator ) .generate( ( ReactiveConnectionSupplier ) source, entity ) + .thenApply( id -> castToIdentifierType( id, persister ) ) .thenCompose( generatedId -> performSaveWithId( entity, context, source, persister, generator, generatedId ) ); } final Object generatedId = ( (BeforeExecutionGenerator) generator ).generate( source, entity, null, INSERT ); - return performSaveWithId( entity, context, source, persister, generator, generatedId ); + final Object id = castToIdentifierType( generatedId, persister ); + return performSaveWithId( entity, context, source, persister, generator, id ); } else { return reactivePerformSave( entity, null, persister, true, context, source, requiresImmediateIdAccess ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/IdentifierGeneration.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/IdentifierGeneration.java index 118703a02..91f2ac83a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/IdentifierGeneration.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/IdentifierGeneration.java @@ -49,12 +49,19 @@ private static Object castLongIdToIdentifierType(Long longId, EntityPersister pe return longId.toString(); } - throw LOG.cannotGenerateIdentifiersOfType( identifierType.getJavaType().getTypeName(), persister.getEntityName() ); + throw LOG.cannotGenerateIdentifiersOfType( + identifierType.getJavaType().getTypeName(), + persister.getEntityName() + ); } private static void validateMaxValue(EntityPersister persister, Long id, int maxValue) { if ( id > maxValue ) { - throw LOG.generatedIdentifierTooBigForTheField(persister.getEntityName(), persister.getIdentifierType().getReturnedClass().getSimpleName(), id ); + throw LOG.generatedIdentifierTooBigForTheField( + persister.getEntityName(), + persister.getIdentifierType().getReturnedClass().getSimpleName(), + id + ); } } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveGeneratorWrapper.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveGeneratorWrapper.java index 682e39391..fd7cda2c5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveGeneratorWrapper.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/impl/ReactiveGeneratorWrapper.java @@ -10,41 +10,28 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.enhanced.DatabaseStructure; -import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.reactive.id.ReactiveIdentifierGenerator; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.session.ReactiveConnectionSupplier; -import java.lang.invoke.MethodHandles; import java.util.Objects; import java.util.concurrent.CompletionStage; /** * @author Gavin King */ -public class ReactiveGeneratorWrapper - implements IdentifierGenerator, ExportableProducer, ReactiveIdentifierGenerator { +public class ReactiveGeneratorWrapper + implements IdentifierGenerator, ExportableProducer, ReactiveIdentifierGenerator { - private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + private final ReactiveIdentifierGenerator reactiveGenerator; + private final IdentifierGenerator generator; - private final DatabaseStructure databaseStructure; - private ReactiveIdentifierGenerator reactiveGenerator; - private IdentifierGenerator generator; - private Class returnedClass; - - public ReactiveGeneratorWrapper(ReactiveIdentifierGenerator reactiveGenerator, Class returnedClass) { - this( reactiveGenerator, null, returnedClass ); + public ReactiveGeneratorWrapper(ReactiveIdentifierGenerator reactiveGenerator) { + this( reactiveGenerator, null ); } - public ReactiveGeneratorWrapper(ReactiveIdentifierGenerator reactiveGenerator, IdentifierGenerator generator, Class returnedClass) { + public ReactiveGeneratorWrapper(ReactiveIdentifierGenerator reactiveGenerator, IdentifierGenerator generator) { this.reactiveGenerator = reactiveGenerator; this.generator = generator; - this.returnedClass = returnedClass; - this.databaseStructure = generator instanceof SequenceStyleGenerator - ? ( (SequenceStyleGenerator) generator ).getDatabaseStructure() - : null; } @Override @@ -52,31 +39,14 @@ public void initialize(SqlStringGenerationContext context) { if ( reactiveGenerator instanceof IdentifierGenerator ) { ( (IdentifierGenerator) reactiveGenerator ).initialize( context ); } - if (generator != null) { + if ( generator != null ) { generator.initialize( context ); } } @Override - public CompletionStage generate(ReactiveConnectionSupplier session, Object entity) { - return reactiveGenerator - .generate( session, entity ) - .thenApply( id -> { - //FIXME: this is just a temp workaround - // The correct approach would be to use an IntegralDataTypeHolder - if ( Integer.class.equals( returnedClass ) ) { - return ( (Number) id ).intValue(); - } - if ( Short.class.equals( returnedClass ) ) { - return ( (Number) id ).shortValue(); - } - return id; - } ) - .thenApply( this::castId ); - } - - private T castId(Object id) { - return (T) id; + public CompletionStage generate(ReactiveConnectionSupplier session, Object entity) { + return reactiveGenerator.generate( session, entity ).thenApply( id -> id ); } @Override 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 index c96591f12..6a8741de0 100644 --- 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 @@ -48,72 +48,54 @@ public ReactiveIdentifierGeneratorFactory(ServiceRegistry serviceRegistry) { public Generator createIdentifierGenerator(String strategy, Type type, Properties config) { Object generator; try { - generator = super.createIdentifierGenerator(strategy, type, config); - } catch (MappingException ignored) { - generator = fallbackCreateIdentifierGenerator( strategy, type, config ); + generator = super.createIdentifierGenerator( strategy, type, 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 ); + return augmentWithReactiveGenerator( (BeforeExecutionGenerator) generator, type, config ); } if ( generator instanceof OnExecutionGenerator ) { - return augmentWithReactiveGenerator( (OnExecutionGenerator)generator, type, config ); + return augmentWithReactiveGenerator( (OnExecutionGenerator) generator, type, config ); } if ( generator instanceof ReactiveIdentifierGenerator ) { - return new ReactiveGeneratorWrapper( (ReactiveIdentifierGenerator) generator, type.getReturnedClass() ); + 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 ) ); } - //TODO this was copied from StandardIdentifierGeneratorFactory#createIdentifierGenerator - // in order to avoid the !Generator.class.isAssignableFrom( clazz ) check in getIdentifierGeneratorClass - // This is suboptimal not only because we are duplicating code, but because this piece cannot access - // the private fields of the super method - private Object fallbackCreateIdentifierGenerator(String strategy, Type type, Properties parameters) { - try { - final Class clazz = fallbackGetIdentifierGeneratorClass( strategy ); - Object result = clazz.getConstructor().newInstance(); - - if ( result instanceof Configurable ) { - ( (Configurable) result ).configure( type, parameters, serviceRegistry ); - } - return result; - } - catch ( Exception e ) { - final String entityName = parameters.getProperty( IdentifierGenerator.ENTITY_NAME ); - throw new MappingException( String.format( "Could not instantiate id generator [entity-name=%s]", entityName ), e ); - } - } - + //TODO: deleteme, after update to ORM @Override public Class getIdentifierGeneratorClass(String strategy) { try { - return super.getIdentifierGeneratorClass(strategy); - } catch (MappingException ignored) { - return fallbackGetIdentifierGeneratorClass(strategy); + return super.getIdentifierGeneratorClass( strategy ); } - } - - //TODO this was copied from StandardIdentifierGeneratorFactory#createIdentifierGenerator - // in order to avoid the !Generator.class.isAssignableFrom( clazz ) check in getIdentifierGeneratorClass - // This is suboptimal not only because we are duplicating code, but because this piece cannot access - // the private fields of the super method - public Class fallbackGetIdentifierGeneratorClass(String strategy) { - if ( "hilo".equals( strategy ) ) { - throw new UnsupportedOperationException( "Support for 'hilo' generator has been removed" ); + catch ( MappingException ignored ) { + // happens because the class does not implement Generator + return generatorClassForName( strategy ); } - final String resolvedStrategy = "native".equals( strategy ) - ? getDialect().getNativeIdentifierGeneratorStrategy() - : strategy; + } + protected Class generatorClassForName(String strategy) { try { - return serviceRegistry.getService( ClassLoaderService.class ) - .classForName( resolvedStrategy ); + return serviceRegistry.getService( ClassLoaderService.class ).classForName( strategy ); } catch ( ClassLoadingException e ) { throw new MappingException( String.format( "Could not interpret id generator strategy [%s]", strategy ) ); @@ -125,9 +107,9 @@ public Generator augmentWithReactiveGenerator(Generator generator, Type type, Pr } public static Generator augmentWithReactiveGenerator(ServiceRegistry serviceRegistry, Generator generator, Type type, Properties params) { - ReactiveIdentifierGenerator reactiveGenerator; + final ReactiveIdentifierGenerator reactiveGenerator; if ( generator instanceof SequenceStyleGenerator ) { - DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); + final DatabaseStructure structure = ( (SequenceStyleGenerator) generator ).getDatabaseStructure(); if ( structure instanceof TableStructure ) { reactiveGenerator = new EmulatedSequenceReactiveIdentifierGenerator(); } @@ -151,22 +133,22 @@ else if ( generator instanceof SelectGenerator ) { //this is not the way ORM does this: instead it passes a //SqlStringGenerationContext to IdentifierGenerator.initialize() - ConfigurationService cs = serviceRegistry.getService( ConfigurationService.class ); + final ConfigurationService cs = serviceRegistry.getService( ConfigurationService.class ); if ( !params.containsKey( PersistentIdentifierGenerator.SCHEMA ) ) { - String schema = cs.getSetting( Settings.DEFAULT_SCHEMA, StandardConverters.STRING ); + final String schema = cs.getSetting( Settings.DEFAULT_SCHEMA, StandardConverters.STRING ); if ( schema != null ) { params.put( PersistentIdentifierGenerator.SCHEMA, schema ); } } if ( !params.containsKey( PersistentIdentifierGenerator.CATALOG ) ) { - String catalog = cs.getSetting( Settings.DEFAULT_CATALOG, StandardConverters.STRING ); + 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, type.getReturnedClass() ); + return new ReactiveGeneratorWrapper( reactiveGenerator, (IdentifierGenerator) generator ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java index 43e9f3ee4..f509eb9a2 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/loader/ast/internal/ReactiveSingleIdEntityLoaderProvidedQueryImpl.java @@ -5,7 +5,6 @@ */ package org.hibernate.reactive.loader.ast.internal; -import java.lang.invoke.MethodHandles; import java.util.concurrent.CompletionStage; import org.hibernate.FlushMode; @@ -16,8 +15,6 @@ import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.reactive.loader.ast.spi.ReactiveSingleIdEntityLoader; -import org.hibernate.reactive.logging.impl.Log; -import org.hibernate.reactive.logging.impl.LoggerFactory; import jakarta.persistence.Parameter; diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinator.java index aca9333e0..eece7b25a 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveInsertCoordinator.java @@ -18,7 +18,6 @@ import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; import org.hibernate.metamodel.mapping.AttributeMapping; -import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.persister.entity.mutation.InsertCoordinator; @@ -47,7 +46,7 @@ public Object coordinateInsert(Object id, Object[] values, Object entity, Shared } public CompletionStage coordinateReactiveInsert(Object id, Object[] currentValues, Object entity, SharedSessionContractImplementor session) { - return reactivePreInsertInMemoryValueGeneration(currentValues, entity, session) + return reactivePreInsertInMemoryValueGeneration( currentValues, entity, session ) .thenCompose( v -> entityPersister().getEntityMetamodel().isDynamicInsert() ? doDynamicInserts( id, currentValues, entity, session ) : doStaticInserts( id, currentValues, entity, session ) @@ -67,8 +66,8 @@ private CompletionStage reactivePreInsertInMemoryValueGeneration(Object[] && !generator.generatedOnExecution() && generator.generatesOnInsert() ) { final Object currentValue = currentValues[i]; - stage = stage.thenCompose( v -> generateValue( session, entity, currentValue, - (BeforeExecutionGenerator) generator, INSERT) + final BeforeExecutionGenerator beforeGenerator = (BeforeExecutionGenerator) generator; + stage = stage.thenCompose( v -> generateValue( session, entity, currentValue, beforeGenerator, INSERT ) .thenAccept( generatedValue -> { currentValues[index] = generatedValue; entityPersister().setPropertyValue( entity, index, generatedValue ); @@ -101,7 +100,6 @@ protected CompletionStage decomposeForReactiveInsert( TableInclusionChecker tableInclusionChecker, SharedSessionContractImplementor session) { final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings(); - final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings(); mutationGroup.forEachOperation( (position, operation) -> { final EntityTableMapping tableDetails = (EntityTableMapping) operation.getTableDetails(); if ( tableInclusionChecker.include( tableDetails ) ) { @@ -181,9 +179,6 @@ private ReactiveMutationExecutor getReactiveMutationExecutor(SharedSessionContra .getFactory() .getServiceRegistry() .getService( MutationExecutorService.class ); - - MutationExecutor executor = mutationExecutorService - .createExecutor( this::getInsertBatchKey, operationGroup, session ); - return (ReactiveMutationExecutor) executor; + return (ReactiveMutationExecutor) mutationExecutorService.createExecutor( this::getInsertBatchKey, operationGroup, session ); } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveUpdateCoordinatorStandard.java index ebd0f4265..ddf7ded17 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 @@ -89,8 +89,8 @@ public CompletionStage coordinateReactiveUpdate( } CompletionStage s = voidFuture(); - return s.thenCompose(v -> reactivePreUpdateInMemoryValueGeneration(entity, values, session)) - .thenCompose(preUpdateGeneratedAttributeIndexes -> { + return s.thenCompose( v -> reactivePreUpdateInMemoryValueGeneration(entity, values, session) ) + .thenCompose( preUpdateGeneratedAttributeIndexes -> { final int[] dirtyAttributeIndexes = dirtyAttributeIndexes( incomingDirtyAttributeIndexes, preUpdateGeneratedAttributeIndexes ); final boolean[] attributeUpdateability; @@ -172,8 +172,8 @@ private CompletionStage reactivePreUpdateInMemoryValueGeneration( && !generator.generatedOnExecution() && generator.generatesOnUpdate() ) { final Object currentValue = currentValues[i]; - result = result.thenCompose( v -> generateValue( session, entity, currentValue, - (BeforeExecutionGenerator) generator, INSERT) + final BeforeExecutionGenerator beforeGenerator = (BeforeExecutionGenerator) generator; + result = result.thenCompose( v -> generateValue( session, entity, currentValue, beforeGenerator, INSERT ) .thenAccept( generatedValue -> { currentValues[index] = generatedValue; entityPersister().setPropertyValue( entity, index, generatedValue ); @@ -261,9 +261,7 @@ private ReactiveMutationExecutor mutationExecutor( final MutationExecutorService mutationExecutorService = session.getSessionFactory() .getServiceRegistry() .getService( MutationExecutorService.class ); - - return (ReactiveMutationExecutor) mutationExecutorService - .createExecutor( this::getBatchKey, operationGroup, session ); + return (ReactiveMutationExecutor) mutationExecutorService.createExecutor( this::getBatchKey, operationGroup, session ); } @Override @@ -286,11 +284,6 @@ protected void doDynamicUpdate( session ); - // and then execute them - final MutationExecutorService mutationExecutorService = session.getSessionFactory() - .getServiceRegistry() - .getService( MutationExecutorService.class ); - final ReactiveMutationExecutor mutationExecutor = mutationExecutor( session, dynamicUpdateGroup ); decomposeForUpdate( @@ -300,7 +293,9 @@ protected void doDynamicUpdate( valuesAnalysis, mutationExecutor, dynamicUpdateGroup, - (attributeIndex, attribute) -> dirtinessChecker.include( attributeIndex, (SingularAttributeMapping) attribute ) ? AttributeAnalysis.DirtynessStatus.CONSIDER_LIKE_DIRTY : AttributeAnalysis.DirtynessStatus.NOT_DIRTY, + (attributeIndex, attribute) -> dirtinessChecker.include( attributeIndex, (SingularAttributeMapping) attribute ) + ? AttributeAnalysis.DirtynessStatus.CONSIDER_LIKE_DIRTY + : AttributeAnalysis.DirtynessStatus.NOT_DIRTY, session ); bindPartitionColumnValueBindings( oldValues, session, mutationExecutor.getJdbcValueBindings() ); @@ -367,7 +362,8 @@ protected void doStaticUpdate( entity, valuesAnalysis, valuesAnalysis.getTablesNeedingUpdate()::contains, - (statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck( statementDetails, affectedRowCount, batchPosition, entityPersister(), id, factory() ), + (statementDetails, affectedRowCount, batchPosition) + -> identifiedResultsCheck( statementDetails, affectedRowCount, batchPosition, entityPersister(), id, factory() ), session ) .whenComplete( (o, throwable) -> mutationExecutor.release() ) 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 a023bc404..23cb7fea5 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 @@ -100,6 +100,7 @@ import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; +import static org.hibernate.reactive.id.impl.IdentifierGeneration.castToIdentifierType; import static org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister.forceInitialize; import static org.hibernate.reactive.session.impl.SessionUtil.checkEntityFound; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; @@ -249,7 +250,8 @@ public CompletionStage reactiveInsert(Object entity) { final Generator generator = persister.getGenerator(); if ( !generator.generatedOnExecution() ) { return generateId( entity, generator ) - .thenCompose( id -> { + .thenCompose( generatedId -> { + final Object id = castToIdentifierType( generatedId, persister ); if ( persister.isVersioned() ) { if ( seedVersion( entity, state, persister, this ) ) { persister.setValues( entity, state ); diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/ReactiveAssertions.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/ReactiveAssertions.java index b2fdc64b3..ece53242e 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/ReactiveAssertions.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/testing/ReactiveAssertions.java @@ -6,7 +6,6 @@ package org.hibernate.reactive.testing; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; From 2e021b14704631c902543d5a8760fb34b031ef44 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 13 Apr 2023 18:22:23 +0200 Subject: [PATCH 2/5] remove unused type --- .../impl/ReactiveMutationStatementPreparer.java | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveMutationStatementPreparer.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveMutationStatementPreparer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveMutationStatementPreparer.java deleted file mode 100644 index bec9ae819..000000000 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveMutationStatementPreparer.java +++ /dev/null @@ -1,12 +0,0 @@ -/* Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.reactive.engine.impl; - -/** - * @see org.hibernate.engine.jdbc.internal.MutationStatementPreparerImpl - */ -public class ReactiveMutationStatementPreparer { -} From f98c587a762515ab2c6249030b08eae1cb845ca0 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 13 Apr 2023 18:22:41 +0200 Subject: [PATCH 3/5] cleanups and sync with core for cascade stuff --- .../reactive/engine/impl/Cascade.java | 126 ++++++++++-------- .../reactive/engine/impl/CascadingAction.java | 2 +- .../engine/impl/CascadingActions.java | 40 +++--- 3 files changed, 97 insertions(+), 71 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java index f147d6ea9..b56382e50 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/Cascade.java @@ -6,8 +6,10 @@ package org.hibernate.reactive.engine.impl; import java.lang.invoke.MethodHandles; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Objects; import java.util.concurrent.CompletionStage; @@ -32,14 +34,15 @@ import org.hibernate.reactive.session.ReactiveSession; import org.hibernate.type.AssociationType; import org.hibernate.type.CollectionType; +import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; -import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.Type; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.reactive.util.impl.CompletionStages.loop; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; +import static org.hibernate.type.ForeignKeyDirection.TO_PARENT; /** * Delegate responsible for, in conjunction with the various @@ -90,7 +93,7 @@ public static CompletionStage fetchLazyAssociationsBeforeCascade( CompletionStage beforeDelete = voidFuture(); if ( persister.hasCascades() ) { CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles(); - Object[] state = persister.getPropertyValues( entity ); + Object[] state = persister.getValues( entity ); for (int i = 0; i < cascadeStyles.length; i++) { if ( cascadeStyles[i].doCascade( action.delegate() ) ) { Object fetchable = state[i]; @@ -105,13 +108,11 @@ public static CompletionStage fetchLazyAssociationsBeforeCascade( /** * Cascade an action from the parent entity instance to all its children. - * - * which is specific to each CascadingAction type */ public CompletionStage cascade() throws HibernateException { return voidFuture().thenCompose(v -> { CacheMode cacheMode = eventSource.getCacheMode(); - if (action==CascadingActions.DELETE) { + if ( action==CascadingActions.DELETE ) { eventSource.setCacheMode( CacheMode.GET ); } eventSource.getPersistenceContextInternal().incrementCascadeLevel(); @@ -135,7 +136,6 @@ private CompletionStage cascadeInternal() throws HibernateException { final String[] propertyNames = persister.getPropertyNames(); final CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles(); final boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties( parent ); - final int componentPathStackDepth = 0; for ( int i = 0; i < types.length; i++) { final CascadeStyle style = cascadeStyles[ i ]; final String propertyName = propertyNames[ i ]; @@ -143,6 +143,7 @@ private CompletionStage cascadeInternal() throws HibernateException { hasUninitializedLazyProperties && !persister.getBytecodeEnhancementMetadata().isAttributeLoaded( parent, propertyName ); + final Type type = types[i]; if ( style.doCascade( action.delegate() ) ) { final Object child; if ( isUninitializedProperty ) { @@ -156,14 +157,14 @@ private CompletionStage cascadeInternal() throws HibernateException { // parent was not in the PersistenceContext continue; } - if ( types[ i ].isCollectionType() ) { + if ( type.isCollectionType() ) { // CollectionType#getCollection gets the PersistentCollection // that corresponds to the uninitialized collection from the // PersistenceContext. If not present, an uninitialized // PersistentCollection will be added to the PersistenceContext. // The action may initialize it later, if necessary. // This needs to be done even when action.performOnLazyProperty() returns false. - final CollectionType collectionType = (CollectionType) types[i]; + final CollectionType collectionType = (CollectionType) type; child = collectionType.getCollection( collectionType.getKeyOfOwner( parent, eventSource ), eventSource, @@ -171,13 +172,13 @@ private CompletionStage cascadeInternal() throws HibernateException { null ); } - else if ( types[ i ].isComponentType() ) { + else if ( type.isComponentType() ) { // Hibernate does not support lazy embeddables, so this shouldn't happen. throw new UnsupportedOperationException( "Lazy components are not supported." ); } - else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) { + else if ( action.performOnLazyProperty() && type.isEntityType() ) { // Only need to initialize a lazy entity attribute when action.performOnLazyProperty() // returns true. LazyAttributeLoadingInterceptor interceptor = persister.getBytecodeEnhancementMetadata() @@ -191,12 +192,12 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) { } } else { - child = persister.getPropertyValue( parent, i ); + child = persister.getValue( parent, i ); } cascadeProperty( - componentPathStackDepth, + null, child, - types[ i ], + type, style, propertyName, false @@ -209,9 +210,9 @@ else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) { // If the property is uninitialized, then there cannot be any orphans. if ( action.deleteOrphans() && !isUninitializedProperty ) { cascadeLogicalOneToOneOrphanRemoval( - componentPathStackDepth, - persister.getPropertyValue( parent, i ), - types[ i ], + null, + persister.getValue( parent, i ), + type, style, propertyName, false @@ -241,7 +242,7 @@ private void noCascade( * Cascade an action to the child or children */ private void cascadeProperty( - final int componentPathStackDepth, + List componentPath, final Object child, final Type type, final CascadeStyle style, @@ -253,7 +254,7 @@ private void cascadeProperty( final AssociationType associationType = (AssociationType) type; if ( cascadeAssociationNow( cascadePoint, associationType ) ) { cascadeAssociation( - componentPathStackDepth, + componentPath, child, type, style, @@ -262,8 +263,14 @@ private void cascadeProperty( } } else if ( type.isComponentType() ) { + if ( componentPath == null && propertyName != null ) { + componentPath = new ArrayList<>(); + } + if ( componentPath != null ) { + componentPath.add( propertyName ); + } cascadeComponent( - componentPathStackDepth, + componentPath, child, (CompositeType) type ); @@ -271,7 +278,7 @@ else if ( type.isComponentType() ) { } cascadeLogicalOneToOneOrphanRemoval( - componentPathStackDepth, + componentPath, child, type, style, @@ -280,7 +287,7 @@ else if ( type.isComponentType() ) { } private void cascadeLogicalOneToOneOrphanRemoval( - final int componentPathStackDepth, + final List componentPath, final Object child, final Type type, final CascadeStyle style, @@ -298,31 +305,38 @@ private void cascadeLogicalOneToOneOrphanRemoval( final EntityEntry entry = persistenceContext.getEntry( parent ); if ( entry != null && entry.getStatus() != Status.SAVING ) { Object loadedValue; - if ( componentPathStackDepth == 0 ) { + if ( componentPath == null ) { // association defined on entity loadedValue = entry.getLoadedValue( propertyName ); } else { // association defined on component - // todo : this is currently unsupported because of the fact that - // we do not know the loaded state of this value properly - // and doing so would be very difficult given how components and - // entities are loaded (and how 'loaded state' is put into the - // EntityEntry). Solutions here are to either: - // 1) properly account for components as a 2-phase load construct - // 2) just assume the association was just now orphaned and - // issue the orphan delete. This would require a special - // set of SQL statements though since we do not know the - // orphaned value, something a delete with a subquery to - // match the owner. -// final EntityType entityType = (EntityType) type; -// final String getPropertyPath = composePropertyPath( entityType.getPropertyName() ); - loadedValue = null; + // Since the loadedState in the EntityEntry is a flat domain type array + // We first have to extract the component object and then ask the component type + // recursively to give us the value of the sub-property of that object + final Type propertyType = entry.getPersister().getPropertyType( componentPath.get(0) ); + if ( propertyType instanceof ComponentType) { + loadedValue = entry.getLoadedValue( componentPath.get( 0 ) ); + ComponentType componentType = (ComponentType) propertyType; + if ( componentPath.size() != 1 ) { + for ( int i = 1; i < componentPath.size(); i++ ) { + final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) ); + loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex ); + componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex]; + } + } + + loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) ); + } + else { + // Association is probably defined in an element collection, so we can't do orphan removals + loadedValue = null; + } } // orphaned if the association was nulled (child == null) or receives a new value while the // entity is managed (without first nulling and manually flushing). - if ( child == null || ( loadedValue != null && child != loadedValue ) ) { + if ( child == null || loadedValue != null && child != loadedValue ) { EntityEntry valueEntry = persistenceContext.getEntry( loadedValue ); if ( valueEntry == null && loadedValue instanceof HibernateProxy ) { @@ -342,8 +356,8 @@ private void cascadeLogicalOneToOneOrphanRemoval( } if ( valueEntry != null ) { - EntityPersister persister = valueEntry.getPersister(); - String entityName = persister.getEntityName(); + final EntityPersister persister = valueEntry.getPersister(); + final String entityName = persister.getEntityName(); if ( LOG.isTraceEnabled() ) { LOG.tracev( "Deleting orphaned entity instance: {0}", @@ -353,8 +367,7 @@ private void cascadeLogicalOneToOneOrphanRemoval( final Object loaded = loadedValue; if ( type.isAssociationType() - && ( (AssociationType) type ).getForeignKeyDirection() - .equals(ForeignKeyDirection.TO_PARENT) ) { + && ( (AssociationType) type ).getForeignKeyDirection().equals(TO_PARENT) ) { // If FK direction is to-parent, we must remove the orphan *before* the queued update(s) // occur. Otherwise, replacing the association on a managed entity, without manually // nulling and flushing, causes FK constraint violations. @@ -390,7 +403,7 @@ private boolean cascadeAssociationNow(final CascadePoint cascadePoint, Associat } private void cascadeComponent( - final int componentPathStackDepth, + List componentPath, final Object child, final CompositeType componentType) { @@ -400,13 +413,14 @@ private void cascadeComponent( for ( int i = 0; i < types.length; i++ ) { final CascadeStyle componentPropertyStyle = componentType.getCascadeStyle( i ); final String subPropertyName = propertyNames[i]; - if ( componentPropertyStyle.doCascade( action.delegate() ) ) { - if (children == null) { + if ( componentPropertyStyle.doCascade( action.delegate() ) + || componentPropertyStyle.hasOrphanDelete() && action.deleteOrphans() ) { + if ( children == null ) { // Get children on demand. children = componentType.getPropertyValues( child, eventSource ); } cascadeProperty( - componentPathStackDepth + 1, + componentPath, children[i], types[i], componentPropertyStyle, @@ -418,7 +432,7 @@ private void cascadeComponent( } private void cascadeAssociation( - final int componentPathStackDepth, + List componentPath, final Object child, final Type type, final CascadeStyle style, @@ -428,7 +442,7 @@ private void cascadeAssociation( } else if ( type.isCollectionType() ) { cascadeCollection( - componentPathStackDepth, + componentPath, child, style, (CollectionType) type @@ -440,12 +454,13 @@ else if ( type.isCollectionType() ) { * Cascade an action to a collection */ private void cascadeCollection( - final int componentPathStackDepth, + List componentPath, final Object child, final CascadeStyle style, final CollectionType type) { final CollectionPersister persister = - eventSource.getFactory().getMetamodel().collectionPersister( type.getRole() ); + eventSource.getFactory().getMappingMetamodel() + .getCollectionDescriptor( type.getRole() ); final Type elemType = persister.getElementType(); CascadePoint elementsCascadePoint = cascadePoint; @@ -456,7 +471,7 @@ private void cascadeCollection( //cascade to current collection elements if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) { cascadeCollectionElements( - componentPathStackDepth, + componentPath, child, type, style, @@ -492,7 +507,7 @@ private void cascadeToOne( * Cascade to the collection elements */ private void cascadeCollectionElements( - final int componentPathStackDepth, + List componentPath, final Object child, final CollectionType collectionType, final CascadeStyle style, @@ -510,7 +525,7 @@ private void cascadeCollectionElements( final Iterator itr = action.getCascadableChildrenIterator( eventSource, collectionType, child ); while ( itr.hasNext() ) { cascadeProperty( - componentPathStackDepth, + componentPath, itr.next(), elemType, style, @@ -527,8 +542,9 @@ private void cascadeCollectionElements( final boolean deleteOrphans = style.hasOrphanDelete() && action.deleteOrphans() && elemType.isEntityType() + && child instanceof PersistentCollection // a newly instantiated collection can't have orphans - && child instanceof PersistentCollection; + && ! ( (PersistentCollection) child ).isNewlyInstantiated(); if ( deleteOrphans ) { final boolean traceEnabled = LOG.isTraceEnabled(); @@ -539,7 +555,7 @@ private void cascadeCollectionElements( // 1. newly instantiated collections // 2. arrays (we can't track orphans for detached arrays) final String entityName = collectionType.getAssociatedEntityName( eventSource.getFactory() ); - deleteOrphans( entityName, (PersistentCollection) child ); + deleteOrphans( entityName, (PersistentCollection) child ); if ( traceEnabled ) { LOG.tracev( "Done deleting orphans for collection: {0}", collectionType.getRole() ); @@ -550,7 +566,7 @@ private void cascadeCollectionElements( /** * Delete any entities that were removed from the collection */ - private void deleteOrphans(String entityName, PersistentCollection pc) throws HibernateException { + private void deleteOrphans(String entityName, PersistentCollection pc) throws HibernateException { //TODO: suck this logic into the collection! final Collection orphans; if ( pc.wasInitialized() ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingAction.java index a457deec7..7c6bdd749 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingAction.java @@ -88,5 +88,5 @@ Iterator getCascadableChildrenIterator( */ boolean performOnLazyProperty(); - org.hibernate.engine.spi.CascadingAction delegate(); + org.hibernate.engine.spi.CascadingAction delegate(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingActions.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingActions.java index 9a0254c1d..d194fe312 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingActions.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/CascadingActions.java @@ -13,7 +13,6 @@ import org.hibernate.LockOptions; import org.hibernate.TransientPropertyValueException; import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.Status; import org.hibernate.event.spi.DeleteContext; import org.hibernate.event.spi.EventSource; @@ -21,7 +20,6 @@ import org.hibernate.event.spi.PersistContext; import org.hibernate.event.spi.RefreshContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.session.ReactiveSession; @@ -30,6 +28,7 @@ import org.hibernate.type.EntityType; import org.hibernate.type.Type; +import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy; import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -58,9 +57,11 @@ public CompletionStage cascade( DeleteContext context, boolean isCascadeDeleteEnabled) { LOG.tracev( "Cascading to delete: {0}", entityName ); - return session.unwrap( ReactiveSession.class ).reactiveFetch( child, true ) - .thenCompose( c -> session.unwrap( ReactiveSession.class ) - .reactiveRemove( entityName, c, isCascadeDeleteEnabled, context ) ); + final ReactiveSession reactiveSession = session.unwrap( ReactiveSession.class ); + //TODO: force-fetching it here circumvents the unloaded-delete optimization + // so we don't actually want to do this + return reactiveSession.reactiveFetch( child, true ) + .thenCompose( c -> reactiveSession.reactiveRemove( entityName, c, isCascadeDeleteEnabled, context ) ); } }; @@ -101,10 +102,19 @@ public CompletionStage cascade( private boolean isInManagedState(Object child, EventSource session) { EntityEntry entry = session.getPersistenceContextInternal().getEntry( child ); - return entry != null && - ( entry.getStatus() == Status.MANAGED - || entry.getStatus() == Status.READ_ONLY - || entry.getStatus() == Status.SAVING ); + if ( entry == null ) { + return false; + } + else { + switch ( entry.getStatus() ) { + case MANAGED: + case READ_ONLY: + case SAVING: + return true; + default: + return false; + } + } } @Override @@ -115,12 +125,12 @@ public CompletionStage noCascade( Type propertyType, int propertyIndex) { if ( propertyType.isEntityType() ) { - Object child = persister.getPropertyValue( parent, propertyIndex ); + final Object child = persister.getValue( parent, propertyIndex ); if ( child != null && !isInManagedState( child, session ) - && !( child instanceof HibernateProxy ) ) { //a proxy cannot be transient and it breaks ForeignKeys.isTransient - final String childEntityName = ( (EntityType) propertyType ).getAssociatedEntityName( - session.getFactory() ); + && !isHibernateProxy( child ) ) { //a proxy cannot be transient and it breaks ForeignKeys.isTransient + final String childEntityName = + ( (EntityType) propertyType ).getAssociatedEntityName( session.getFactory() ); return ForeignKeys.isTransient( childEntityName, child, null, session ) .thenAccept( isTrans -> { if ( isTrans ) { @@ -144,7 +154,7 @@ public CompletionStage noCascade( * @see org.hibernate.Session#merge(Object) */ public static final CascadingAction MERGE = - new BaseCascadingAction( org.hibernate.engine.spi.CascadingActions.MERGE ) { + new BaseCascadingAction<>( org.hibernate.engine.spi.CascadingActions.MERGE ) { @Override public CompletionStage cascade( EventSource session, @@ -211,7 +221,7 @@ public boolean deleteOrphans() { } @Override - public org.hibernate.engine.spi.CascadingAction delegate() { + public org.hibernate.engine.spi.CascadingAction delegate() { return delegate; } From 2b89262a02989d8f84b32ffbb150ae52af83af8a Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 13 Apr 2023 18:59:50 +0200 Subject: [PATCH 4/5] cleanups and sync with core for ForeignKeys --- .../reactive/engine/impl/ForeignKeys.java | 164 ++++++++++-------- .../impl/ReactiveAbstractEntityPersister.java | 27 +-- .../entity/impl/ReactiveEntityPersister.java | 12 +- 3 files changed, 120 insertions(+), 83 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java index f8617dcfd..fd0449ef1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ForeignKeys.java @@ -10,19 +10,23 @@ import org.hibernate.TransientObjectException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; -import org.hibernate.engine.internal.ManagedTypeHelper; import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; +import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; +import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy; +import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture; import static org.hibernate.reactive.util.impl.CompletionStages.falseFuture; import static org.hibernate.reactive.util.impl.CompletionStages.loop; @@ -111,48 +115,60 @@ else if ( type.isEntityType() ) { else { // if we're dealing with a lazy property, it may need to be // initialized to determine if the value is nullifiable - if ( isDelete - && value == LazyPropertyInitializer.UNFETCHED_PROPERTY - && !session.getPersistenceContextInternal().isNullifiableEntityKeysEmpty() ) { - throw new UnsupportedOperationException( "lazy property initialization not supported" ); + if ( needToInitialize( value, entityType ) ) { + returnedStage = ( (ReactiveEntityPersister) persister ) + .reactiveInitializeLazyProperty( propertyName, self, session ) + .thenCompose( possiblyInitializedValue -> { + if ( possiblyInitializedValue == null ) { + // The uninitialized value was initialized to null + return nullFuture(); + } + else { + // If the value is not nullifiable, make sure that the + // possibly initialized value is returned. + return isNullifiable( entityType.getAssociatedEntityName(), value ) + .thenApply( trans -> trans ? null : value ); + } } + ); + } + else { + returnedStage = isNullifiable( entityType.getAssociatedEntityName(), value ) + .thenApply( trans -> trans ? null : value ); } - // If the value is not nullifiable, make sure that the - // possibly initialized value is returned. - returnedStage = isNullifiable( entityType.getAssociatedEntityName(), value ) - .thenApply( trans -> trans ? null : value ); } } else if ( type.isAnyType() ) { - returnedStage = isNullifiable( null, value ).thenApply( trans -> trans ? null : value ); + returnedStage = isNullifiable( null, value ).thenApply( trans -> trans ? null : value ); } else if ( type.isComponentType() ) { - final CompositeType actype = (CompositeType) type; - final Object[] subValues = actype.getPropertyValues( value, session ); - final Type[] subtypes = actype.getSubtypes(); - final String[] subPropertyNames = actype.getPropertyNames(); + final CompositeType compositeType = (CompositeType) type; + final Object[] subValues = compositeType.getPropertyValues( value, session ); + final Type[] subtypes = compositeType.getSubtypes(); + final String[] subPropertyNames = compositeType.getPropertyNames(); CompletionStage loop = falseFuture(); for ( int i = 0; i < subValues.length; i++ ) { final int index = i; - loop = loop - .thenCompose( substitute -> nullifyTransientReferences( - subValues[index], - StringHelper.qualify( propertyName, subPropertyNames[index] ), - subtypes[index] - ) - .thenApply( replacement -> { - if ( replacement != subValues[index] ) { - subValues[index] = replacement; - return Boolean.TRUE; - } - return substitute; - } ) - ); + loop = loop.thenCompose( substitute -> nullifyTransientReferences( + subValues[index], + StringHelper.qualify( propertyName, subPropertyNames[index] ), + subtypes[index] + ) + .thenApply( replacement -> { + if ( replacement != subValues[index] ) { + subValues[index] = replacement; + return true; + } + else { + return substitute; + } + } ) + ); } returnedStage = loop.thenApply( substitute -> { if ( substitute ) { // todo : need to account for entity mode on the CompositeType interface :( - actype.setPropertyValues( value, subValues ); + compositeType.setPropertyValues( value, subValues ); } return value; } ); @@ -162,21 +178,23 @@ else if ( type.isComponentType() ) { } return returnedStage.thenApply( returnedValue -> { - trackDirt( value, propertyName, returnedValue ); + // value != returnedValue if either: + // 1) returnedValue was nullified (set to null); + // or 2) returnedValue was initialized, but not nullified. + // When bytecode-enhancement is used for dirty-checking, the change should + // only be tracked when returnedValue was nullified (1)). + if ( value != returnedValue && returnedValue == null ) { + processIfSelfDirtinessTracker( self, SelfDirtinessTracker::$$_hibernate_trackChange, propertyName ); + } return returnedValue; } ); } - private void trackDirt(Object value, String propertyName, Object returnedValue) { - // value != returnedValue if either: - // 1) returnedValue was nullified (set to null); - // or 2) returnedValue was initialized, but not nullified. - // When bytecode-enhancement is used for dirty-checking, the change should - // only be tracked when returnedValue was nullified (1)). - if ( value != returnedValue && returnedValue == null - && ManagedTypeHelper.isSelfDirtinessTracker( self ) ) { - ManagedTypeHelper.asSelfDirtinessTracker( self ).$$_hibernate_trackChange( propertyName ); - } + private boolean needToInitialize(Object value, Type type) { + return isDelete + && value == LazyPropertyInitializer.UNFETCHED_PROPERTY + && type.isEntityType() + && !session.getPersistenceContextInternal().isNullifiableEntityKeysEmpty(); } /** @@ -192,17 +210,29 @@ private CompletionStage isNullifiable(final String entityName, Object o return falseFuture(); } - if ( object instanceof HibernateProxy ) { - // if its an uninitialized proxy it can't be transient - final LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer(); - if ( li.getImplementation( session ) == null ) { - return falseFuture(); - // ie. we never have to null out a reference to - // an uninitialized proxy + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + + final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); + if ( lazyInitializer != null ) { + // if it's an uninitialized proxy it can only be + // transient if we did an unloaded-delete on the + // proxy itself, in which case there is no entry + // for it, but its key has already been registered + // as nullifiable + Object entity = lazyInitializer.getImplementation( session ); + if ( entity == null ) { + // an unloaded proxy might be scheduled for deletion + completedFuture( persistenceContext.containsDeletedUnloadedEntityKey( + session.generateEntityKey( + lazyInitializer.getIdentifier(), + session.getFactory().getMappingMetamodel() + .getEntityDescriptor( lazyInitializer.getEntityName() ) + ) + ) ); } else { //unwrap it - object = li.getImplementation( session ); + object = entity; } } @@ -210,7 +240,8 @@ private CompletionStage isNullifiable(final String entityName, Object o // unless we are using native id generation, in which // case we definitely need to nullify if ( object == self ) { - return completedFuture( isEarlyInsert || ( isDelete && session.getJdbcServices().getDialect().hasSelfReferentialForeignKeyBug() ) ); + return completedFuture( isEarlyInsert + || isDelete && session.getJdbcServices().getDialect().hasSelfReferentialForeignKeyBug() ); } // See if the entity is already bound to this session, if not look at the @@ -218,13 +249,10 @@ private CompletionStage isNullifiable(final String entityName, Object o // id is not "unsaved" (that is, we rely on foreign keys to keep // database integrity) - final EntityEntry entityEntry = session.getPersistenceContextInternal().getEntry( object ); - if ( entityEntry == null ) { - return isTransient( entityName, object, null, session ); - } - else { - return completedFuture( entityEntry.isNullifiable( isEarlyInsert, session ) ); - } + final EntityEntry entityEntry = persistenceContext.getEntry( object ); + return entityEntry == null + ? isTransient( entityName, object, null, session ) + : completedFuture( entityEntry.isNullifiable( isEarlyInsert, session ) ); } } @@ -242,7 +270,7 @@ private CompletionStage isNullifiable(final String entityName, Object o * @return {@code true} if the given entity is not transient (meaning it is either detached/persistent) */ public static CompletionStage isNotTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session) { - if ( entity instanceof HibernateProxy ) { + if ( isHibernateProxy( entity ) ) { return trueFuture(); } @@ -251,7 +279,6 @@ public static CompletionStage isNotTransient(String entityName, Object } // todo : shouldn't assumed be revered here? - return isTransient( entityName, entity, assumed, session ) .thenApply( trans -> !trans ); } @@ -296,7 +323,8 @@ public static CompletionStage isTransient(String entityName, Object ent } // hit the database, after checking the session cache for a snapshot - ReactivePersistenceContextAdapter persistenceContext = (ReactivePersistenceContextAdapter) session.getPersistenceContextInternal(); + ReactivePersistenceContextAdapter persistenceContext = + (ReactivePersistenceContextAdapter) session.getPersistenceContextInternal(); Object id = persister.getIdentifier( entity, session ); return persistenceContext.reactiveGetDatabaseSnapshot( id, persister ).thenApply( Objects::isNull ); } @@ -394,8 +422,8 @@ private static CompletionStage collectNonNullableTransientEntities( if ( !isNullable && !entityType.isOneToOne() ) { return nullifier .isNullifiable( entityType.getAssociatedEntityName(), value ) - .thenAccept( isNullifiable -> { - if ( isNullifiable ) { + .thenAccept( nullifiable -> { + if ( nullifiable ) { nonNullableTransientEntities.add( propertyName, value ); } } ); @@ -405,20 +433,20 @@ else if ( type.isAnyType() ) { if ( !isNullable ) { return nullifier .isNullifiable( null, value ) - .thenAccept( isNullifiable -> { - if ( isNullifiable ) { + .thenAccept( nullifiable -> { + if ( nullifiable ) { nonNullableTransientEntities.add( propertyName, value ); } } ); } } else if ( type.isComponentType() ) { - final CompositeType actype = (CompositeType) type; - final boolean[] subValueNullability = actype.getPropertyNullability(); + final CompositeType compositeType = (CompositeType) type; + final boolean[] subValueNullability = compositeType.getPropertyNullability(); if ( subValueNullability != null ) { - final String[] subPropertyNames = actype.getPropertyNames(); - final Object[] subvalues = actype.getPropertyValues( value, session ); - final Type[] subtypes = actype.getSubtypes(); + final String[] subPropertyNames = compositeType.getPropertyNames(); + final Object[] subvalues = compositeType.getPropertyValues( value, session ); + final Type[] subtypes = compositeType.getSubtypes(); return loop( 0, subtypes.length, i -> collectNonNullableTransientEntities( nullifier, 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 0ac143abe..4deaa284a 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 @@ -302,20 +302,27 @@ private CompletionStage currentVersion(SharedSessionContractImplementor } } - @SuppressWarnings("unchecked") - default CompletionStage reactiveInitializeLazyProperty(Attribute field, E entity, SharedSessionContractImplementor session) { - String fieldName = field.getName(); - Object result = initializeLazyProperty( fieldName, entity, session ); - if (result instanceof CompletionStage) { + default CompletionStage reactiveInitializeLazyProperty( + Attribute field, E entity, + SharedSessionContractImplementor session) { + return reactiveInitializeLazyProperty( field.getName(), entity, session ); + } + + default CompletionStage reactiveInitializeLazyProperty( + String field, + E entity, + SharedSessionContractImplementor session) { + final Object result = initializeLazyProperty( field, entity, session ); + if ( result instanceof CompletionStage ) { return (CompletionStage) result; } - else if (result instanceof PersistentCollection) { + else if ( result instanceof PersistentCollection ) { // Hibernate core doesn't set the field when it's a // collection. That's inconsistent with what happens // for other lazy fields, so let's set the field here - String[] propertyNames = getPropertyNames(); - for (int index=0; index collection = (PersistentCollection) result; return collection.wasInitialized() ? completedFuture( (T) collection ) : ((ReactiveSession) session).reactiveInitializeCollection( collection, false ) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java index 93aa1ade7..e96898781 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java @@ -18,7 +18,6 @@ import jakarta.persistence.metamodel.Attribute; -import static org.hibernate.reactive.util.impl.CompletionStages.nullFuture; /** * A reactive {@link EntityPersister}. Supports non-blocking insert/update/delete operations. @@ -141,12 +140,15 @@ CompletionStage reactiveProcessUpdateGenerated( */ CompletionStage reactiveGetDatabaseSnapshot(Object id, SharedSessionContractImplementor session); - default CompletionStage reactiveInitializeLazyProperty( + CompletionStage reactiveInitializeLazyProperty( Attribute field, E entity, - SharedSessionContractImplementor session) { - return nullFuture(); - } + SharedSessionContractImplementor session); + + CompletionStage reactiveInitializeLazyProperty( + String field, + E entity, + SharedSessionContractImplementor session); CompletionStage reactiveInitializeEnhancedEntityUsedAsProxy( Object entity, From 356b62105680b5ce120353cbea6e09684cda21da Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 13 Apr 2023 19:45:08 +0200 Subject: [PATCH 5/5] cleanups and sync with core for CollectionActions --- .../ReactiveCollectionRecreateAction.java | 12 +- .../impl/ReactiveCollectionRemoveAction.java | 105 +++++------------ .../impl/ReactiveCollectionUpdateAction.java | 106 +++++------------- 3 files changed, 65 insertions(+), 158 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java index 6ac5f3337..5f2842183 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRecreateAction.java @@ -29,16 +29,14 @@ public ReactiveCollectionRecreateAction(final PersistentCollection collection, f @Override public CompletionStage reactiveExecute() { - final ReactiveCollectionPersister persister = (ReactiveCollectionPersister) getPersister(); - final SharedSessionContractImplementor session = getSession(); - final Object key = getKey(); - + // this method is called when a new non-null collection is persisted + // or when an existing (non-null) collection is moved to a new owner final PersistentCollection collection = getCollection(); - preRecreate(); - + final SharedSessionContractImplementor session = getSession(); + final ReactiveCollectionPersister persister = (ReactiveCollectionPersister) getPersister(); return persister - .reactiveRecreate( collection, key, session ) + .reactiveRecreate( collection, getKey(), session ) .thenAccept( v -> { // FIXME: I think we could move everything in a method reference call session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java index 525c8d92f..4b086cf34 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionRemoveAction.java @@ -12,22 +12,18 @@ import org.hibernate.action.internal.CollectionAction; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.PostCollectionRecreateEvent; -import org.hibernate.event.spi.PostCollectionRecreateEventListener; import org.hibernate.event.spi.PostCollectionRemoveEvent; import org.hibernate.event.spi.PostCollectionRemoveEventListener; -import org.hibernate.event.spi.PreCollectionRecreateEvent; -import org.hibernate.event.spi.PreCollectionRecreateEventListener; import org.hibernate.event.spi.PreCollectionRemoveEvent; import org.hibernate.event.spi.PreCollectionRemoveEventListener; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.reactive.engine.ReactiveExecutable; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; -import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.stat.spi.StatisticsImplementor; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; + public class ReactiveCollectionRemoveAction extends CollectionAction implements ReactiveExecutable { private final Object affectedOwner; private final boolean emptySnapshot; @@ -68,47 +64,33 @@ public ReactiveCollectionRemoveAction( @Override public CompletionStage reactiveExecute() { - final Object key = getKey(); - final SharedSessionContractImplementor session = getSession(); - final ReactiveCollectionPersister reactivePersister = (ReactiveCollectionPersister) getPersister(); - final CollectionPersister corePersister = getPersister(); - final PersistentCollection collection = getCollection(); - final StatisticsImplementor statistics = session.getFactory().getStatistics(); + preRemove(); - CompletionStage removeStage = CompletionStages.voidFuture(); - - if ( !emptySnapshot ) { + final SharedSessionContractImplementor session = getSession(); + CompletionStage removeStage; + if ( emptySnapshot ) { + removeStage = voidFuture(); + } + else { + final ReactiveCollectionPersister reactivePersister = (ReactiveCollectionPersister) getPersister(); // an existing collection that was either non-empty or uninitialized // is replaced by null or a different collection // (if the collection is uninitialized, hibernate has no way of // knowing if the collection is actually empty without querying the db) - removeStage = removeStage.thenAccept( v -> preRemove() ) - .thenCompose( v -> reactivePersister - .reactiveRemove( key, session ) - .thenAccept( ignore -> { - evict(); - postRemove(); - }) - ); + removeStage = reactivePersister.reactiveRemove( getKey(), session ); } - if( collection != null ) { - return removeStage.thenAccept(v -> { + return removeStage.thenAccept( v -> { + final PersistentCollection collection = getCollection(); + if ( collection != null ) { session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); - evict(); - postRemove(); - if ( statistics.isStatisticsEnabled() ) { - statistics.updateCollection( corePersister.getRole() ); - } - } ); - } - return removeStage.thenAccept(v -> { + } evict(); postRemove(); + final StatisticsImplementor statistics = session.getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { - statistics.updateCollection( corePersister.getRole() ); + statistics.updateCollection( getPersister().getRole() ); } } ); - } @Override @@ -118,57 +100,32 @@ public void execute() throws HibernateException { } private void preRemove() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_PRE_COLLECTION_REMOVE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PreCollectionRemoveEvent event = new PreCollectionRemoveEvent( + getFastSessionServices().eventListenerGroup_PRE_COLLECTION_REMOVE + .fireLazyEventOnEachListener( this::newPreCollectionRemoveEvent, + PreCollectionRemoveEventListener::onPreRemoveCollection ); + } + + private PreCollectionRemoveEvent newPreCollectionRemoveEvent() { + return new PreCollectionRemoveEvent( getPersister(), getCollection(), eventSource(), affectedOwner ); - for ( PreCollectionRemoveEventListener listener : listenerGroup.listeners() ) { - listener.onPreRemoveCollection( event ); - } } private void postRemove() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_POST_COLLECTION_REMOVE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PostCollectionRemoveEvent event = new PostCollectionRemoveEvent( + getFastSessionServices().eventListenerGroup_POST_COLLECTION_REMOVE + .fireLazyEventOnEachListener( this::newPostCollectionRemoveEvent, + PostCollectionRemoveEventListener::onPostRemoveCollection ); + } + + private PostCollectionRemoveEvent newPostCollectionRemoveEvent() { + return new PostCollectionRemoveEvent( getPersister(), getCollection(), eventSource(), affectedOwner ); - for ( PostCollectionRemoveEventListener listener : listenerGroup.listeners() ) { - listener.onPostRemoveCollection( event ); - } - } - - private void preRecreate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_PRE_COLLECTION_RECREATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PreCollectionRecreateEvent event = new PreCollectionRecreateEvent( getPersister(), getCollection(), eventSource() ); - for ( PreCollectionRecreateEventListener listener : listenerGroup.listeners() ) { - listener.onPreRecreateCollection( event ); - } - } - - private void postRecreate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_POST_COLLECTION_RECREATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PostCollectionRecreateEvent event = new PostCollectionRecreateEvent( getPersister(), getCollection(), eventSource() ); - for ( PostCollectionRecreateEventListener listener : listenerGroup.listeners() ) { - listener.onPostRecreateCollection( event ); - } } - } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java index 1860b1f8a..21129a1e5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveCollectionUpdateAction.java @@ -13,14 +13,9 @@ import org.hibernate.action.internal.CollectionAction; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.PostCollectionRecreateEvent; -import org.hibernate.event.spi.PostCollectionRecreateEventListener; import org.hibernate.event.spi.PostCollectionUpdateEvent; import org.hibernate.event.spi.PostCollectionUpdateEventListener; -import org.hibernate.event.spi.PreCollectionRecreateEvent; -import org.hibernate.event.spi.PreCollectionRecreateEventListener; import org.hibernate.event.spi.PreCollectionUpdateEvent; import org.hibernate.event.spi.PreCollectionUpdateEventListener; import org.hibernate.persister.collection.CollectionPersister; @@ -28,10 +23,10 @@ import org.hibernate.reactive.logging.impl.Log; import org.hibernate.reactive.logging.impl.LoggerFactory; import org.hibernate.reactive.persister.collection.impl.ReactiveCollectionPersister; -import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.stat.spi.StatisticsImplementor; import static org.hibernate.pretty.MessageHelper.collectionInfoString; +import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture; /** @@ -59,15 +54,13 @@ public CompletionStage reactiveExecute() { final Object key = getKey(); final SharedSessionContractImplementor session = getSession(); final ReactiveCollectionPersister reactivePersister = (ReactiveCollectionPersister) getPersister(); - final CollectionPersister corePersister = getPersister(); + final CollectionPersister persister = getPersister(); final PersistentCollection collection = getCollection(); - final boolean affectedByFilters = corePersister.isAffectedByEnabledFilters( session ); + final boolean affectedByFilters = persister.isAffectedByEnabledFilters( session ); preUpdate(); - CompletionStage updateStage = CompletionStages.voidFuture(); - - // And then make sure that each operations is executed in its own stage maintaining the same order as in ORM + final CompletionStage updateStage; if ( !collection.wasInitialized() ) { // If there were queued operations, they would have been processed // and cleared by now. @@ -76,53 +69,35 @@ public CompletionStage reactiveExecute() { throw new AssertionFailure( "collection is not dirty" ); } //do nothing - we only need to notify the cache... + updateStage = voidFuture(); } else if ( !affectedByFilters && collection.empty() ) { - if ( !emptySnapshot ) { - updateStage = updateStage - .thenCompose( v -> reactivePersister.reactiveRemove( key, session ) ) - .thenAccept( count -> { /* We don't care, maybe we can log it as debug */} ); - } + updateStage = emptySnapshot ? voidFuture() : reactivePersister.reactiveRemove( key, session ); } - else if ( collection.needsRecreate( corePersister ) ) { + else if ( collection.needsRecreate( persister ) ) { if ( affectedByFilters ) { - throw LOG.cannotRecreateCollectionWhileFilterIsEnabled( collectionInfoString( corePersister, collection, key, session ) ); - } - if ( !emptySnapshot ) { - updateStage = updateStage - .thenCompose( v -> reactivePersister.reactiveRemove( key, session ) ) - .thenAccept( count -> { /* We don't care, maybe we can log it as debug */} ); + throw LOG.cannotRecreateCollectionWhileFilterIsEnabled( collectionInfoString( persister, collection, key, session ) ); } - - return updateStage - .thenCompose( v -> reactivePersister - .reactiveRecreate( collection, key, session ) - .thenAccept( ignore -> { - session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); - evict(); - postUpdate(); - final StatisticsImplementor statistics = session.getFactory().getStatistics(); - if ( statistics.isStatisticsEnabled() ) { - statistics.updateCollection( corePersister.getRole() ); - } - }) - ); + updateStage = emptySnapshot + ? reactivePersister.reactiveRecreate( collection, key, session ) + : reactivePersister.reactiveRemove( key, session ) + .thenCompose( v -> reactivePersister.reactiveRecreate( collection, key, session ) ); } else { - updateStage = updateStage + updateStage = voidFuture() .thenCompose( v -> reactivePersister.reactiveDeleteRows( collection, key, session ) ) .thenCompose( v -> reactivePersister.reactiveUpdateRows( collection, key, session ) ) .thenCompose( v -> reactivePersister.reactiveInsertRows( collection, key, session ) ); } - return updateStage.thenAccept(v -> { + return updateStage.thenAccept( v -> { session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); evict(); postUpdate(); final StatisticsImplementor statistics = session.getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { - statistics.updateCollection( corePersister.getRole() ); + statistics.updateCollection( persister.getRole() ); } } ); } @@ -134,54 +109,31 @@ public void execute() throws HibernateException { } private void preUpdate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_PRE_COLLECTION_UPDATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PreCollectionUpdateEvent event = new PreCollectionUpdateEvent( + getFastSessionServices().eventListenerGroup_PRE_COLLECTION_UPDATE + .fireLazyEventOnEachListener( this::newPreCollectionUpdateEvent, + PreCollectionUpdateEventListener::onPreUpdateCollection ); + } + + private PreCollectionUpdateEvent newPreCollectionUpdateEvent() { + return new PreCollectionUpdateEvent( getPersister(), getCollection(), eventSource() ); - for ( PreCollectionUpdateEventListener listener : listenerGroup.listeners() ) { - listener.onPreUpdateCollection( event ); - } } private void postUpdate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_POST_COLLECTION_UPDATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PostCollectionUpdateEvent event = new PostCollectionUpdateEvent( + getFastSessionServices().eventListenerGroup_POST_COLLECTION_UPDATE + .fireLazyEventOnEachListener( this::newPostCollectionUpdateEvent, + PostCollectionUpdateEventListener::onPostUpdateCollection ); + } + + private PostCollectionUpdateEvent newPostCollectionUpdateEvent() { + return new PostCollectionUpdateEvent( getPersister(), getCollection(), eventSource() ); - for ( PostCollectionUpdateEventListener listener : listenerGroup.listeners() ) { - listener.onPostUpdateCollection( event ); - } } - private void preRecreate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_PRE_COLLECTION_RECREATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PreCollectionRecreateEvent event = new PreCollectionRecreateEvent( getPersister(), getCollection(), eventSource() ); - for ( PreCollectionRecreateEventListener listener : listenerGroup.listeners() ) { - listener.onPreRecreateCollection( event ); - } - } - - private void postRecreate() { - final EventListenerGroup listenerGroup = getFastSessionServices().eventListenerGroup_POST_COLLECTION_RECREATE; - if ( listenerGroup.isEmpty() ) { - return; - } - final PostCollectionRecreateEvent event = new PostCollectionRecreateEvent( getPersister(), getCollection(), eventSource() ); - for ( PostCollectionRecreateEventListener listener : listenerGroup.listeners() ) { - listener.onPostRecreateCollection( event ); - } - } }