finish big refactor of AbstractMultiIdEntityLoader and children
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
ae538102f9
commit
55255e9d4a
|
@ -262,7 +262,7 @@ public final class CollectionHelper {
|
|||
return copy;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(Collection collection) {
|
||||
public static boolean isEmpty(Collection<?> collection) {
|
||||
return collection == null || collection.isEmpty();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
|
@ -20,10 +21,12 @@ import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
|||
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
||||
import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic;
|
||||
import static org.hibernate.loader.ast.internal.LoaderHelper.getReadOnlyFromLoadQueryInfluencers;
|
||||
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
|
||||
|
@ -37,11 +40,13 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
|
|||
private final EntityMappingType entityDescriptor;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final EntityIdentifierMapping identifierMapping;
|
||||
protected final Object[] idArray;
|
||||
|
||||
public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) {
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
this.sessionFactory = sessionFactory;
|
||||
identifierMapping = getLoadable().getIdentifierMapping();
|
||||
idArray = (Object[]) Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 );
|
||||
}
|
||||
|
||||
protected EntityMappingType getEntityDescriptor() {
|
||||
|
@ -76,31 +81,43 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
|
|||
@Override
|
||||
public final <K> List<T> load(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) {
|
||||
assert ids != null;
|
||||
if ( loadOptions.isOrderReturnEnabled() ) {
|
||||
return performOrderedMultiLoad( ids, loadOptions, session );
|
||||
}
|
||||
else {
|
||||
return performUnorderedMultiLoad( ids, loadOptions, session );
|
||||
return loadOptions.isOrderReturnEnabled()
|
||||
? performOrderedMultiLoad( ids, loadOptions, session )
|
||||
: performUnorderedMultiLoad( ids, loadOptions, session );
|
||||
}
|
||||
|
||||
private List<T> performUnorderedMultiLoad(
|
||||
Object[] ids,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session) {
|
||||
assert !loadOptions.isOrderReturnEnabled();
|
||||
assert ids != null;
|
||||
if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
|
||||
MULTI_KEY_LOAD_LOGGER.tracef( "#performUnorderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
|
||||
}
|
||||
return unorderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
|
||||
}
|
||||
|
||||
protected List<T> performOrderedMultiLoad(
|
||||
Object[] ids,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session) {
|
||||
assert loadOptions.isOrderReturnEnabled();
|
||||
assert ids != null;
|
||||
if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
|
||||
MULTI_KEY_LOAD_LOGGER.tracef( "#performOrderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
|
||||
}
|
||||
return orderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
|
||||
}
|
||||
|
||||
assert loadOptions.isOrderReturnEnabled();
|
||||
|
||||
final boolean coerce = !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled();
|
||||
private List<T> orderedMultiLoad(
|
||||
Object[] ids,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
LockOptions lockOptions,
|
||||
EventSource session) {
|
||||
final boolean idCoercionEnabled = isIdCoercionEnabled();
|
||||
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<Object> result = arrayList( ids.length );
|
||||
|
@ -109,10 +126,10 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
|
|||
final List<Integer> elementPositionsLoadedByBatch = new ArrayList<>();
|
||||
|
||||
for ( int i = 0; i < ids.length; i++ ) {
|
||||
final Object id = coerce ? idType.coerce( ids[i], session ) : ids[i];
|
||||
final Object id = idCoercionEnabled ? 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 ( !loadFromEnabledCaches( 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 );
|
||||
|
@ -142,13 +159,27 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
|
|||
return (List<T>) result;
|
||||
}
|
||||
|
||||
protected static LockOptions lockOptions(MultiIdLoadOptions loadOptions) {
|
||||
return loadOptions.getLockOptions() == null
|
||||
? new LockOptions( LockMode.NONE )
|
||||
: loadOptions.getLockOptions();
|
||||
}
|
||||
|
||||
protected abstract int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions);
|
||||
|
||||
protected abstract void handleResults(MultiIdLoadOptions loadOptions, EventSource session, List<Integer> elementPositionsLoadedByBatch, List<Object> result);
|
||||
protected abstract void handleResults(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session,
|
||||
List<Integer> elementPositionsLoadedByBatch,
|
||||
List<Object> result);
|
||||
|
||||
protected abstract void loadEntitiesById(List<Object> idsInBatch, LockOptions lockOptions, MultiIdLoadOptions loadOptions, EventSource session);
|
||||
protected abstract void loadEntitiesById(
|
||||
List<Object> idsInBatch,
|
||||
LockOptions lockOptions,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session);
|
||||
|
||||
protected boolean loadFromCaches(
|
||||
protected boolean loadFromEnabledCaches(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session,
|
||||
Object id,
|
||||
|
@ -157,48 +188,189 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
|
|||
List<Object> result,
|
||||
int i) {
|
||||
if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
final LoadEvent loadEvent = new LoadEvent(
|
||||
id,
|
||||
getLoadable().getJavaType().getJavaTypeClass().getName(),
|
||||
lockOptions,
|
||||
session,
|
||||
getReadOnlyFromLoadQueryInfluencers( session )
|
||||
return loadFromCaches( loadOptions, entityKey, result, i,
|
||||
new LoadEvent(
|
||||
id,
|
||||
getLoadable().getJavaType().getJavaTypeClass().getName(),
|
||||
lockOptions,
|
||||
session,
|
||||
getReadOnlyFromLoadQueryInfluencers( session )
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Object managedEntity = null;
|
||||
private boolean loadFromCaches(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EntityKey entityKey,
|
||||
List<Object> result,
|
||||
int i,
|
||||
LoadEvent loadEvent) {
|
||||
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 ( 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 );
|
||||
if ( managedEntity != null
|
||||
&& !loadOptions.isReturnOfDeletedEntitiesEnabled()
|
||||
&& !persistenceContextEntry.isManaged() ) {
|
||||
// put a null in the result
|
||||
result.add( i, null );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract <K> List<T> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session);
|
||||
protected List<T> unorderedMultiLoad(
|
||||
Object[] ids,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
LockOptions lockOptions,
|
||||
EventSource session) {
|
||||
final List<T> result = arrayList( ids.length );
|
||||
final Object[] unresolvableIds =
|
||||
resolveInCachesIfEnabled( ids, loadOptions, lockOptions, session,
|
||||
(position, entityKey, resolvedRef) -> result.add( (T) resolvedRef ) );
|
||||
if ( !isEmpty( unresolvableIds ) ) {
|
||||
loadEntitiesWithUnresolvedIds( loadOptions, lockOptions, session, unresolvableIds, result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract void loadEntitiesWithUnresolvedIds(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
LockOptions lockOptions,
|
||||
EventSource session,
|
||||
Object[] unresolvableIds,
|
||||
List<T> result);
|
||||
|
||||
protected final <R> Object[] resolveInCachesIfEnabled(
|
||||
Object[] ids,
|
||||
@NonNull MultiIdLoadOptions loadOptions,
|
||||
@NonNull LockOptions lockOptions,
|
||||
EventSource session,
|
||||
ResolutionConsumer<R> resolutionConsumer) {
|
||||
return loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled()
|
||||
// the user requested that we exclude ids corresponding to already managed
|
||||
// entities from the generated load SQL. So here we will iterate all
|
||||
// incoming id values and see whether it corresponds to an existing
|
||||
// entity associated with the PC - if it does we add it to the result
|
||||
// list immediately and remove its id from the group of ids to load.
|
||||
// we'll load all of them from the database
|
||||
? resolveInCaches( ids, loadOptions, lockOptions, session, resolutionConsumer )
|
||||
: ids;
|
||||
}
|
||||
|
||||
protected final <R> Object[] resolveInCaches(
|
||||
Object[] ids,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
LockOptions lockOptions,
|
||||
EventSource session,
|
||||
ResolutionConsumer<R> resolutionConsumer) {
|
||||
|
||||
final boolean idCoercionEnabled = isIdCoercionEnabled();
|
||||
final JavaType<?> idType = getLoadable().getIdentifierMapping().getJavaType();
|
||||
|
||||
List<Object> unresolvedIds = null;
|
||||
for ( int i = 0; i < ids.length; i++ ) {
|
||||
final Object id = idCoercionEnabled ? idType.coerce( ids[i], session ) : ids[i];
|
||||
final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() );
|
||||
unresolvedIds = loadFromCaches( id, entityKey, i, unresolvedIds, loadOptions, resolutionConsumer,
|
||||
new LoadEvent(
|
||||
id,
|
||||
getLoadable().getJavaType().getJavaTypeClass().getName(),
|
||||
lockOptions,
|
||||
session,
|
||||
getReadOnlyFromLoadQueryInfluencers( session )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( isEmpty( unresolvedIds ) ) {
|
||||
// all the given ids were already associated with the Session
|
||||
return null;
|
||||
}
|
||||
else if ( unresolvedIds.size() == ids.length ) {
|
||||
// we need to load all the ids
|
||||
return ids;
|
||||
}
|
||||
else {
|
||||
// we need to load only some the ids
|
||||
return unresolvedIds.toArray( idArray );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIdCoercionEnabled() {
|
||||
return !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled();
|
||||
}
|
||||
|
||||
public interface ResolutionConsumer<T> {
|
||||
void consume(int position, EntityKey entityKey, T resolvedRef);
|
||||
}
|
||||
private <R, K> List<K> loadFromCaches(
|
||||
K id, EntityKey entityKey, int i,
|
||||
List<K> unresolvedIds,
|
||||
MultiIdLoadOptions loadOptions,
|
||||
ResolutionConsumer<R> resolutionConsumer,
|
||||
LoadEvent loadEvent) {
|
||||
Object cachedEntity = null;
|
||||
|
||||
// look for it in the Session first
|
||||
final CacheEntityLoaderHelper.PersistenceContextEntry persistenceContextEntry =
|
||||
loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET );
|
||||
if ( loadOptions.isSessionCheckingEnabled() ) {
|
||||
cachedEntity = persistenceContextEntry.getEntity();
|
||||
|
||||
if ( cachedEntity != null
|
||||
&& !loadOptions.isReturnOfDeletedEntitiesEnabled()
|
||||
&& !persistenceContextEntry.isManaged() ) {
|
||||
resolutionConsumer.consume( i, entityKey, null );
|
||||
return unresolvedIds;
|
||||
}
|
||||
}
|
||||
|
||||
if ( cachedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
cachedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(
|
||||
loadEvent,
|
||||
getLoadable().getEntityPersister(),
|
||||
entityKey
|
||||
);
|
||||
}
|
||||
|
||||
if ( cachedEntity != null ) {
|
||||
//noinspection unchecked
|
||||
resolutionConsumer.consume( i, entityKey, (R) cachedEntity);
|
||||
}
|
||||
else {
|
||||
if ( unresolvedIds == null ) {
|
||||
unresolvedIds = new ArrayList<>();
|
||||
}
|
||||
unresolvedIds.add( id );
|
||||
}
|
||||
return unresolvedIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,29 +4,20 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
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.internal.CacheEntityLoaderHelper.PersistenceContextEntry;
|
||||
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
||||
|
@ -35,19 +26,12 @@ import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
|||
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcParametersList;
|
||||
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
|
||||
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
||||
import org.hibernate.sql.results.spi.ManagedResultConsumer;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.hibernate.engine.internal.BatchFetchQueueHelper.removeBatchLoadableEntityKey;
|
||||
import static org.hibernate.engine.spi.SubselectFetch.createRegistrationHandler;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
||||
import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic;
|
||||
import static org.hibernate.loader.ast.internal.LoaderHelper.getReadOnlyFromLoadQueryInfluencers;
|
||||
import static org.hibernate.loader.ast.internal.LoaderHelper.loadByArrayParameter;
|
||||
import static org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelectBySingleArrayParameter;
|
||||
import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.resolveArrayJdbcMapping;
|
||||
|
@ -64,11 +48,11 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
|
|||
EntityMappingType entityDescriptor,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( entityDescriptor, sessionFactory );
|
||||
final Class<?> arrayClass = createTypedArray( 0 ).getClass();
|
||||
final Class<?> idArrayClass = idArray.getClass();
|
||||
arrayJdbcMapping = resolveArrayJdbcMapping(
|
||||
getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ),
|
||||
getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( idArrayClass ),
|
||||
getIdentifierMapping().getJdbcMapping(),
|
||||
arrayClass,
|
||||
idArrayClass,
|
||||
getSessionFactory()
|
||||
);
|
||||
jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping );
|
||||
|
@ -133,7 +117,7 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
|
|||
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(1);
|
||||
jdbcParameterBindings.addBinding( jdbcParameter,
|
||||
new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch.toArray( createTypedArray(0) ) ) );
|
||||
new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch.toArray( idArray ) ) );
|
||||
|
||||
getJdbcSelectExecutor().executeQuery(
|
||||
getSqlAstTranslatorFactory().buildSelectTranslator( getSessionFactory(), sqlAst )
|
||||
|
@ -156,36 +140,12 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
|
|||
}
|
||||
|
||||
@Override
|
||||
protected <K> List<E> performUnorderedMultiLoad(
|
||||
K[] ids,
|
||||
protected void loadEntitiesWithUnresolvedIds(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session) {
|
||||
if ( MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
|
||||
MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER.tracef(
|
||||
"MultiIdEntityLoaderArrayParam#performUnorderedMultiLoad - %s",
|
||||
getLoadable().getEntityName()
|
||||
);
|
||||
}
|
||||
|
||||
final List<E> result = CollectionHelper.arrayList( ids.length );
|
||||
final LockOptions lockOptions = loadOptions.getLockOptions() == null
|
||||
? new LockOptions( LockMode.NONE )
|
||||
: loadOptions.getLockOptions();
|
||||
|
||||
//noinspection unchecked
|
||||
final K[] idsToLoadFromDatabase = processResolvableEntities(
|
||||
ids,
|
||||
(index, entityKey, resolvedEntity) -> result.add( (E) resolvedEntity ),
|
||||
loadOptions,
|
||||
lockOptions,
|
||||
session
|
||||
);
|
||||
|
||||
if ( idsToLoadFromDatabase == null ) {
|
||||
// all the given ids were already associated with the Session
|
||||
return result;
|
||||
}
|
||||
|
||||
LockOptions lockOptions,
|
||||
EventSource session,
|
||||
Object[] unresolvableIds,
|
||||
List<E> result) {
|
||||
final SelectStatement sqlAst = createSelectBySingleArrayParameter(
|
||||
getLoadable(),
|
||||
getIdentifierMapping(),
|
||||
|
@ -194,12 +154,13 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
|
|||
jdbcParameter,
|
||||
getSessionFactory()
|
||||
);
|
||||
|
||||
final JdbcOperationQuerySelect jdbcSelectOperation =
|
||||
getSqlAstTranslatorFactory().buildSelectTranslator( getSessionFactory(), sqlAst )
|
||||
.translate( NO_BINDINGS, QueryOptions.NONE );
|
||||
|
||||
final List<E> databaseResults = loadByArrayParameter(
|
||||
idsToLoadFromDatabase,
|
||||
unresolvableIds,
|
||||
sqlAst,
|
||||
jdbcSelectOperation,
|
||||
jdbcParameter,
|
||||
|
@ -213,108 +174,14 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
|
|||
);
|
||||
result.addAll( databaseResults );
|
||||
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for ( int i = 0; i < idsToLoadFromDatabase.length; i++ ) {
|
||||
final Object id = idsToLoadFromDatabase[i];
|
||||
if ( id == null ) {
|
||||
// skip any of the null padded ids
|
||||
// - actually we could probably even break here
|
||||
continue;
|
||||
}
|
||||
// found or not, remove the key from the batch-fetch queue
|
||||
removeBatchLoadableEntityKey( id, getLoadable(), session );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public interface ResolutionConsumer<T> {
|
||||
void consume(int position, EntityKey entityKey, T resolvedRef);
|
||||
}
|
||||
|
||||
protected final <R,K> K[] processResolvableEntities(
|
||||
K[] ids,
|
||||
ResolutionConsumer<R> resolutionConsumer,
|
||||
@NonNull MultiIdLoadOptions loadOptions,
|
||||
@NonNull LockOptions lockOptions,
|
||||
EventSource session) {
|
||||
if ( !loadOptions.isSessionCheckingEnabled()
|
||||
&& !loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
// we'll load all of them from the database
|
||||
return ids;
|
||||
}
|
||||
|
||||
boolean foundAnyResolvedEntities = false;
|
||||
List<K> nonResolvedIds = null;
|
||||
|
||||
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(),
|
||||
lockOptions,
|
||||
session,
|
||||
getReadOnlyFromLoadQueryInfluencers( session )
|
||||
);
|
||||
|
||||
Object managedEntity = null;
|
||||
|
||||
// look for it in the Session first
|
||||
final PersistenceContextEntry persistenceContextEntry =
|
||||
loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET );
|
||||
if ( loadOptions.isSessionCheckingEnabled() ) {
|
||||
managedEntity = persistenceContextEntry.getEntity();
|
||||
|
||||
if ( managedEntity != null
|
||||
&& !loadOptions.isReturnOfDeletedEntitiesEnabled()
|
||||
&& !persistenceContextEntry.isManaged() ) {
|
||||
foundAnyResolvedEntities = true;
|
||||
resolutionConsumer.consume( i, entityKey, null );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(
|
||||
loadEvent,
|
||||
getLoadable().getEntityPersister(),
|
||||
entityKey
|
||||
);
|
||||
}
|
||||
|
||||
if ( managedEntity != null ) {
|
||||
foundAnyResolvedEntities = true;
|
||||
|
||||
//noinspection unchecked
|
||||
resolutionConsumer.consume( i, entityKey, (R) managedEntity);
|
||||
}
|
||||
else {
|
||||
if ( nonResolvedIds == null ) {
|
||||
nonResolvedIds = new ArrayList<>();
|
||||
}
|
||||
//noinspection unchecked
|
||||
nonResolvedIds.add( (K) id );
|
||||
for ( Object id : unresolvableIds ) {
|
||||
// skip any of the null padded ids
|
||||
// (actually we could probably even break on the first null)
|
||||
if ( id != null ) {
|
||||
// found or not, remove the key from the batch-fetch queue
|
||||
removeBatchLoadableEntityKey( id, getLoadable(), session );
|
||||
}
|
||||
}
|
||||
|
||||
if ( foundAnyResolvedEntities ) {
|
||||
if ( isEmpty( nonResolvedIds ) ) {
|
||||
// all the given ids were already associated with the Session
|
||||
return null;
|
||||
}
|
||||
|
||||
return nonResolvedIds.toArray( createTypedArray(0) );
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
private <X> X[] createTypedArray(@SuppressWarnings("SameParameterValue") int length) {
|
||||
//noinspection unchecked
|
||||
return (X[]) Array.newInstance( getIdentifierMapping().getJavaType().getJavaTypeClass(), length );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.BatchFetchQueue;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
|
@ -18,9 +15,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.SubselectFetch;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.LoadEvent;
|
||||
import org.hibernate.event.spi.LoadEventListener;
|
||||
import org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.PersistenceContextEntry;
|
||||
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.MultiKeyLoadSizingStrategy;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -32,15 +26,11 @@ import org.hibernate.sql.exec.spi.JdbcParametersList;
|
|||
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.lang.System.arraycopy;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.hibernate.engine.spi.SubselectFetch.createRegistrationHandler;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic;
|
||||
import static org.hibernate.loader.ast.internal.LoaderHelper.getReadOnlyFromLoadQueryInfluencers;
|
||||
import static org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelect;
|
||||
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
|
||||
|
||||
|
@ -205,123 +195,22 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<T> performUnorderedMultiLoad(
|
||||
Object[] ids,
|
||||
protected void loadEntitiesWithUnresolvedIds(
|
||||
MultiIdLoadOptions loadOptions,
|
||||
EventSource session) {
|
||||
assert !loadOptions.isOrderReturnEnabled();
|
||||
assert ids != null;
|
||||
|
||||
if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
|
||||
MULTI_KEY_LOAD_LOGGER.tracef( "#performUnorderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
|
||||
}
|
||||
|
||||
final List<T> result = arrayList( ids.length );
|
||||
|
||||
final LockOptions lockOptions = loadOptions.getLockOptions() == null
|
||||
? new LockOptions( LockMode.NONE )
|
||||
: loadOptions.getLockOptions();
|
||||
|
||||
if ( loadOptions.isSessionCheckingEnabled() || loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
// the user requested that we exclude ids corresponding to already managed
|
||||
// entities from the generated load SQL. So here we will iterate all
|
||||
// incoming id values and see whether it corresponds to an existing
|
||||
// entity associated with the PC - if it does we add it to the result
|
||||
// list immediately and remove its id from the group of ids to load.
|
||||
boolean foundAnyManagedEntities = false;
|
||||
final List<Object> 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 = 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(),
|
||||
lockOptions,
|
||||
session,
|
||||
getReadOnlyFromLoadQueryInfluencers( session )
|
||||
);
|
||||
|
||||
Object managedEntity = null;
|
||||
|
||||
// look for it in the Session first
|
||||
final PersistenceContextEntry persistenceContextEntry =
|
||||
loadFromSessionCacheStatic( loadEvent, entityKey, LoadEventListener.GET );
|
||||
if ( loadOptions.isSessionCheckingEnabled() ) {
|
||||
managedEntity = persistenceContextEntry.getEntity();
|
||||
|
||||
if ( managedEntity != null
|
||||
&& !loadOptions.isReturnOfDeletedEntitiesEnabled()
|
||||
&& !persistenceContextEntry.isManaged() ) {
|
||||
foundAnyManagedEntities = true;
|
||||
result.add( null );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( managedEntity == null && loadOptions.isSecondLevelCacheCheckingEnabled() ) {
|
||||
managedEntity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache(
|
||||
loadEvent,
|
||||
getLoadable().getEntityPersister(),
|
||||
entityKey
|
||||
);
|
||||
}
|
||||
|
||||
if ( managedEntity != null ) {
|
||||
foundAnyManagedEntities = true;
|
||||
//noinspection unchecked
|
||||
result.add( (T) managedEntity );
|
||||
}
|
||||
else {
|
||||
nonManagedIds.add( id );
|
||||
}
|
||||
}
|
||||
|
||||
if ( foundAnyManagedEntities ) {
|
||||
if ( nonManagedIds.isEmpty() ) {
|
||||
// all the given ids were already associated with the Session
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
// over-write the ids to be loaded with the collection of
|
||||
// just non-managed ones
|
||||
ids = nonManagedIds.toArray(
|
||||
(Object[]) Array.newInstance(
|
||||
ids.getClass().getComponentType(),
|
||||
nonManagedIds.size()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numberOfIdsLeft = ids.length;
|
||||
final int maxBatchSize;
|
||||
if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) {
|
||||
maxBatchSize = loadOptions.getBatchSize();
|
||||
}
|
||||
else {
|
||||
maxBatchSize = getBatchLoadSizingStrategy().determineOptimalBatchLoadSize(
|
||||
getIdentifierMapping().getJdbcTypeCount(),
|
||||
numberOfIdsLeft,
|
||||
isInClauseParameterPaddingEnabled()
|
||||
);
|
||||
}
|
||||
|
||||
LockOptions lockOptions,
|
||||
EventSource session,
|
||||
Object[] unresolvableIds,
|
||||
List<T> result) {
|
||||
final int maxBatchSize = maxBatchSize( unresolvableIds, loadOptions );
|
||||
int numberOfIdsLeft = unresolvableIds.length;
|
||||
int idPosition = 0;
|
||||
while ( numberOfIdsLeft > 0 ) {
|
||||
final int batchSize = Math.min( numberOfIdsLeft, maxBatchSize );
|
||||
final Object[] idsInBatch = new Object[ batchSize ];
|
||||
System.arraycopy( ids, idPosition, idsInBatch, 0, batchSize );
|
||||
final Object[] idsInBatch = new Object[batchSize];
|
||||
arraycopy( unresolvableIds, idPosition, idsInBatch, 0, batchSize );
|
||||
result.addAll( listEntitiesById( asList( idsInBatch ), lockOptions, loadOptions, session ) );
|
||||
numberOfIdsLeft = numberOfIdsLeft - batchSize;
|
||||
idPosition += batchSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue