diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java index 4f3db569faf4..4d1564dfe976 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java @@ -11,13 +11,11 @@ import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -39,7 +37,8 @@ public abstract class AbstractBatchEntitySelectFetchInitializer owningEntityInitializer; - public static abstract class AbstractBatchEntitySelectFetchInitializerData extends EntitySelectFetchInitializerData { + public static abstract class AbstractBatchEntitySelectFetchInitializerData + extends EntitySelectFetchInitializerData { final boolean batchDisabled; // per-row state @@ -48,10 +47,11 @@ public static abstract class AbstractBatchEntitySelectFetchInitializerData exten public AbstractBatchEntitySelectFetchInitializerData( AbstractBatchEntitySelectFetchInitializer initializer, RowProcessingState rowProcessingState) { - super( initializer, rowProcessingState ); + super( rowProcessingState ); batchDisabled = rowProcessingState.isScrollResult() - || !rowProcessingState.getLoadQueryInfluencers().effectivelyBatchLoadable( initializer.toOneMapping.getEntityMappingType().getEntityPersister() ); + || !rowProcessingState.getLoadQueryInfluencers() + .effectivelyBatchLoadable( initializer.toOneMapping.getEntityMappingType().getEntityPersister() ); } } @@ -73,51 +73,47 @@ public AbstractBatchEntitySelectFetchInitializer( @Override public void resolveKey(Data data) { - if ( data.getState() != State.UNINITIALIZED ) { - return; - } - - data.entityKey = null; - data.setInstance( null ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - //noinspection unchecked - final Initializer initializer = (Initializer) keyAssembler.getInitializer(); - if ( initializer != null ) { - final InitializerData subData = initializer.getData( rowProcessingState ); - initializer.resolveKey( subData ); - data.entityIdentifier = null; - data.setState( subData.getState() == State.MISSING ? State.MISSING : State.KEY_RESOLVED ); - } - else { - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); - data.setState( data.entityIdentifier == null ? State.MISSING : State.KEY_RESOLVED ); + if ( data.getState() == State.UNINITIALIZED ) { + data.entityKey = null; + data.setInstance( null ); + final RowProcessingState rowProcessingState = data.getRowProcessingState(); + //noinspection unchecked + final Initializer initializer = (Initializer) keyAssembler.getInitializer(); + if ( initializer != null ) { + final InitializerData subData = initializer.getData( rowProcessingState ); + initializer.resolveKey( subData ); + data.entityIdentifier = null; + data.setState( subData.getState() == State.MISSING ? State.MISSING : State.KEY_RESOLVED ); + } + else { + data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + data.setState( data.entityIdentifier == null ? State.MISSING : State.KEY_RESOLVED ); + } } } @Override public void resolveInstance(Data data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - data.setState( State.RESOLVED ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - if ( data.entityIdentifier == null ) { - // entityIdentifier can be null if the identifier is based on an initializer - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + if ( data.getState() == State.KEY_RESOLVED ) { + data.setState( State.RESOLVED ); + final RowProcessingState rowProcessingState = data.getRowProcessingState(); if ( data.entityIdentifier == null ) { - data.entityKey = null; - data.setInstance( null ); - data.setState( State.MISSING ); - return; + // entityIdentifier can be null if the identifier is based on an initializer + data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + if ( data.entityIdentifier == null ) { + data.entityKey = null; + data.setInstance( null ); + data.setState( State.MISSING ); + return; + } } + resolveInstanceFromIdentifier( data ); } - resolveInstanceFromIdentifier( data ); } protected void resolveInstanceFromIdentifier(Data data) { if ( data.batchDisabled ) { - initialize( data ); + initializeIfNecessary( data ); } else { data.entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); @@ -136,87 +132,92 @@ public void resolveInstance(Object instance, Data data) { data.setState( State.MISSING ); data.entityKey = null; data.setInstance( null ); - return; } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - // Only need to extract the identifier if the identifier has a many to one - final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); - data.entityKey = null; - data.entityIdentifier = null; - if ( lazyInitializer == null ) { - // Entity is most probably initialized - data.setInstance( instance ); - if ( concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() - && isPersistentAttributeInterceptable( instance ) - && getAttributeInterceptor( instance ) - instanceof EnhancementAsProxyLazinessInterceptor enhancementInterceptor ) { - if ( enhancementInterceptor.isInitialized() ) { - data.setState( State.INITIALIZED ); + else { + final RowProcessingState rowProcessingState = data.getRowProcessingState(); + // Only need to extract the identifier if the identifier has a many to one + final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); + data.entityKey = null; + data.entityIdentifier = null; + if ( lazyInitializer == null ) { + // Entity is most probably initialized + data.setInstance( instance ); + if ( concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() + && isPersistentAttributeInterceptable( instance ) + && getAttributeInterceptor( instance ) + instanceof EnhancementAsProxyLazinessInterceptor enhancementInterceptor ) { + if ( enhancementInterceptor.isInitialized() ) { + data.setState( State.INITIALIZED ); + } + else { + data.setState( State.RESOLVED ); + data.entityIdentifier = enhancementInterceptor.getIdentifier(); + } + if ( keyIsEager && data.entityIdentifier == null ) { + data.entityIdentifier = + concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); + } } else { + // If the entity initializer is null, we know the entity is fully initialized, + // otherwise it will be initialized by some other initializer data.setState( State.RESOLVED ); - data.entityIdentifier = enhancementInterceptor.getIdentifier(); + data.entityIdentifier = + concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); } } - else { - // If the entity initializer is null, we know the entity is fully initialized, - // otherwise it will be initialized by some other initializer + else if ( lazyInitializer.isUninitialized() ) { data.setState( State.RESOLVED ); - data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); + data.entityIdentifier = lazyInitializer.getInternalIdentifier(); + } + else { + // Entity is initialized + data.setState( State.INITIALIZED ); + if ( keyIsEager ) { + data.entityIdentifier = lazyInitializer.getInternalIdentifier(); + } + data.setInstance( lazyInitializer.getImplementation() ); } - if ( keyIsEager && data.entityIdentifier == null ) { - data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); + + if ( data.getState() == State.RESOLVED ) { + resolveInstanceFromIdentifier( data ); } - } - else if ( lazyInitializer.isUninitialized() ) { - data.setState( State.RESOLVED ); - data.entityIdentifier = lazyInitializer.getInternalIdentifier(); - } - else { - // Entity is initialized - data.setState( State.INITIALIZED ); if ( keyIsEager ) { - data.entityIdentifier = lazyInitializer.getInternalIdentifier(); + final Initializer initializer = keyAssembler.getInitializer(); + assert initializer != null; + initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); + } + else if ( rowProcessingState.needsResolveState() ) { + // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit + keyAssembler.resolveState( rowProcessingState ); } - data.setInstance( lazyInitializer.getImplementation() ); - } - - if ( data.getState() == State.RESOLVED ) { - resolveInstanceFromIdentifier( data ); - } - if ( keyIsEager ) { - final Initializer initializer = keyAssembler.getInitializer(); - assert initializer != null; - initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); - } - else if ( rowProcessingState.needsResolveState() ) { - // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit - keyAssembler.resolveState( rowProcessingState ); } } @Override public void initializeInstance(Data data) { - if ( data.getState() != State.RESOLVED ) { - return; - } - data.setState( State.INITIALIZED ); - if ( data.batchDisabled ) { - Hibernate.initialize( data.getInstance() ); + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); + if ( data.batchDisabled ) { + Hibernate.initialize( data.getInstance() ); + } } } protected Object getExistingInitializedInstance(Data data) { - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.getEntityHolder( data.entityKey ); + final EntityHolder holder = + data.getRowProcessingState().getSession() + .getPersistenceContextInternal() + .getEntityHolder( data.entityKey ); if ( holder != null && holder.getEntity() != null && holder.isEventuallyInitialized() ) { return holder.getEntity(); } - // we need to register a resolution listener only if there is not an already initialized instance - // or an instance that another initializer is loading - registerResolutionListener( data ); - return null; + else { + // we need to register a resolution listener only if there is not an already initialized instance + // or an instance that another initializer is loading + registerResolutionListener( data ); + return null; + } } protected void registerToBatchFetchQueue(Data data) { @@ -238,7 +239,7 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) { data.setState( State.MISSING ); } else { - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance ); + final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); if ( lazyInitializer != null && lazyInitializer.isUninitialized() ) { data.entityKey = new EntityKey( lazyInitializer.getInternalIdentifier(), concreteDescriptor ); registerToBatchFetchQueue( data ); @@ -277,22 +278,13 @@ protected static Object loadInstance( protected AttributeMapping[] getParentEntityAttributes(String attributeName) { final EntityPersister entityDescriptor = owningEntityInitializer.getEntityDescriptor(); - final AttributeMapping[] parentEntityAttributes = new AttributeMapping[ - entityDescriptor.getRootEntityDescriptor() - .getSubclassEntityNames() - .size() - ]; - parentEntityAttributes[entityDescriptor.getSubclassId()] = getParentEntityAttribute( - entityDescriptor, - toOneMapping, - attributeName - ); + final int size = entityDescriptor.getRootEntityDescriptor().getSubclassEntityNames().size(); + final AttributeMapping[] parentEntityAttributes = new AttributeMapping[size]; + parentEntityAttributes[entityDescriptor.getSubclassId()] = + getParentEntityAttribute( entityDescriptor, toOneMapping, attributeName ); for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) { - parentEntityAttributes[subMappingType.getSubclassId()] = getParentEntityAttribute( - subMappingType, - toOneMapping, - attributeName - ); + parentEntityAttributes[subMappingType.getSubclassId()] = + getParentEntityAttribute( subMappingType, toOneMapping, attributeName ); } return parentEntityAttributes; } @@ -302,8 +294,9 @@ protected static AttributeMapping getParentEntityAttribute( ToOneAttributeMapping referencedModelPart, String attributeName) { final AttributeMapping parentAttribute = subMappingType.findAttributeMapping( attributeName ); - if ( parentAttribute != null && parentAttribute.getDeclaringType() == referencedModelPart.getDeclaringType() - .findContainingEntityMapping() ) { + if ( parentAttribute != null + && parentAttribute.getDeclaringType() + == referencedModelPart.getDeclaringType().findContainingEntityMapping() ) { // These checks are needed to avoid setting the instance using the wrong (child's) model part or // setting it multiple times in case parent and child share the same attribute name for the association. return parentAttribute; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java index 4034a824c4a9..e87af8dda7b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java @@ -4,9 +4,6 @@ */ package org.hibernate.sql.results.graph.entity.internal; -import org.hibernate.EntityFilterException; -import org.hibernate.FetchNotFoundException; -import org.hibernate.annotations.NotFoundAction; import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -37,7 +34,7 @@ public EntitySelectFetchByUniqueKeyInitializer( } @Override - protected void initialize(EntitySelectFetchInitializerData data) { + protected void initializeIfNecessary(EntitySelectFetchInitializerData data) { final String entityName = concreteDescriptor.getEntityName(); final String uniqueKeyPropertyName = fetchedAttribute.getReferencedPropertyName(); @@ -61,18 +58,7 @@ protected void initialize(EntitySelectFetchInitializerData data) { data.setInstance( instance ); if ( instance == null ) { - if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { - if ( affectedByFilter ) { - throw new EntityFilterException( - entityName, - data.entityIdentifier, - toOneMapping.getNavigableRole().getFullPath() - ); - } - if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { - throw new FetchNotFoundException( entityName, data.entityIdentifier ); - } - } + handleNotFound( data, entityName ); } // If the entity was not in the Persistence Context, but was found now, // add it to the Persistence Context diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java index b7fc9523439a..0ce42ecb32a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java @@ -19,7 +19,6 @@ import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -57,7 +56,7 @@ public static class EntitySelectFetchInitializerData extends InitializerData { // per-row state protected @Nullable Object entityIdentifier; - public EntitySelectFetchInitializerData(EntitySelectFetchInitializer initializer, RowProcessingState rowProcessingState) { + public EntitySelectFetchInitializerData(RowProcessingState rowProcessingState) { super( rowProcessingState ); } @@ -100,7 +99,7 @@ public EntitySelectFetchInitializer( @Override protected InitializerData createInitializerData(RowProcessingState rowProcessingState) { - return new EntitySelectFetchInitializerData( this, rowProcessingState ); + return new EntitySelectFetchInitializerData( rowProcessingState ); } public ModelPart getInitializedPart(){ @@ -135,20 +134,17 @@ public void resolveFromPreviousRow(Data data) { @Override public void resolveInstance(Data data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); - - if ( data.entityIdentifier == null ) { - data.setState( State.MISSING ); - data.setInstance( null ); - return; + if ( data.getState() == State.KEY_RESOLVED ) { + data.entityIdentifier = keyAssembler.assemble( data.getRowProcessingState() ); + if ( data.entityIdentifier == null ) { + data.setState( State.MISSING ); + data.setInstance( null ); + } + else { + data.setState( State.INITIALIZED ); + initializeIfNecessary( data ); + } } - data.setState( State.INITIALIZED ); - initialize( data ); } @Override @@ -194,43 +190,24 @@ else if ( rowProcessingState.needsResolveState() ) { @Override public void initializeInstance(Data data) { - if ( data.getState() != State.RESOLVED ) { - return; + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); + Hibernate.initialize( data.getInstance() ); } + } + + protected void initializeIfNecessary(EntitySelectFetchInitializerData data) { + final boolean found = alreadyInitialized( data ); data.setState( State.INITIALIZED ); - Hibernate.initialize( data.getInstance() ); + if ( !found ) { + initialize( data ); + } } - protected void initialize(EntitySelectFetchInitializerData data) { + private void initialize(EntitySelectFetchInitializerData data) { final RowProcessingState rowProcessingState = data.getRowProcessingState(); final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final EntityKey entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); - - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.getEntityHolder( entityKey ); - if ( holder != null ) { - data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) ); - if ( holder.getEntityInitializer() == null ) { - if ( data.getInstance() != null && Hibernate.isInitialized( data.getInstance() ) ) { - data.setState( State.INITIALIZED ); - return; - } - } - else if ( holder.getEntityInitializer() != this ) { - // the entity is already being loaded elsewhere - data.setState( State.INITIALIZED ); - return; - } - else if ( data.getInstance() == null ) { - // todo: maybe mark this as resolved instead? - assert holder.getProxy() == null : "How to handle this case?"; - data.setState( State.INITIALIZED ); - return; - } - } - data.setState( State.INITIALIZED ); final String entityName = concreteDescriptor.getEntityName(); - final Object instance = session.internalLoad( entityName, data.entityIdentifier, @@ -240,19 +217,8 @@ else if ( data.getInstance() == null ) { data.setInstance( instance ); if ( instance == null ) { - if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { - if ( affectedByFilter ) { - throw new EntityFilterException( - entityName, - data.entityIdentifier, - toOneMapping.getNavigableRole().getFullPath() - ); - } - if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { - throw new FetchNotFoundException( entityName, data.entityIdentifier ); - } - } - persistenceContext.claimEntityHolderIfPossible( + handleNotFound( data, entityName ); + session.getPersistenceContextInternal().claimEntityHolderIfPossible( new EntityKey( data.entityIdentifier, concreteDescriptor ), instance, rowProcessingState.getJdbcValuesSourceProcessingState(), @@ -260,10 +226,50 @@ else if ( data.getInstance() == null ) { ); } - final boolean unwrapProxy = toOneMapping.isUnwrapProxy() && isEnhancedForLazyLoading; - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( data.getInstance() ); + final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() ); if ( lazyInitializer != null ) { - lazyInitializer.setUnwrap( unwrapProxy ); + lazyInitializer.setUnwrap( toOneMapping.isUnwrapProxy() && isEnhancedForLazyLoading ); + } + } + + void handleNotFound(EntitySelectFetchInitializerData data, String entityName) { + if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { + if ( affectedByFilter ) { + final String fullPath = toOneMapping.getNavigableRole().getFullPath(); + throw new EntityFilterException( entityName, data.entityIdentifier, fullPath ); + } + if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { + throw new FetchNotFoundException( entityName, data.entityIdentifier ); + } + } + } + + private boolean alreadyInitialized(EntitySelectFetchInitializerData data) { + final PersistenceContext persistenceContext = + data.getRowProcessingState().getSession().getPersistenceContextInternal(); + final EntityHolder holder = + persistenceContext.getEntityHolder( new EntityKey( data.entityIdentifier, concreteDescriptor ) ); + if ( holder == null ) { + return false; + } + else { + data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) ); // note side effect + if ( holder.getEntityInitializer() == null ) { + return data.getInstance() != null + && Hibernate.isInitialized( data.getInstance() ); + } + else if ( holder.getEntityInitializer() != this ) { + // the entity is already being loaded elsewhere + return true; + } + else if ( data.getInstance() == null ) { + // todo: maybe mark this as resolved instead? + assert holder.getProxy() == null : "How to handle this case?"; + return true; + } + else { + return false; + } } }