HHH-14075 : Changes to loaders and TwoPhaseLoad to allow "internal" loading to be reused by hibernate-reactive

This commit is contained in:
Gail Badner 2020-06-17 16:09:47 -07:00 committed by Sanne Grinovero
parent 9756b0fba8
commit 467203e8c4
12 changed files with 362 additions and 85 deletions

View File

@ -35,13 +35,13 @@ import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.graph.spi.AttributeNodeImplementor; import org.hibernate.graph.spi.AttributeNodeImplementor;
import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FastSessionServices;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.stat.internal.StatsHelper; import org.hibernate.stat.internal.StatsHelper;
import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper; import org.hibernate.type.TypeHelper;
@ -121,18 +121,13 @@ public final class TwoPhaseLoad {
final boolean readOnly, final boolean readOnly,
final SharedSessionContractImplementor session, final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent) { final PreLoadEvent preLoadEvent) {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final EntityEntry entityEntry = persistenceContext.getEntry( entity );
if ( entityEntry == null ) {
throw new AssertionFailure( "possible non-threadsafe access to the session" );
}
final EventListenerGroup<PreLoadEventListener> listenerGroup = session final EventListenerGroup<PreLoadEventListener> listenerGroup = session
.getFactory() .getFactory()
.getServiceRegistry() .getServiceRegistry()
.getService( EventListenerRegistry.class ) .getService( EventListenerRegistry.class )
.getEventListenerGroup( EventType.PRE_LOAD ); .getEventListenerGroup( EventType.PRE_LOAD );
final Iterable<PreLoadEventListener> listeners = listenerGroup.listeners(); final Iterable<PreLoadEventListener> listeners = listenerGroup.listeners();
doInitializeEntity( entity, entityEntry, readOnly, session, preLoadEvent, listeners ); initializeEntity( entity, readOnly, session, preLoadEvent, listeners, EntityResolver.DEFAULT );
} }
/** /**
@ -155,22 +150,46 @@ public final class TwoPhaseLoad {
final SharedSessionContractImplementor session, final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent, final PreLoadEvent preLoadEvent,
final Iterable<PreLoadEventListener> preLoadEventListeners) { final Iterable<PreLoadEventListener> preLoadEventListeners) {
initializeEntity( entity, readOnly, session, preLoadEvent, preLoadEventListeners, EntityResolver.DEFAULT );
}
/**
* Perform the second step of 2-phase load. Fully initialize the entity
* instance.
* <p/>
* After processing a JDBC result set, we "resolve" all the associations
* between the entities which were instantiated and had their state
* "hydrated" into an array
*
* @param entity The entity being loaded
* @param readOnly Is the entity being loaded as read-only
* @param session The Session
* @param preLoadEvent The (re-used) pre-load event
* @param preLoadEventListeners the pre-load event listeners
* @param entityResolver the resolver used for to-one entity associations
* (not used when an entity is a bytecode-enhanced lazy entity)
*/
public static void initializeEntity(
final Object entity,
final boolean readOnly,
final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent,
final Iterable<PreLoadEventListener> preLoadEventListeners,
final EntityResolver entityResolver) {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityEntry entityEntry = persistenceContext.getEntry( entity ); final EntityEntry entityEntry = persistenceContext.getEntry( entity );
if ( entityEntry == null ) { if ( entityEntry == null ) {
throw new AssertionFailure( "possible non-threadsafe access to the session" ); throw new AssertionFailure( "possible non-threadsafe access to the session" );
} }
doInitializeEntity( entity, entityEntry, readOnly, session, preLoadEvent, preLoadEventListeners ); initializeEntityEntryLoadedState( entity, entityEntry, session, entityResolver );
initializeEntityFromEntityEntryLoadedState( entity, entityEntry, readOnly, session, preLoadEvent, preLoadEventListeners );
} }
private static void doInitializeEntity( public static void initializeEntityEntryLoadedState(
final Object entity, final Object entity,
final EntityEntry entityEntry, final EntityEntry entityEntry,
final boolean readOnly,
final SharedSessionContractImplementor session, final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent, final EntityResolver entityResolver) throws HibernateException {
final Iterable<PreLoadEventListener> preLoadEventListeners) throws HibernateException {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityPersister persister = entityEntry.getPersister(); final EntityPersister persister = entityEntry.getPersister();
final Serializable id = entityEntry.getId(); final Serializable id = entityEntry.getId();
final Object[] hydratedState = entityEntry.getLoadedState(); final Object[] hydratedState = entityEntry.getLoadedState();
@ -229,18 +248,36 @@ public final class TwoPhaseLoad {
// we know value != LazyPropertyInitializer.UNFETCHED_PROPERTY // we know value != LazyPropertyInitializer.UNFETCHED_PROPERTY
Boolean overridingEager = getOverridingEager( session, entityName, propertyNames[i], types[i], debugEnabled ); Boolean overridingEager = getOverridingEager( session, entityName, propertyNames[i], types[i], debugEnabled );
hydratedState[i] = types[i].resolve( value, session, entity, overridingEager ); hydratedState[i] = types[i].isEntityType()
? entityResolver.resolve( (EntityType) types[i], value, session, entity, overridingEager )
: types[i].resolve( value, session, entity, overridingEager );
} }
else { else {
if ( debugEnabled ) { if ( debugEnabled ) {
LOG.debugf( "Skipping <unknown> attribute : `%s`", propertyNames[i] ); LOG.debugf( "Skipping <unknown> attribute : `%s`", propertyNames[i] );
} }
} }
if ( session.getFetchGraphLoadContext() != fetchGraphContext ) { if ( session.getFetchGraphLoadContext() != fetchGraphContext ) {
session.setFetchGraphLoadContext( fetchGraphContext ); session.setFetchGraphLoadContext( fetchGraphContext );
} }
} }
}
public static void initializeEntityFromEntityEntryLoadedState(
final Object entity,
final EntityEntry entityEntry,
final boolean readOnly,
final SharedSessionContractImplementor session,
final PreLoadEvent preLoadEvent,
final Iterable<PreLoadEventListener> preLoadEventListeners) throws HibernateException {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityPersister persister = entityEntry.getPersister();
final Serializable id = entityEntry.getId();
final Object[] hydratedState = entityEntry.getLoadedState();
final boolean debugEnabled = LOG.isDebugEnabled();
//Must occur after resolving identifiers! //Must occur after resolving identifiers!
if ( session.isEventSource() ) { if ( session.isEventSource() ) {
@ -600,4 +637,23 @@ public final class TwoPhaseLoad {
false false
); );
} }
/**
* Implementations determine how a to-one associations is resolved.
*
* @see #initializeEntity(Object, boolean, SharedSessionContractImplementor, PreLoadEvent, Iterable, EntityResolver)
*/
public interface EntityResolver {
Object resolve(
EntityType entityType,
Object value,
SharedSessionContractImplementor session,
Object owner,
Boolean overridingEager
);
EntityResolver DEFAULT = (entityType, value, session, owner, overridingEager) ->
entityType.resolve( value, session, owner, overridingEager );
}
} }

