lay foundation for making batch/subselect fetching per-session

still need to fix caching of loaders in persisters
This commit is contained in:
Gavin 2023-05-21 11:47:07 +02:00 committed by Gavin King
parent ed213d7cdf
commit e102dea3be
41 changed files with 338 additions and 172 deletions

View File

@ -145,4 +145,8 @@ public interface SessionBuilder {
* @see jakarta.persistence.PersistenceContextType
*/
SessionBuilder autoClose(boolean autoClose);
SessionBuilder defaultBatchFetchSize(int batchSize);
SessionBuilder subselectFetchEnabled(boolean enabled);
}

View File

@ -60,6 +60,10 @@ public interface SharedSessionBuilder extends SessionBuilder {
*/
SharedSessionBuilder flushMode();
SharedSessionBuilder defaultBatchFetchSize();
SharedSessionBuilder subselectFetchEnabled();
/**
* Signifies that the autoClose flag from the original session should be used to create the new session.
*
@ -84,4 +88,10 @@ public interface SharedSessionBuilder extends SessionBuilder {
@Override
SharedSessionBuilder autoClose(boolean autoClose);
@Override
SharedSessionBuilder defaultBatchFetchSize(int batchSize);
@Override
SharedSessionBuilder subselectFetchEnabled(boolean enabled);
}

View File

@ -1029,8 +1029,8 @@ public interface AvailableSettings {
* a value greater than one, Hibernate will use batch fetching, when
* possible, to fetch any association.
* <p>
* By default, Hibernate only uses batch fetching for associations
* explicitly annotated {@code @BatchSize}.
* By default, Hibernate only uses batch fetching for entities and
* collections explicitly annotated {@code @BatchSize}.
*
* @see org.hibernate.annotations.BatchSize
* @see org.hibernate.boot.SessionFactoryBuilder#applyDefaultBatchFetchSize(int)

View File

@ -7,7 +7,6 @@
package org.hibernate.engine.internal;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -21,6 +20,8 @@ import org.hibernate.persister.entity.EntityPersister;
import org.jboss.logging.Logger;
import static java.util.Arrays.asList;
/**
* @author Gail Badner
*/
@ -30,7 +31,7 @@ public class BatchFetchQueueHelper {
BatchFetchQueueHelper.class.getName()
);
private BatchFetchQueueHelper(){
private BatchFetchQueueHelper() {
}
/**
@ -47,23 +48,20 @@ public class BatchFetchQueueHelper {
List<?> results,
EntityPersister persister,
SharedSessionContractImplementor session) {
if ( !persister.isBatchLoadable() ) {
return;
}
if ( ids.length == results.size() ) {
return;
}
LOG.debug( "Not all entities were loaded." );
Set<Serializable> idSet = new HashSet<>( Arrays.asList( ids ) );
for ( Object result : results ) {
// All results should be in the PersistenceContext
idSet.remove( session.getContextEntityIdentifier( result ) );
}
if ( LOG.isDebugEnabled() ) {
LOG.debug( "Entities of type [" + persister.getEntityName() + "] not found; IDs: " + idSet );
}
for ( Object id : idSet ) {
removeBatchLoadableEntityKey( id, persister, session );
if ( ids.length != results.size()
&& session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
LOG.debug( "Not all entities were loaded." );
final Set<Serializable> idSet = new HashSet<>( asList( ids ) );
for ( Object result : results ) {
// All results should be in the PersistenceContext
idSet.remove( session.getContextEntityIdentifier( result ) );
}
if ( LOG.isDebugEnabled() ) {
LOG.debug( "Entities of type [" + persister.getEntityName() + "] not found; IDs: " + idSet );
}
for ( Object id : idSet ) {
removeBatchLoadableEntityKey( id, persister, session );
}
}
}
@ -75,9 +73,8 @@ public class BatchFetchQueueHelper {
Object id,
EntityPersister persister,
SharedSessionContractImplementor session) {
final EntityKey entityKey = session.generateEntityKey( id, persister );
final BatchFetchQueue batchFetchQueue = session.getPersistenceContextInternal().getBatchFetchQueue();
batchFetchQueue.removeBatchLoadableEntityKey( entityKey );
session.getPersistenceContextInternal().getBatchFetchQueue()
.removeBatchLoadableEntityKey( session.generateEntityKey( id, persister ) );
}
/**
@ -87,8 +84,7 @@ public class BatchFetchQueueHelper {
public static void removeBatchLoadableEntityKey(
EntityKey entityKey,
SharedSessionContractImplementor session) {
final BatchFetchQueue batchFetchQueue = session.getPersistenceContextInternal().getBatchFetchQueue();
batchFetchQueue.removeBatchLoadableEntityKey( entityKey );
session.getPersistenceContextInternal().getBatchFetchQueue().removeBatchLoadableEntityKey( entityKey );
}
public static void removeBatchLoadableEntityKey(

View File

@ -868,7 +868,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
public void addUninitializedCollection(CollectionPersister persister, PersistentCollection<?> collection, Object id) {
final CollectionEntry ce = new CollectionEntry( collection, persister, id, flushing );
addCollection( collection, ce, id );
if ( session.getLoadQueryInfluencers().effectiveBatchSize( persister ) > 1 ) {
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
getBatchFetchQueue().addBatchLoadableCollection( collection, ce );
}
}
@ -877,7 +877,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection<?> collection) {
final CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
addCollection( collection, ce, collection.getKey() );
if ( session.getLoadQueryInfluencers().effectiveBatchSize( persister ) > 1 ) {
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
getBatchFetchQueue().addBatchLoadableCollection( collection, ce );
}
}

View File

@ -32,7 +32,6 @@ public abstract class AbstractDelegatingSessionBuilder implements SessionBuilder
this.delegate = delegate;
}
@SuppressWarnings("unchecked")
protected SessionBuilder getThis() {
return this;
}
@ -123,4 +122,16 @@ public abstract class AbstractDelegatingSessionBuilder implements SessionBuilder
delegate.flushMode( flushMode );
return this;
}
@Override
public SessionBuilder defaultBatchFetchSize(int batchSize) {
delegate.defaultBatchFetchSize( batchSize );
return this;
}
@Override
public SessionBuilder subselectFetchEnabled(boolean enabled) {
delegate.subselectFetchEnabled( enabled );
return this;
}
}

View File

@ -32,7 +32,6 @@ public abstract class AbstractDelegatingSharedSessionBuilder implements SharedSe
this.delegate = delegate;
}
@SuppressWarnings("unchecked")
protected SharedSessionBuilder getThis() {
return this;
}
@ -165,4 +164,28 @@ public abstract class AbstractDelegatingSharedSessionBuilder implements SharedSe
delegate.jdbcTimeZone( timeZone );
return this;
}
@Override
public SharedSessionBuilder defaultBatchFetchSize(int batchSize) {
delegate.defaultBatchFetchSize( batchSize );
return this;
}
@Override
public SharedSessionBuilder subselectFetchEnabled(boolean enabled) {
delegate.subselectFetchEnabled( enabled );
return this;
}
@Override
public SharedSessionBuilder defaultBatchFetchSize() {
delegate.defaultBatchFetchSize();
return this;
}
@Override
public SharedSessionBuilder subselectFetchEnabled() {
delegate.subselectFetchEnabled();
return this;
}
}

View File

@ -145,7 +145,7 @@ public class BatchFetchQueue {
* already associated with the {@link PersistenceContext}.
*/
public void addBatchLoadableEntityKey(EntityKey key) {
if ( key.isBatchLoadable() ) {
if ( key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) ) {
if ( batchLoadableEntityKeys == null ) {
batchLoadableEntityKeys = CollectionHelper.mapOfSize( 12 );
}
@ -165,10 +165,11 @@ public class BatchFetchQueue {
* if necessary
*/
public void removeBatchLoadableEntityKey(EntityKey key) {
if ( batchLoadableEntityKeys != null && key.isBatchLoadable() ) {
LinkedHashSet<EntityKey> set = batchLoadableEntityKeys.get( key.getEntityName() );
if ( batchLoadableEntityKeys != null
&& key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) ) {
final LinkedHashSet<EntityKey> set = batchLoadableEntityKeys.get( key.getEntityName() );
if ( set != null ) {
set.remove(key);
set.remove( key );
}
}
}
@ -177,7 +178,8 @@ public class BatchFetchQueue {
* Intended for test usage. Really has no use-case in Hibernate proper.
*/
public boolean containsEntityKey(EntityKey key) {
if ( batchLoadableEntityKeys != null && key.isBatchLoadable() ) {
if ( batchLoadableEntityKeys != null
&& key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) ) {
LinkedHashSet<EntityKey> set = batchLoadableEntityKeys.get( key.getEntityName() );
if ( set != null ) {
return set.contains( key );
@ -523,4 +525,7 @@ public class BatchFetchQueue {
return false;
}
public SharedSessionContractImplementor getSession() {
return context.getSession();
}
}

View File

@ -204,7 +204,7 @@ public final class CollectionEntry implements Serializable {
: null;
collection.setSnapshot( loadedKey, role, snapshot );
final SharedSessionContractImplementor session = ((AbstractPersistentCollection<?>) collection).getSession();
if ( session.getLoadQueryInfluencers().effectiveBatchSize( loadedPersister ) > 1 ) {
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( loadedPersister ) ) {
session.getPersistenceContextInternal()
.getBatchFetchQueue()
.removeBatchLoadableCollection( this );

View File

@ -61,8 +61,8 @@ public final class EntityKey implements Serializable {
return result;
}
public boolean isBatchLoadable() {
return persister.isBatchLoadable();
public boolean isBatchLoadable(LoadQueryInfluencers influencers) {
return influencers.effectivelyBatchLoadable( persister );
}
public Object getIdentifierValue() {

View File

@ -18,8 +18,10 @@ import java.util.function.Supplier;
import org.hibernate.Filter;
import org.hibernate.UnknownProfileException;
import org.hibernate.internal.FilterImpl;
import org.hibernate.internal.SessionCreationOptions;
import org.hibernate.loader.ast.spi.CascadingFetchProfile;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
/**
* Centralize all options which can influence the SQL query needed to load an
@ -36,7 +38,10 @@ public class LoadQueryInfluencers implements Serializable {
* Static reference useful for cases where we are creating load SQL
* outside the context of any influencers. One such example is
* anything created by the session factory.
*
* @deprecated use {@link #LoadQueryInfluencers(SessionFactoryImplementor)}
*/
@Deprecated(forRemoval = true)
public static final LoadQueryInfluencers NONE = new LoadQueryInfluencers();
private final SessionFactoryImplementor sessionFactory;
@ -49,25 +54,28 @@ public class LoadQueryInfluencers implements Serializable {
//Lazily initialized!
private HashMap<String,Filter> enabledFilters;
private Boolean subselectFetchEnabled;
private boolean subselectFetchEnabled;
private Integer batchSize;
private int batchSize = -1;
private final EffectiveEntityGraph effectiveEntityGraph = new EffectiveEntityGraph();
private Boolean readOnly;
public LoadQueryInfluencers() {
this( null, null );
this.sessionFactory = null;
}
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory) {
this( sessionFactory, null );
this.sessionFactory = sessionFactory;
batchSize = sessionFactory.getSessionFactoryOptions().getDefaultBatchFetchSize();
subselectFetchEnabled = sessionFactory.getSessionFactoryOptions().isSubselectFetchEnabled();
}
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, Boolean readOnly) {
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, SessionCreationOptions options) {
this.sessionFactory = sessionFactory;
this.readOnly = readOnly;
batchSize = options.getDefaultBatchFetchSize();
subselectFetchEnabled = options.isSubselectFetchEnabled();
}
public SessionFactoryImplementor getSessionFactory() {
@ -255,28 +263,46 @@ public class LoadQueryInfluencers implements Serializable {
this.readOnly = readOnly;
}
public Integer getBatchSize() {
public int getBatchSize() {
return batchSize;
}
public void setBatchSize(Integer batchSize) {
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
public int effectiveBatchSize(CollectionPersister persister) {
return batchSize != null ? batchSize : persister.getBatchSize();
int persisterBatchSize = persister.getBatchSize();
// persister-specific batch size overrides global setting
// (note that due to legacy, -1 means no explicit setting)
return persisterBatchSize >= 0 ? persisterBatchSize : batchSize;
}
public Boolean getSubselectFetchEnabled() {
public boolean effectivelyBatchLoadable(CollectionPersister persister) {
return batchSize > 1 || persister.isBatchLoadable();
}
public int effectiveBatchSize(EntityPersister persister) {
int persisterBatchSize = persister.getBatchSize();
// persister-specific batch size overrides global setting
// (note that due to legacy, -1 means no explicit setting)
return persisterBatchSize >= 0 ? persisterBatchSize : batchSize;
}
public boolean effectivelyBatchLoadable(EntityPersister persister) {
return batchSize > 1 || persister.isBatchLoadable();
}
public boolean getSubselectFetchEnabled() {
return subselectFetchEnabled;
}
public void setSubselectFetchEnabled(Boolean subselectFetchEnabled) {
public void setSubselectFetchEnabled(boolean subselectFetchEnabled) {
this.subselectFetchEnabled = subselectFetchEnabled;
}
public boolean effectiveSubselectFetchEnabled(CollectionPersister persister) {
return subselectFetchEnabled != null ? subselectFetchEnabled : persister.isSubselectLoadable();
return subselectFetchEnabled || persister.isSubselectLoadable();
}
private void checkMutability() {
@ -286,4 +312,9 @@ public class LoadQueryInfluencers implements Serializable {
throw new IllegalStateException( "Cannot modify context-less LoadQueryInfluencers" );
}
}
public boolean hasSubselectLoadableCollections(EntityPersister persister) {
return persister.hasSubselectLoadableCollections()
|| subselectFetchEnabled && persister.hasCollections();
}
}

View File

@ -150,13 +150,9 @@ public class SubselectFetch {
}
public void addKey(EntityKey key, LoadingEntityEntry entry) {
final EntityPersister persister = entry.getDescriptor();
boolean subselectsPossible =
persister.hasCollections()
&& persister.getFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|| persister.hasSubselectLoadableCollections();
if ( subselectsPossible && shouldAddSubselectFetch( entry ) ) {
if ( batchFetchQueue.getSession().getLoadQueryInfluencers()
.hasSubselectLoadableCollections( entry.getDescriptor() )
&& shouldAddSubselectFetch( entry ) ) {
final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent(
entry.getEntityInitializer().getNavigablePath(),
navigablePath -> new SubselectFetch(
@ -178,17 +174,18 @@ public class SubselectFetch {
if ( entry.getEntityInitializer() instanceof EntityResultInitializer ) {
return true;
}
else {
final NavigablePath entityInitializerParent = entry.getEntityInitializer().getNavigablePath().getParent();
final NavigablePath entityInitializerParent = entry.getEntityInitializer().getNavigablePath().getParent();
// We want to add only the collections of the loading entities
for ( DomainResult<?> domainResult : loadingSqlAst.getDomainResultDescriptors() ) {
if ( domainResult.getNavigablePath().equals( entityInitializerParent ) ) {
return true;
// We want to add only the collections of the loading entities
for ( DomainResult<?> domainResult : loadingSqlAst.getDomainResultDescriptors() ) {
if ( domainResult.getNavigablePath().equals( entityInitializerParent ) ) {
return true;
}
}
}
return false;
return false;
}
}
}
}

View File

@ -325,7 +325,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
EntityPersister persister,
EntityKey keyToLoad,
EventSource session) {
if ( keyToLoad.isBatchLoadable() ) {
if ( keyToLoad.isBatchLoadable( session.getLoadQueryInfluencers() ) ) {
// Add a batch-fetch entry into the queue for this entity
session.getPersistenceContextInternal().getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
}
@ -565,10 +565,9 @@ public class DefaultLoadEventListener implements LoadEventListener {
}
private Object load(LoadEvent event, EntityPersister persister, EntityKey keyToLoad) {
final EventSource session = event.getSession();
final Object entity = loadFromCacheOrDatasource( event, persister, keyToLoad );
if ( entity != null && persister.hasNaturalIdentifier() ) {
session.getPersistenceContextInternal().getNaturalIdResolutions()
event.getSession().getPersistenceContextInternal().getNaturalIdResolutions()
.cacheResolutionFromLoad(
event.getEntityId(),
persister.getNaturalIdMapping().extractNaturalIdFromEntity( entity ),
@ -579,13 +578,12 @@ public class DefaultLoadEventListener implements LoadEventListener {
}
private Object loadFromCacheOrDatasource(LoadEvent event, EntityPersister persister, EntityKey keyToLoad) {
final EventSource session = event.getSession();
final Object entity = CacheEntityLoaderHelper.INSTANCE.loadFromSecondLevelCache( event, persister, keyToLoad );
if ( entity != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
"Resolved object in second-level cache: {0}",
infoString( persister, event.getEntityId(), session.getFactory() )
infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
return entity;
@ -594,7 +592,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
"Object not resolved in any cache: {0}",
infoString( persister, event.getEntityId(), session.getFactory() )
infoString( persister, event.getEntityId(), event.getSession().getFactory() )
);
}
return loadFromDatasource( event, persister );

View File

@ -73,21 +73,23 @@ public class EvictVisitor extends AbstractVisitor {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final CollectionEntry ce = persistenceContext.removeCollectionEntry( collection );
final CollectionPersister persister = ce.getLoadedPersister();
final Object loadedKey = ce.getLoadedKey();
if ( LOG.isDebugEnabled() ) {
LOG.debugf(
"Evicting collection: %s",
collectionInfoString( persister, collection, ce.getLoadedKey(), session )
collectionInfoString( persister, collection, loadedKey, session )
);
}
if ( persister != null
&& session.getLoadQueryInfluencers().effectiveBatchSize(persister) > 1 ) {
persistenceContext.getBatchFetchQueue().removeBatchLoadableCollection( ce );
}
if ( persister != null && ce.getLoadedKey() != null ) {
//TODO: is this 100% correct?
persistenceContext.removeCollectionByKey( new CollectionKey( persister, ce.getLoadedKey() ) );
if ( persister != null ) {
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
persistenceContext.getBatchFetchQueue().removeBatchLoadableCollection( ce );
}
if ( loadedKey != null ) {
//TODO: is this 100% correct?
persistenceContext.removeCollectionByKey( new CollectionKey( persister, loadedKey) );
}
}
}

View File

@ -28,6 +28,10 @@ public interface SessionCreationOptions {
FlushMode getInitialSessionFlushMode();
boolean isSubselectFetchEnabled();
int getDefaultBatchFetchSize();
boolean shouldAutoClose();
boolean shouldAutoClear();

View File

@ -31,6 +31,7 @@ import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionEventListener;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
@ -1195,6 +1196,8 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
private String tenantIdentifier;
private TimeZone jdbcTimeZone;
private boolean explicitNoInterceptor;
private int defaultBatchFetchSize;
private boolean subselectFetchEnabled;
// Lazy: defaults can be built by invoking the builder in fastSessionServices.defaultSessionEventListeners
// (Need a fresh build for each Session as the listener instances can't be reused across sessions)
@ -1212,6 +1215,8 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
this.statementInspector = sessionFactoryOptions.getStatementInspector();
this.connectionHandlingMode = sessionFactoryOptions.getPhysicalConnectionHandlingMode();
this.autoClose = sessionFactoryOptions.isAutoCloseSessionEnabled();
this.defaultBatchFetchSize = sessionFactoryOptions.getDefaultBatchFetchSize();
this.subselectFetchEnabled = sessionFactoryOptions.isSubselectFetchEnabled();
final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = sessionFactory.getCurrentTenantIdentifierResolver();
if ( currentTenantIdentifierResolver != null ) {
@ -1241,6 +1246,16 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
return flushMode;
}
@Override
public boolean isSubselectFetchEnabled() {
return subselectFetchEnabled;
}
@Override
public int getDefaultBatchFetchSize() {
return defaultBatchFetchSize;
}
@Override
public boolean shouldAutoClose() {
return autoClose;
@ -1345,6 +1360,18 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
return this;
}
@Override
public SessionBuilder defaultBatchFetchSize(int batchSize) {
defaultBatchFetchSize = batchSize;
return this;
}
@Override
public SessionBuilder subselectFetchEnabled(boolean enabled) {
subselectFetchEnabled = enabled;
return null;
}
@Override
public SessionBuilderImpl flushMode(FlushMode flushMode) {
this.flushMode = flushMode;
@ -1428,6 +1455,16 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
return FlushMode.ALWAYS;
}
@Override
public boolean isSubselectFetchEnabled() {
return false;
}
@Override
public int getDefaultBatchFetchSize() {
return -1;
}
@Override
public boolean shouldAutoClose() {
return false;

View File

@ -239,7 +239,7 @@ public class SessionImpl
}
}
loadQueryInfluencers = new LoadQueryInfluencers( factory );
loadQueryInfluencers = new LoadQueryInfluencers( factory, options );
final StatisticsImplementor statistics = factory.getStatistics();
if ( statistics.isStatisticsEnabled() ) {
@ -1932,6 +1932,7 @@ public class SessionImpl
loadQueryInfluencers.disableFetchProfile( name );
}
// TODO: unused for now, should we promote to Session?
public void setSubselectFetchingEnabled(boolean enabled) {
loadQueryInfluencers.setSubselectFetchEnabled( enabled );
}
@ -1940,6 +1941,7 @@ public class SessionImpl
return loadQueryInfluencers.getSubselectFetchEnabled();
}
// TODO: unused for now, should we promote to Session?
public void setFetchBatchSize(int batchSize) {
loadQueryInfluencers.setBatchSize( batchSize );
}
@ -2147,6 +2149,28 @@ public class SessionImpl
return this;
}
@Override
public SharedSessionBuilderImpl defaultBatchFetchSize(int batchSize) {
super.defaultBatchFetchSize( batchSize );
return this;
}
@Override
public SharedSessionBuilderImpl subselectFetchEnabled(boolean enabled) {
super.subselectFetchEnabled( enabled );
return this;
}
@Override
public SharedSessionBuilder defaultBatchFetchSize() {
return defaultBatchFetchSize( session.getFetchBatchSize() );
}
@Override
public SharedSessionBuilder subselectFetchEnabled() {
return subselectFetchEnabled( session.isSubselectFetchingEnabled() );
}
@Override
public SharedSessionBuilderImpl autoClose() {
autoClose( session.autoClose );
@ -2911,11 +2935,7 @@ public class SessionImpl
}
private Boolean getReadOnlyFromLoadQueryInfluencers() {
Boolean readOnly = null;
if ( loadQueryInfluencers != null ) {
readOnly = loadQueryInfluencers.getReadOnly();
}
return readOnly;
return loadQueryInfluencers != null ? loadQueryInfluencers.getReadOnly() : null;
}
@Override @Deprecated(forRemoval = true)

View File

@ -62,13 +62,13 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
public class StatelessSessionImpl extends AbstractSharedSessionContract implements StatelessSession {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( StatelessSessionImpl.class );
private static final LoadQueryInfluencers NO_INFLUENCERS = new LoadQueryInfluencers( null ) {
@Override
private static final LoadQueryInfluencers NO_INFLUENCERS = new LoadQueryInfluencers() {
@Override @Deprecated
public String getInternalFetchProfile() {
return null;
}
@Override
@Override @Deprecated
public void setInternalFetchProfile(String internalFetchProfile) {
}
};

View File

@ -156,7 +156,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
lockOptions,
fetchProcessor,
true,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
sessionFactory
);

View File

@ -76,7 +76,7 @@ class DatabaseSnapshotExecutor {
LockOptions.NONE,
(fetchParent, creationState) -> ImmutableFetchList.EMPTY,
true,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
sessionFactory
);

View File

@ -178,7 +178,7 @@ public class EntityBatchLoaderArrayParam<T>
sqlAst = LoaderSelectBuilder.createSelectBySingleArrayParameter(
getLoadable(),
identifierMapping,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
LockOptions.NONE,
jdbcParameter,
sessionFactory

View File

@ -312,7 +312,7 @@ public class EntityBatchLoaderInPredicate<T>
identifierMapping,
null,
sqlBatchSize,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
LockOptions.NONE,
jdbcParameters::add,
sessionFactory

View File

@ -227,9 +227,8 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
getSessionFactory()
);
final JdbcServices jdbcServices = getSessionFactory().getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstTranslatorFactory sqlAstTranslatorFactory =
getSessionFactory().getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
int offset = 0;
@ -252,10 +251,7 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
.translate( jdbcParameterBindings, QueryOptions.NONE );
final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler;
final EntityPersister persister = getLoadable().getEntityPersister();
if ( persister.hasCollections()
&& session.getSessionFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|| persister.hasSubselectLoadableCollections() ) {
if ( session.getLoadQueryInfluencers().hasSubselectLoadableCollections( getLoadable().getEntityPersister() ) ) {
subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
session.getPersistenceContext().getBatchFetchQueue(),
sqlAst,

View File

@ -84,9 +84,8 @@ public class MultiNaturalIdLoadingBatcher {
this.keyValueResolver = keyValueResolver;
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstTranslatorFactory sqlAstTranslatorFactory =
sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
this.jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
.translate( null, QueryOptions.NONE );
}
@ -139,10 +138,7 @@ public class MultiNaturalIdLoadingBatcher {
private <E> List<E> performLoad(JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session) {
final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler;
final EntityPersister persister = entityDescriptor.getEntityPersister();
if ( persister.hasCollections()
&& session.getSessionFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|| persister.hasSubselectLoadableCollections() ) {
if ( session.getLoadQueryInfluencers().hasSubselectLoadableCollections( entityDescriptor.getEntityPersister() ) ) {
subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
session.getPersistenceContext().getBatchFetchQueue(),
sqlSelect,

View File

@ -34,6 +34,8 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import static java.util.Collections.singletonList;
/**
* @author Steve Ebersole
*/
@ -73,7 +75,7 @@ public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEn
Collections.emptyList(),
uniqueKeyAttribute,
null,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
LockOptions.NONE,
jdbcParameters::add,
sessionFactory
@ -125,10 +127,10 @@ public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEn
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
final SelectStatement sqlAst = LoaderSelectBuilder.createSelectByUniqueKey(
entityDescriptor,
Collections.singletonList( entityDescriptor.getIdentifierMapping() ),
singletonList( entityDescriptor.getIdentifierMapping() ),
uniqueKeyAttribute,
null,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
LockOptions.NONE,
jdbcParameters::add,
sessionFactory

View File

@ -69,7 +69,7 @@ public class GeneratedValuesProcessor {
entityDescriptor.getIdentifierMapping(),
null,
1,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( sessionFactory ),
LockOptions.READ,
jdbcParameters::add,
sessionFactory

View File

@ -280,8 +280,7 @@ public abstract class AbstractCollectionPersister
// isSet = collectionBinding.isSet();
// isSorted = collectionBinding.isSorted();
isPrimitiveArray = collectionBootDescriptor.isPrimitiveArray();
subselectLoadable = collectionBootDescriptor.isSubselectLoadable()
|| factory.getSessionFactoryOptions().isSubselectFetchEnabled();
subselectLoadable = collectionBootDescriptor.isSubselectLoadable();
qualifiedTableName = determineTableName( table );
@ -310,11 +309,7 @@ public abstract class AbstractCollectionPersister
hasOrphanDelete = collectionBootDescriptor.hasOrphanDelete();
int batch = collectionBootDescriptor.getBatchSize();
if ( batch == -1 ) {
batch = creationContext.getSessionFactoryOptions().getDefaultBatchFetchSize();
}
batchSize = batch;
batchSize = collectionBootDescriptor.getBatchSize();
isVersioned = collectionBootDescriptor.isOptimisticLocked();
@ -620,7 +615,7 @@ public abstract class AbstractCollectionPersister
@Override
public void postInstantiate() throws MappingException {
if ( queryLoaderName == null ) {
collectionLoader = createCollectionLoader( LoadQueryInfluencers.NONE );
collectionLoader = createCollectionLoader( new LoadQueryInfluencers( factory ) );
}
else {
// We pass null as metamodel because we did the initialization during construction already
@ -632,8 +627,8 @@ public abstract class AbstractCollectionPersister
if ( attributeMapping.getIndexDescriptor() != null ) {
collectionElementLoaderByIndex = new CollectionElementLoaderByIndex(
attributeMapping,
LoadQueryInfluencers.NONE,
getFactory()
new LoadQueryInfluencers( factory ),
factory
);
}
@ -691,7 +686,7 @@ public abstract class AbstractCollectionPersister
localCopy = collectionLoader;
}
else {
localCopy = createCollectionLoader( LoadQueryInfluencers.NONE );
localCopy = createCollectionLoader( new LoadQueryInfluencers( factory ) );
}
standardCollectionLoader = localCopy;
}
@ -753,7 +748,7 @@ public abstract class AbstractCollectionPersister
protected CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
if ( canUseReusableCollectionLoader( loadQueryInfluencers ) ) {
if ( reusableCollectionLoader == null ) {
reusableCollectionLoader = generateCollectionLoader( LoadQueryInfluencers.NONE );
reusableCollectionLoader = generateCollectionLoader( new LoadQueryInfluencers( factory ) );
}
return reusableCollectionLoader;
}
@ -769,8 +764,8 @@ public abstract class AbstractCollectionPersister
}
private CollectionLoader generateCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
if ( batchSize > 1 ) {
if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) {
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
return getFactory().getServiceRegistry()
.getService( BatchLoaderFactory.class )
.createCollectionBatchLoader( batchSize, loadQueryInfluencers, attributeMapping, getFactory() );
@ -894,8 +889,8 @@ public abstract class AbstractCollectionPersister
LockOptions.NONE,
(fetchParent, creationState) -> ImmutableFetchList.EMPTY,
true,
LoadQueryInfluencers.NONE,
getFactory()
new LoadQueryInfluencers( factory ),
factory
);
final NavigablePath entityPath = new NavigablePath( attributeMapping.getRootPathName() );
@ -1525,6 +1520,11 @@ public abstract class AbstractCollectionPersister
return batchSize;
}
@Override
public boolean isBatchLoadable() {
return batchSize > 1;
}
@Override
public String getMappedByProperty() {
return mappedByProperty;

View File

@ -310,7 +310,10 @@ public interface CollectionPersister extends Restrictable {
boolean elementExists(Object key, Object element, SharedSessionContractImplementor session);
Object getElementByIndex(Object key, Object index, SharedSessionContractImplementor session, Object owner);
default int getBatchSize() {
return 0;
return -1;
}
default boolean isBatchLoadable() {
return getBatchSize() > 1;
}
default boolean isSubselectLoadable() {
return false;

View File

@ -110,7 +110,6 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.FilterImpl;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.internal.util.LazyValue;
import org.hibernate.internal.util.StringHelper;
@ -507,13 +506,8 @@ public abstract class AbstractEntityPersister
final Dialect dialect = creationContext.getDialect();
int batch = persistentClass.getBatchSize();
if ( batch == -1 ) {
batch = creationContext.getSessionFactoryOptions().getDefaultBatchFetchSize();
}
batchSize = batch;
hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections()
|| entityMetamodel.hasCollections() && factory.getSessionFactoryOptions().isSubselectFetchEnabled();
batchSize = persistentClass.getBatchSize();
hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections();
hasPartitionedSelectionMapping = persistentClass.hasPartitionedSelectionMapping();
hasCollectionNotReferencingPK = persistentClass.hasCollectionNotReferencingPK();
@ -532,19 +526,17 @@ public abstract class AbstractEntityPersister
if ( persistentClass.getLoaderName() != null ) {
// 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() );
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 if ( batchSize > 1 ) {
singleIdLoader = createBatchingIdEntityLoader( this, batchSize, factory );
}
else {
singleIdLoader = new SingleIdEntityLoaderStandardImpl<>( this, factory );
singleIdLoader = createSingleIdEntityLoader( new LoadQueryInfluencers( factory ) );
}
multiIdLoader = buildMultiIdLoader( persistentClass );
@ -815,6 +807,16 @@ public abstract class AbstractEntityPersister
fullDiscriminatorValues = toObjectArray( values );
}
private SingleIdEntityLoader<?> createSingleIdEntityLoader(LoadQueryInfluencers loadQueryInfluencers) {
if ( loadQueryInfluencers.effectivelyBatchLoadable( this ) ) {
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
return createBatchingIdEntityLoader( this, batchSize, factory );
}
else {
return new SingleIdEntityLoaderStandardImpl<>( this, factory );
}
}
public static Map<String, String> getEntityNameByTableNameMap(PersistentClass persistentClass) {
final Map<String, String> entityNameByTableNameMap = new HashMap<>();
PersistentClass superType = persistentClass.getSuperPersistentClass();
@ -1188,7 +1190,7 @@ public abstract class AbstractEntityPersister
getIdentifierMapping(),
null,
1,
LoadQueryInfluencers.NONE,
new LoadQueryInfluencers( factory ),
LockOptions.NONE,
jdbcParameters::add,
factory
@ -1643,6 +1645,11 @@ public abstract class AbstractEntityPersister
return batchSize > 1;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public String[] getIdentifierColumnNames() {
return rootTableKeyColumnNames;
@ -1707,8 +1714,8 @@ public abstract class AbstractEntityPersister
LockOptions.NONE,
this::fetchProcessor,
true,
LoadQueryInfluencers.NONE,
getFactory()
new LoadQueryInfluencers( factory ),
factory
);
final NavigablePath entityPath = new NavigablePath( getRootPathName() );

View File

@ -749,9 +749,22 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
ClassMetadata getClassMetadata();
/**
* Is batch loading enabled?
* The batch size for batch loading.
*
* @see org.hibernate.engine.spi.LoadQueryInfluencers#effectiveBatchSize(EntityPersister)
*/
boolean isBatchLoadable();
default int getBatchSize() {
return -1;
}
/**
* Is batch loading enabled?
*
* @see org.hibernate.engine.spi.LoadQueryInfluencers#effectivelyBatchLoadable(EntityPersister)
*/
default boolean isBatchLoadable() {
return getBatchSize() > 1;
}
/**
* Is select snapshot before update enabled?

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultAssembler;
@ -75,6 +76,11 @@ public class JdbcValuesMappingImpl extends StandardJdbcValuesMapping {
public SqlAstCreationContext getSqlAstCreationContext() {
return creationState.getSqlAstCreationContext();
}
@Override
public ExecutionContext getExecutionContext() {
return creationState.getExecutionContext();
}
};
}
return super.resolveAssemblers( finalCreationState );

View File

@ -66,7 +66,7 @@ public class FakeSqmToSqlAstConverter extends BaseSemanticQueryWalker implements
@Override
public LoadQueryInfluencers getLoadQueryInfluencers() {
return LoadQueryInfluencers.NONE;
return new LoadQueryInfluencers( getCreationContext().getSessionFactory() );
}
@Override

View File

@ -12,6 +12,7 @@ import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.exec.spi.ExecutionContext;
/**
* @author Steve Ebersole
@ -30,4 +31,6 @@ public interface AssemblerCreationState {
Supplier<Initializer> producer);
SqlAstCreationContext getSqlAstCreationContext();
ExecutionContext getExecutionContext();
}

View File

@ -351,9 +351,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// 2) build the EntityKey
entityKey = new EntityKey( id, concreteDescriptor );
// 3) schedule the EntityKey for batch loading, if possible
if ( concreteDescriptor.isBatchLoadable() ) {
final SharedSessionContractImplementor session = rowProcessingState.getSession();
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( concreteDescriptor ) ) {
final PersistenceContext persistenceContext =
rowProcessingState.getSession().getPersistenceContextInternal();
session.getPersistenceContextInternal();
if ( !persistenceContext.containsEntity( entityKey ) ) {
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( entityKey );
}

View File

@ -19,6 +19,10 @@ import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import static org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializerBuilder.BatchMode.BATCH_INITIALIZE;
import static org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializerBuilder.BatchMode.BATCH_LOAD;
import static org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializerBuilder.BatchMode.NONE;
public class EntitySelectFetchInitializerBuilder {
public static AbstractFetchParentAccess createInitializer(
@ -83,8 +87,12 @@ public class EntitySelectFetchInitializerBuilder {
EntityPersister entityPersister,
FetchParentAccess parentAccess,
AssemblerCreationState creationState) {
if ( !entityPersister.isBatchLoadable() || creationState.isScrollResult() ) {
return BatchMode.NONE;
if ( creationState.isScrollResult()
|| !creationState.getExecutionContext()
.getSession()
.getLoadQueryInfluencers()
.effectivelyBatchLoadable( entityPersister ) ) {
return NONE;
}
while ( parentAccess.isEmbeddableInitializer() ) {
final EmbeddableInitializer embeddableInitializer = parentAccess.asEmbeddableInitializer();
@ -95,9 +103,11 @@ public class EntitySelectFetchInitializerBuilder {
if ( initializedPart.isEntityIdentifierMapping()
// todo: check if the virtual check is necessary
|| initializedPart.isVirtual()
// If the parent embeddable has a custom instantiator, we can't inject entities later through setValues()
|| !( initializedPart.getMappedType().getRepresentationStrategy().getInstantiator() instanceof StandardEmbeddableInstantiator ) ) {
return entityPersister.hasSubclasses() ? BatchMode.NONE : BatchMode.BATCH_INITIALIZE;
// If the parent embeddable has a custom instantiator,
// we can't inject entities later through setValues()
|| !( initializedPart.getMappedType().getRepresentationStrategy().getInstantiator()
instanceof StandardEmbeddableInstantiator ) ) {
return entityPersister.hasSubclasses() ? NONE : BATCH_INITIALIZE;
}
parentAccess = parentAccess.getFetchParentAccess();
if ( parentAccess == null ) {
@ -111,10 +121,10 @@ public class EntitySelectFetchInitializerBuilder {
if ( cacheAccess != null ) {
// Do batch initialization instead of batch loading if the parent entity is cacheable
// to avoid putting entity state into the cache at a point when the association is not yet set
return BatchMode.BATCH_INITIALIZE;
return BATCH_INITIALIZE;
}
}
return BatchMode.BATCH_LOAD;
return BATCH_LOAD;
}
enum BatchMode {

View File

@ -121,6 +121,11 @@ public class ResultsHelper {
public SqlAstCreationContext getSqlAstCreationContext() {
return sessionFactory;
}
@Override
public ExecutionContext getExecutionContext() {
return executionContext;
}
}
);

View File

@ -120,7 +120,7 @@ public class ManyToOneType extends EntityType {
//cannot batch fetch by unique key (property-ref associations)
if ( uniqueKeyPropertyName == null && id != null ) {
final EntityPersister persister = getAssociatedEntityPersister( session.getFactory() );
if ( persister.isBatchLoadable() ) {
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
final EntityKey entityKey = session.generateEntityKey( id, persister );
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( !persistenceContext.containsEntity( entityKey ) ) {

View File

@ -529,11 +529,6 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return null;
}
@Override
public boolean isBatchLoadable() {
return false;
}
@Override
public boolean isSelectBeforeUpdateRequired() {
return false;

View File

@ -22,7 +22,6 @@ import org.hibernate.engine.spi.AbstractDelegatingSharedSessionBuilder;
@SuppressWarnings("unused")
public class TestDelegatingSharedSessionBuilder extends AbstractDelegatingSharedSessionBuilder {
@SuppressWarnings("rawtypes")
public TestDelegatingSharedSessionBuilder(SharedSessionBuilder delegate) {
super( delegate );
}

View File

@ -545,11 +545,6 @@ public class PersisterClassProviderTest {
return null;
}
@Override
public boolean isBatchLoadable() {
return false;
}
@Override
public boolean isSelectBeforeUpdateRequired() {
return false;

View File

@ -616,10 +616,6 @@ public class CustomPersister implements EntityPersister {
return true;
}
public boolean isBatchLoadable() {
return false;
}
public Type getPropertyType(String propertyName) {
throw new UnsupportedOperationException();
}