HHH-16651 make persisters return adapted loaders for session batch size

This commit is contained in:
Gavin 2023-05-21 18:37:49 +02:00 committed by Gavin King
parent 3097c47b3d
commit bbd8df93ca
11 changed files with 222 additions and 190 deletions

View File

@ -46,7 +46,7 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader {
public CollectionLoaderSubSelectFetch( public CollectionLoaderSubSelectFetch(
PluralAttributeMapping attributeMapping, PluralAttributeMapping attributeMapping,
DomainResult cachedDomainResult, DomainResult<?> cachedDomainResult,
SubselectFetch subselect, SubselectFetch subselect,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
this.attributeMapping = attributeMapping; this.attributeMapping = attributeMapping;

View File

@ -7,7 +7,6 @@
package org.hibernate.loader.ast.internal; package org.hibernate.loader.ast.internal;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
@ -16,7 +15,6 @@ import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.loader.ast.spi.EntityBatchLoader; import org.hibernate.loader.ast.spi.EntityBatchLoader;
import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader; import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
@ -26,15 +24,9 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_DEBUG_ENABLED; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_DEBUG_ENABLED;
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
@ -162,13 +154,10 @@ public class EntityBatchLoaderArrayParam<T>
@Override @Override
public void prepare() { public void prepare() {
identifierMapping = (BasicEntityIdentifierMapping) getLoadable().getIdentifierMapping(); identifierMapping = (BasicEntityIdentifierMapping) getLoadable().getIdentifierMapping();
final Class<?> arrayClass = Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 ).getClass(); final Class<?> arrayClass =
Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 ).getClass();
final BasicTypeRegistry basicTypeRegistry = sessionFactory.getTypeConfiguration().getBasicTypeRegistry();
final BasicType<?> arrayBasicType = basicTypeRegistry.getRegisteredType( arrayClass );
arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping(
arrayBasicType, sessionFactory.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ),
identifierMapping.getJdbcMapping(), identifierMapping.getJdbcMapping(),
arrayClass, arrayClass,
sessionFactory sessionFactory

View File

@ -56,16 +56,18 @@ public class EntityBatchLoaderInPredicate<T>
/** /**
* @param domainBatchSize The maximum number of entities we will initialize for each {@link #load load} * @param domainBatchSize The maximum number of entities we will initialize for each {@link #load load}
* @param sqlBatchSize The number of keys our SQL AST should be able to fetch
*/ */
public EntityBatchLoaderInPredicate( public EntityBatchLoaderInPredicate(
int domainBatchSize, int domainBatchSize,
int sqlBatchSize,
EntityMappingType entityDescriptor, EntityMappingType entityDescriptor,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
super( entityDescriptor, sessionFactory ); super( entityDescriptor, sessionFactory );
this.domainBatchSize = domainBatchSize; this.domainBatchSize = domainBatchSize;
this.sqlBatchSize = sqlBatchSize; int idColumnCount = entityDescriptor.getEntityPersister().getIdentifierType().getColumnSpan( sessionFactory );
this.sqlBatchSize = sessionFactory.getJdbcServices()
.getDialect()
.getBatchLoadSizingStrategy()
.determineOptimalBatchLoadSize( idColumnCount, domainBatchSize, false );
if ( MULTI_KEY_LOAD_DEBUG_ENABLED ) { if ( MULTI_KEY_LOAD_DEBUG_ENABLED ) {
MULTI_KEY_LOAD_LOGGER.debugf( MULTI_KEY_LOAD_LOGGER.debugf(

View File

@ -40,8 +40,6 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -385,7 +383,7 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
return ids; return ids;
} }
private <X> X[] createTypedArray(@SuppressWarnings("SameParameterValue") int length) { private <X> X[] createTypedArray(@SuppressWarnings("SameParameterValue") int length) {
//noinspection unchecked //noinspection unchecked
return (X[]) Array.newInstance( getIdentifierMapping().getJavaType().getJavaTypeClass(), length ); return (X[]) Array.newInstance( getIdentifierMapping().getJavaType().getJavaTypeClass(), length );
} }
@ -393,14 +391,9 @@ public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoade
@Override @Override
public void prepare() { public void prepare() {
super.prepare(); super.prepare();
final Class<?> arrayClass = createTypedArray( 0 ).getClass(); final Class<?> arrayClass = createTypedArray( 0 ).getClass();
final BasicTypeRegistry basicTypeRegistry = getSessionFactory().getTypeConfiguration().getBasicTypeRegistry();
final BasicType<?> arrayBasicType = basicTypeRegistry.getRegisteredType( arrayClass );
arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping(
arrayBasicType, getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ),
getIdentifierMapping().getJdbcMapping(), getIdentifierMapping().getJdbcMapping(),
arrayClass, arrayClass,
getSessionFactory() getSessionFactory()

View File

@ -16,7 +16,6 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
@ -28,7 +27,6 @@ import org.hibernate.event.spi.LoadEvent;
import org.hibernate.event.spi.LoadEventListener; import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstTranslatorFactory;
@ -54,10 +52,10 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
public MultiIdEntityLoaderStandard( public MultiIdEntityLoaderStandard(
EntityPersister entityDescriptor, EntityPersister entityDescriptor,
PersistentClass bootDescriptor, int idColumnSpan,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
super( entityDescriptor, sessionFactory ); super( entityDescriptor, sessionFactory );
this.idJdbcTypeCount = bootDescriptor.getIdentifier().getColumnSpan(); this.idJdbcTypeCount = idColumnSpan;
assert idJdbcTypeCount > 0; assert idJdbcTypeCount > 0;
} }

View File

@ -18,9 +18,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.supportsSqlArrayType;
/** /**
* Standard {@link BatchLoaderFactory} implementation * Standard {@link BatchLoaderFactory} implementation
* *
@ -35,23 +36,18 @@ public class StandardBatchLoaderFactory implements BatchLoaderFactory {
public <T> EntityBatchLoader<T> createEntityBatchLoader( public <T> EntityBatchLoader<T> createEntityBatchLoader(
int domainBatchSize, EntityMappingType entityDescriptor, int domainBatchSize, EntityMappingType entityDescriptor,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory) {
final Dialect dialect = factory.getJdbcServices().getDialect();
// NOTE : don't use the EntityIdentifierMapping here because it will not be known until later // NOTE : don't use the EntityIdentifierMapping here because it will not be known until later
final Type identifierType = entityDescriptor.getEntityPersister().getIdentifierType(); final Type identifierType = entityDescriptor.getEntityPersister().getIdentifierType();
final int idColumnCount = identifierType.getColumnSpan( factory ); if ( identifierType.getColumnSpan( factory ) == 1
&& supportsSqlArrayType( factory.getJdbcServices().getDialect() )
if ( idColumnCount == 1
&& MultiKeyLoadHelper.supportsSqlArrayType( dialect )
&& identifierType instanceof BasicType ) { && identifierType instanceof BasicType ) {
// we can use a single ARRAY parameter to send all the ids // we can use a single ARRAY parameter to send all the ids
return new EntityBatchLoaderArrayParam<>( domainBatchSize, entityDescriptor, factory ); return new EntityBatchLoaderArrayParam<>( domainBatchSize, entityDescriptor, factory );
} }
else {
final int optimalBatchSize = dialect return new EntityBatchLoaderInPredicate<>( domainBatchSize, entityDescriptor, factory );
.getBatchLoadSizingStrategy() }
.determineOptimalBatchLoadSize( idColumnCount, domainBatchSize, false );
return new EntityBatchLoaderInPredicate<>( domainBatchSize, optimalBatchSize, entityDescriptor, factory );
} }
@Override @Override
@ -60,15 +56,13 @@ public class StandardBatchLoaderFactory implements BatchLoaderFactory {
LoadQueryInfluencers influencers, LoadQueryInfluencers influencers,
PluralAttributeMapping attributeMapping, PluralAttributeMapping attributeMapping,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory) {
final Dialect dialect = factory.getJdbcServices().getDialect(); if ( attributeMapping.getKeyDescriptor().getJdbcTypeCount() == 1
final int columnCount = attributeMapping.getKeyDescriptor().getJdbcTypeCount(); && supportsSqlArrayType( factory.getJdbcServices().getDialect() ) ) {
if ( columnCount == 1
&& dialect.supportsStandardArrays()
&& dialect.getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY ) {
// we can use a single ARRAY parameter to send all the ids // we can use a single ARRAY parameter to send all the ids
return new CollectionBatchLoaderArrayParam( domainBatchSize, influencers, attributeMapping, factory ); return new CollectionBatchLoaderArrayParam( domainBatchSize, influencers, attributeMapping, factory );
} }
else {
return new CollectionBatchLoaderInPredicate( domainBatchSize, influencers, attributeMapping, factory ); return new CollectionBatchLoaderInPredicate( domainBatchSize, influencers, attributeMapping, factory );
}
} }
} }

View File

@ -30,17 +30,21 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
default boolean isAffectedByInfluencers(LoadQueryInfluencers influencers) { default boolean isAffectedByInfluencers(LoadQueryInfluencers influencers) {
return isAffectedByEntityGraph( influencers ) return isAffectedByEntityGraph( influencers )
|| isAffectedByEnabledFetchProfiles( influencers ) || isAffectedByEnabledFetchProfiles( influencers )
|| isAffectedByEnabledFilters( influencers ); || isAffectedByEnabledFilters( influencers )
|| influencers.getBatchSize() != getBatchSize();
} }
default boolean isNotAffectedByInfluencers(LoadQueryInfluencers influencers) { default boolean isNotAffectedByInfluencers(LoadQueryInfluencers influencers) {
return !isAffectedByEntityGraph( influencers ) return !isAffectedByEntityGraph( influencers )
&& !isAffectedByEnabledFetchProfiles( influencers ) && !isAffectedByEnabledFetchProfiles( influencers )
&& !isAffectedByEnabledFilters( influencers ) && !isAffectedByEnabledFilters( influencers )
&& influencers.getEnabledCascadingFetchProfile() == null; && influencers.getBatchSize() == getBatchSize()
&& influencers.getEnabledCascadingFetchProfile() == null;
} }
int getBatchSize();
/** /**
* Whether any of the "influencers" affect this loadable. * Whether any of the "influencers" affect this loadable.
*/ */

View File

@ -885,6 +885,11 @@ public class PluralAttributeMappingImpl
} }
} }
@Override
public int getBatchSize() {
return getCollectionDescriptor().getBatchSize();
}
@Override @Override
public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) { public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) {
return getCollectionDescriptor().isAffectedByEnabledFilters( influencers ); return getCollectionDescriptor().isAffectedByEnabledFilters( influencers );

View File

@ -6,17 +6,6 @@
*/ */
package org.hibernate.persister.collection; package org.hibernate.persister.collection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode; import org.hibernate.FetchMode;
import org.hibernate.Filter; import org.hibernate.Filter;
@ -27,6 +16,7 @@ import org.hibernate.MappingException;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.Remove; import org.hibernate.Remove;
import org.hibernate.TransientObjectException; import org.hibernate.TransientObjectException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.CollectionDataAccess; import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.cache.spi.entry.CacheEntryStructure;
@ -124,7 +114,6 @@ import org.hibernate.sql.model.ast.RestrictedTableMutation;
import org.hibernate.sql.model.internal.TableDeleteStandard; import org.hibernate.sql.model.internal.TableDeleteStandard;
import org.hibernate.sql.model.jdbc.JdbcDeleteMutation; import org.hibernate.sql.model.jdbc.JdbcDeleteMutation;
import org.hibernate.sql.model.jdbc.JdbcMutationOperation; import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList; import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
@ -132,6 +121,17 @@ import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER; import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
@ -228,7 +228,7 @@ public abstract class AbstractCollectionPersister
private final Comparator<?> comparator; private final Comparator<?> comparator;
private CollectionLoader collectionLoader; private CollectionLoader collectionLoader;
private volatile CollectionLoader standardCollectionLoader; // private volatile CollectionLoader standardCollectionLoader;
private CollectionElementLoaderByIndex collectionElementLoaderByIndex; private CollectionElementLoaderByIndex collectionElementLoaderByIndex;
private PluralAttributeMapping attributeMapping; private PluralAttributeMapping attributeMapping;
@ -570,14 +570,8 @@ public abstract class AbstractCollectionPersister
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// "mapping model" // "mapping model"
if ( queryLoaderName != null ) { if ( hasNamedQueryLoader() ) {
final NamedQueryMemento namedQueryMemento = factory getNamedQueryMemento( collectionBootDescriptor.getMetadata() );
.getQueryEngine()
.getNamedObjectRepository()
.resolve( factory, collectionBootDescriptor.getMetadata(), queryLoaderName );
if ( namedQueryMemento == null ) {
throw new IllegalArgumentException( "Could not resolve named load-query [" + navigableRole + "] : " + queryLoaderName );
}
} }
tableMapping = buildCollectionTableMapping( collectionBootDescriptor, getTableName(), getCollectionSpaces() ); tableMapping = buildCollectionTableMapping( collectionBootDescriptor, getTableName(), getCollectionSpaces() );
@ -614,14 +608,12 @@ public abstract class AbstractCollectionPersister
@Override @Override
public void postInstantiate() throws MappingException { public void postInstantiate() throws MappingException {
if ( queryLoaderName == null ) { if ( hasNamedQueryLoader() ) {
collectionLoader = createCollectionLoader( new LoadQueryInfluencers( factory ) ); // We pass null as metamodel because we did the initialization during construction already
collectionLoader = new CollectionLoaderNamedQuery( this, getNamedQueryMemento( null ) );
} }
else { else {
// We pass null as metamodel because we did the initialization during construction already collectionLoader = createCollectionLoader( new LoadQueryInfluencers( factory ) );
final NamedQueryMemento namedQueryMemento = factory.getQueryEngine().getNamedObjectRepository()
.resolve( factory, null, queryLoaderName );
collectionLoader = new CollectionLoaderNamedQuery( this, namedQueryMemento );
} }
if ( attributeMapping.getIndexDescriptor() != null ) { if ( attributeMapping.getIndexDescriptor() != null ) {
@ -635,6 +627,17 @@ public abstract class AbstractCollectionPersister
logStaticSQL(); logStaticSQL();
} }
private NamedQueryMemento getNamedQueryMemento(MetadataImplementor bootModel) {
final NamedQueryMemento memento =
factory.getQueryEngine().getNamedObjectRepository()
.resolve( factory, bootModel, queryLoaderName );
if ( memento == null ) {
throw new IllegalArgumentException( "Could not resolve named query '" + queryLoaderName
+ "' for loading collection '" + getName() + "'" );
}
return memento;
}
protected void logStaticSQL() { protected void logStaticSQL() {
if ( !ModelMutationLogging.MODEL_MUTATION_LOGGER_DEBUG_ENABLED ) { if ( !ModelMutationLogging.MODEL_MUTATION_LOGGER_DEBUG_ENABLED ) {
return; return;
@ -676,51 +679,50 @@ public abstract class AbstractCollectionPersister
// lazily initialize instance field via 'double-checked locking' // lazily initialize instance field via 'double-checked locking'
// see https://en.wikipedia.org/wiki/Double-checked_locking on why 'volatile' and local copy is used // see https://en.wikipedia.org/wiki/Double-checked_locking on why 'volatile' and local copy is used
protected CollectionLoader getStandardCollectionLoader() { // protected CollectionLoader getStandardCollectionLoader() {
CollectionLoader localCopy = standardCollectionLoader; // CollectionLoader localCopy = standardCollectionLoader;
if ( localCopy == null ) { // if ( localCopy == null ) {
synchronized (this) { // synchronized (this) {
localCopy = standardCollectionLoader; // localCopy = standardCollectionLoader;
if ( localCopy == null ) { // if ( localCopy == null ) {
if ( queryLoaderName != null ) { // localCopy = createCollectionLoader( new LoadQueryInfluencers( factory ) );
localCopy = collectionLoader; // standardCollectionLoader = localCopy;
} // }
else { // }
localCopy = createCollectionLoader( new LoadQueryInfluencers( factory ) ); // }
} // return localCopy;
standardCollectionLoader = localCopy; // }
}
} private boolean hasNamedQueryLoader() {
} return queryLoaderName != null;
return localCopy; }
public CollectionLoader getCollectionLoader() {
return collectionLoader;
} }
protected CollectionLoader determineLoaderToUse(Object key, SharedSessionContractImplementor session) { protected CollectionLoader determineLoaderToUse(Object key, SharedSessionContractImplementor session) {
if ( queryLoaderName != null ) { if ( hasNamedQueryLoader() ) {
// if there is a user-specified loader, return that // if there is a user-specified loader, return that
return getStandardCollectionLoader(); return getCollectionLoader();
} }
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
if ( loadQueryInfluencers.effectiveSubselectFetchEnabled( this ) ) { final LoadQueryInfluencers influencers = session.getLoadQueryInfluencers();
if ( influencers.effectiveSubselectFetchEnabled( this ) ) {
final CollectionLoader subSelectLoader = resolveSubSelectLoader( key, session ); final CollectionLoader subSelectLoader = resolveSubSelectLoader( key, session );
if ( subSelectLoader != null ) { if ( subSelectLoader != null ) {
return subSelectLoader; return subSelectLoader;
} }
} }
if ( !loadQueryInfluencers.hasEnabledFilters() return attributeMapping.isAffectedByInfluencers( influencers )
&& !isAffectedByEnabledFetchProfiles( loadQueryInfluencers ) ) { ? createCollectionLoader( influencers )
return getStandardCollectionLoader(); : getCollectionLoader();
}
else {
return createCollectionLoader( loadQueryInfluencers );
}
} }
private CollectionLoader resolveSubSelectLoader(Object key, SharedSessionContractImplementor session) { private CollectionLoader resolveSubSelectLoader(Object key, SharedSessionContractImplementor session) {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final SubselectFetch subselect = final SubselectFetch subselect =
persistenceContext.getBatchFetchQueue() persistenceContext.getBatchFetchQueue()
.getSubselect( session.generateEntityKey( key, getOwnerEntityPersister() ) ); .getSubselect( session.generateEntityKey( key, getOwnerEntityPersister() ) );
@ -728,50 +730,47 @@ public abstract class AbstractCollectionPersister
return null; return null;
} }
else { else {
// Take care of any entities that might have // Remove keys of any entities that have been evicted
// been evicted!
subselect.getResultingEntityKeys() subselect.getResultingEntityKeys()
.removeIf( entityKey -> !persistenceContext.containsEntity( entityKey ) ); .removeIf( entityKey -> !persistenceContext.containsEntity( entityKey ) );
// Run a subquery loader // Run a subquery loader
return createSubSelectLoader( subselect, session ); return createSubSelectLoader( subselect, session );
} }
} }
protected CollectionLoader createSubSelectLoader(SubselectFetch subselect, SharedSessionContractImplementor session) { protected CollectionLoader createSubSelectLoader(SubselectFetch subselect, SharedSessionContractImplementor session) {
//noinspection RedundantCast return new CollectionLoaderSubSelectFetch( attributeMapping, null, subselect, session );
return new CollectionLoaderSubSelectFetch( attributeMapping, (DomainResult<?>) null, subselect, session );
} }
//
// private CollectionLoader reusableCollectionLoader;
//
// protected CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
// if ( canUseReusableCollectionLoader( loadQueryInfluencers ) ) {
// if ( reusableCollectionLoader == null ) {
// reusableCollectionLoader = generateCollectionLoader( new LoadQueryInfluencers( factory ) );
// }
// return reusableCollectionLoader;
// }
// else {
// // create a one-off
// return generateCollectionLoader( loadQueryInfluencers );
// }
// }
//
// private boolean canUseReusableCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
// // we can reuse it so long as none of the enabled influencers affect it
// return attributeMapping.isNotAffectedByInfluencers( loadQueryInfluencers );
// }
private CollectionLoader reusableCollectionLoader; private CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
protected CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
if ( canUseReusableCollectionLoader( loadQueryInfluencers ) ) {
if ( reusableCollectionLoader == null ) {
reusableCollectionLoader = generateCollectionLoader( new LoadQueryInfluencers( factory ) );
}
return reusableCollectionLoader;
}
else {
// create a one-off
return generateCollectionLoader( loadQueryInfluencers );
}
}
private boolean canUseReusableCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
// we can reuse it so long as none of the enabled influencers affect it
return attributeMapping.isNotAffectedByInfluencers( loadQueryInfluencers );
}
private CollectionLoader generateCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) { if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) {
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this ); final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
return getFactory().getServiceRegistry() return factory.getServiceRegistry()
.getService( BatchLoaderFactory.class ) .getService( BatchLoaderFactory.class )
.createCollectionBatchLoader( batchSize, loadQueryInfluencers, attributeMapping, getFactory() ); .createCollectionBatchLoader( batchSize, loadQueryInfluencers, attributeMapping, factory );
} }
else { else {
return new CollectionLoaderSingleKey( attributeMapping, loadQueryInfluencers, getFactory() ); return new CollectionLoaderSingleKey( attributeMapping, loadQueryInfluencers, factory );
} }
} }
@ -1564,16 +1563,13 @@ public abstract class AbstractCollectionPersister
@Override @Override
public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers influencers) { public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers influencers) {
if ( affectingFetchProfiles == null ) { if ( affectingFetchProfiles != null && influencers.hasEnabledFetchProfiles() ) {
return false; for ( String profileName : affectingFetchProfiles.keySet() ) {
} if ( influencers.isFetchProfileEnabled( profileName ) ) {
return true;
for ( Map.Entry<String, Fetch.Style> entry : affectingFetchProfiles.entrySet() ) { }
if ( influencers.isFetchProfileEnabled( entry.getKey() ) ) {
return true;
} }
} }
return false; return false;
} }
@ -1581,11 +1577,12 @@ public abstract class AbstractCollectionPersister
public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) { public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) {
if ( influencers.hasEnabledFilters() ) { if ( influencers.hasEnabledFilters() ) {
final Map<String, Filter> enabledFilters = influencers.getEnabledFilters(); final Map<String, Filter> enabledFilters = influencers.getEnabledFilters();
return ( filterHelper != null && filterHelper.isAffectedBy( enabledFilters ) ) return filterHelper != null && filterHelper.isAffectedBy( enabledFilters )
|| ( manyToManyFilterHelper != null && manyToManyFilterHelper.isAffectedBy( enabledFilters ) ); || manyToManyFilterHelper != null && manyToManyFilterHelper.isAffectedBy( enabledFilters );
}
else {
return false;
} }
return false;
} }
@Override @Override

View File

@ -44,6 +44,7 @@ import org.hibernate.Remove;
import org.hibernate.StaleObjectStateException; import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException; import org.hibernate.StaleStateException;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
@ -301,6 +302,7 @@ import static org.hibernate.internal.util.collections.ArrayHelper.toTypeArray;
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty; import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize; import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;
import static org.hibernate.internal.util.collections.CollectionHelper.toSmallList; import static org.hibernate.internal.util.collections.CollectionHelper.toSmallList;
import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.supportsSqlArrayType;
import static org.hibernate.metamodel.RepresentationMode.POJO; import static org.hibernate.metamodel.RepresentationMode.POJO;
import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR; import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR;
import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR; import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR;
@ -327,8 +329,8 @@ public abstract class AbstractEntityPersister
private final String sqlAliasStem; private final String sqlAliasStem;
private final SingleIdEntityLoader<?> singleIdLoader; private SingleIdEntityLoader<?> singleIdLoader;
private final MultiIdEntityLoader<?> multiIdLoader; private MultiIdEntityLoader<?> multiIdLoader;
private NaturalIdLoader<?> naturalIdLoader; private NaturalIdLoader<?> naturalIdLoader;
private MultiNaturalIdLoader<?> multiNaturalIdLoader; private MultiNaturalIdLoader<?> multiNaturalIdLoader;
@ -432,6 +434,8 @@ public abstract class AbstractEntityPersister
protected AttributeMappingsMap declaredAttributeMappings = AttributeMappingsMap.builder().build(); protected AttributeMappingsMap declaredAttributeMappings = AttributeMappingsMap.builder().build();
protected AttributeMappingsList staticFetchableList; protected AttributeMappingsList staticFetchableList;
private final String queryLoaderName;
private BeforeExecutionGenerator versionGenerator; private BeforeExecutionGenerator versionGenerator;
protected ReflectionOptimizer.AccessOptimizer accessOptimizer; protected ReflectionOptimizer.AccessOptimizer accessOptimizer;
@ -524,22 +528,7 @@ public abstract class AbstractEntityPersister
final String rowId = persistentClass.getRootTable().getRowId(); final String rowId = persistentClass.getRootTable().getRowId();
rowIdName = rowId == null ? null : dialect.rowId( rowId ); rowIdName = rowId == null ? null : dialect.rowId( rowId );
if ( persistentClass.getLoaderName() != null ) { queryLoaderName = persistentClass.getLoaderName();
// We must resolve the named query on-demand through the boot model because it isn't initialized yet
final NamedQueryMemento namedQueryMemento =
factory.getQueryEngine().getNamedObjectRepository()
.resolve( factory, creationContext.getBootModel(), persistentClass.getLoaderName() );
if ( namedQueryMemento == null ) {
throw new IllegalArgumentException( "Could not resolve named load-query [" + getEntityName()
+ "] : " + persistentClass.getLoaderName() );
}
singleIdLoader = new SingleIdEntityLoaderProvidedQueryImpl<>( this, namedQueryMemento );
}
else {
singleIdLoader = createSingleIdEntityLoader( new LoadQueryInfluencers( factory ) );
}
multiIdLoader = buildMultiIdLoader( persistentClass );
final TypeConfiguration typeConfiguration = creationContext.getTypeConfiguration(); final TypeConfiguration typeConfiguration = creationContext.getTypeConfiguration();
final SqmFunctionRegistry functionRegistry = creationContext.getFunctionRegistry(); final SqmFunctionRegistry functionRegistry = creationContext.getFunctionRegistry();
@ -805,12 +794,29 @@ public abstract class AbstractEntityPersister
fullDiscriminatorSQLValues = toStringArray( sqlValues ); fullDiscriminatorSQLValues = toStringArray( sqlValues );
fullDiscriminatorValues = toObjectArray( values ); fullDiscriminatorValues = toObjectArray( values );
if ( hasNamedQueryLoader() ) {
getNamedQueryMemento( creationContext.getBootModel() );
}
}
private NamedQueryMemento getNamedQueryMemento(MetadataImplementor bootModel) {
final NamedQueryMemento memento =
factory.getQueryEngine().getNamedObjectRepository()
.resolve( factory, bootModel, queryLoaderName );
if ( memento == null ) {
throw new IllegalArgumentException( "Could not resolve named query '" + queryLoaderName
+ "' for loading entity '" + getEntityName() + "'" );
}
return memento;
} }
private SingleIdEntityLoader<?> createSingleIdEntityLoader(LoadQueryInfluencers loadQueryInfluencers) { private SingleIdEntityLoader<?> createSingleIdEntityLoader(LoadQueryInfluencers loadQueryInfluencers) {
if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) { if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) {
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this ); final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
return createBatchingIdEntityLoader( this, batchSize, factory ); return factory.getServiceRegistry()
.getService( BatchLoaderFactory.class )
.createEntityBatchLoader( batchSize, this, factory );
} }
else { else {
return new SingleIdEntityLoaderStandardImpl<>( this, factory ); return new SingleIdEntityLoaderStandardImpl<>( this, factory );
@ -836,12 +842,14 @@ public abstract class AbstractEntityPersister
return entityNameByTableNameMap; return entityNameByTableNameMap;
} }
private MultiIdEntityLoader<Object> buildMultiIdLoader(PersistentClass persistentClass) { private MultiIdEntityLoader<Object> buildMultiIdLoader() {
if ( persistentClass.getIdentifier() instanceof BasicValue if ( getIdentifierType() instanceof BasicType
&& MultiKeyLoadHelper.supportsSqlArrayType( factory.getServiceRegistry().getService( JdbcServices.class ).getDialect() ) ) { && supportsSqlArrayType( factory.getJdbcServices().getDialect() ) ) {
return new MultiIdEntityLoaderArrayParam<>( this, factory ); return new MultiIdEntityLoaderArrayParam<>( this, factory );
} }
return new MultiIdEntityLoaderStandard<>( this, persistentClass, factory ); else {
return new MultiIdEntityLoaderStandard<>( this, identifierColumnSpan, factory );
}
} }
private String getIdentitySelectString(Dialect dialect) { private String getIdentitySelectString(Dialect dialect) {
@ -1051,15 +1059,6 @@ public abstract class AbstractEntityPersister
return tableNames; return tableNames;
} }
private static SingleIdEntityLoader<?> createBatchingIdEntityLoader(
EntityMappingType entityDescriptor,
int domainBatchSize,
SessionFactoryImplementor factory) {
return factory.getServiceRegistry()
.getService( BatchLoaderFactory.class )
.createEntityBatchLoader( domainBatchSize, entityDescriptor, factory );
}
/** /**
* We might need to use cache invalidation if we have formulas, * We might need to use cache invalidation if we have formulas,
* dynamic update, or secondary tables. * dynamic update, or secondary tables.
@ -3390,8 +3389,28 @@ public abstract class AbstractEntityPersister
@Override @Override
public final void postInstantiate() throws MappingException { public final void postInstantiate() throws MappingException {
doLateInit(); doLateInit();
prepareLoader( singleIdLoader ); if ( hasNamedQueryLoader() ) {
// We must resolve the named query on-demand through the boot model because it isn't initialized yet
final NamedQueryMemento memento = getNamedQueryMemento( null );
singleIdLoader = new SingleIdEntityLoaderProvidedQueryImpl<>( this, memento );
prepareLoader( singleIdLoader );
}
else {
singleIdLoader = createAndPrepareSingleIdEntityLoader( new LoadQueryInfluencers( factory ) );
}
multiIdLoader = buildAndPrepareMultiIdLoader();
}
private MultiIdEntityLoader<Object> buildAndPrepareMultiIdLoader() {
MultiIdEntityLoader<Object> multiIdLoader = buildMultiIdLoader();
prepareLoader( multiIdLoader ); prepareLoader( multiIdLoader );
return multiIdLoader;
}
private SingleIdEntityLoader<?> createAndPrepareSingleIdEntityLoader(LoadQueryInfluencers influencers) {
SingleIdEntityLoader<?> singleIdLoader = createSingleIdEntityLoader( influencers );
prepareLoader( singleIdLoader );
return singleIdLoader;
} }
private void prepareLoader(Loader loader) { private void prepareLoader(Loader loader) {
@ -3430,14 +3449,29 @@ public abstract class AbstractEntityPersister
LOG.tracev( "Fetching entity: {0}", infoString( this, id, getFactory() ) ); LOG.tracev( "Fetching entity: {0}", infoString( this, id, getFactory() ) );
} }
if ( optionalObject == null ) { final SingleIdEntityLoader<?> loader = determineLoaderToUse( session );
return singleIdLoader.load( id, lockOptions, readOnly, session ); return optionalObject == null
? loader.load( id, lockOptions, readOnly, session )
: loader.load( id, optionalObject, lockOptions, readOnly, session );
}
protected SingleIdEntityLoader<?> determineLoaderToUse(SharedSessionContractImplementor session) {
if ( hasNamedQueryLoader() ) {
return getSingleIdLoader();
} }
else { else {
return singleIdLoader.load( id, optionalObject, lockOptions, readOnly, session ); final LoadQueryInfluencers influencers = session.getLoadQueryInfluencers();
// no subselect fetching for entities for now
return isAffectedByInfluencers( influencers )
? createAndPrepareSingleIdEntityLoader( influencers )
: getSingleIdLoader();
} }
} }
private boolean hasNamedQueryLoader() {
return queryLoaderName != null;
}
public SingleIdEntityLoader<?> getSingleIdLoader() { public SingleIdEntityLoader<?> getSingleIdLoader() {
return singleIdLoader; return singleIdLoader;
} }
@ -3461,7 +3495,7 @@ public abstract class AbstractEntityPersister
loaded = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( loadEvent, this, entityKey ); loaded = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( loadEvent, this, entityKey );
} }
if ( loaded == null ) { if ( loaded == null ) {
loaded = singleIdLoader.load( identifier, entity, LockOptions.NONE, session ); loaded = determineLoaderToUse( session ).load( identifier, entity, LockOptions.NONE, session );
} }
if ( loaded == null ) { if ( loaded == null ) {
@ -3517,7 +3551,7 @@ public abstract class AbstractEntityPersister
@Override @Override
public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers loadQueryInfluencers) { public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers loadQueryInfluencers) {
if ( affectingFetchProfileNames != null ) { if ( affectingFetchProfileNames != null && loadQueryInfluencers.hasEnabledFetchProfiles() ) {
for ( String profileName : loadQueryInfluencers.getEnabledFetchProfileNames() ) { for ( String profileName : loadQueryInfluencers.getEnabledFetchProfileNames() ) {
if ( affectingFetchProfileNames.contains( profileName ) ) { if ( affectingFetchProfileNames.contains( profileName ) ) {
return true; return true;
@ -3529,7 +3563,7 @@ public abstract class AbstractEntityPersister
@Override @Override
public boolean isAffectedByEnabledFilters(LoadQueryInfluencers loadQueryInfluencers) { public boolean isAffectedByEnabledFilters(LoadQueryInfluencers loadQueryInfluencers) {
if ( loadQueryInfluencers.hasEnabledFilters() && filterHelper != null ) { if ( filterHelper != null && loadQueryInfluencers.hasEnabledFilters() ) {
if ( filterHelper.isAffectedBy( loadQueryInfluencers.getEnabledFilters() ) ) { if ( filterHelper.isAffectedBy( loadQueryInfluencers.getEnabledFilters() ) ) {
return true; return true;
} }

View File

@ -16,6 +16,7 @@ import java.util.function.Consumer;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader; import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
@ -728,4 +729,19 @@ public class AnonymousTupleEntityValuedModelPart
public boolean containsTableReference(String tableExpression) { public boolean containsTableReference(String tableExpression) {
return ( (TableGroupProducer) delegate ).containsTableReference( tableExpression ); return ( (TableGroupProducer) delegate ).containsTableReference( tableExpression );
} }
@Override
public int getBatchSize() {
return -1;
}
@Override
public boolean isAffectedByInfluencers(LoadQueryInfluencers influencers) {
return false;
}
@Override
public boolean isNotAffectedByInfluencers(LoadQueryInfluencers influencers) {
return true;
}
} }