HHH-14075 Changes to loaders and TwoPhaseLoad to allow "internal" loading to be reused by hibernate-reactive
This commit is contained in:
parent
18407c954b
commit
99c56527c8
|
@ -35,13 +35,13 @@ import org.hibernate.event.spi.PreLoadEventListener;
|
|||
import org.hibernate.graph.spi.AttributeNodeImplementor;
|
||||
import org.hibernate.graph.spi.GraphImplementor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.FastSessionServices;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.stat.internal.StatsHelper;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.TypeHelper;
|
||||
|
||||
|
@ -121,18 +121,13 @@ public final class TwoPhaseLoad {
|
|||
final boolean readOnly,
|
||||
final SharedSessionContractImplementor session,
|
||||
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
|
||||
.getFactory()
|
||||
.getServiceRegistry()
|
||||
.getService( EventListenerRegistry.class )
|
||||
.getEventListenerGroup( EventType.PRE_LOAD );
|
||||
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 PreLoadEvent preLoadEvent,
|
||||
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 EntityEntry entityEntry = persistenceContext.getEntry( entity );
|
||||
if ( entityEntry == null ) {
|
||||
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 EntityEntry entityEntry,
|
||||
final boolean readOnly,
|
||||
final SharedSessionContractImplementor session,
|
||||
final PreLoadEvent preLoadEvent,
|
||||
final Iterable<PreLoadEventListener> preLoadEventListeners) throws HibernateException {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final EntityResolver entityResolver) throws HibernateException {
|
||||
final EntityPersister persister = entityEntry.getPersister();
|
||||
final Serializable id = entityEntry.getId();
|
||||
final Object[] hydratedState = entityEntry.getLoadedState();
|
||||
|
@ -229,18 +248,36 @@ public final class TwoPhaseLoad {
|
|||
|
||||
// we know value != LazyPropertyInitializer.UNFETCHED_PROPERTY
|
||||
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 {
|
||||
if ( debugEnabled ) {
|
||||
LOG.debugf( "Skipping <unknown> attribute : `%s`", propertyNames[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( session.getFetchGraphLoadContext() != 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!
|
||||
if ( session.isEventSource() ) {
|
||||
|
@ -600,4 +637,23 @@ public final class TwoPhaseLoad {
|
|||
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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -986,12 +986,48 @@ public abstract class Loader {
|
|||
int maxRows,
|
||||
List<AfterLoadAction> afterLoadActions) throws SQLException {
|
||||
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 LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
|
||||
final boolean createSubselects = isSubselectLoadingEnabled();
|
||||
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
|
||||
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
|
||||
final List results = new ArrayList();
|
||||
final List<Object> results = new ArrayList<>();
|
||||
|
||||
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
|
||||
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 );
|
||||
|
||||
initializeEntitiesAndCollections(
|
||||
hydratedObjects,
|
||||
rs,
|
||||
session,
|
||||
queryParameters.isReadOnly( session ),
|
||||
afterLoadActions
|
||||
);
|
||||
if ( createSubselects ) {
|
||||
createSubselects( subselectResultKeys, queryParameters, session );
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1087,7 @@ public abstract class Loader {
|
|||
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
|
||||
|
||||
Set[] keySets = transpose( keys );
|
||||
|
|
|
@ -62,7 +62,7 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||
return sql;
|
||||
}
|
||||
|
||||
protected final Loadable[] getEntityPersisters() {
|
||||
public final Loadable[] getEntityPersisters() {
|
||||
return persisters;
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ public abstract class OuterJoinLoader extends BasicLoader {
|
|||
return aliases;
|
||||
}
|
||||
|
||||
protected final CollectionPersister[] getCollectionPersisters() {
|
||||
public final CollectionPersister[] getCollectionPersisters() {
|
||||
return collectionPersisters;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.BatchingLoadQueryDetailsFactory;
|
||||
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.spi.LoadQueryDetails;
|
||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||
|
@ -58,7 +59,8 @@ public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlan
|
|||
SessionFactoryImplementor factory,
|
||||
String[] uniqueKeyColumnNames,
|
||||
Type uniqueKeyType,
|
||||
QueryBuildingParameters buildingParameters) {
|
||||
QueryBuildingParameters buildingParameters,
|
||||
ResultSetProcessorResolver resultSetProcessorResolver) {
|
||||
super( factory );
|
||||
this.entityPersister = entityPersister;
|
||||
this.uniqueKeyType = uniqueKeyType;
|
||||
|
@ -96,7 +98,42 @@ public abstract class AbstractLoadPlanBasedEntityLoader extends AbstractLoadPlan
|
|||
plan,
|
||||
uniqueKeyColumnNames,
|
||||
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,
|
||||
Type uniqueKeyType,
|
||||
QueryBuildingParameters buildingParameters) {
|
||||
super( factory );
|
||||
this.entityPersister = entityPersister;
|
||||
this.uniqueKeyType = uniqueKeyType;
|
||||
this.entityName = entityPersister.getEntityName();
|
||||
|
||||
this.staticLoadQuery = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
|
||||
this(
|
||||
entityPersister,
|
||||
factory,
|
||||
entityLoaderQueryDetailsTemplate,
|
||||
buildingParameters
|
||||
uniqueKeyType,
|
||||
buildingParameters,
|
||||
ResultSetProcessorResolver.DEFAULT
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.ReaderCollector;
|
||||
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.spi.QueryBuildingParameters;
|
||||
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||
|
@ -104,6 +105,10 @@ public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
|
|||
*
|
||||
*/
|
||||
protected void generate() {
|
||||
generate( ResultSetProcessorResolver.DEFAULT );
|
||||
}
|
||||
|
||||
protected void generate(ResultSetProcessorResolver resultSetProcessorResolver) {
|
||||
// There are 2 high-level requirements to perform here:
|
||||
// 1) Determine the SQL required to carry out the given LoadPlan (and fulfill
|
||||
// {@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() );
|
||||
|
||||
this.sqlStatement = select.toStatementString();
|
||||
this.resultSetProcessor = new ResultSetProcessorImpl(
|
||||
this.resultSetProcessor = resultSetProcessorResolver.resolveResultSetProcessor(
|
||||
loadPlan,
|
||||
queryProcessor.getAliasResolutionContext(),
|
||||
getReaderCollector().buildRowReader(),
|
||||
getReaderCollector(),
|
||||
shouldUseOptionalEntityInstance(),
|
||||
isSubselectLoadingEnabled( fetchStats )
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.loader.plan.exec.internal;
|
||||
|
||||
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.spi.LoadQueryDetails;
|
||||
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
|
||||
* that add additional joins here)
|
||||
* @param factory The SessionFactory
|
||||
* @oaram resultSetProcessorResolver The ResultSet processor resolver.
|
||||
*
|
||||
* @return The EntityLoadQueryDetails
|
||||
*/
|
||||
|
@ -45,7 +47,8 @@ public class BatchingLoadQueryDetailsFactory {
|
|||
LoadPlan loadPlan,
|
||||
String[] keyColumnNames,
|
||||
QueryBuildingParameters buildingParameters,
|
||||
SessionFactoryImplementor factory) {
|
||||
SessionFactoryImplementor factory,
|
||||
ResultSetProcessorResolver resultSetProcessorResolver) {
|
||||
|
||||
// TODO: how should shouldUseOptionalEntityInformation be used?
|
||||
// final int batchSize = buildingParameters.getBatchSize();
|
||||
|
@ -64,7 +67,54 @@ public class BatchingLoadQueryDetailsFactory {
|
|||
aliasResolutionContext,
|
||||
rootReturn,
|
||||
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(
|
||||
EntityLoadQueryDetails entityLoadQueryDetailsTemplate,
|
||||
QueryBuildingParameters buildingParameters) {
|
||||
return new EntityLoadQueryDetails(
|
||||
return makeEntityLoadQueryDetails(
|
||||
entityLoadQueryDetailsTemplate,
|
||||
buildingParameters
|
||||
buildingParameters,
|
||||
ResultSetProcessorResolver.DEFAULT
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.ReaderCollector;
|
||||
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.query.internal.SelectStatementBuilder;
|
||||
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
|
||||
* that add additional joins here)
|
||||
* @param factory The SessionFactory
|
||||
* @param resultSetProcessorResolver The ResultSet resolver.
|
||||
*/
|
||||
protected EntityLoadQueryDetails(
|
||||
LoadPlan loadPlan,
|
||||
|
@ -65,7 +67,9 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
|
|||
AliasResolutionContextImpl aliasResolutionContext,
|
||||
EntityReturn rootReturn,
|
||||
QueryBuildingParameters buildingParameters,
|
||||
SessionFactoryImplementor factory) {
|
||||
SessionFactoryImplementor factory,
|
||||
ResultSetProcessorResolver resultSetProcessorResolver) {
|
||||
|
||||
super(
|
||||
loadPlan,
|
||||
aliasResolutionContext,
|
||||
|
@ -82,21 +86,55 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
|
|||
new EntityReturnReader( rootReturn ),
|
||||
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(
|
||||
EntityLoadQueryDetails initialEntityLoadQueryDetails,
|
||||
QueryBuildingParameters buildingParameters) {
|
||||
QueryBuildingParameters buildingParameters,
|
||||
ResultSetProcessorResolver resultSetProcessorResolver) {
|
||||
this(
|
||||
initialEntityLoadQueryDetails.getLoadPlan(),
|
||||
initialEntityLoadQueryDetails.getKeyColumnNames(),
|
||||
new AliasResolutionContextImpl( initialEntityLoadQueryDetails.getSessionFactory() ),
|
||||
(EntityReturn) initialEntityLoadQueryDetails.getRootReturn(),
|
||||
buildingParameters,
|
||||
initialEntityLoadQueryDetails.getSessionFactory()
|
||||
initialEntityLoadQueryDetails.getSessionFactory(),
|
||||
resultSetProcessorResolver
|
||||
);
|
||||
}
|
||||
protected EntityLoadQueryDetails(
|
||||
EntityLoadQueryDetails initialEntityLoadQueryDetails,
|
||||
QueryBuildingParameters buildingParameters) {
|
||||
this( initialEntityLoadQueryDetails, buildingParameters, ResultSetProcessorResolver.DEFAULT );
|
||||
}
|
||||
|
||||
public boolean hasCollectionInitializers() {
|
||||
return CollectionHelper.isNotEmpty( readerCollector.getArrayReferenceInitializers() ) ||
|
||||
|
|
|
@ -224,7 +224,7 @@ public abstract class AbstractRowReader implements RowReader {
|
|||
postLoad( postLoadEvent, context, hydratedEntityRegistrations, afterLoadActionList );
|
||||
}
|
||||
|
||||
private void finishLoadingArrays(ResultSetProcessingContextImpl context) {
|
||||
protected void finishLoadingArrays(ResultSetProcessingContextImpl context) {
|
||||
for ( CollectionReferenceInitializer arrayReferenceInitializer : arrayReferenceInitializers ) {
|
||||
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 ) {
|
||||
collectionReferenceInitializer.endLoading( context );
|
||||
}
|
||||
}
|
||||
|
||||
private void afterInitialize(ResultSetProcessingContextImpl context,
|
||||
protected void afterInitialize(ResultSetProcessingContextImpl context,
|
||||
List<HydratedEntityRegistration> hydratedEntityRegistrations) {
|
||||
if ( hydratedEntityRegistrations == null ) {
|
||||
return;
|
||||
|
@ -279,7 +279,7 @@ public abstract class AbstractRowReader implements RowReader {
|
|||
}
|
||||
}
|
||||
|
||||
private void postLoad(
|
||||
protected void postLoad(
|
||||
PostLoadEvent postLoadEvent,
|
||||
ResultSetProcessingContextImpl context,
|
||||
List<HydratedEntityRegistration> hydratedEntityRegistrations,
|
||||
|
|
|
@ -315,10 +315,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
return hydratedEntityRegistrationList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-protected
|
||||
*/
|
||||
void wrapUp() {
|
||||
public void wrapUp() {
|
||||
createSubselects();
|
||||
|
||||
if ( hydratedEntityRegistrationList != null ) {
|
||||
|
|
|
@ -80,11 +80,57 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
|
|||
ResultTransformer forcedResultTransformer,
|
||||
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 int maxRows;
|
||||
final List loadResults;
|
||||
final List<Object> loadResults;
|
||||
|
||||
final RowSelection selection = queryParameters.getRowSelection();
|
||||
if ( LimitHelper.hasMaxRows( selection ) ) {
|
||||
maxRows = selection.getMaxRows();
|
||||
|
@ -99,19 +145,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
|
|||
maxRows = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl(
|
||||
resultSet,
|
||||
session,
|
||||
loadPlan,
|
||||
aliasResolutionContext,
|
||||
readOnly,
|
||||
shouldUseOptionalEntityInstance,
|
||||
returnProxies,
|
||||
queryParameters,
|
||||
namedParameterContext,
|
||||
hadSubselectFetches
|
||||
);
|
||||
|
||||
if ( traceEnabled ) {
|
||||
LOG.trace( "Processing result set" );
|
||||
}
|
||||
|
@ -134,17 +167,10 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
|
|||
LOG.tracev( "Done processing result set ({0} rows)", count );
|
||||
}
|
||||
|
||||
rowReader.finishUp( context, afterLoadActionList );
|
||||
context.wrapUp();
|
||||
|
||||
session.getPersistenceContextInternal().initializeNonLazyCollections();
|
||||
|
||||
return loadResults;
|
||||
}
|
||||
|
||||
|
||||
private void handlePotentiallyEmptyCollectionRootReturns(
|
||||
LoadPlan loadPlan,
|
||||
protected void handlePotentiallyEmptyCollectionRootReturns(
|
||||
Serializable[] collectionKeys,
|
||||
ResultSet resultSet,
|
||||
SharedSessionContractImplementor session) {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
|
@ -471,6 +471,18 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
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
|
||||
public Type getSemiResolvedType(SessionFactoryImplementor factory) {
|
||||
return getAssociatedEntityPersister( factory ).getIdentifierType();
|
||||
|
@ -682,12 +694,10 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
getAssociatedEntityPersister( session.getFactory() )
|
||||
.isInstrumented();
|
||||
|
||||
boolean eager = overridingEager != null ? overridingEager : this.eager;
|
||||
|
||||
Object proxyOrEntity = session.internalLoad(
|
||||
getAssociatedEntityName(),
|
||||
id,
|
||||
eager,
|
||||
isEager( overridingEager ),
|
||||
isNullable()
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue