HHH-17309 Get rid of LoadingEntityEntry

This commit is contained in:
Christian Beikov 2023-10-17 11:37:59 +02:00
parent ec3efdbe39
commit d8bad73f58
31 changed files with 424 additions and 367 deletions

View File

@ -52,6 +52,9 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.collections.IdentityMap;
@ -61,6 +64,9 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.spi.LoadContexts;
import org.hibernate.type.CollectionType;
@ -369,6 +375,42 @@ public class StatefulPersistenceContext implements PersistenceContext {
return (Object[]) snapshot;
}
@Override
public EntityHolder claimEntityHolderIfPossible(
EntityKey key,
Object entity,
JdbcValuesSourceProcessingState processingState,
EntityInitializer initializer) {
EntityHolderImpl holder = EntityHolderImpl.forEntity( key, key.getPersister(), entity );
final EntityHolderImpl oldHolder = getOrInitializeEntitiesByKey().putIfAbsent(
key,
holder
);
if ( oldHolder != null ) {
if ( entity != null ) {
assert oldHolder.entity == null || oldHolder.entity == entity;
oldHolder.entity = entity;
}
// Skip setting a new entity initializer if there already is one owner
// Also skip if an entity exists which is different from the effective optional object.
// The effective optional object is the current object to be refreshed,
// which always needs re-initialization, even if already initialized
if ( oldHolder.entityInitializer != null
|| oldHolder.entity != null && oldHolder.state != EntityHolderState.ENHANCED_PROXY && (
processingState.getProcessingOptions().getEffectiveOptionalObject() == null
|| oldHolder.entity != processingState.getProcessingOptions().getEffectiveOptionalObject() )
) {
return oldHolder;
}
holder = oldHolder;
}
assert holder.entityInitializer == null || holder.entityInitializer == initializer;
holder.entityInitializer = initializer;
holder.processingState = processingState;
return holder;
}
@Override
public EntityHolderImpl getEntityHolder(EntityKey key) {
return entitiesByKey == null ? null : entitiesByKey.get( key );
@ -380,11 +422,73 @@ public class StatefulPersistenceContext implements PersistenceContext {
}
@Override
public void addEntity(EntityKey key, Object entity) {
final EntityHolderImpl holder = getOrInitializeEntitiesByKey().putIfAbsent( key, EntityHolderImpl.forEntity( entity ) );
if ( holder != null ) {
holder.entity = entity;
public void postLoad(JdbcValuesSourceProcessingState processingState, Consumer<EntityHolder> holderConsumer) {
if ( entitiesByKey == null ) {
return;
}
final Callback callback = processingState.getExecutionContext().getCallback();
final EventListenerGroup<PostLoadEventListener> listenerGroup = getSession().getFactory()
.getFastSessionServices()
.eventListenerGroup_POST_LOAD;
final PostLoadEvent postLoadEvent = getSession().isEventSource() ? new PostLoadEvent( getSession().asEventSource() ) : null;
for ( Iterator<EntityHolderImpl> iterator = entitiesByKey.values().iterator(); iterator.hasNext(); ) {
final EntityHolderImpl holder = iterator.next();
if ( holder.processingState == processingState ) {
if ( holderConsumer != null ) {
holderConsumer.accept( holder );
}
if ( holder.entity == null ) {
// It's possible that we tried to load an entity and found out it doesn't exist,
// in which case we added an entry with a null proxy and entity.
// Remove that empty entry on post load to avoid unwanted side effects
iterator.remove();
continue;
}
if ( postLoadEvent != null ) {
postLoadEvent.reset();
postLoadEvent.setEntity( holder.entity )
.setId( holder.entityKey.getIdentifier() )
.setPersister( holder.getDescriptor() );
listenerGroup.fireEventOnEachListener(
postLoadEvent,
PostLoadEventListener::onPostLoad
);
}
if ( callback != null ) {
callback.invokeAfterLoadActions(
holder.getEntity(),
holder.getDescriptor(),
getSession()
);
if ( holder.reloaded ) {
callback.invokeAfterLoadActions(
holder.getEntity(),
holder.getDescriptor(),
getSession()
);
}
}
holder.processingState = null;
holder.entityInitializer = null;
holder.reloaded = false;
}
}
}
@Override
public void addEntity(EntityKey key, Object entity) {
EntityHolderImpl holder = EntityHolderImpl.forEntity( key, key.getPersister(), entity );
final EntityHolderImpl oldHolder = getOrInitializeEntitiesByKey().putIfAbsent(
key,
holder
);
if ( oldHolder != null ) {
// assert oldHolder.entity == null || oldHolder.entity == entity;
oldHolder.entity = entity;
holder = oldHolder;
}
holder.state = EntityHolderState.INITIALIZED;
final BatchFetchQueue fetchQueue = this.batchFetchQueue;
if ( fetchQueue != null ) {
fetchQueue.removeBatchLoadableEntityKey( key );
@ -394,13 +498,13 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override
public Object getEntity(EntityKey key) {
final EntityHolderImpl holder = entitiesByKey == null ? null : entitiesByKey.get( key );
return holder == null ? null : holder.entity;
return holder == null || holder.state == EntityHolderState.UNINITIALIZED ? null : holder.entity;
}
@Override
public boolean containsEntity(EntityKey key) {
final EntityHolderImpl holder = entitiesByKey == null ? null : entitiesByKey.get( key );
return holder != null && holder.entity != null;
return holder != null && holder.entity != null && holder.state != EntityHolderState.UNINITIALIZED;
}
@Override
@ -656,7 +760,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
// any earlier proxy takes precedence
final EntityHolderImpl holder = getOrInitializeEntitiesByKey().putIfAbsent(
key,
EntityHolderImpl.forProxy( proxy )
EntityHolderImpl.forProxy( key, persister, proxy )
);
if ( holder != null && holder.proxy == null ) {
holder.proxy = proxy;
@ -803,10 +907,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override
public void addEnhancedProxy(EntityKey key, PersistentAttributeInterceptable entity) {
final EntityHolderImpl holder = getOrInitializeEntitiesByKey().putIfAbsent( key, EntityHolderImpl.forEntity( entity ) );
if ( holder != null ) {
holder.entity = entity;
EntityHolderImpl holder = EntityHolderImpl.forEntity( key, key.getPersister(), entity );
final EntityHolderImpl oldHolder = getOrInitializeEntitiesByKey().putIfAbsent(
key,
holder
);
if ( oldHolder != null ) {
oldHolder.entity = entity;
holder = oldHolder;
}
holder.state = EntityHolderState.ENHANCED_PROXY;
}
@Override
@ -1094,7 +1204,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override
public void addProxy(EntityKey key, Object proxy) {
final EntityHolderImpl holder = getOrInitializeEntitiesByKey().putIfAbsent( key, EntityHolderImpl.forProxy( proxy ) );
final EntityHolderImpl holder = getOrInitializeEntitiesByKey().putIfAbsent(
key,
EntityHolderImpl.forProxy( key, key.getPersister(), proxy )
);
if ( holder != null ) {
holder.proxy = proxy;
}
@ -1658,8 +1771,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
writeMapToStream( entitiesByKey, oos, "entitiesByKey", (entry, stream) -> {
entry.getKey().serialize( stream );
final EntityHolderImpl holder = entry.getValue();
stream.writeObject( holder.descriptor.getEntityName() );
stream.writeObject( holder.entity );
stream.writeObject( holder.proxy );
stream.writeObject( holder.state );
} );
writeMapToStream( entitiesByUniqueKey, oos, "entitiesByUniqueKey", (entry, stream) -> {
entry.getKey().serialize( stream );
@ -1775,9 +1890,12 @@ public class StatefulPersistenceContext implements PersistenceContext {
rtn.entitiesByKey = CollectionHelper.mapOfSize(Math.max(count, INIT_COLL_SIZE));
for ( int i = 0; i < count; i++ ) {
final EntityKey ek = EntityKey.deserialize( ois, sfi );
final EntityPersister persister = sfi.getMappingMetamodel().getEntityDescriptor( (String) ois.readObject() );
final Object entity = ois.readObject();
final Object proxy = ois.readObject();
final EntityHolderImpl holder = EntityHolderImpl.forEntity( entity );
final EntityHolderState state = (EntityHolderState) ois.readObject();
final EntityHolderImpl holder = EntityHolderImpl.forEntity( ek, persister, entity );
holder.state = state;
if ( proxy != null ) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( proxy );
if ( lazyInitializer != null ) {
@ -2014,12 +2132,32 @@ public class StatefulPersistenceContext implements PersistenceContext {
}
private static class EntityHolderImpl implements EntityHolder, Serializable {
private final EntityKey entityKey;
private final EntityPersister descriptor;
Object entity;
Object proxy;
EntityInitializer entityInitializer;
JdbcValuesSourceProcessingState processingState;
boolean reloaded;
EntityHolderState state;
private EntityHolderImpl(Object entity, Object proxy) {
private EntityHolderImpl(EntityKey entityKey, EntityPersister descriptor, Object entity, Object proxy) {
assert entityKey != null && descriptor != null;
this.entityKey = entityKey;
this.descriptor = descriptor;
this.entity = entity;
this.proxy = proxy;
this.state = EntityHolderState.UNINITIALIZED;
}
@Override
public EntityKey getEntityKey() {
return entityKey;
}
@Override
public EntityPersister getDescriptor() {
return descriptor;
}
@Override
@ -2032,13 +2170,36 @@ public class StatefulPersistenceContext implements PersistenceContext {
return proxy;
}
public static EntityHolderImpl forProxy(Object proxy) {
return new EntityHolderImpl( null, proxy );
@Override
public EntityInitializer getEntityInitializer() {
return entityInitializer;
}
public static EntityHolderImpl forEntity(Object entity) {
return new EntityHolderImpl( entity, null );
@Override
public void markAsReloaded(JdbcValuesSourceProcessingState processingState) {
assert this.processingState == null;
this.reloaded = true;
this.processingState = processingState;
}
@Override
public boolean isEventuallyInitialized() {
return state == EntityHolderState.INITIALIZED || entityInitializer != null;
}
public static EntityHolderImpl forProxy(EntityKey entityKey, EntityPersister descriptor, Object proxy) {
return new EntityHolderImpl( entityKey, descriptor, null, proxy );
}
public static EntityHolderImpl forEntity(EntityKey entityKey, EntityPersister descriptor, Object entity) {
return new EntityHolderImpl( entityKey, descriptor, entity, null );
}
}
enum EntityHolderState {
UNINITIALIZED,
ENHANCED_PROXY,
INITIALIZED
}
// NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -6,12 +6,54 @@
*/
package org.hibernate.engine.spi;
public interface EntityHolder {
Object getEntity();
Object getProxy();
import org.hibernate.Incubating;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
default Object getManagedObject() {
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Holder for an entry in the {@link PersistenceContext} for an {@link EntityKey}.
*
* @since 6.4
*/
@Incubating
public interface EntityHolder {
EntityKey getEntityKey();
EntityPersister getDescriptor();
/**
* The entity object, or {@code null} if no entity object was registered yet.
*/
@Nullable Object getEntity();
/**
* The proxy object, or {@code null} if no proxy object was registered yet.
*/
@Nullable Object getProxy();
/**
* The entity initializer that claims to initialize the entity for this holder.
* Will be {@code null} if entity is initialized already or the entity holder is not claimed yet.
*/
@Nullable EntityInitializer getEntityInitializer();
/**
* The proxy if there is one and otherwise the entity.
*/
default @Nullable Object getManagedObject() {
final Object proxy = getProxy();
return proxy == null ? getEntity() : proxy;
}
/**
* Marks the entity holder as reloaded to potentially trigger follow-on locking.
*
* @param processingState The processing state within which this entity is reloaded.
*/
void markAsReloaded(JdbcValuesSourceProcessingState processingState);
/**
* Whether the entity is already initialized or will be initialized through an initializer eventually.
*/
boolean isEventuallyInitialized();
}

View File

@ -10,9 +10,11 @@ import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.LockMode;
import org.hibernate.query.Query;
@ -20,8 +22,12 @@ import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.spi.LoadContexts;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the state of "stuff" Hibernate is tracking, including (not exhaustive):
* <ul>
@ -489,12 +495,31 @@ public interface PersistenceContext {
// @Deprecated
// HashSet getNullifiableEntityKeys();
/**
* Return an existing entity holder for the entity key, possibly creating one if necessary.
* Will claim the entity holder by registering the given entity initializer, if it isn't claimed yet.
*
* @param key The key under which to add an entity
* @param entity The entity instance to add
* @param processingState The processing state which initializes the entity if successfully claimed
* @param initializer The initializer to claim the entity instance
*/
@Incubating
EntityHolder claimEntityHolderIfPossible(
EntityKey key,
@Nullable Object entity,
JdbcValuesSourceProcessingState processingState,
EntityInitializer initializer);
EntityHolder getEntityHolder(EntityKey key);
boolean containsEntityHolder(EntityKey key);
EntityHolder removeEntityHolder(EntityKey key);
@Incubating
void postLoad(JdbcValuesSourceProcessingState processingState, Consumer<EntityHolder> loadedConsumer);
/**
* Doubly internal
*/

View File

@ -12,13 +12,14 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.internal.util.NullnessUtil;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
/**
* Encapsulates details related to entities which contain sub-select-fetchable
@ -118,12 +119,12 @@ public class SubselectFetch {
}
public interface RegistrationHandler {
void addKey(EntityKey key, LoadingEntityEntry entry);
void addKey(EntityHolder holder);
}
private static final RegistrationHandler NO_OP_REG_HANDLER = new RegistrationHandler() {
@Override
public void addKey(EntityKey key, LoadingEntityEntry entry) {
public void addKey(EntityHolder holder) {
}
} ;
@ -146,23 +147,25 @@ public class SubselectFetch {
this.loadingJdbcParameterBindings = loadingJdbcParameterBindings;
}
public void addKey(EntityKey key, LoadingEntityEntry entry) {
@Override
public void addKey(EntityHolder holder) {
if ( batchFetchQueue.getSession().getLoadQueryInfluencers()
.hasSubselectLoadableCollections( entry.getDescriptor() ) ) {
.hasSubselectLoadableCollections( holder.getDescriptor() ) ) {
final EntityInitializer entityInitializer = NullnessUtil.castNonNull( holder.getEntityInitializer() );
final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent(
entry.getEntityInitializer().getNavigablePath(),
entityInitializer.getNavigablePath(),
navigablePath -> new SubselectFetch(
loadingSqlAst.getQuerySpec(),
loadingSqlAst.getQuerySpec()
.getFromClause()
.findTableGroup( entry.getEntityInitializer().getNavigablePath() ),
.findTableGroup( entityInitializer.getNavigablePath() ),
loadingJdbcParameters,
loadingJdbcParameterBindings,
new HashSet<>()
)
);
subselectFetch.resultingEntityKeys.add( key );
batchFetchQueue.addSubselect( key, subselectFetch );
subselectFetch.resultingEntityKeys.add( holder.getEntityKey() );
batchFetchQueue.addSubselect( holder.getEntityKey(), subselectFetch );
}
}
}

View File

@ -274,7 +274,8 @@ public class DefaultLoadEventListener implements LoadEventListener {
private Object loadWithRegularProxy(LoadEvent event, EntityPersister persister, EntityKey keyToLoad, LoadType options) {
// This is the case where the proxy is a separate object:
// look for a proxy
final EntityHolder holder = event.getSession().getPersistenceContextInternal().getEntityHolder( keyToLoad );
final PersistenceContext persistenceContext = event.getSession().getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.getEntityHolder( keyToLoad );
final Object proxy = holder == null ? null : holder.getProxy();
if ( proxy != null ) {
// narrow the existing proxy to the type we're looking for

View File

@ -347,7 +347,7 @@ public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<
}
}
getJdbcValuesSourceProcessingState().finishUp();
getJdbcValuesSourceProcessingState().finishUp( false );
}
finally {
persistenceContext.afterLoad();

View File

@ -132,7 +132,7 @@ public class ScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
currentRow = getRowReader().readRow( getRowProcessingState(), getProcessingOptions() );
getRowProcessingState().finishRowProcessing();
getJdbcValuesSourceProcessingState().finishUp();
getJdbcValuesSourceProcessingState().finishUp( false );
}
finally {
persistenceContext.afterLoad();

View File

@ -19,6 +19,7 @@ import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
@ -398,9 +399,19 @@ public class CacheEntityLoaderHelper {
subclassPersister = factory.getRuntimeMetamodels()
.getMappingMetamodel()
.getEntityDescriptor( entry.getSubclass() );
entity = instanceToLoad == null
? source.instantiate( subclassPersister, entityId )
: instanceToLoad;
if ( instanceToLoad != null ) {
entity = instanceToLoad;
}
else {
final EntityHolder holder = source.getPersistenceContextInternal().getEntityHolder( entityKey );
if ( holder != null && holder.getEntity() != null ) {
// Use the entity which might already be
entity = holder.getEntity();
}
else {
entity = source.instantiate( subclassPersister, entityId );
}
}
if ( isPersistentAttributeInterceptable( entity ) ) {
PersistentAttributeInterceptor persistentAttributeInterceptor = asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();

View File

@ -10,7 +10,7 @@ import org.hibernate.LockOptions;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -27,7 +27,6 @@ import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
@ -148,8 +147,8 @@ public class CollectionLoaderSingleKey implements CollectionLoader {
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchableKeysHandler.addKey( entityKey, entry );
public void registerLoadingEntityHolder(EntityHolder holder) {
subSelectFetchableKeysHandler.addKey( holder );
}
}

View File

@ -6,11 +6,10 @@
*/
package org.hibernate.loader.ast.internal;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.sql.exec.internal.BaseExecutionContext;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
class ExecutionContextWithSubselectFetchHandler extends BaseExecutionContext {
@ -24,9 +23,9 @@ class ExecutionContextWithSubselectFetchHandler extends BaseExecutionContext {
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
public void registerLoadingEntityHolder(EntityHolder holder) {
if ( subSelectFetchableKeysHandler != null ) {
subSelectFetchableKeysHandler.addKey( entityKey, entry );
subSelectFetchableKeysHandler.addKey( holder );
}
}

View File

@ -7,14 +7,13 @@
package org.hibernate.loader.ast.internal;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryOptionsAdapter;
import org.hibernate.sql.exec.internal.BaseExecutionContext;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
/**
* @author Steve Ebersole
@ -75,8 +74,8 @@ class SingleIdExecutionContext extends BaseExecutionContext {
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchableKeysHandler.addKey( entityKey, entry );
public void registerLoadingEntityHolder(EntityHolder holder) {
subSelectFetchableKeysHandler.addKey( holder );
}
}

View File

@ -46,7 +46,6 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.embeddable.internal.NonAggregatedIdentifierMappingFetch;
import org.hibernate.sql.results.graph.embeddable.internal.NonAggregatedIdentifierMappingResult;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
/**
* A "non-aggregated" composite identifier.
@ -293,10 +292,8 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
toOneAttributeMapping.getAttributeName()
);
if ( o == null ) {
final LoadingEntityEntry loadingEntityEntry = persistenceContext.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( loadingEntityEntry != null ) {
o = loadingEntityEntry.getEntityInstance();
if ( holder != null && holder.isEventuallyInitialized() ) {
o = holder.getEntity();
}
else {
o = session.internalLoad(

View File

@ -86,6 +86,7 @@ import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryFactory;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.NaturalIdResolutions;
@ -276,7 +277,6 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.graph.entity.internal.EntityResultImpl;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
@ -4112,14 +4112,15 @@ public abstract class AbstractEntityPersister
final Object version = getVersion( entity );
final Boolean isUnsaved = versionMapping.getUnsavedStrategy().isUnsaved( version );
if ( isUnsaved != null ) {
if ( isUnsaved ) {
if ( version == null && session.getPersistenceContext().hasLoadContext() ) {
if ( isUnsaved ) {
final PersistenceContext persistenceContext;
if ( version == null && ( persistenceContext = session.getPersistenceContext() ).hasLoadContext()
&& !persistenceContext.getLoadContexts().isLoadingFinished() ) {
// check if we're currently loading this entity instance, the version
// will be null but the entity cannot be considered transient
final LoadingEntityEntry loadingEntityEntry = session.getPersistenceContext()
.getLoadContexts()
.findLoadingEntityEntry( new EntityKey( id, this ) );
if ( loadingEntityEntry != null && loadingEntityEntry.getEntityInstance() == entity ) {
final EntityHolder holder = persistenceContext
.getEntityHolder( new EntityKey( id, this ) );
if ( holder != null && holder.isEventuallyInitialized() && holder.getEntity() == entity ) {
return false;
}
}

View File

@ -15,7 +15,7 @@ import jakarta.persistence.Tuple;
import org.hibernate.AssertionFailure;
import org.hibernate.InstantiationException;
import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
@ -42,7 +42,6 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
import org.hibernate.sql.results.internal.RowTransformerConstructorImpl;
import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl;
@ -497,8 +496,8 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchKeyHandler.addKey( entityKey, entry );
public void registerLoadingEntityHolder(EntityHolder holder) {
subSelectFetchKeyHandler.addKey( holder );
}
@Override

View File

@ -221,6 +221,7 @@ public class OutputsImpl implements Outputs {
executionContext,
processingOptions
);
final ArrayList<Object> results = new ArrayList<>();
try {
final RowProcessingStateStandardImpl rowProcessingState = new RowProcessingStateStandardImpl(
jdbcValuesSourceProcessingState,
@ -229,8 +230,6 @@ public class OutputsImpl implements Outputs {
jdbcValues
);
final ArrayList<Object> results = new ArrayList<>();
while ( rowProcessingState.next() ) {
results.add( rowReader.readRow( rowProcessingState, processingOptions ) );
rowProcessingState.finishRowProcessing();
@ -248,7 +247,7 @@ public class OutputsImpl implements Outputs {
}
finally {
rowReader.finishUp( jdbcValuesSourceProcessingState );
jdbcValuesSourceProcessingState.finishUp();
jdbcValuesSourceProcessingState.finishUp( results.size() > 1 );
jdbcValues.finishUp( this.context.getSession() );
}
}

View File

@ -7,14 +7,13 @@
package org.hibernate.sql.exec.spi;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
/**
* A context for execution of SQL statements expressed via
@ -65,18 +64,10 @@ public interface ExecutionContext {
return null;
}
/**
* @deprecated use {@link #registerSubselect(EntityKey, LoadingEntityEntry)} instead.
*/
@Deprecated
default void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
default void registerLoadingEntityHolder(EntityHolder holder) {
// by default do nothing
}
default void registerSubselect(EntityKey entityKey, LoadingEntityEntry entry) {
registerLoadingEntityEntry( entityKey, entry );
}
/**
* Hook to allow delaying calls to {@link LogicalConnectionImplementor#afterStatement()}.
* Mainly used in the case of batching and multi-table mutations

View File

@ -6,9 +6,9 @@
*/
package org.hibernate.sql.exec.spi;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
/**
* @author Steve Ebersole
@ -21,19 +21,9 @@ public class StandardEntityInstanceResolver {
EntityKey entityKey,
boolean eager,
SharedSessionContractImplementor session) {
// First, look for it in the PC as a managed entity
final Object managedEntity = session.getPersistenceContext().getEntity( entityKey );
if ( managedEntity != null ) {
// todo (6.0) : check status? aka, return deleted entities?
return managedEntity;
}
// Next, check currently loading entities
final LoadingEntityEntry loadingEntry = session.getPersistenceContext()
.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( loadingEntry != null ) {
return loadingEntry.getEntityInstance();
final EntityHolder holder = session.getPersistenceContext().getEntityHolder( entityKey );
if ( holder != null && holder.isEventuallyInitialized() ) {
return holder.getEntity();
}
// Lastly, try to load from database

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -18,7 +19,6 @@ import org.hibernate.sql.exec.internal.BaseExecutionContext;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityFetch;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.sql.results.spi.RowReader;
@ -131,8 +131,8 @@ public class NestedRowProcessingState extends BaseExecutionContext implements Ro
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
processingState.registerLoadingEntityEntry( entityKey, entry );
public void registerLoadingEntityHolder(EntityHolder holder) {
processingState.registerLoadingEntityHolder( holder );
}
@Override

View File

@ -388,17 +388,19 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
missing = true;
return;
}
// Special case map proxy to avoid stack overflows
// We know that a map proxy will always be of "the right type" so just use that object
final LoadingEntityEntry existingLoadingEntry =
rowProcessingState.getSession().getPersistenceContextInternal().getLoadContexts()
.findLoadingEntityEntry( entityKey );
setIsOwningInitializer( entityKey.getIdentifier(), existingLoadingEntry );
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.claimEntityHolderIfPossible(
entityKey,
null,
rowProcessingState.getJdbcValuesSourceProcessingState(),
this
);
isOwningInitializer = holder.getEntityInitializer() == this;
if ( entityInstance == null ) {
resolveEntityInstance( rowProcessingState, existingLoadingEntry, entityKey.getIdentifier() );
resolveEntityInstance( rowProcessingState, holder, entityKey.getIdentifier() );
}
else if ( existingLoadingEntry != null && existingLoadingEntry.getEntityInitializer() != this ) {
else if ( !isOwningInitializer ) {
entityInstance = holder.getManagedObject();
isInitialized = true;
}
}
@ -486,7 +488,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
protected void resolveEntityInstance(
RowProcessingState rowProcessingState,
LoadingEntityEntry existingLoadingEntry,
EntityHolder holder,
Object entityIdentifier) {
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isTraceEnabled() ) {
@ -498,30 +500,34 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
);
}
final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal();
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
final Object proxy = holder == null ? null : holder.getProxy();
final Object proxy = holder.getProxy();
final boolean unwrapProxy = proxy != null && referencedModelPart instanceof ToOneAttributeMapping
&& ( (ToOneAttributeMapping) referencedModelPart ).isUnwrapProxy()
&& getConcreteDescriptor().getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
final Object entityInstanceFromExecutionContext = getEntityInstanceFromExecutionContext( rowProcessingState );
final Object entityFromExecutionContext;
if ( !unwrapProxy && isProxyInstance( proxy ) ) {
if ( entityInstanceFromExecutionContext != null ) {
entityInstance = entityInstanceFromExecutionContext;
registerLoadingEntityInstanceFromExecutionContext( rowProcessingState, entityInstance );
if ( ( entityFromExecutionContext = getEntityFromExecutionContext( rowProcessingState ) ) != null ) {
entityInstance = entityFromExecutionContext;
// If the entity comes from the execution context, it is treated as not initialized
// so that we can refresh the data as requested
registerReloadedEntity( rowProcessingState, holder );
}
else {
entityInstance = proxy;
if ( Hibernate.isInitialized( entityInstance ) ) {
this.isInitialized = true;
registerReloadedEntity( rowProcessingState, holder );
}
}
}
else {
final Object existingEntity = holder == null ? null : holder.getEntity();
final Object existingEntity = holder.getEntity();
if ( existingEntity != null ) {
entityInstance = existingEntity;
if ( existingLoadingEntry == null ) {
if ( holder.getEntityInitializer() == null ) {
if ( isExistingEntityInitialized( existingEntity ) ) {
this.isInitialized = true;
registerReloadedEntity( rowProcessingState, existingEntity );
registerReloadedEntity( rowProcessingState, holder );
notifyResolutionListeners( entityInstance );
if ( rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) {
// We still need to read result set values to correctly populate the query cache
@ -532,15 +538,18 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
registerLoadingEntityInstanceFromExecutionContext( rowProcessingState, entityInstance );
}
}
else if ( !isOwningInitializer ) {
this.isInitialized = true;
}
}
else if ( entityInstanceFromExecutionContext != null ) {
entityInstance = entityInstanceFromExecutionContext;
else if ( ( entityFromExecutionContext = getEntityFromExecutionContext( rowProcessingState ) ) != null ) {
entityInstance = entityFromExecutionContext;
registerLoadingEntityInstanceFromExecutionContext( rowProcessingState, entityInstance );
}
else {
// look to see if another initializer from a parent load context or an earlier
// initializer is already loading the entity
entityInstance = resolveInstance( entityIdentifier, existingLoadingEntry, rowProcessingState );
entityInstance = resolveInstance( entityIdentifier, holder, rowProcessingState );
if ( isOwningInitializer && !isInitialized && identifierAssembler instanceof EmbeddableAssembler ) {
// If this is the owning initializer and the returned object is not initialized,
// this means that the entity instance was just instantiated.
@ -549,16 +558,15 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
identifierAssembler.assemble( rowProcessingState );
}
}
upgradeLockMode( rowProcessingState );
}
upgradeLockMode( rowProcessingState );
}
protected abstract void registerLoadingEntityInstanceFromExecutionContext(
RowProcessingState rowProcessingState,
Object instance);
protected Object getEntityInstanceFromExecutionContext(RowProcessingState rowProcessingState) {
protected Object getEntityFromExecutionContext(RowProcessingState rowProcessingState) {
final ExecutionContext executionContext = rowProcessingState.getJdbcValuesSourceProcessingState()
.getExecutionContext();
if ( rootEntityDescriptor == executionContext.getRootEntityDescriptor()
@ -614,37 +622,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
private void setIsOwningInitializer(Object entityIdentifier,LoadingEntityEntry existingLoadingEntry) {
if ( existingLoadingEntry != null ) {
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isDebugEnabled() ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found existing loading entry [%s] - using loading instance",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier )
);
}
if ( existingLoadingEntry.getEntityInitializer() == this ) {
isOwningInitializer = true;
}
else {
isInitialized = true;
}
}
else {
isOwningInitializer = true;
}
}
protected boolean isOwningInitializer() {
return isOwningInitializer;
}
private Object resolveInstance(
Object entityIdentifier,
LoadingEntityEntry existingLoadingEntry,
EntityHolder holder,
RowProcessingState rowProcessingState) {
if ( isOwningInitializer ) {
assert existingLoadingEntry == null || existingLoadingEntry.getEntityInstance() == null;
assert holder.getEntity() == null;
return resolveEntityInstance( entityIdentifier, rowProcessingState );
}
else {
@ -654,10 +637,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
getSimpleConcreteImplName(),
toLoggableString( getNavigablePath(), entityIdentifier ),
existingLoadingEntry.getEntityInitializer()
holder.getEntityInitializer()
);
}
return existingLoadingEntry.getEntityInstance();
return holder.getEntity();
}
}
@ -675,7 +658,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// EARLY EXIT!!!
// because the second level cache has reference cache entries, the entity is initialized
isInitialized = true;
registerReloadedEntity( rowProcessingState, cached );
registerReloadedEntity( rowProcessingState );
return cached;
}
}
@ -729,21 +712,25 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
protected void registerLoadingEntity(RowProcessingState rowProcessingState, Object instance) {
rowProcessingState.getJdbcValuesSourceProcessingState()
.registerLoadingEntity(
entityKey,
new LoadingEntityEntry( this, entityKey, concreteDescriptor, instance )
);
rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
entityKey,
instance,
rowProcessingState.getJdbcValuesSourceProcessingState(),
this
);
}
protected void registerReloadedEntity(RowProcessingState rowProcessingState, Object instance) {
protected void registerReloadedEntity(RowProcessingState rowProcessingState) {
if ( rowProcessingState.hasCallbackActions() ) {
rowProcessingState.getSession().getPersistenceContextInternal().getEntityHolder( entityKey )
.markAsReloaded( rowProcessingState.getJdbcValuesSourceProcessingState() );
}
}
protected void registerReloadedEntity(RowProcessingState rowProcessingState, EntityHolder holder) {
if ( rowProcessingState.hasCallbackActions() ) {
// This is only needed for follow-on locking, so skip registering the entity if there is no callback
rowProcessingState.getJdbcValuesSourceProcessingState()
.registerReloadedEntity(
entityKey,
new LoadingEntityEntry( this, entityKey, concreteDescriptor, instance )
);
holder.markAsReloaded( rowProcessingState.getJdbcValuesSourceProcessingState() );
}
}
@ -754,11 +741,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( lazyInitializer != null ) {
Object instance = persistenceContext.getEntity( entityKey );
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
Object instance = holder.getEntity();
if ( instance == null ) {
instance = resolveInstance(
entityKey.getIdentifier(),
persistenceContext.getLoadContexts().findLoadingEntityEntry( entityKey ),
holder,
rowProcessingState
);
initializeEntity( instance, rowProcessingState );
@ -788,15 +776,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
then when the EntitySelectFetchInitializer#initializeInstance() is executed before the EntityResultInitializer one
the persistence context will contain the instances retrieved form the 2LC
*/
final Object entity = persistenceContext.getEntity( entityKey );
if ( entity != null ) {
entityInstance = entity;
registerLoadingEntity( rowProcessingState, entityInstance );
initializeEntityInstance( entityInstance, rowProcessingState );
}
else {
initializeEntity( entityInstance, rowProcessingState );
}
assert persistenceContext.getEntityHolder( entityKey ).getEntityInitializer() == this;
initializeEntity( entityInstance, rowProcessingState );
entityInstanceForNotify = entityInstance;
}
else {
@ -1100,8 +1081,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
if ( entry.getStatus() != Status.LOADING ) {
// If the instance to initialize is the main entity, we can't skip this.
// This can happen if we initialize an enhanced proxy.
return !isEntityReturn()
|| rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
return rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
.getEffectiveOptionalObject() != toInitialize;
}
else {

View File

@ -15,7 +15,9 @@ import org.hibernate.persister.entity.EntityPersister;
* Representation of an entity being loaded, containing its state
*
* @author Steve Ebersole
* @deprecated Now modeled through {@link org.hibernate.engine.spi.EntityHolder}
*/
@Deprecated(forRemoval = true)
public class LoadingEntityEntry {
private final EntityInitializer entityInitializer;
private final EntityKey entityKey;

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.results.graph.entity.internal;
import java.util.function.Consumer;
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.PersistentAttributeInterceptable;
@ -23,7 +24,6 @@ import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
@ -105,24 +105,9 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
assert entityKey != null;
final SharedSessionContractImplementor session = rowProcessingState.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContext();
final Object instance = persistenceContext.getEntity( entityKey );
if ( instance == null ) {
final LoadingEntityEntry loadingEntityEntry = persistenceContext
.getLoadContexts().findLoadingEntityEntry( entityKey );
if ( loadingEntityEntry != null ) {
return loadingEntityEntry.getEntityInstance();
}
}
else if ( isInitialized( instance ) ) {
return instance;
}
else {
// the instance is not initialized but there is another initialzier that is loading it
final LoadingEntityEntry loadingEntityEntry = persistenceContext
.getLoadContexts().findLoadingEntityEntry( entityKey );
if ( loadingEntityEntry != null ) {
return loadingEntityEntry.getEntityInstance();
}
final EntityHolder holder = persistenceContext.getEntityHolder( 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 initialzier is loading

View File

@ -13,6 +13,7 @@ import java.util.List;
import java.util.Map;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -129,7 +130,8 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
final Object loadedInstance = loadInstance( entityKey, referencedModelPart, session );
for ( ParentInfo parentInfo : parentInfos ) {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final Object entity = persistenceContext.getEntity( parentInfo.initializerEntityKey );
final EntityHolder holder = persistenceContext.getEntityHolder( parentInfo.initializerEntityKey );
final Object entity = holder.getEntity();
setInstance(
firstEntityInitializer,
referencedModelPart,

View File

@ -25,7 +25,6 @@ import org.hibernate.sql.results.graph.AbstractFetchParentAccess;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.Type;
@ -102,16 +101,9 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp
final EntityKey entityKey = new EntityKey( identifier, concreteDescriptor );
final PersistenceContext persistenceContext = session.getPersistenceContext();
final LoadingEntityEntry loadingEntityLocally = persistenceContext.getLoadContexts()
.findLoadingEntityEntry( entityKey );
if ( loadingEntityLocally != null ) {
entityInstance = loadingEntityLocally.getEntityInstance();
}
if ( entityInstance == null ) {
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
if ( holder != null && holder.getEntity() != null ) {
entityInstance = persistenceContext.proxyFor( holder );
}
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
if ( holder != null && holder.getEntity() != null ) {
entityInstance = persistenceContext.proxyFor( holder );
}
}
if ( entityInstance == null ) {

View File

@ -11,6 +11,7 @@ import java.util.function.Consumer;
import org.hibernate.FetchNotFoundException;
import org.hibernate.Hibernate;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -29,9 +30,7 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityLoadingLogging;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.sql.results.spi.LoadContexts;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
@ -140,16 +139,8 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
final EntityKey entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
entityInstance = persistenceContext.getEntity( entityKey );
if ( entityInstance != null && Hibernate.isInitialized( entityInstance )) {
isInitialized = true;
return;
}
final LoadContexts loadContexts = session.getPersistenceContext().getLoadContexts();
final LoadingEntityEntry existingLoadingEntry = loadContexts.findLoadingEntityEntry( entityKey );
if ( existingLoadingEntry != null ) {
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
if ( holder != null ) {
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isDebugEnabled() ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Found existing loading entry [%s] - using loading instance",
@ -160,29 +151,29 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
)
);
}
this.entityInstance = existingLoadingEntry.getEntityInstance();
final EntityInitializer entityInitializer = existingLoadingEntry.getEntityInitializer();
if ( entityInitializer != this ) {
entityInstance = holder.getEntity();
if ( holder.getEntityInitializer() == null ) {
if ( entityInstance != null && Hibernate.isInitialized( entityInstance ) ) {
isInitialized = true;
return;
}
}
else if ( holder.getEntityInitializer() != this ) {
// the entity is already being loaded elsewhere
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isDebugEnabled() ) {
EntityLoadingLogging.ENTITY_LOADING_LOGGER.debugf(
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
CONCRETE_NAME,
toLoggableString( getNavigablePath(), entityIdentifier ),
entityInitializer
holder.getEntityInitializer()
);
}
// EARLY EXIT!!!
isInitialized = true;
return;
}
else {
if ( entityInstance == null ) {
isInitialized = true;
return;
}
else if ( entityInstance == null ) {
isInitialized = true;
return;
}
}
@ -205,11 +196,12 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) {
throw new FetchNotFoundException( entityName, entityIdentifier );
}
rowProcessingState.getJdbcValuesSourceProcessingState()
.registerLoadingEntity(
entityKey,
new LoadingEntityEntry( this, entityKey, concreteDescriptor, entityInstance )
);
rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible(
entityKey,
entityInstance,
rowProcessingState.getJdbcValuesSourceProcessingState(),
this
);
}
if ( EntityLoadingLogging.ENTITY_LOADING_LOGGER.isDebugEnabled() ) {

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.results.internal;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
@ -18,7 +19,6 @@ import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.entity.EntityFetch;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesCacheHit;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
@ -179,8 +179,8 @@ public class RowProcessingStateStandardImpl extends BaseExecutionContext impleme
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
executionContext.registerLoadingEntityEntry( entityKey, entry );
public void registerLoadingEntityHolder(EntityHolder holder) {
executionContext.registerLoadingEntityHolder( holder );
}
@Override

View File

@ -111,7 +111,6 @@ public class StandardRowReader<T> implements RowReader<T> {
@Override
public void finishUp(JdbcValuesSourceProcessingState processingState) {
processingState.registerSubselect();
initializers.endLoading( processingState.getExecutionContext() );
}

View File

@ -10,26 +10,23 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
import org.hibernate.sql.results.graph.collection.internal.ArrayInitializer;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
@ -43,8 +40,6 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
private final ExecutionContext executionContext;
private final JdbcValuesSourceProcessingOptions processingOptions;
private Map<EntityKey, LoadingEntityEntry> loadingEntityMap;
private Map<EntityKey, LoadingEntityEntry> reloadedEntityMap;
private Map<EntityUniqueKey, Initializer> initializerByUniquKeyMap;
private Map<CollectionKey, LoadingCollectionEntry> loadingCollectionMap;
private List<CollectionInitializer> arrayInitializers;
@ -94,25 +89,6 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
return postLoadEvent;
}
@Override
public void registerLoadingEntity(
EntityKey entityKey,
LoadingEntityEntry loadingEntry) {
if ( loadingEntityMap == null ) {
loadingEntityMap = new HashMap<>();
}
loadingEntityMap.put( entityKey, loadingEntry );
}
@Override
public void registerReloadedEntity(EntityKey entityKey, LoadingEntityEntry loadingEntry) {
if ( reloadedEntityMap == null ) {
reloadedEntityMap = new HashMap<>();
}
reloadedEntityMap.put( entityKey, loadingEntry );
}
@Override
public void registerInitializer(EntityUniqueKey entityKey, Initializer initializer) {
if ( initializerByUniquKeyMap == null ) {
@ -126,11 +102,6 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
return initializerByUniquKeyMap == null ? null : initializerByUniquKeyMap.get( entityKey );
}
@Override
public LoadingEntityEntry findLoadingEntityLocally(EntityKey entityKey) {
return loadingEntityMap == null ? null : loadingEntityMap.get( entityKey );
}
@Override
public LoadingCollectionEntry findLoadingCollectionLocally(CollectionKey key) {
if ( loadingCollectionMap == null ) {
@ -140,22 +111,6 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
return loadingCollectionMap.get( key );
}
@Override
public void registerSubselect() {
if ( loadingEntityMap != null && loadingEntityMap.size() > 1 ) {
loadingEntityMap.forEach(
(entityKey, loadingEntityEntry) ->
executionContext.registerSubselect( entityKey, loadingEntityEntry )
);
}
else {
LOG.tracef(
"Skipping create subselects because there are fewer than 2 results, so query by key is more efficient.",
getClass().getName()
);
}
}
@Override
public void registerLoadingCollection(CollectionKey key, LoadingCollectionEntry loadingCollectionEntry) {
if ( loadingCollectionMap == null ) {
@ -177,61 +132,18 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
}
@Override
public void finishUp() {
public void finishUp(boolean registerSubselects) {
// now we can finalize loading collections
finishLoadingCollections();
postLoad();
}
private void postLoad() {
final Callback callback = executionContext.getCallback();
if ( loadingEntityMap != null ) {
final EventListenerGroup<PostLoadEventListener> listenerGroup = executionContext.getSession().getFactory()
.getFastSessionServices()
.eventListenerGroup_POST_LOAD;
loadingEntityMap.forEach(
(entityKey, loadingEntityEntry) -> {
if ( loadingEntityEntry.getEntityInstance() != null ) {
if ( postLoadEvent != null ) {
postLoadEvent.reset();
postLoadEvent.setEntity( loadingEntityEntry.getEntityInstance() )
.setId( entityKey.getIdentifier() )
.setPersister( loadingEntityEntry.getDescriptor() );
listenerGroup.fireEventOnEachListener(
postLoadEvent,
PostLoadEventListener::onPostLoad
);
}
if ( callback != null ) {
callback.invokeAfterLoadActions(
loadingEntityEntry.getEntityInstance(),
loadingEntityEntry.getDescriptor(),
getSession()
);
}
}
}
);
final Consumer<EntityHolder> holderConsumer;
if ( registerSubselects ) {
holderConsumer = executionContext::registerLoadingEntityHolder;
}
loadingEntityMap = null;
if ( reloadedEntityMap != null ) {
if ( callback != null ) {
reloadedEntityMap.forEach(
(entityKey, loadingEntityEntry) -> {
callback.invokeAfterLoadActions(
loadingEntityEntry.getEntityInstance(),
loadingEntityEntry.getDescriptor(),
getSession()
);
}
);
}
reloadedEntityMap = null;
else {
holderConsumer = null;
}
executionContext.getSession().getPersistenceContextInternal().postLoad( this, holderConsumer );
}
@SuppressWarnings("SimplifiableIfStatement")

View File

@ -7,7 +7,6 @@
package org.hibernate.sql.results.jdbc.spi;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.PostLoadEvent;
@ -15,7 +14,6 @@ import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.spi.LoadContexts;
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.exec.spi.ExecutionContext;
@ -41,24 +39,6 @@ public interface JdbcValuesSourceProcessingState {
PreLoadEvent getPreLoadEvent();
PostLoadEvent getPostLoadEvent();
/**
* Find a LoadingEntityEntry locally to this context.
*
* @see LoadContexts#findLoadingEntityEntry(EntityKey)
*/
LoadingEntityEntry findLoadingEntityLocally(EntityKey entityKey);
/**
* Registers a LoadingEntityEntry locally to this context
*/
void registerLoadingEntity(
EntityKey entityKey,
LoadingEntityEntry loadingEntry);
void registerReloadedEntity(
EntityKey entityKey,
LoadingEntityEntry loadingEntry);
void registerInitializer(
EntityUniqueKey entityKey,
Initializer initializer);
@ -80,8 +60,5 @@ public interface JdbcValuesSourceProcessingState {
CollectionKey collectionKey,
LoadingCollectionEntry loadingCollectionEntry);
default void registerSubselect() {
}
void finishUp();
void finishUp(boolean registerSubselects);
}

View File

@ -174,12 +174,14 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
results = new Results<>( domainResultJavaType );
}
int readRows = 0;
if ( uniqueSemantic == UniqueSemantic.FILTER
|| uniqueSemantic == UniqueSemantic.ASSERT && rowProcessingState.hasCollectionInitializers()
|| uniqueSemantic == UniqueSemantic.ALLOW && isEntityResultType ) {
while ( rowProcessingState.next() ) {
results.addUnique( rowReader.readRow( rowProcessingState, processingOptions ) );
rowProcessingState.finishRowProcessing();
readRows++;
}
}
else if ( uniqueSemantic == UniqueSemantic.ASSERT ) {
@ -194,18 +196,20 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
);
}
rowProcessingState.finishRowProcessing();
readRows++;
}
}
else {
while ( rowProcessingState.next() ) {
results.add( rowReader.readRow( rowProcessingState, processingOptions ) );
rowProcessingState.finishRowProcessing();
readRows++;
}
}
try {
rowReader.finishUp( jdbcValuesSourceProcessingState );
jdbcValuesSourceProcessingState.finishUp();
jdbcValuesSourceProcessingState.finishUp( readRows > 1 );
}
finally {
persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState );

View File

@ -15,7 +15,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
/**
@ -51,10 +50,6 @@ public class LoadContexts {
return jdbcValuesSourceProcessingStateStack.getRoot() == null;
}
public LoadingEntityEntry findLoadingEntityEntry(final EntityKey entityKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirstWithParameter( entityKey, JdbcValuesSourceProcessingState::findLoadingEntityLocally );
}
public LoadingCollectionEntry findLoadingCollectionEntry(final CollectionKey collectionKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirstWithParameter( collectionKey, JdbcValuesSourceProcessingState::findLoadingCollectionLocally );
}

View File

@ -93,7 +93,7 @@ public class SubselectTest {
List<String> sqlQueries = statementInspector.getSqlQueries();
assertThat( sqlQueries.size() ).isEqualTo( 1 );
String query = sqlQueries.get( 0 );
assertFalse( containsSubquery( query ), " The query should contain a subquery" );
assertFalse( containsSubquery( query ), " The query should not contain a subquery" );
}
);