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 129b9e236..1031f8b17 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 @@ -30,7 +30,6 @@ 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.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -69,11 +68,13 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.type.BasicType; +import org.hibernate.type.Type; import jakarta.persistence.metamodel.Attribute; import static java.lang.invoke.MethodHandles.lookup; import static java.util.Collections.emptyMap; +import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.generator.EventType.UPDATE; import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize; @@ -105,6 +106,8 @@ */ public interface ReactiveAbstractEntityPersister extends ReactiveEntityPersister { + Log LOG = make( Log.class, lookup() ); + /** * A self-reference of type {@code AbstractEntityPersister}. * @@ -374,32 +377,51 @@ default CompletionStage reactiveInitializeLazyPropertiesFromDatastore( EntityEntry entry, String fieldName, SharedSessionContractImplementor session) { + return isNonLazyPropertyName( fieldName ) + ? initLazyProperty( entity, id, entry, fieldName, session ) + : initLazyProperties( entity, id, entry, fieldName, session ); + } - if ( !hasLazyProperties() ) { - throw new AssertionFailure( "no lazy properties" ); - } + boolean isNonLazyPropertyName(String fieldName); - final PersistentAttributeInterceptor interceptor = ManagedTypeHelper.asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor(); - if ( interceptor == null ) { - throw new AssertionFailure( "Expecting bytecode interceptor to be non-null" ); - } + private CompletionStage initLazyProperty( + Object entity, + Object id, + EntityEntry entry, + String fieldName, + SharedSessionContractImplementor session) { + // An eager property can be lazy because of an applied EntityGraph + final int propertyIndex = getPropertyIndex( fieldName ); + final List partsToSelect = List.of( getAttributeMapping( propertyIndex ) ); + return reactiveGetOrCreateLazyLoadPlan( fieldName, partsToSelect ) + .load( id, session ) + .thenApply( results -> { + final Object result = results[0]; + initializeLazyProperty( entity, entry, result, propertyIndex, getPropertyTypes()[propertyIndex] ); + return result; + } ); + } + + ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List partsToSelect); - make( Log.class, lookup() ).tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName ); + private CompletionStage initLazyProperties( + Object entity, + Object id, + EntityEntry entry, + String fieldName, + SharedSessionContractImplementor session) { - final String fetchGroup = getEntityPersister().getBytecodeEnhancementMetadata() - .getLazyAttributesMetadata() - .getFetchGroupName( fieldName ); - final List fetchGroupAttributeDescriptors = getEntityPersister().getBytecodeEnhancementMetadata() - .getLazyAttributesMetadata() - .getFetchGroupAttributeDescriptors( fetchGroup ); + assert hasLazyProperties(); + LOG.tracef( "Initializing lazy properties from datastore (triggered for '%s')", fieldName ); - @SuppressWarnings("deprecation") - Set initializedLazyAttributeNames = interceptor.getInitializedLazyAttributeNames(); + final var interceptor = asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor(); + assert interceptor != null : "Expecting bytecode interceptor to be non-null"; + final Set initializedLazyAttributeNames = interceptor.getInitializedLazyAttributeNames(); + LOG.tracef( "Initializing lazy properties from datastore (triggered for `%s`)", fieldName ); - // FIXME: How do I pass this to the query? - Object[] arguments = PreparedStatementAdaptor.bind( - statement -> getIdentifierType().nullSafeSet( statement, id, 1, session ) - ); + final var lazyAttributesMetadata = getBytecodeEnhancementMetadata().getLazyAttributesMetadata(); + final String fetchGroup = lazyAttributesMetadata.getFetchGroupName( fieldName ); + final var fetchGroupAttributeDescriptors = lazyAttributesMetadata.getFetchGroupAttributeDescriptors( fetchGroup ); return reactiveGetSQLLazySelectLoadPlan( fetchGroup ) .load( id, session ) @@ -420,51 +442,55 @@ default CompletionStage initLazyProperty( PersistentAttributeInterceptor interceptor, List fetchGroupAttributeDescriptors, Set initializedLazyAttributeNames, - Object[] values) { // Load all the lazy properties that are in the same fetch group - CompletionStage resultStage = nullFuture(); - int i = 0; - for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) { - if ( initializedLazyAttributeNames.contains( fetchGroupAttributeDescriptor.getName() ) ) { + Object[] results) { // Load all the lazy properties that are in the same fetch group + CompletionStage finalResultStage = nullFuture(); + final int[] i = { 0 }; + for ( var fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) { + final String attributeName = fetchGroupAttributeDescriptor.getName(); + final boolean previousInitialized = initializedLazyAttributeNames.contains( attributeName ); + final int index = i[0]++; + if ( previousInitialized ) { // Already initialized - if ( fetchGroupAttributeDescriptor.getName().equals( fieldName ) ) { - resultStage = completedFuture( entry.getLoadedValue( fetchGroupAttributeDescriptor.getName() ) ); + if ( attributeName.equals( fieldName ) ) { + finalResultStage = finalResultStage + .thenApply( finalResult -> entry.getLoadedValue( fetchGroupAttributeDescriptor.getName() ) ); } + // it's already been initialized (e.g. by a write) so we don't want to overwrite + // TODO: we should consider un-marking an attribute as dirty based on the selected value + // - we know the current value: + // getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() ); + // - we know the selected value (see selectedValue below) + // - we can use the attribute Type to tell us if they are the same + // - assuming entity is a SelfDirtinessTracker we can also know if the attribute is currently + // considered dirty, and if really not dirty we would do the un-marking + // - of course that would mean a new method on SelfDirtinessTracker to allow un-marking continue; } - final Object selectedValue = values[i++]; - if ( selectedValue instanceof CompletionStage ) { + final Object result = results[index]; + if ( result instanceof CompletionStage ) { // This happens with a lazy one-to-one (bytecode enhancement enabled) - CompletionStage selectedValueStage = (CompletionStage) selectedValue; - resultStage = resultStage - .thenCompose( result -> selectedValueStage - .thenApply( selected -> { - final boolean set = initializeLazyProperty( - fieldName, - entity, - entry, - fetchGroupAttributeDescriptor.getLazyIndex(), - selected - ); - if ( set ) { - interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() ); - return selected; - } - return result; - } ) - ); + final CompletionStage resultStage = (CompletionStage) result; + finalResultStage = finalResultStage.thenCompose( finalResult -> resultStage + .thenApply( value -> { + if ( initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, result ) ) { + interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() ); + return value; + } + return finalResult; + } ) + ); } else { - final boolean set = initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor.getLazyIndex(), selectedValue ); - if ( set ) { - resultStage = completedFuture( selectedValue ); + if ( initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, result ) ) { interceptor.attributeInitialized( fetchGroupAttributeDescriptor.getName() ); + finalResultStage = finalResultStage.thenApply( finalResult -> result ); } } } - return resultStage.thenApply( result -> { - make( Log.class, lookup() ).trace( "Done initializing lazy properties" ); + return finalResultStage.thenApply( result -> { + LOG.trace( "Done initializing lazy properties" ); return result; } ); } @@ -526,9 +552,11 @@ private CompletionStage loadFromDatabaseOrCache( .load( identifier, entity, lockOptions, session ); } - SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions); + boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue); - boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue); + void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type); + + SingleIdEntityLoader determineLoaderToUse(SharedSessionContractImplementor session, LockOptions lockOptions); Object initializeLazyProperty(String fieldName, Object entity, SharedSessionContractImplementor session); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java index c5d068db7..62ddf29f5 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveAbstractPersisterDelegate.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import org.hibernate.FetchMode; @@ -23,6 +24,7 @@ import org.hibernate.generator.Generator; import org.hibernate.generator.values.GeneratedValues; import org.hibernate.id.IdentityGenerator; +import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.spi.BatchLoaderFactory; import org.hibernate.loader.ast.spi.EntityBatchLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; @@ -34,6 +36,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ManagedMappingType; +import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl; import org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor; @@ -48,6 +51,7 @@ import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.reactive.loader.ast.internal.ReactiveMultiIdEntityLoaderArrayParam; import org.hibernate.reactive.loader.ast.internal.ReactiveMultiIdEntityLoaderStandard; +import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdEntityLoaderProvidedQueryImpl; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdEntityLoaderStandardImpl; import org.hibernate.reactive.loader.ast.internal.ReactiveSingleUniqueKeyEntityLoaderStandard; @@ -64,6 +68,8 @@ import org.hibernate.reactive.sql.results.internal.ReactiveEntityResultImpl; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetch; @@ -90,6 +96,7 @@ public class ReactiveAbstractPersisterDelegate { private final EntityPersister entityDescriptor; private Map> uniqueKeyLoadersNew; + private ConcurrentHashMap nonLazyPropertyLoadPlansByName; public ReactiveAbstractPersisterDelegate( final EntityPersister entityPersister, @@ -347,6 +354,61 @@ else if ( entityIdentifierMapping instanceof EmbeddedIdentifierMappingImpl embed } } + /* + * Same as AbstractEntityPersister#getOrCreateLazyLoadPlan + */ + public ReactiveSingleIdArrayLoadPlan getOrCreateLazyLoadPlan(String fieldName, List partsToSelect) { + var propertyLoadPlansByName = nonLazyPropertyLoadPlansByName; + if ( propertyLoadPlansByName == null ) { + propertyLoadPlansByName = new ConcurrentHashMap<>(); + final ReactiveSingleIdArrayLoadPlan newLazyLoanPlan = createLazyLoadPlan( partsToSelect ); + propertyLoadPlansByName.put( fieldName, newLazyLoanPlan ); + nonLazyPropertyLoadPlansByName = propertyLoadPlansByName; + return newLazyLoanPlan; + } + else { + final ReactiveSingleIdArrayLoadPlan lazyLoanPlan = nonLazyPropertyLoadPlansByName.get( fieldName ); + if ( lazyLoanPlan == null ) { + final ReactiveSingleIdArrayLoadPlan newLazyLoanPlan = createLazyLoadPlan( partsToSelect ); + nonLazyPropertyLoadPlansByName.put( fieldName, newLazyLoanPlan ); + return newLazyLoanPlan; + } + else { + return lazyLoanPlan; + } + } + } + + private ReactiveSingleIdArrayLoadPlan createLazyLoadPlan(List partsToSelect) { + if ( partsToSelect.isEmpty() ) { + // only one-to-one is lazily fetched + return null; + } + else { + final LockOptions lockOptions = new LockOptions(); + final JdbcParametersList.Builder jdbcParametersBuilder = JdbcParametersList.newBuilder(); + final SelectStatement select = LoaderSelectBuilder.createSelect( + entityDescriptor, + partsToSelect, + entityDescriptor.getIdentifierMapping(), + null, + 1, + new LoadQueryInfluencers( entityDescriptor.getFactory() ), + lockOptions, + jdbcParametersBuilder::add, + entityDescriptor.getFactory() + ); + return new ReactiveSingleIdArrayLoadPlan( + entityDescriptor, + entityDescriptor.getIdentifierMapping(), + select, + jdbcParametersBuilder.build(), + lockOptions, + entityDescriptor.getFactory() + ); + } + } + private static class ReactiveNonAggregatedIdentifierMappingImpl extends NonAggregatedIdentifierMappingImpl { public ReactiveNonAggregatedIdentifierMappingImpl( 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 2443d4d9b..e9be58295 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 @@ -12,6 +12,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.engine.spi.CascadeStyle; @@ -27,6 +28,7 @@ import org.hibernate.mapping.Property; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; +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; @@ -52,6 +54,7 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.EntityType; +import org.hibernate.type.Type; import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; @@ -60,8 +63,7 @@ * An {@link ReactiveEntityPersister} backed by {@link JoinedSubclassEntityPersister} * and {@link ReactiveAbstractEntityPersister}. */ -public class ReactiveJoinedSubclassEntityPersister extends JoinedSubclassEntityPersister - implements ReactiveAbstractEntityPersister { +public class ReactiveJoinedSubclassEntityPersister extends JoinedSubclassEntityPersister implements ReactiveAbstractEntityPersister { private static final Log LOG = make( Log.class, lookup() ); @@ -76,6 +78,16 @@ public ReactiveJoinedSubclassEntityPersister( reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } + @Override + public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue) { + return super.initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, propValue ); + } + + @Override + public void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type) { + super.initializeLazyProperty( entity, entry, propValue, index, type ); + } + @Override protected SingleIdEntityLoader buildSingleIdEntityLoader() { return reactiveDelegate.buildSingleIdEntityLoader(); @@ -389,4 +401,13 @@ public ReactiveSingleIdArrayLoadPlan reactiveGetSQLLazySelectLoadPlan(String fet return this.getLazyLoadPlanByFetchGroup( getSubclassPropertyNameClosure() ).get(fetchGroup ); } + @Override + public ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List partsToSelect) { + return reactiveDelegate.getOrCreateLazyLoadPlan( fieldName, partsToSelect ); + } + + @Override + public boolean isNonLazyPropertyName(String fieldName) { + return super.isNonLazyPropertyName( fieldName ); + } } 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 2f5878737..787ef5b5c 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 @@ -13,6 +13,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.engine.spi.CascadeStyle; @@ -30,6 +31,7 @@ import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; +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; @@ -56,6 +58,7 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.EntityType; +import org.hibernate.type.Type; import static java.lang.invoke.MethodHandles.lookup; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; @@ -80,6 +83,16 @@ public ReactiveSingleTableEntityPersister( reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } + @Override + public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue) { + return super.initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, propValue ); + } + + @Override + public void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type) { + super.initializeLazyProperty( entity, entry, propValue, index, type ); + } + @Override public GeneratedValuesMutationDelegate createInsertDelegate() { return ReactiveAbstractEntityPersister.super.createReactiveInsertDelegate(); @@ -209,11 +222,6 @@ protected AttributeMapping buildPluralAttributeMapping( ); } - @Override - public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue) { - return super.initializeLazyProperty(fieldName, entity, entry, lazyIndex, selectedValue); - } - @Override public Object initializeLazyPropertiesFromDatastore(final Object entity, final Object id, final EntityEntry entry, final String fieldName, final SharedSessionContractImplementor session) { return reactiveInitializeLazyPropertiesFromDatastore( entity, id, entry, fieldName, session ); @@ -444,4 +452,14 @@ public ReactiveSingleIdArrayLoadPlan reactiveGetSQLLazySelectLoadPlan(String fet public NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) { return ReactiveAbstractEntityPersister.super.generateNaturalIdMapping(creationProcess, bootEntityDescriptor); } + + @Override + public ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List partsToSelect) { + return reactiveDelegate.getOrCreateLazyLoadPlan( fieldName, partsToSelect ); + } + + @Override + public boolean isNonLazyPropertyName(String fieldName) { + return super.isNonLazyPropertyName( fieldName ); + } } 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 bba3cf86d..71b91d4a5 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 @@ -14,6 +14,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeDescriptor; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.engine.spi.CascadeStyle; @@ -30,6 +31,7 @@ import org.hibernate.mapping.Property; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; +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; @@ -56,6 +58,7 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.EntityType; +import org.hibernate.type.Type; /** * An {@link ReactiveEntityPersister} backed by {@link UnionSubclassEntityPersister} @@ -76,6 +79,16 @@ public ReactiveUnionSubclassEntityPersister( reactiveDelegate = new ReactiveAbstractPersisterDelegate( this, persistentClass, new ReactiveRuntimeModelCreationContext( creationContext ) ); } + @Override + public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, LazyAttributeDescriptor fetchGroupAttributeDescriptor, Object propValue) { + return super.initializeLazyProperty( fieldName, entity, entry, fetchGroupAttributeDescriptor, propValue ); + } + + @Override + public void initializeLazyProperty(Object entity, EntityEntry entry, Object propValue, int index, Type type) { + super.initializeLazyProperty( entity, entry, propValue, index, type ); + } + @Override protected SingleIdEntityLoader buildSingleIdEntityLoader() { return reactiveDelegate.buildSingleIdEntityLoader(); @@ -184,11 +197,6 @@ public Generator getGenerator() throws HibernateException { return reactiveDelegate.reactive( super.getGenerator() ); } - @Override - public boolean initializeLazyProperty(String fieldName, Object entity, EntityEntry entry, int lazyIndex, Object selectedValue) { - return super.initializeLazyProperty( fieldName, entity, entry, lazyIndex, selectedValue ); - } - /** * @see #insertReactive(Object[], Object, SharedSessionContractImplementor) */ @@ -415,4 +423,14 @@ protected ReactiveSingleUniqueKeyEntityLoader getReactiveUniqueKeyLoader public ReactiveSingleIdArrayLoadPlan reactiveGetSQLLazySelectLoadPlan(String fetchGroup) { return this.getLazyLoadPlanByFetchGroup( getSubclassPropertyNameClosure() ).get(fetchGroup ); } + + @Override + public ReactiveSingleIdArrayLoadPlan reactiveGetOrCreateLazyLoadPlan(String fieldName, List partsToSelect) { + return reactiveDelegate.getOrCreateLazyLoadPlan( fieldName, partsToSelect ); + } + + @Override + public boolean isNonLazyPropertyName(String fieldName) { + return super.isNonLazyPropertyName( fieldName ); + } } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java index 81f1addf5..4f499f5cf 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.concurrent.CompletionStage; -import java.util.function.Supplier; import org.hibernate.ScrollMode; import org.hibernate.engine.spi.SubselectFetch; @@ -130,9 +129,6 @@ private static CompletionStage executeQueryInterpreter( ReactiveResultsConsumer resultsConsumer) { final ReactiveSharedSessionContractImplementor session = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.jdbcOperation(); - // I'm using a supplier so that the whenComplete at the end will catch any errors, like a finally-block - final Supplier fetchHandlerSupplier = () -> SubselectFetch - .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.statement(), JdbcParametersList.empty(), jdbcParameterBindings ); return CompletionStages .supplyStage( () -> { final var subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler(