HHH-16890 StackOverflowError when loading entities with @Proxy(lazy = false)
This commit is contained in:
parent
abaaa09225
commit
75d834efe9
|
@ -351,16 +351,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
else {
|
||||
// 2) build the EntityKey
|
||||
entityKey = new EntityKey( id, concreteDescriptor );
|
||||
// 3) schedule the EntityKey for batch loading, if possible
|
||||
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( concreteDescriptor ) ) {
|
||||
final PersistenceContext persistenceContext =
|
||||
session.getPersistenceContextInternal();
|
||||
if ( !persistenceContext.containsEntity( entityKey ) ) {
|
||||
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,11 @@ 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.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
|
@ -19,8 +23,11 @@ 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;
|
||||
|
||||
public abstract class AbstractBatchEntitySelectFetchInitializer extends AbstractFetchParentAccess
|
||||
implements EntityInitializer {
|
||||
|
||||
|
@ -32,7 +39,7 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
|
|||
protected final ToOneAttributeMapping referencedModelPart;
|
||||
protected final EntityInitializer firstEntityInitializer;
|
||||
|
||||
protected Object entityInstance;
|
||||
protected Object initializedEntityInstance;
|
||||
protected EntityKey entityKey;
|
||||
|
||||
protected State state = State.UNINITIALIZED;
|
||||
|
@ -90,18 +97,54 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
|
|||
}
|
||||
else {
|
||||
entityKey = new EntityKey( entityIdentifier, concreteDescriptor );
|
||||
|
||||
state = State.KEY_RESOLVED;
|
||||
|
||||
rowProcessingState.getSession().getPersistenceContext()
|
||||
.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
|
||||
registerResolutionListener();
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getExistingInitializedInstance(RowProcessingState rowProcessingState) {
|
||||
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;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isInitialized(Object entity) {
|
||||
final PersistentAttributeInterceptable attributeInterceptable = asPersistentAttributeInterceptableOrNull(
|
||||
entity );
|
||||
if ( attributeInterceptable == null ) {
|
||||
return true;
|
||||
}
|
||||
final PersistentAttributeInterceptor interceptor =
|
||||
attributeInterceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
return ( (EnhancementAsProxyLazinessInterceptor) interceptor ).isInitialized();
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerToBatchFetchQueue(RowProcessingState rowProcessingState) {
|
||||
assert entityKey != null;
|
||||
rowProcessingState.getSession().getPersistenceContext()
|
||||
.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
initializedEntityInstance = null;
|
||||
entityKey = null;
|
||||
state = State.UNINITIALIZED;
|
||||
clearResolutionListeners();
|
||||
|
@ -114,7 +157,7 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
|
|||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return entityInstance;
|
||||
return initializedEntityInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,8 +172,8 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
|
|||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
if ( entityInstance != null ) {
|
||||
listener.accept( entityInstance );
|
||||
if ( initializedEntityInstance != null ) {
|
||||
listener.accept( initializedEntityInstance );
|
||||
}
|
||||
else {
|
||||
super.registerResolutionListener( listener );
|
||||
|
|
|
@ -63,12 +63,23 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB
|
|||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state == State.INITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
resolveKey( rowProcessingState, referencedModelPart, parentAccess );
|
||||
if ( entityKey == null ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
|
||||
entityInstance = BATCH_PROPERTY;
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
|
||||
initializedEntityInstance = BATCH_PROPERTY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -39,7 +39,21 @@ public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelect
|
|||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
if ( state == State.INITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
resolveKey( rowProcessingState, referencedModelPart, parentAccess );
|
||||
if ( entityKey == null ) {
|
||||
return;
|
||||
}
|
||||
state = State.INITIALIZED;
|
||||
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.hibernate.spi.NavigablePath;
|
|||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
|
@ -46,32 +45,29 @@ public class BatchInitializeEntitySelectFetchInitializer extends AbstractBatchEn
|
|||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
resolveKey( rowProcessingState, referencedModelPart, parentAccess );
|
||||
if ( state == State.INITIALIZED ) {
|
||||
return;
|
||||
}
|
||||
resolveKey( rowProcessingState, referencedModelPart, parentAccess );
|
||||
|
||||
if ( entityKey == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = State.INITIALIZED;
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
entityInstance = session.getPersistenceContext().getEntity( entityKey );
|
||||
if ( entityInstance == null ) {
|
||||
final LoadingEntityEntry loadingEntityEntry = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.findLoadingEntityLocally( entityKey );
|
||||
if ( loadingEntityEntry != null ) {
|
||||
loadingEntityEntry.getEntityInitializer().resolveInstance( rowProcessingState );
|
||||
entityInstance = loadingEntityEntry.getEntityInstance();
|
||||
}
|
||||
else {
|
||||
// Force creating a proxy
|
||||
entityInstance = session.internalLoad(
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
toBatchLoad.add( entityKey );
|
||||
}
|
||||
initializedEntityInstance = getExistingInitializedInstance( rowProcessingState );
|
||||
if ( initializedEntityInstance == null ) {
|
||||
// need to add the key to the batch queue only when the entity has not been already loaded or
|
||||
// there isn't another initializer that is loading it
|
||||
registerToBatchFetchQueue( rowProcessingState );
|
||||
// Force creating a proxy
|
||||
initializedEntityInstance = rowProcessingState.getSession().internalLoad(
|
||||
entityKey.getEntityName(),
|
||||
entityKey.getIdentifier(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
toBatchLoad.add( entityKey );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.spi.NavigablePath;
|
|||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.results.graph.Initializer;
|
||||
import org.hibernate.sql.results.graph.collection.internal.AbstractCollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.internal.AbstractBatchEntitySelectFetchInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
|
@ -118,7 +119,8 @@ public final class InitializersList {
|
|||
private static boolean initializeFirst(final Initializer initializer) {
|
||||
return !( initializer instanceof EntityDelayedFetchInitializer )
|
||||
&& !( initializer instanceof EntitySelectFetchInitializer )
|
||||
&& !( initializer instanceof AbstractCollectionInitializer );
|
||||
&& !( initializer instanceof AbstractCollectionInitializer )
|
||||
&& !(initializer instanceof AbstractBatchEntitySelectFetchInitializer );
|
||||
}
|
||||
|
||||
InitializersList build(final Map<NavigablePath, Initializer> initializerMap) {
|
||||
|
|
Loading…
Reference in New Issue