View File

@ -986,12 +986,48 @@ public abstract class Loader {
int maxRows, int maxRows,
List<AfterLoadAction> afterLoadActions) throws SQLException { List<AfterLoadAction> afterLoadActions) throws SQLException {
final int entitySpan = getEntityPersisters().length; final int entitySpan = getEntityPersisters().length;
final boolean createSubselects = isSubselectLoadingEnabled();
final List<EntityKey[]> subselectResultKeys = createSubselects ? new ArrayList<>() : null;
final List<Object> hydratedObjects = entitySpan == 0 ? null : new ArrayList<>( entitySpan * 10 );
final List results = getRowsFromResultSet(
rs,
queryParameters,
session,
returnProxies,
forcedResultTransformer,
maxRows,
hydratedObjects,
subselectResultKeys
);
initializeEntitiesAndCollections(
hydratedObjects,
rs,
session,
queryParameters.isReadOnly( session ),
afterLoadActions
);
if ( createSubselects ) {
createSubselects( subselectResultKeys, queryParameters, session );
}
return results;
}
protected List<Object> getRowsFromResultSet(
ResultSet rs,
QueryParameters queryParameters,
SharedSessionContractImplementor session,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
int maxRows,
List<Object> hydratedObjects,
List<EntityKey[]> subselectResultKeys) throws SQLException {
final int entitySpan = getEntityPersisters().length;
final boolean createSubselects = isSubselectLoadingEnabled();
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() ); final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled(); final List<Object> results = new ArrayList<>();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final List results = new ArrayList();
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session ); handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
@ -1023,16 +1059,6 @@ public abstract class Loader {
LOG.tracev( "Done processing result set ({0} rows)", count ); LOG.tracev( "Done processing result set ({0} rows)", count );
initializeEntitiesAndCollections(
hydratedObjects,
rs,
session,
queryParameters.isReadOnly( session ),
afterLoadActions
);
if ( createSubselects ) {
createSubselects( subselectResultKeys, queryParameters, session );
}
return results; return results;
} }
@ -1061,7 +1087,7 @@ public abstract class Loader {
return result; return result;
} }
private void createSubselects(List keys, QueryParameters queryParameters, SharedSessionContractImplementor session) { protected void createSubselects(List keys, QueryParameters queryParameters, SharedSessionContractImplementor session) {
if ( keys.size() > 1 ) { //if we only returned one entity, query by key is more efficient if ( keys.size() > 1 ) { //if we only returned one entity, query by key is more efficient
Set[] keySets = transpose( keys ); Set[] keySets = transpose( keys );

View File

@ -62,7 +62,7 @@ public abstract class OuterJoinLoader extends BasicLoader {
return sql; return sql;
} }
protected final Loadable[] getEntityPersisters() { public final Loadable[] getEntityPersisters() {
return persisters; return persisters;
} }
@ -90,7 +90,7 @@ public abstract class OuterJoinLoader extends BasicLoader {
return aliases; return aliases;
} }
protected final CollectionPersister[] getCollectionPersisters() { public final CollectionPersister[] getCollectionPersisters() {
return collectionPersisters; return collectionPersisters;
} }

View File

@ -31,6 +31,7 @@ import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader; import org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader;
import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory; import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory;
import org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails; import org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessorResolver;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
@ -58,7 +59,8 @@ public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlan
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
String[] uniqueKeyColumnNames, String[] uniqueKeyColumnNames,
Type uniqueKeyType, Type uniqueKeyType,
QueryBuildingParameters buildingParameters) { QueryBuildingParameters buildingParameters,
ResultSetProcessorResolver resultSetProcessorResolver) {
super( factory ); super( factory );
this.entityPersister = entityPersister; this.entityPersister = entityPersister;
this.uniqueKeyType = uniqueKeyType; this.uniqueKeyType = uniqueKeyType;
@ -96,7 +98,42 @@ public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlan
plan, plan,
uniqueKeyColumnNames, uniqueKeyColumnNames,
buildingParameters, buildingParameters,
factory factory,
resultSetProcessorResolver
);
}
public AbstractLoadPlanBasedEntityLoader(
OuterJoinLoadable entityPersister,
SessionFactoryImplementor factory,
String[] uniqueKeyColumnNames,
Type uniqueKeyType,
QueryBuildingParameters buildingParameters) {
this(
entityPersister,
factory,
uniqueKeyColumnNames,
uniqueKeyType,
buildingParameters,ResultSetProcessorResolver.DEFAULT
);
}
protected AbstractLoadPlanBasedEntityLoader(
OuterJoinLoadable entityPersister,
SessionFactoryImplementor factory,
EntityLoadQueryDetails entityLoaderQueryDetailsTemplate,
Type uniqueKeyType,
QueryBuildingParameters buildingParameters,
ResultSetProcessorResolver resultSetProcessorResolver) {
super( factory );
this.entityPersister = entityPersister;
this.uniqueKeyType = uniqueKeyType;
this.entityName = entityPersister.getEntityName();
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
entityLoaderQueryDetailsTemplate,
buildingParameters,
resultSetProcessorResolver
); );
} }
@ -106,14 +143,13 @@ public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlan
EntityLoadQueryDetails entityLoaderQueryDetailsTemplate, EntityLoadQueryDetails entityLoaderQueryDetailsTemplate,
Type uniqueKeyType, Type uniqueKeyType,
QueryBuildingParameters buildingParameters) { QueryBuildingParameters buildingParameters) {
super( factory ); this(
this.entityPersister = entityPersister; entityPersister,
this.uniqueKeyType = uniqueKeyType; factory,
this.entityName = entityPersister.getEntityName();
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
entityLoaderQueryDetailsTemplate, entityLoaderQueryDetailsTemplate,
buildingParameters uniqueKeyType,
buildingParameters,
ResultSetProcessorResolver.DEFAULT
); );
} }

