Fix Stack Overflow caused by EntitySelectFetchByUniqueKeyInitializer

This commit is contained in:
Andrea Boriero 2021-12-17 17:53:26 +01:00 committed by Steve Ebersole
parent ecd9d2a193
commit d6bdca77f2
5 changed files with 76 additions and 13 deletions

View File

@ -15,7 +15,7 @@ import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.query.NavigablePath;
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.jdbc.spi.JdbcValuesSourceProcessingState;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
@ -66,16 +66,27 @@ public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchIn
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
entityInstance = persistenceContext.getEntity( euk );
if ( entityInstance == null ) {
entityInstance = ( (UniqueKeyLoadable) concreteDescriptor ).loadByUniqueKey(
uniqueKeyPropertyName,
entityIdentifier,
session
);
final EntitySelectFetchByUniqueKeyInitializer initializer = (EntitySelectFetchByUniqueKeyInitializer) persistenceContext.getLoadContexts()
.findInitializer( euk );
if ( initializer == null ) {
final JdbcValuesSourceProcessingState jdbcValuesSourceProcessingState = rowProcessingState.getJdbcValuesSourceProcessingState();
jdbcValuesSourceProcessingState.registerInitilaizer( euk, this );
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( entityInstance != null ) {
persistenceContext.addEntity( euk, entityInstance );
entityInstance = ( (UniqueKeyLoadable) concreteDescriptor ).loadByUniqueKey(
uniqueKeyPropertyName,
entityIdentifier,
session
);
// If the entity was not in the Persistence Context, but was found now,
// add it to the Persistence Context
if ( entityInstance != null ) {
persistenceContext.addEntity( euk, entityInstance );
}
notifyResolutionListeners(entityInstance);
}
else {
registerResolutionListener( instance -> entityInstance = instance );
}
}
if ( entityInstance != null ) {
@ -83,4 +94,5 @@ public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchIn
}
isInitialized = true;
}
}

View File

@ -14,12 +14,14 @@ import java.util.function.BiConsumer;
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.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.mapping.UniqueKey;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.results.graph.Initializer;
@ -47,6 +49,7 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
private Map<EntityKey, LoadingEntityEntry> loadingEntityMap;
private Map<EntityKey, Initializer> initializerMap;
private Map<EntityUniqueKey, Initializer> initializerByUniquKeyMap;
private Map<CollectionKey, LoadingCollectionEntry> loadingCollectionMap;
private List<CollectionInitializer> arrayInitializers;
@ -122,6 +125,19 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
}
@Override
public void registerInitilaizer(EntityUniqueKey entityKey, Initializer initializer) {
if ( initializerByUniquKeyMap == null ) {
initializerByUniquKeyMap = new HashMap<>();
}
initializerByUniquKeyMap.put( entityKey, initializer );
}
@Override
public Initializer findInitializer(EntityUniqueKey entityKey) {
return initializerByUniquKeyMap == null ? null : initializerByUniquKeyMap.get( entityKey );
}
@Override
public Initializer findInitializer(EntityKey entityKey) {
return initializerMap == null ? null : initializerMap.get( entityKey );

View File

@ -8,9 +8,11 @@ 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;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.spi.LoadContexts;
import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry;
@ -60,8 +62,14 @@ public interface JdbcValuesSourceProcessingState {
EntityKey entityKey,
Initializer initializer);
void registerInitilaizer(
EntityUniqueKey entityKey,
Initializer initializer);
Initializer findInitializer(EntityKey entityKey);
Initializer findInitializer(EntityUniqueKey entityKey);
/**
* Find a LoadingCollectionEntry locally to this context.

View File

@ -8,11 +8,13 @@ package org.hibernate.sql.results.spi;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreLogging;
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;
@ -58,6 +60,18 @@ public class LoadContexts {
);
}
public Initializer findInitializer(EntityKey key){
return jdbcValuesSourceProcessingStateStack.findCurrentFirst(
state -> state.findInitializer( key )
);
}
public Initializer findInitializer(EntityUniqueKey key){
return jdbcValuesSourceProcessingStateStack.findCurrentFirst(
state -> state.findInitializer( key )
);
}
/**
* Retrieves the persistence context to which this is bound.
*

View File

@ -49,14 +49,20 @@ public class OneToOneOwnerByContainedEagerCyclesTest extends BaseCoreFunctionalT
containing2.setChild( containing3 );
containing3.setParent( containing2 );
Containing containing4 = new Containing();
containing4.setId( 4 );
containing3.setChild( containing4 );
containing4.setParent( containing3 );
Contained contained1 = new Contained();
contained1.setId( 4 );
contained1.setId( 5 );
contained1.setText( "initialValue" );
containing2.setContained( contained1 );
contained1.setContaining( containing2 );
Contained contained2 = new Contained();
contained2.setId( 5 );
contained2.setId( 6 );
contained2.setText( "initialOutOfScopeValue" );
containing3.setContained( contained2 );
contained2.setContaining( containing3 );
@ -64,14 +70,21 @@ public class OneToOneOwnerByContainedEagerCyclesTest extends BaseCoreFunctionalT
session.persist( contained1 );
session.persist( contained2 );
session.persist( containing3 );
session.persist( containing4 );
session.persist( containing2 );
session.persist( containing1 );
} );
// Test updating the value
inTransaction( session -> {
Contained contained = session.get( Contained.class, 4 );
Contained contained = session.get( Contained.class, 5 );
assertThat( contained ).isNotNull();
final Containing containing2 = contained.containing;
assertThat( containing2 ).isNotNull();
final Containing containing3 = containing2.child;
assertThat( containing3 ).isNotNull();
assertThat( containing3.parent ).isEqualTo( containing2 );
assertThat( containing3.child ).isNotNull();
} );
}