From 6c5f5772234f58f70f1fa93b3c6ca167760a0c24 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 25 Oct 2024 23:01:37 +0200 Subject: [PATCH] partial refactoring of AbstractMultiIdEntityLoader and children Signed-off-by: Gavin King --- .../internal/AbstractMultiIdEntityLoader.java | 126 +++++++- .../MultiIdEntityLoaderArrayParam.java | 161 ++-------- .../internal/MultiIdEntityLoaderStandard.java | 291 ++++++------------ 3 files changed, 241 insertions(+), 337 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java index 38501fcae1..06bbafbeea 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java @@ -4,15 +4,25 @@ */ package org.hibernate.loader.ast.internal; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.LoadEvent; +import org.hibernate.event.spi.LoadEventListener; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.type.descriptor.java.JavaType; +import java.util.ArrayList; import java.util.List; +import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic; + /** * Base support for {@link MultiIdEntityLoader} implementations. * @@ -57,7 +67,121 @@ public abstract class AbstractMultiIdEntityLoader implements MultiIdEntityLoa } } - protected abstract List performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session); + protected List performOrderedMultiLoad( + Object[] ids, + MultiIdLoadOptions loadOptions, + EventSource session) { + if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) { + MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( "#performOrderedMultiLoad(`%s`, ..)", + getLoadable().getEntityName() ); + } + + assert loadOptions.isOrderReturnEnabled(); + + final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final JavaType idType = getLoadable().getIdentifierMapping().getJavaType(); + + final LockOptions lockOptions = loadOptions.getLockOptions() == null + ? new LockOptions( LockMode.NONE ) + : loadOptions.getLockOptions(); + + final int maxBatchSize = maxBatchSize( ids, loadOptions ); + + final List result = CollectionHelper.arrayList( ids.length ); + + final List idsInBatch = new ArrayList<>(); + final List elementPositionsLoadedByBatch = new ArrayList<>(); + + for ( int i = 0; i < ids.length; i++ ) { + final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i]; + final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); + + if ( !loadFromCaches( loadOptions, session, id, lockOptions, entityKey, result, i ) ) { + // if we did not hit any of the continues above, + // then we need to batch load the entity state. + idsInBatch.add( id ); + + if ( idsInBatch.size() >= maxBatchSize ) { + // we've hit the allotted max-batch-size, perform an "intermediate load" + loadEntitiesById( idsInBatch, lockOptions, loadOptions, session ); + idsInBatch.clear(); + } + + // Save the EntityKey instance for use later + result.add( i, entityKey ); + elementPositionsLoadedByBatch.add( i ); + } + } + + if ( !idsInBatch.isEmpty() ) { + // we still have ids to load from the processing above since + // the last max-batch-size trigger, perform a load for them + loadEntitiesById( idsInBatch, lockOptions, loadOptions, session ); + } + + // for each result where we set the EntityKey earlier, replace them + handleResults( loadOptions, session, elementPositionsLoadedByBatch, result ); + + //noinspection unchecked + return (List) result; + } + + protected abstract int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions); + + protected abstract void handleResults(MultiIdLoadOptions loadOptions, EventSource session, List elementPositionsLoadedByBatch, List result); + + protected abstract void loadEntitiesById(List idsInBatch, LockOptions lockOptions, MultiIdLoadOptions loadOptions, EventSource session); + + protected boolean loadFromCaches( + MultiIdLoadOptions loadOptions, + EventSource session, + Object id, + LockOptions lockOptions, + EntityKey entityKey, + List result, + int i) { + if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) { + final LoadEvent loadEvent = new LoadEvent( + id, + getLoadable().getJavaType().getJavaTypeClass().getName(), + lockOptions, + session, + LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) + ); + + Object managedEntity = null; + + if ( loadOptions.isSessionCheckingEnabled() ) { + // look for it in the Session first + final CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry = + loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET ); + managedEntity = persistenceContextEntry.getEntity(); + + if ( managedEntity != null + && !loadOptions.isReturnOfDeletedEntitiesEnabled() + && !persistenceContextEntry.isManaged() ) { + // put a null in the result + result.add( i, null ); + return true; + } + } + + if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { + // look for it in the SessionFactory + managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( + loadEvent, + getLoadable().getEntityPersister(), + entityKey + ); + } + + if ( managedEntity != null ) { + result.add( i, managedEntity ); + return true; + } + } + return false; + } protected abstract List performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java index f5f20e5cbf..e5c10d1ff4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java @@ -73,116 +73,7 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade } @Override - protected List performOrderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) { - if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) { - MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef( - "MultiIdEntityLoaderArrayParam#performOrderedMultiLoad - %s", - getLoadable().getEntityName() - ); - } - - assert loadOptions.isOrderReturnEnabled(); - - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); - final JavaType idType = getLoadable().getIdentifierMapping().getJavaType(); - - final LockOptions lockOptions = loadOptions.getLockOptions() == null - ? new LockOptions( LockMode.NONE ) - : loadOptions.getLockOptions(); - - final int maxBatchSize = maxBatchSize( ids, loadOptions ); - - final List result = CollectionHelper.arrayList( ids.length ); - - final List idsInBatch = new ArrayList<>(); - final List elementPositionsLoadedByBatch = new ArrayList<>(); - - for ( int i = 0; i < ids.length; i++ ) { - final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i]; - final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); - - if ( !loadFromCaches( loadOptions, session, id, lockOptions, entityKey, result, i ) ) { - // if we did not hit any of the continues above, - // then we need to batch load the entity state. - idsInBatch.add( id ); - - if ( idsInBatch.size() >= maxBatchSize ) { - // we've hit the allotted max-batch-size, perform an "intermediate load" - loadEntitiesById( loadOptions, session, lockOptions, idsInBatch ); - idsInBatch.clear(); - } - - // Save the EntityKey instance for use later - result.add( i, entityKey ); - elementPositionsLoadedByBatch.add( i ); - } - } - - if ( !idsInBatch.isEmpty() ) { - // we still have ids to load from the processing above since - // the last max-batch-size trigger, perform a load for them - loadEntitiesById( loadOptions, session, lockOptions, idsInBatch ); - } - - // for each result where we set the EntityKey earlier, replace them - handleResults( loadOptions, session, elementPositionsLoadedByBatch, result ); - - //noinspection unchecked - return (List) result; - } - - private boolean loadFromCaches( - MultiIdLoadOptions loadOptions, - EventSource session, - Object id, - LockOptions lockOptions, - EntityKey entityKey, - List result, - int i) { - if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) { - final LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) - ); - - Object managedEntity = null; - - if ( loadOptions.isSessionCheckingEnabled() ) { - // look for it in the Session first - final PersistenceContextEntry persistenceContextEntry = - loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET ); - managedEntity = persistenceContextEntry.getEntity(); - - if ( managedEntity != null - && !loadOptions.isReturnOfDeletedEntitiesEnabled() - && !persistenceContextEntry.isManaged() ) { - // put a null in the result - result.add( i, null ); - return true; - } - } - - if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { - // look for it in the SessionFactory - managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey - ); - } - - if ( managedEntity != null ) { - result.add( i, managedEntity ); - return true; - } - } - return false; - } - - private static void handleResults( + protected void handleResults( MultiIdLoadOptions loadOptions, EventSource session, List elementPositionsLoadedByBatch, @@ -209,7 +100,8 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade } } - private int maxBatchSize(K[] ids, MultiIdLoadOptions loadOptions) { + @Override + protected int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions) { if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) { return loadOptions.getBatchSize(); } @@ -225,8 +117,12 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade } } - private void loadEntitiesById( - MultiIdLoadOptions loadOptions, EventSource session, LockOptions lockOptions, List idsToLoadFromDatabase) { + @Override + protected void loadEntitiesById( + List idsToLoadFromDatabase, + LockOptions lockOptions, + MultiIdLoadOptions loadOptions, + EventSource session) { final SelectStatement sqlAst = LoaderSelectBuilder.createSelectBySingleArrayParameter( getLoadable(), getIdentifierMapping(), @@ -361,22 +257,16 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade return ids; } - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); - boolean foundAnyResolvedEntities = false; List nonResolvedIds = null; - for ( int i = 0; i < ids.length; i++ ) { - final Object id; - if ( coerce ) { - //noinspection unchecked - id = (K) getLoadable().getIdentifierMapping().getJavaType().coerce( ids[i], session ); - } - else { - id = ids[i]; - } + final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final JavaType idType = getLoadable().getIdentifierMapping().getJavaType(); + for ( int i = 0; i < ids.length; i++ ) { + final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i]; final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); + final LoadEvent loadEvent = new LoadEvent( id, getLoadable().getJavaType().getJavaTypeClass().getName(), @@ -385,18 +275,15 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) ); - Object resolvedEntity = null; + Object managedEntity = null; // look for it in the Session first - final PersistenceContextEntry persistenceContextEntry = loadFromSessionCacheStatic( - loadEvent, - entityKey, - LoadEventListener.GET - ); + final PersistenceContextEntry persistenceContextEntry = + loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET ); if ( loadOptions.isSessionCheckingEnabled() ) { - resolvedEntity = persistenceContextEntry.getEntity(); + managedEntity = persistenceContextEntry.getEntity(); - if ( resolvedEntity != null + if ( managedEntity != null && !loadOptions.isReturnOfDeletedEntitiesEnabled() && !persistenceContextEntry.isManaged() ) { foundAnyResolvedEntities = true; @@ -405,25 +292,25 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade } } - if ( resolvedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { - resolvedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( + if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { + managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( loadEvent, getLoadable().getEntityPersister(), entityKey ); } - if ( resolvedEntity != null ) { + if ( managedEntity != null ) { foundAnyResolvedEntities = true; //noinspection unchecked - resolutionConsumer.consume( i, entityKey, (R) resolvedEntity); + resolutionConsumer.consume( i, entityKey, (R) managedEntity); } else { if ( nonResolvedIds == null ) { nonResolvedIds = new ArrayList<>(); } - //noinspection unchecked,CastCanBeRemovedNarrowingVariableType + //noinspection unchecked nonResolvedIds.add( (K) id ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java index e817bb6a1f..5533a19fc9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java @@ -6,7 +6,6 @@ package org.hibernate.loader.ast.internal; import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -39,6 +38,7 @@ import org.hibernate.type.descriptor.java.JavaType; import org.jboss.logging.Logger; import static java.lang.Boolean.TRUE; +import static java.util.Arrays.asList; import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic; /** @@ -62,116 +62,7 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< } @Override - protected List performOrderedMultiLoad( - Object[] ids, - MultiIdLoadOptions loadOptions, - EventSource session) { - if ( log.isTraceEnabled() ) { - log.tracef( "#performOrderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() ); - } - - assert loadOptions.isOrderReturnEnabled(); - - final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); - final JavaType idType = getLoadable().getIdentifierMapping().getJavaType(); - - final LockOptions lockOptions = loadOptions.getLockOptions() == null - ? new LockOptions( LockMode.NONE ) - : loadOptions.getLockOptions(); - - final int maxBatchSize = maxBatchSize( ids, loadOptions ); - - final List result = CollectionHelper.arrayList( ids.length ); - - final List idsInBatch = new ArrayList<>(); - final List elementPositionsLoadedByBatch = new ArrayList<>(); - - for ( int i = 0; i < ids.length; i++ ) { - final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i]; - final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); - - if ( !loadFromCaches( loadOptions, session, id, lockOptions, entityKey, result, i ) ) { - // if we did not hit any of the continues above, - // then we need to batch load the entity state. - idsInBatch.add( id ); - - if ( idsInBatch.size() >= maxBatchSize ) { - // we've hit the allotted max-batch-size, perform an "intermediate load" - loadEntitiesById( idsInBatch, lockOptions, loadOptions, session ); - idsInBatch.clear(); - } - - // Save the EntityKey instance for use later - result.add( i, entityKey ); - elementPositionsLoadedByBatch.add( i ); - } - } - - if ( !idsInBatch.isEmpty() ) { - // we still have ids to load from the processing above since - // the last max-batch-size trigger, perform a load for them - loadEntitiesById( idsInBatch, lockOptions, loadOptions, session ); - } - - // for each result where we set the EntityKey earlier, replace them - handleResults( loadOptions, session, elementPositionsLoadedByBatch, result ); - - //noinspection unchecked - return (List) result; - } - - private boolean loadFromCaches( - MultiIdLoadOptions loadOptions, - EventSource session, - Object id, - LockOptions lockOptions, - EntityKey entityKey, - List result, - int i) { - if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) { - final LoadEvent loadEvent = new LoadEvent( - id, - getLoadable().getJavaType().getJavaTypeClass().getName(), - lockOptions, - session, - LoaderHelper.getReadOnlyFromLoadQueryInfluencers( session ) - ); - - Object managedEntity = null; - - if ( loadOptions.isSessionCheckingEnabled() ) { - // look for it in the Session first - final PersistenceContextEntry persistenceContextEntry = - loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET ); - managedEntity = persistenceContextEntry.getEntity(); - - if ( managedEntity != null - && !loadOptions.isReturnOfDeletedEntitiesEnabled() - && !persistenceContextEntry.isManaged() ) { - // put a null in the result - result.add( i, null ); - return true; - } - } - - if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) { - // look for it in the SessionFactory - managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( - loadEvent, - getLoadable().getEntityPersister(), - entityKey - ); - } - - if ( managedEntity != null ) { - result.add( i, managedEntity ); - return true; - } - } - return false; - } - - private static void handleResults( + protected void handleResults( MultiIdLoadOptions loadOptions, EventSource session, List elementPositionsLoadedByBatch, @@ -197,7 +88,8 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< } } - private int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions) { + @Override + protected int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions) { if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) { return loadOptions.getBatchSize(); } @@ -211,86 +103,97 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< } } - private List loadEntitiesById( + @Override + protected void loadEntitiesById( List idsInBatch, LockOptions lockOptions, MultiIdLoadOptions loadOptions, EventSource session) { assert idsInBatch != null; - assert ! idsInBatch.isEmpty(); + assert !idsInBatch.isEmpty(); + listEntitiesById( idsInBatch, lockOptions, loadOptions, session ); + } + + private List listEntitiesById( + List idsInBatch, + LockOptions lockOptions, + MultiIdLoadOptions loadOptions, + EventSource session) { final int numberOfIdsInBatch = idsInBatch.size(); if ( numberOfIdsInBatch == 1 ) { return performSingleMultiLoad( idsInBatch.get( 0 ), lockOptions, session ); } - - if ( log.isTraceEnabled() ) { - log.tracef( "#loadEntitiesById(`%s`, `%s`, ..)", getLoadable().getEntityName(), numberOfIdsInBatch ); - } - - JdbcParametersList.Builder jdbcParametersBuilder = JdbcParametersList.newBuilder( numberOfIdsInBatch * idJdbcTypeCount ); - - final SelectStatement sqlAst = LoaderSelectBuilder.createSelect( - getLoadable(), - // null here means to select everything - null, - getLoadable().getIdentifierMapping(), - null, - numberOfIdsInBatch, - session.getLoadQueryInfluencers(), - lockOptions, - jdbcParametersBuilder::add, - getSessionFactory() - ); - JdbcParametersList jdbcParameters = jdbcParametersBuilder.build(); - - final SqlAstTranslatorFactory sqlAstTranslatorFactory = - getSessionFactory().getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory(); - - final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() ); - int offset = 0; - - for ( int i = 0; i < numberOfIdsInBatch; i++ ) { - final Object id = idsInBatch.get( i ); - - offset += jdbcParameterBindings.registerParametersForEachJdbcValue( - id, - offset, - getLoadable().getIdentifierMapping(), - jdbcParameters, - session - ); - } - - // we should have used all the JdbcParameter references (created bindings for all) - assert offset == jdbcParameters.size(); - final JdbcOperationQuerySelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( getSessionFactory(), sqlAst ) - .translate( jdbcParameterBindings, QueryOptions.NONE ); - - final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler; - if ( session.getLoadQueryInfluencers().hasSubselectLoadableCollections( getLoadable().getEntityPersister() ) ) { - subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( - session.getPersistenceContext().getBatchFetchQueue(), - sqlAst, - jdbcParameters, - jdbcParameterBindings - ); - } else { - subSelectFetchableKeysHandler = null; - } + if ( log.isTraceEnabled() ) { + log.tracef( "#loadEntitiesById(`%s`, `%s`, ..)", getLoadable().getEntityName(), numberOfIdsInBatch ); + } - return session.getJdbcServices().getJdbcSelectExecutor().list( - jdbcSelect, - jdbcParameterBindings, - new ExecutionContextWithSubselectFetchHandler( session, - subSelectFetchableKeysHandler, - TRUE.equals( loadOptions.getReadOnly(session) ) ), - RowTransformerStandardImpl.instance(), - null, - ListResultsConsumer.UniqueSemantic.FILTER, - idsInBatch.size() - ); + final JdbcParametersList.Builder jdbcParametersBuilder = + JdbcParametersList.newBuilder( numberOfIdsInBatch * idJdbcTypeCount ); + + final SelectStatement sqlAst = LoaderSelectBuilder.createSelect( + getLoadable(), + // null here means to select everything + null, + getLoadable().getIdentifierMapping(), + null, + numberOfIdsInBatch, + session.getLoadQueryInfluencers(), + lockOptions, + jdbcParametersBuilder::add, + getSessionFactory() + ); + JdbcParametersList jdbcParameters = jdbcParametersBuilder.build(); + + final SqlAstTranslatorFactory sqlAstTranslatorFactory = + getSessionFactory().getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory(); + + final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() ); + int offset = 0; + + for ( int i = 0; i < numberOfIdsInBatch; i++ ) { + final Object id = idsInBatch.get( i ); + + offset += jdbcParameterBindings.registerParametersForEachJdbcValue( + id, + offset, + getLoadable().getIdentifierMapping(), + jdbcParameters, + session + ); + } + + // we should have used all the JdbcParameter references (created bindings for all) + assert offset == jdbcParameters.size(); + final JdbcOperationQuerySelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( getSessionFactory(), sqlAst ) + .translate( jdbcParameterBindings, QueryOptions.NONE ); + + final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler; + if ( session.getLoadQueryInfluencers().hasSubselectLoadableCollections( getLoadable().getEntityPersister() ) ) { + subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext().getBatchFetchQueue(), + sqlAst, + jdbcParameters, + jdbcParameterBindings + ); + } + else { + subSelectFetchableKeysHandler = null; + } + + return session.getJdbcServices().getJdbcSelectExecutor().list( + jdbcSelect, + jdbcParameterBindings, + new ExecutionContextWithSubselectFetchHandler( session, + subSelectFetchableKeysHandler, + TRUE.equals( loadOptions.getReadOnly( session ) ) ), + RowTransformerStandardImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + idsInBatch.size() + ); + } } private List performSingleMultiLoad(Object id, LockOptions lockOptions, SharedSessionContractImplementor session) { @@ -313,7 +216,7 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< final List result = CollectionHelper.arrayList( ids.length ); - final LockOptions lockOptions = (loadOptions.getLockOptions() == null) + final LockOptions lockOptions = loadOptions.getLockOptions() == null ? new LockOptions( LockMode.NONE ) : loadOptions.getLockOptions(); @@ -327,17 +230,13 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< final List nonManagedIds = new ArrayList<>(); final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); + final JavaType idType = getLoadable().getIdentifierMapping().getJavaType(); + for ( int i = 0; i < ids.length; i++ ) { - final Object id; - if ( coerce ) { - id = getLoadable().getIdentifierMapping().getJavaType().coerce( ids[i], session ); - } - else { - id = ids[i]; - } + final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i]; final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); - LoadEvent loadEvent = new LoadEvent( + final LoadEvent loadEvent = new LoadEvent( id, getLoadable().getJavaType().getJavaTypeClass().getName(), lockOptions, @@ -348,12 +247,8 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< Object managedEntity = null; // look for it in the Session first - PersistenceContextEntry persistenceContextEntry = CacheEntityLoaderHelper.INSTANCE - .loadFromSessionCache( - loadEvent, - entityKey, - LoadEventListener.GET - ); + final PersistenceContextEntry persistenceContextEntry = + loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET ); if ( loadOptions.isSessionCheckingEnabled() ) { managedEntity = persistenceContextEntry.getEntity(); @@ -386,7 +281,7 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< if ( foundAnyManagedEntities ) { if ( nonManagedIds.isEmpty() ) { - // all of the given ids were already associated with the Session + // all the given ids were already associated with the Session return result; } else { @@ -422,9 +317,7 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< final Object[] idsInBatch = new Object[ batchSize ]; System.arraycopy( ids, idPosition, idsInBatch, 0, batchSize ); - result.addAll( - loadEntitiesById( Arrays.asList( idsInBatch ), lockOptions, loadOptions, session ) - ); + result.addAll( listEntitiesById( asList( idsInBatch ), lockOptions, loadOptions, session ) ); numberOfIdsLeft = numberOfIdsLeft - batchSize; idPosition += batchSize;