View File

@ -17,6 +17,7 @@ import org.hibernate.loader.plan.exec.process.spi.CollectionReferenceInitializer
import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer; import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan.exec.process.spi.ReaderCollector; import org.hibernate.loader.plan.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessorResolver;
import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder; import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
@ -104,6 +105,10 @@ public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
* *
*/ */
protected void generate() { protected void generate() {
generate( ResultSetProcessorResolver.DEFAULT );
}
protected void generate(ResultSetProcessorResolver resultSetProcessorResolver) {
// There are 2 high-level requirements to perform here: // There are 2 high-level requirements to perform here:
// 1) Determine the SQL required to carry out the given LoadPlan (and fulfill // 1) Determine the SQL required to carry out the given LoadPlan (and fulfill
// {@code LoadQueryDetails#getSqlStatement()}). SelectStatementBuilder collects the ongoing efforts to // {@code LoadQueryDetails#getSqlStatement()}). SelectStatementBuilder collects the ongoing efforts to
@ -190,10 +195,10 @@ public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, queryProcessor.getAliasResolutionContext() ); LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, queryProcessor.getAliasResolutionContext() );
this.sqlStatement = select.toStatementString(); this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl( this.resultSetProcessor = resultSetProcessorResolver.resolveResultSetProcessor(
loadPlan, loadPlan,
queryProcessor.getAliasResolutionContext(), queryProcessor.getAliasResolutionContext(),
getReaderCollector().buildRowReader(), getReaderCollector(),
shouldUseOptionalEntityInstance(), shouldUseOptionalEntityInstance(),
isSubselectLoadingEnabled( fetchStats ) isSubselectLoadingEnabled( fetchStats )
); );

View File

@ -7,6 +7,7 @@
package org.hibernate.loader.plan.exec.internal; package org.hibernate.loader.plan.exec.internal;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessorResolver;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
@ -38,6 +39,7 @@ public class BatchingLoadQueryDetailsFactory {
* @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those * @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here) * that add additional joins here)
* @param factory The SessionFactory * @param factory The SessionFactory
* @oaram resultSetProcessorResolver The ResultSet processor resolver.
* *
* @return The EntityLoadQueryDetails * @return The EntityLoadQueryDetails
*/ */
@ -45,7 +47,8 @@ public class BatchingLoadQueryDetailsFactory {
LoadPlan loadPlan, LoadPlan loadPlan,
String[] keyColumnNames, String[] keyColumnNames,
QueryBuildingParameters buildingParameters, QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory,
ResultSetProcessorResolver resultSetProcessorResolver) {
// TODO: how should shouldUseOptionalEntityInformation be used? // TODO: how should shouldUseOptionalEntityInformation be used?
// final int batchSize = buildingParameters.getBatchSize(); // final int batchSize = buildingParameters.getBatchSize();
@ -64,7 +67,54 @@ public class BatchingLoadQueryDetailsFactory {
aliasResolutionContext, aliasResolutionContext,
rootReturn, rootReturn,
buildingParameters, buildingParameters,
factory factory,
resultSetProcessorResolver
);
}
/**
* Returns an EntityLoadQueryDetails object from the given inputs.
*
* @param loadPlan The load plan
* @param keyColumnNames The columns to load the entity by (the PK columns or some other unique set of columns)
* @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @param factory The SessionFactory
*
* @return The EntityLoadQueryDetails
*/
public EntityLoadQueryDetails makeEntityLoadQueryDetails(
LoadPlan loadPlan,
String[] keyColumnNames,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
return makeEntityLoadQueryDetails(
loadPlan,
keyColumnNames,
buildingParameters,
factory,
ResultSetProcessorResolver.DEFAULT
);
}
/**
* Returns a EntityLoadQueryDetails object based on an existing one and additional elements specific to this one.
*
* @param entityLoadQueryDetailsTemplate the template
* @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @oaram resultSetProcessorResolver The ResultSet processor resolver.
*
* @return The EntityLoadQueryDetails
*/
public EntityLoadQueryDetails makeEntityLoadQueryDetails(
EntityLoadQueryDetails entityLoadQueryDetailsTemplate,
QueryBuildingParameters buildingParameters,
ResultSetProcessorResolver resultSetProcessorResolver) {
return new EntityLoadQueryDetails(
entityLoadQueryDetailsTemplate,
buildingParameters,
resultSetProcessorResolver
); );
} }
@ -79,9 +129,10 @@ public class BatchingLoadQueryDetailsFactory {
public EntityLoadQueryDetails makeEntityLoadQueryDetails( public EntityLoadQueryDetails makeEntityLoadQueryDetails(
EntityLoadQueryDetails entityLoadQueryDetailsTemplate, EntityLoadQueryDetails entityLoadQueryDetailsTemplate,
QueryBuildingParameters buildingParameters) { QueryBuildingParameters buildingParameters) {
return new EntityLoadQueryDetails( return makeEntityLoadQueryDetails(
entityLoadQueryDetailsTemplate, entityLoadQueryDetailsTemplate,
buildingParameters buildingParameters,
ResultSetProcessorResolver.DEFAULT
); );
} }

View File

@ -22,6 +22,7 @@ import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessingContex
import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer; import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan.exec.process.spi.ReaderCollector; import org.hibernate.loader.plan.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessorResolver;
import org.hibernate.loader.plan.exec.process.spi.RowReader; import org.hibernate.loader.plan.exec.process.spi.RowReader;
import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder; import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
@ -58,6 +59,7 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
* @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those * @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here) * that add additional joins here)
* @param factory The SessionFactory * @param factory The SessionFactory
* @param resultSetProcessorResolver The ResultSet resolver.
*/ */
protected EntityLoadQueryDetails( protected EntityLoadQueryDetails(
LoadPlan loadPlan, LoadPlan loadPlan,
@ -65,7 +67,9 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
AliasResolutionContextImpl aliasResolutionContext, AliasResolutionContextImpl aliasResolutionContext,
EntityReturn rootReturn, EntityReturn rootReturn,
QueryBuildingParameters buildingParameters, QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory,
ResultSetProcessorResolver resultSetProcessorResolver) {
super( super(
loadPlan, loadPlan,
aliasResolutionContext, aliasResolutionContext,
@ -82,21 +86,55 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
new EntityReturnReader( rootReturn ), new EntityReturnReader( rootReturn ),
new EntityReferenceInitializerImpl( rootReturn, entityReferenceAliases, true ) new EntityReferenceInitializerImpl( rootReturn, entityReferenceAliases, true )
); );
generate(); generate( resultSetProcessorResolver );
}
/**
* Constructs a EntityLoadQueryDetails object from the given inputs.
*
* @param loadPlan The load plan
* @param keyColumnNames The columns to load the entity by (the PK columns or some other unique set of columns)
* @param buildingParameters Any influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @param factory The SessionFactory
*/
protected EntityLoadQueryDetails(
LoadPlan loadPlan,
String[] keyColumnNames,
AliasResolutionContextImpl aliasResolutionContext,
EntityReturn rootReturn,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
this(
loadPlan,
keyColumnNames,
aliasResolutionContext,
rootReturn,
buildingParameters,
factory,
ResultSetProcessorResolver.DEFAULT
);
} }
protected EntityLoadQueryDetails( protected EntityLoadQueryDetails(
EntityLoadQueryDetails initialEntityLoadQueryDetails, EntityLoadQueryDetails initialEntityLoadQueryDetails,
QueryBuildingParameters buildingParameters) { QueryBuildingParameters buildingParameters,
ResultSetProcessorResolver resultSetProcessorResolver) {
this( this(
initialEntityLoadQueryDetails.getLoadPlan(), initialEntityLoadQueryDetails.getLoadPlan(),
initialEntityLoadQueryDetails.getKeyColumnNames(), initialEntityLoadQueryDetails.getKeyColumnNames(),
new AliasResolutionContextImpl( initialEntityLoadQueryDetails.getSessionFactory() ), new AliasResolutionContextImpl( initialEntityLoadQueryDetails.getSessionFactory() ),
(EntityReturn) initialEntityLoadQueryDetails.getRootReturn(), (EntityReturn) initialEntityLoadQueryDetails.getRootReturn(),
buildingParameters, buildingParameters,
initialEntityLoadQueryDetails.getSessionFactory() initialEntityLoadQueryDetails.getSessionFactory(),
resultSetProcessorResolver
); );
} }
protected EntityLoadQueryDetails(
EntityLoadQueryDetails initialEntityLoadQueryDetails,
QueryBuildingParameters buildingParameters) {
this( initialEntityLoadQueryDetails, buildingParameters, ResultSetProcessorResolver.DEFAULT );
}
public boolean hasCollectionInitializers() { public boolean hasCollectionInitializers() {
return CollectionHelper.isNotEmpty( readerCollector.getArrayReferenceInitializers() ) || return CollectionHelper.isNotEmpty( readerCollector.getArrayReferenceInitializers() ) ||

View File

@ -224,7 +224,7 @@ public abstract class AbstractRowReader implements RowReader {
postLoad( postLoadEvent, context, hydratedEntityRegistrations, afterLoadActionList ); postLoad( postLoadEvent, context, hydratedEntityRegistrations, afterLoadActionList );
} }
private void finishLoadingArrays(ResultSetProcessingContextImpl context) { protected void finishLoadingArrays(ResultSetProcessingContextImpl context) {
for ( CollectionReferenceInitializer arrayReferenceInitializer : arrayReferenceInitializers ) { for ( CollectionReferenceInitializer arrayReferenceInitializer : arrayReferenceInitializers ) {
arrayReferenceInitializer.endLoading( context ); arrayReferenceInitializer.endLoading( context );
} }
@ -262,13 +262,13 @@ public abstract class AbstractRowReader implements RowReader {
} }
} }
private void finishLoadingCollections(ResultSetProcessingContextImpl context) { protected void finishLoadingCollections(ResultSetProcessingContextImpl context) {
for ( CollectionReferenceInitializer collectionReferenceInitializer : collectionReferenceInitializers ) { for ( CollectionReferenceInitializer collectionReferenceInitializer : collectionReferenceInitializers ) {
collectionReferenceInitializer.endLoading( context ); collectionReferenceInitializer.endLoading( context );
} }
} }
private void afterInitialize(ResultSetProcessingContextImpl context, protected void afterInitialize(ResultSetProcessingContextImpl context,
List<HydratedEntityRegistration> hydratedEntityRegistrations) { List<HydratedEntityRegistration> hydratedEntityRegistrations) {
if ( hydratedEntityRegistrations == null ) { if ( hydratedEntityRegistrations == null ) {
return; return;
@ -279,7 +279,7 @@ public abstract class AbstractRowReader implements RowReader {
} }
} }
private void postLoad( protected void postLoad(
PostLoadEvent postLoadEvent, PostLoadEvent postLoadEvent,
ResultSetProcessingContextImpl context, ResultSetProcessingContextImpl context,
List<HydratedEntityRegistration> hydratedEntityRegistrations, List<HydratedEntityRegistration> hydratedEntityRegistrations,

View File

@ -315,10 +315,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
return hydratedEntityRegistrationList; return hydratedEntityRegistrationList;
} }
/** public void wrapUp() {
* Package-protected
*/
void wrapUp() {
createSubselects(); createSubselects();
if ( hydratedEntityRegistrationList != null ) { if ( hydratedEntityRegistrationList != null ) {

View File

@ -80,11 +80,57 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
ResultTransformer forcedResultTransformer, ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActionList) throws SQLException { List<AfterLoadAction> afterLoadActionList) throws SQLException {
handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session ); handlePotentiallyEmptyCollectionRootReturns( queryParameters.getCollectionKeys(), resultSet, session );
final ResultSetProcessingContextImpl context = createResultSetProcessingContext(
resultSet,
session,
queryParameters,
namedParameterContext,
returnProxies,
readOnly
);
final List loadResults = extractRows( resultSet, queryParameters, context );
rowReader.finishUp( context, afterLoadActionList );
context.wrapUp();
session.getPersistenceContextInternal().initializeNonLazyCollections();
return loadResults;
}
protected ResultSetProcessingContextImpl createResultSetProcessingContext(
ResultSet resultSet,
final SharedSessionContractImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
boolean returnProxies,
boolean readOnly) {
return new ResultSetProcessingContextImpl(
resultSet,
session,
loadPlan,
aliasResolutionContext,
readOnly,
shouldUseOptionalEntityInstance,
returnProxies,
queryParameters,
namedParameterContext,
hadSubselectFetches
);
}
protected List<Object> extractRows(
ResultSet resultSet,
QueryParameters queryParameters,
final ResultSetProcessingContextImpl context) throws SQLException {
final boolean traceEnabled = LOG.isTraceEnabled(); final boolean traceEnabled = LOG.isTraceEnabled();
final int maxRows; final int maxRows;
final List loadResults; final List<Object> loadResults;
final RowSelection selection = queryParameters.getRowSelection(); final RowSelection selection = queryParameters.getRowSelection();
if ( LimitHelper.hasMaxRows( selection ) ) { if ( LimitHelper.hasMaxRows( selection ) ) {
maxRows = selection.getMaxRows(); maxRows = selection.getMaxRows();
@ -99,19 +145,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
maxRows = Integer.MAX_VALUE; maxRows = Integer.MAX_VALUE;
} }
final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl(
resultSet,
session,
loadPlan,
aliasResolutionContext,
readOnly,
shouldUseOptionalEntityInstance,
returnProxies,
queryParameters,
namedParameterContext,
hadSubselectFetches
);
if ( traceEnabled ) { if ( traceEnabled ) {
LOG.trace( "Processing result set" ); LOG.trace( "Processing result set" );
} }
@ -134,17 +167,10 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
LOG.tracev( "Done processing result set ({0} rows)", count ); LOG.tracev( "Done processing result set ({0} rows)", count );
} }
rowReader.finishUp( context, afterLoadActionList );
context.wrapUp();
session.getPersistenceContextInternal().initializeNonLazyCollections();
return loadResults; return loadResults;
} }
protected void handlePotentiallyEmptyCollectionRootReturns(
private void handlePotentiallyEmptyCollectionRootReturns(
LoadPlan loadPlan,
Serializable[] collectionKeys, Serializable[] collectionKeys,
ResultSet resultSet, ResultSet resultSet,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.loader.plan.exec.process.spi;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.LoadPlan;
public interface ResultSetProcessorResolver {
ResultSetProcessor resolveResultSetProcessor(
LoadPlan loadPlan,
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
boolean shouldUseOptionalEntityInstance,
boolean hadSubselectFetches
);
ResultSetProcessorResolver DEFAULT =
(loadPlan, aliasResolutionContext, readerCollector, shouldUseOptionalEntityInstance, hadSubselectFetches) ->
new ResultSetProcessorImpl(
loadPlan,
aliasResolutionContext,
readerCollector.buildRowReader(),
shouldUseOptionalEntityInstance,
hadSubselectFetches
);
}

View File

@ -471,6 +471,18 @@ public abstract class EntityType extends AbstractType implements AssociationType
return null; return null;
} }
/**
* Would an entity be eagerly loaded given the value provided for {@code overridingEager}?
*
* @param overridingEager can override eager from the mapping.
*
* @return If {@code overridingEager} is null, then it does not override.
* If true or false then it overrides the mapping value.
*/
public boolean isEager(Boolean overridingEager) {
return overridingEager != null ? overridingEager : this.eager;
}
@Override @Override
public Type getSemiResolvedType(SessionFactoryImplementor factory) { public Type getSemiResolvedType(SessionFactoryImplementor factory) {
return getAssociatedEntityPersister( factory ).getIdentifierType(); return getAssociatedEntityPersister( factory ).getIdentifierType();
@ -682,12 +694,10 @@ public abstract class EntityType extends AbstractType implements AssociationType
getAssociatedEntityPersister( session.getFactory() ) getAssociatedEntityPersister( session.getFactory() )
.isInstrumented(); .isInstrumented();
boolean eager = overridingEager != null ? overridingEager : this.eager;
Object proxyOrEntity = session.internalLoad( Object proxyOrEntity = session.internalLoad(
getAssociatedEntityName(), getAssociatedEntityName(),
id, id,
eager, isEager( overridingEager ),
isNullable() isNullable()
); );