HHH-16651 introduce new setting to globally enable the use of subselect fetching
and lay foundation for making this settable on the Session
This commit is contained in:
parent
2b0bc61873
commit
eb959722f9
|
@ -46,7 +46,7 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
else {
|
||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||
if ( isNegated ) {
|
||||
appendSql( "not(" );
|
||||
appendSql( "not (" );
|
||||
}
|
||||
booleanExpressionPredicate.getExpression().accept( this );
|
||||
if ( isNegated ) {
|
||||
|
@ -188,7 +188,7 @@ public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends Ab
|
|||
@Override
|
||||
public void visitInArrayPredicate(InArrayPredicate inArrayPredicate) {
|
||||
inArrayPredicate.getTestExpression().accept( this );
|
||||
appendSql( " = ANY(" );
|
||||
appendSql( " = any(" );
|
||||
inArrayPredicate.getArrayParameter().accept( this );
|
||||
appendSql( ')' );
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ import static org.hibernate.cfg.AvailableSettings.USE_SCROLLABLE_RESULTSET;
|
|||
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SQL_COMMENTS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_STRUCTURED_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SUBSELECT_FETCH;
|
||||
import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN;
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
|
||||
|
@ -194,6 +195,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
private boolean delayBatchFetchLoaderCreations;
|
||||
private int defaultBatchFetchSize;
|
||||
private Integer maximumFetchDepth;
|
||||
private boolean subselectFetchEnabled;
|
||||
private NullPrecedence defaultNullPrecedence;
|
||||
private boolean orderUpdatesEnabled;
|
||||
private boolean orderInsertsEnabled;
|
||||
|
@ -361,6 +363,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
this.batchFetchStyle = BatchFetchStyle.interpret( configurationSettings.get( BATCH_FETCH_STYLE ) );
|
||||
this.delayBatchFetchLoaderCreations = configurationService.getSetting( DELAY_ENTITY_LOADER_CREATIONS, BOOLEAN, true );
|
||||
this.defaultBatchFetchSize = getInt( DEFAULT_BATCH_FETCH_SIZE, configurationSettings, -1 );
|
||||
this.subselectFetchEnabled = getBoolean( USE_SUBSELECT_FETCH, configurationSettings );
|
||||
this.maximumFetchDepth = getInteger( MAX_FETCH_DEPTH, configurationSettings );
|
||||
final String defaultNullPrecedence = getString(
|
||||
AvailableSettings.DEFAULT_NULL_ORDERING, configurationSettings, "none", "first", "last"
|
||||
|
@ -972,6 +975,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
return maximumFetchDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubselectFetchEnabled() {
|
||||
return subselectFetchEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NullPrecedence getDefaultNullPrecedence() {
|
||||
return defaultNullPrecedence;
|
||||
|
@ -1342,6 +1350,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
this.maximumFetchDepth = depth;
|
||||
}
|
||||
|
||||
public void applySubselectFetchEnabled(boolean subselectFetchEnabled) {
|
||||
this.subselectFetchEnabled = subselectFetchEnabled;
|
||||
}
|
||||
|
||||
public void applyDefaultNullPrecedence(NullPrecedence nullPrecedence) {
|
||||
this.defaultNullPrecedence = nullPrecedence;
|
||||
}
|
||||
|
|
|
@ -187,6 +187,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
|||
return delegate.getMaximumFetchDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubselectFetchEnabled() {
|
||||
return delegate.isSubselectFetchEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NullPrecedence getDefaultNullPrecedence() {
|
||||
return delegate.getDefaultNullPrecedence();
|
||||
|
|
|
@ -144,6 +144,8 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
|
|||
|
||||
Integer getMaximumFetchDepth();
|
||||
|
||||
boolean isSubselectFetchEnabled();
|
||||
|
||||
NullPrecedence getDefaultNullPrecedence();
|
||||
|
||||
boolean isOrderUpdatesEnabled();
|
||||
|
|
|
@ -1032,6 +1032,17 @@ public interface AvailableSettings {
|
|||
*/
|
||||
String DEFAULT_BATCH_FETCH_SIZE = "hibernate.default_batch_fetch_size";
|
||||
|
||||
/**
|
||||
* When enabled, Hibernate will use subselect fetching, when possible, to
|
||||
* fetch any collection.
|
||||
* <p>
|
||||
* By default, Hibernate only uses subselect fetching for collections
|
||||
* explicitly annotated {@code @Fetch(SUBSELECT)}.
|
||||
*
|
||||
* @see org.hibernate.annotations.FetchMode#SUBSELECT
|
||||
*/
|
||||
String USE_SUBSELECT_FETCH = "hibernate.use_subselect_fetch";
|
||||
|
||||
/**
|
||||
* When enabled, specifies that JDBC scrollable {@code ResultSet}s may be used.
|
||||
* This property is only necessary when there is no {@code ConnectionProvider},
|
||||
|
|
|
@ -46,7 +46,7 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
else {
|
||||
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||
if ( isNegated ) {
|
||||
appendSql( "not(" );
|
||||
appendSql( "not (" );
|
||||
}
|
||||
booleanExpressionPredicate.getExpression().accept( this );
|
||||
if ( isNegated ) {
|
||||
|
@ -160,7 +160,7 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
@Override
|
||||
public void visitInArrayPredicate(InArrayPredicate inArrayPredicate) {
|
||||
inArrayPredicate.getTestExpression().accept( this );
|
||||
appendSql( " = ANY(" );
|
||||
appendSql( " = any(" );
|
||||
inArrayPredicate.getArrayParameter().accept( this );
|
||||
appendSql( ')' );
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ public class DerbySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlA
|
|||
if ( inListPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in(" );
|
||||
appendSql( " in (" );
|
||||
renderCommaSeparated( listExpressions );
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
}
|
||||
|
|
|
@ -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 ( persister.getBatchSize() > 1 ) {
|
||||
if ( session.getLoadQueryInfluencers().effectiveBatchSize( persister ) > 1 ) {
|
||||
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 ( persister.getBatchSize() > 1 ) {
|
||||
if ( session.getLoadQueryInfluencers().effectiveBatchSize( persister ) > 1 ) {
|
||||
getBatchFetchQueue().addBatchLoadableCollection( collection, ce );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.engine.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.collection.spi.AbstractPersistentCollection;
|
||||
|
@ -21,6 +15,12 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* We need an entry to tell us all about the current state
|
||||
* of a collection with respect to its persistent state
|
||||
|
@ -202,10 +202,10 @@ public final class CollectionEntry implements Serializable {
|
|||
snapshot = loadedPersister.isMutable()
|
||||
? collection.getSnapshot( loadedPersister )
|
||||
: null;
|
||||
collection.setSnapshot(loadedKey, role, snapshot);
|
||||
if ( loadedPersister.getBatchSize() > 1 ) {
|
||||
( (AbstractPersistentCollection<?>) collection ).getSession()
|
||||
.getPersistenceContextInternal()
|
||||
collection.setSnapshot( loadedKey, role, snapshot );
|
||||
final SharedSessionContractImplementor session = ((AbstractPersistentCollection<?>) collection).getSession();
|
||||
if ( session.getLoadQueryInfluencers().effectiveBatchSize( loadedPersister ) > 1 ) {
|
||||
session.getPersistenceContextInternal()
|
||||
.getBatchFetchQueue()
|
||||
.removeBatchLoadableCollection( this );
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.Filter;
|
|||
import org.hibernate.UnknownProfileException;
|
||||
import org.hibernate.internal.FilterImpl;
|
||||
import org.hibernate.loader.ast.spi.CascadingFetchProfile;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
/**
|
||||
* Centralize all options which can influence the SQL query needed to load an
|
||||
|
@ -48,6 +49,10 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
//Lazily initialized!
|
||||
private HashMap<String,Filter> enabledFilters;
|
||||
|
||||
private Boolean subselectFetchEnabled;
|
||||
|
||||
private Integer batchSize;
|
||||
|
||||
private final EffectiveEntityGraph effectiveEntityGraph = new EffectiveEntityGraph();
|
||||
|
||||
private Boolean readOnly;
|
||||
|
@ -57,7 +62,7 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
}
|
||||
|
||||
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory) {
|
||||
this(sessionFactory, null);
|
||||
this( sessionFactory, null );
|
||||
}
|
||||
|
||||
public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, Boolean readOnly) {
|
||||
|
@ -73,13 +78,13 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public <T> T fromInternalFetchProfile(CascadingFetchProfile profile, Supplier<T> supplier) {
|
||||
final CascadingFetchProfile previous = this.enabledCascadingFetchProfile;
|
||||
this.enabledCascadingFetchProfile = profile;
|
||||
final CascadingFetchProfile previous = enabledCascadingFetchProfile;
|
||||
enabledCascadingFetchProfile = profile;
|
||||
try {
|
||||
return supplier.get();
|
||||
}
|
||||
finally {
|
||||
this.enabledCascadingFetchProfile = previous;
|
||||
enabledCascadingFetchProfile = previous;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,6 +255,30 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public Integer getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
public void setBatchSize(Integer batchSize) {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
|
||||
public int effectiveBatchSize(CollectionPersister persister) {
|
||||
return batchSize != null ? batchSize : persister.getBatchSize();
|
||||
}
|
||||
|
||||
public Boolean getSubselectFetchEnabled() {
|
||||
return subselectFetchEnabled;
|
||||
}
|
||||
|
||||
public void setSubselectFetchEnabled(Boolean subselectFetchEnabled) {
|
||||
this.subselectFetchEnabled = subselectFetchEnabled;
|
||||
}
|
||||
|
||||
public boolean effectiveSubselectFetchEnabled(CollectionPersister persister) {
|
||||
return subselectFetchEnabled != null ? subselectFetchEnabled : persister.isSubselectLoadable();
|
||||
}
|
||||
|
||||
private void checkMutability() {
|
||||
if ( sessionFactory == null ) {
|
||||
// that's the signal that this is the immutable, context-less
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
|
@ -149,11 +150,13 @@ public class SubselectFetch {
|
|||
}
|
||||
|
||||
public void addKey(EntityKey key, LoadingEntityEntry entry) {
|
||||
if ( !entry.getDescriptor().hasSubselectLoadableCollections() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( shouldAddSubselectFetch( entry ) ) {
|
||||
final EntityPersister persister = entry.getDescriptor();
|
||||
boolean subselectsPossible =
|
||||
persister.hasCollections()
|
||||
&& persister.getFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|
||||
|| persister.hasSubselectLoadableCollections();
|
||||
if ( subselectsPossible && shouldAddSubselectFetch( entry ) ) {
|
||||
final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent(
|
||||
entry.getEntityInitializer().getNavigablePath(),
|
||||
navigablePath -> new SubselectFetch(
|
||||
|
|
|
@ -15,9 +15,11 @@ import org.hibernate.engine.spi.PersistenceContext;
|
|||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.type.CollectionType;
|
||||
|
||||
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
|
||||
|
||||
/**
|
||||
* Evict any collections referenced by the object from the session cache.
|
||||
* This will NOT pick up any collections that were dereferenced, so they
|
||||
|
@ -37,8 +39,8 @@ public class EvictVisitor extends AbstractVisitor {
|
|||
|
||||
@Override
|
||||
Object processCollection(Object collection, CollectionType type) throws HibernateException {
|
||||
if (collection != null) {
|
||||
evictCollection(collection, type);
|
||||
if ( collection != null ) {
|
||||
evictCollection( collection, type );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -55,34 +57,37 @@ public class EvictVisitor extends AbstractVisitor {
|
|||
}
|
||||
else if ( value == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
final Object keyOfOwner = type.getKeyOfOwner( owner, session );
|
||||
collection = (PersistentCollection<?>) type.getCollection( keyOfOwner, session, owner, Boolean.FALSE );
|
||||
collection = (PersistentCollection<?>) type.getCollection( keyOfOwner, session, owner, false );
|
||||
}
|
||||
else {
|
||||
return; //EARLY EXIT!
|
||||
}
|
||||
|
||||
if ( collection != null && collection.unsetSession(session) ) {
|
||||
evictCollection(collection);
|
||||
if ( collection != null && collection.unsetSession( session ) ) {
|
||||
evictCollection( collection );
|
||||
}
|
||||
}
|
||||
|
||||
private void evictCollection(PersistentCollection<?> collection) {
|
||||
final PersistenceContext persistenceContext = getSession().getPersistenceContextInternal();
|
||||
CollectionEntry ce = persistenceContext.removeCollectionEntry( collection );
|
||||
final EventSource session = getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final CollectionEntry ce = persistenceContext.removeCollectionEntry( collection );
|
||||
final CollectionPersister persister = ce.getLoadedPersister();
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
"Evicting collection: %s",
|
||||
MessageHelper.collectionInfoString( ce.getLoadedPersister(),
|
||||
collection,
|
||||
ce.getLoadedKey(),
|
||||
getSession() ) );
|
||||
collectionInfoString( persister, collection, ce.getLoadedKey(), session )
|
||||
);
|
||||
}
|
||||
if (ce.getLoadedPersister() != null && ce.getLoadedPersister().getBatchSize() > 1) {
|
||||
persistenceContext.getBatchFetchQueue().removeBatchLoadableCollection(ce);
|
||||
|
||||
if ( persister != null
|
||||
&& session.getLoadQueryInfluencers().effectiveBatchSize(persister) > 1 ) {
|
||||
persistenceContext.getBatchFetchQueue().removeBatchLoadableCollection( ce );
|
||||
}
|
||||
if ( ce.getLoadedPersister() != null && ce.getLoadedKey() != null ) {
|
||||
if ( persister != null && ce.getLoadedKey() != null ) {
|
||||
//TODO: is this 100% correct?
|
||||
persistenceContext.removeCollectionByKey( new CollectionKey( ce.getLoadedPersister(), ce.getLoadedKey() ) );
|
||||
persistenceContext.removeCollectionByKey( new CollectionKey( persister, ce.getLoadedKey() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -265,9 +265,9 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
|||
@Override
|
||||
public Integer getConfiguredJdbcBatchSize() {
|
||||
final Integer sessionJdbcBatchSize = jdbcBatchSize;
|
||||
return sessionJdbcBatchSize == null ?
|
||||
fastSessionServices.defaultJdbcBatchSize :
|
||||
sessionJdbcBatchSize;
|
||||
return sessionJdbcBatchSize == null
|
||||
? fastSessionServices.defaultJdbcBatchSize
|
||||
: sessionJdbcBatchSize;
|
||||
}
|
||||
|
||||
protected void addSharedSessionTransactionObserver(TransactionCoordinator transactionCoordinator) {
|
||||
|
|
|
@ -1932,6 +1932,22 @@ public class SessionImpl
|
|||
loadQueryInfluencers.disableFetchProfile( name );
|
||||
}
|
||||
|
||||
public void setSubselectFetchingEnabled(boolean enabled) {
|
||||
loadQueryInfluencers.setSubselectFetchEnabled( enabled );
|
||||
}
|
||||
|
||||
public boolean isSubselectFetchingEnabled() {
|
||||
return loadQueryInfluencers.getSubselectFetchEnabled();
|
||||
}
|
||||
|
||||
public void setFetchBatchSize(int batchSize) {
|
||||
loadQueryInfluencers.setBatchSize( batchSize );
|
||||
}
|
||||
|
||||
public int getFetchBatchSize() {
|
||||
return loadQueryInfluencers.getBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LobHelper getLobHelper() {
|
||||
if ( lobHelper == null ) {
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
|||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
|
||||
/**
|
||||
* A one-time use CollectionLoader for applying a sub-select fetch
|
||||
* A one-time use {@link CollectionLoader} for applying a subselect fetch.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
|
|
@ -252,7 +252,10 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
|
|||
.translate( jdbcParameterBindings, QueryOptions.NONE );
|
||||
|
||||
final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler;
|
||||
if ( getLoadable().getEntityPersister().hasSubselectLoadableCollections() ) {
|
||||
final EntityPersister persister = getLoadable().getEntityPersister();
|
||||
if ( persister.hasCollections()
|
||||
&& session.getSessionFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|
||||
|| persister.hasSubselectLoadableCollections() ) {
|
||||
subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
|
||||
session.getPersistenceContext().getBatchFetchQueue(),
|
||||
sqlAst,
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.hibernate.internal.util.collections.CollectionHelper;
|
|||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
|
@ -138,7 +139,10 @@ public class MultiNaturalIdLoadingBatcher {
|
|||
private <E> List<E> performLoad(JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session) {
|
||||
final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler;
|
||||
|
||||
if ( entityDescriptor.getEntityPersister().hasSubselectLoadableCollections() ) {
|
||||
final EntityPersister persister = entityDescriptor.getEntityPersister();
|
||||
if ( persister.hasCollections()
|
||||
&& session.getSessionFactory().getSessionFactoryOptions().isSubselectFetchEnabled()
|
||||
|| persister.hasSubselectLoadableCollections() ) {
|
||||
subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
|
||||
session.getPersistenceContext().getBatchFetchQueue(),
|
||||
sqlSelect,
|
||||
|
|
|
@ -61,9 +61,9 @@ public final class FetchOptionsHelper {
|
|||
}
|
||||
}
|
||||
else {
|
||||
CollectionPersister persister = (CollectionPersister) type.getAssociatedJoinable( sessionFactory );
|
||||
final CollectionPersister persister = (CollectionPersister) type.getAssociatedJoinable( sessionFactory );
|
||||
if ( persister instanceof AbstractCollectionPersister
|
||||
&& ( (AbstractCollectionPersister) persister ).isSubselectLoadable() ) {
|
||||
&& persister.isSubselectLoadable() ) {
|
||||
return FetchStyle.SUBSELECT;
|
||||
}
|
||||
else if ( persister.getBatchSize() > 0 ) {
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.engine.profile.internal.FetchProfileAffectee;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
|
@ -281,7 +280,8 @@ public abstract class AbstractCollectionPersister
|
|||
// isSet = collectionBinding.isSet();
|
||||
// isSorted = collectionBinding.isSorted();
|
||||
isPrimitiveArray = collectionBootDescriptor.isPrimitiveArray();
|
||||
subselectLoadable = collectionBootDescriptor.isSubselectLoadable();
|
||||
subselectLoadable = collectionBootDescriptor.isSubselectLoadable()
|
||||
|| factory.getSessionFactoryOptions().isSubselectFetchEnabled();
|
||||
|
||||
qualifiedTableName = determineTableName( table );
|
||||
|
||||
|
@ -705,48 +705,47 @@ public abstract class AbstractCollectionPersister
|
|||
// if there is a user-specified loader, return that
|
||||
return getStandardCollectionLoader();
|
||||
}
|
||||
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
final CollectionLoader subSelectLoader = resolveSubSelectLoader( key, session );
|
||||
if ( subSelectLoader != null ) {
|
||||
return subSelectLoader;
|
||||
if ( loadQueryInfluencers.effectiveSubselectFetchEnabled( this ) ) {
|
||||
final CollectionLoader subSelectLoader = resolveSubSelectLoader( key, session );
|
||||
if ( subSelectLoader != null ) {
|
||||
return subSelectLoader;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! session.getLoadQueryInfluencers().hasEnabledFilters() && ! isAffectedByEnabledFetchProfiles( session.getLoadQueryInfluencers() ) ) {
|
||||
if ( !loadQueryInfluencers.hasEnabledFilters()
|
||||
&& !isAffectedByEnabledFetchProfiles( loadQueryInfluencers ) ) {
|
||||
return getStandardCollectionLoader();
|
||||
}
|
||||
|
||||
return createCollectionLoader( session.getLoadQueryInfluencers() );
|
||||
else {
|
||||
return createCollectionLoader( loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
private CollectionLoader resolveSubSelectLoader(Object key, SharedSessionContractImplementor session) {
|
||||
if ( !isSubselectLoadable() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
|
||||
final EntityKey ownerEntityKey = session.generateEntityKey( key, getOwnerEntityPersister() );
|
||||
final SubselectFetch subselect = persistenceContext.getBatchFetchQueue().getSubselect( ownerEntityKey );
|
||||
final SubselectFetch subselect =
|
||||
persistenceContext.getBatchFetchQueue()
|
||||
.getSubselect( session.generateEntityKey( key, getOwnerEntityPersister() ) );
|
||||
if ( subselect == null ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// Take care of any entities that might have
|
||||
// been evicted!
|
||||
subselect.getResultingEntityKeys()
|
||||
.removeIf( entityKey -> !persistenceContext.containsEntity( entityKey ) );
|
||||
|
||||
// Take care of any entities that might have
|
||||
// been evicted!
|
||||
subselect.getResultingEntityKeys().removeIf( o -> !persistenceContext.containsEntity( o ) );
|
||||
|
||||
// Run a subquery loader
|
||||
return createSubSelectLoader( subselect, session );
|
||||
// Run a subquery loader
|
||||
return createSubSelectLoader( subselect, session );
|
||||
}
|
||||
}
|
||||
|
||||
protected CollectionLoader createSubSelectLoader(SubselectFetch subselect, SharedSessionContractImplementor session) {
|
||||
//noinspection RedundantCast
|
||||
return new CollectionLoaderSubSelectFetch(
|
||||
attributeMapping,
|
||||
(DomainResult<?>) null,
|
||||
subselect,
|
||||
session
|
||||
);
|
||||
return new CollectionLoaderSubSelectFetch( attributeMapping, (DomainResult<?>) null, subselect, session );
|
||||
}
|
||||
|
||||
private CollectionLoader reusableCollectionLoader;
|
||||
|
@ -758,9 +757,10 @@ public abstract class AbstractCollectionPersister
|
|||
}
|
||||
return reusableCollectionLoader;
|
||||
}
|
||||
|
||||
// create a one-off
|
||||
return generateCollectionLoader( loadQueryInfluencers );
|
||||
else {
|
||||
// create a one-off
|
||||
return generateCollectionLoader( loadQueryInfluencers );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canUseReusableCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
|
||||
|
@ -769,13 +769,15 @@ public abstract class AbstractCollectionPersister
|
|||
}
|
||||
|
||||
private CollectionLoader generateCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
|
||||
final int batchSize = getBatchSize();
|
||||
final int batchSize = loadQueryInfluencers.effectiveBatchSize( this );
|
||||
if ( batchSize > 1 ) {
|
||||
return getFactory().getServiceRegistry()
|
||||
.getService( BatchLoaderFactory.class )
|
||||
.createCollectionBatchLoader( batchSize, loadQueryInfluencers, attributeMapping, getFactory() );
|
||||
}
|
||||
return new CollectionLoaderSingleKey( attributeMapping, loadQueryInfluencers, getFactory() );
|
||||
else {
|
||||
return new CollectionLoaderSingleKey( attributeMapping, loadQueryInfluencers, getFactory() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1371,6 +1373,7 @@ public abstract class AbstractCollectionPersister
|
|||
return isAffectedByEnabledFilters( session.getLoadQueryInfluencers() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubselectLoadable() {
|
||||
return subselectLoadable;
|
||||
}
|
||||
|
|
|
@ -302,12 +302,19 @@ public interface CollectionPersister extends Restrictable {
|
|||
throw new UnsupportedOperationException( "CollectionPersister used for [" + getRole() + "] does not support SQL AST" );
|
||||
}
|
||||
|
||||
boolean isExtraLazy();
|
||||
default boolean isExtraLazy() {
|
||||
return false;
|
||||
}
|
||||
int getSize(Object key, SharedSessionContractImplementor session);
|
||||
boolean indexExists(Object key, Object index, SharedSessionContractImplementor session);
|
||||
boolean elementExists(Object key, Object element, SharedSessionContractImplementor session);
|
||||
Object getElementByIndex(Object key, Object index, SharedSessionContractImplementor session, Object owner);
|
||||
int getBatchSize();
|
||||
default int getBatchSize() {
|
||||
return 0;
|
||||
}
|
||||
default boolean isSubselectLoadable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the property this collection is mapped by
|
||||
|
|
|
@ -512,7 +512,8 @@ public abstract class AbstractEntityPersister
|
|||
batch = creationContext.getSessionFactoryOptions().getDefaultBatchFetchSize();
|
||||
}
|
||||
batchSize = batch;
|
||||
hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections();
|
||||
hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections()
|
||||
|| entityMetamodel.hasCollections() && factory.getSessionFactoryOptions().isSubselectFetchEnabled();
|
||||
hasPartitionedSelectionMapping = persistentClass.hasPartitionedSelectionMapping();
|
||||
hasCollectionNotReferencingPK = persistentClass.hasCollectionNotReferencingPK();
|
||||
|
||||
|
|
|
@ -6939,7 +6939,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
if ( inSubQueryPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in" );
|
||||
appendSql( " in " );
|
||||
inSubQueryPredicate.getSubQuery().accept( this );
|
||||
}
|
||||
else if ( !supportsRowValueConstructorSyntaxInInSubQuery() ) {
|
||||
|
@ -6957,7 +6957,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
if ( inSubQueryPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in" );
|
||||
appendSql( " in " );
|
||||
inSubQueryPredicate.getSubQuery().accept( this );
|
||||
}
|
||||
}
|
||||
|
@ -6966,7 +6966,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
if ( inSubQueryPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in" );
|
||||
appendSql( " in " );
|
||||
inSubQueryPredicate.getSubQuery().accept( this );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -966,10 +966,6 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public boolean isExtraLazy() {
|
||||
return false; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public int getSize(Object key, SharedSessionContractImplementor session) {
|
||||
return 0; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
@ -986,11 +982,6 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMappedByProperty() {
|
||||
return null;
|
||||
|
|
|
@ -104,7 +104,7 @@ public class DepthOneBatchTest {
|
|||
);
|
||||
|
||||
assertThat( executedQueries.get( 4 ).toLowerCase() ).isEqualTo(
|
||||
"select u1_0.group_id,u1_1.user_id,a1_0.agency_id,a1_0.agency_txt,u1_1.user_name from group_user u1_0 join user_table u1_1 on u1_1.user_id=u1_0.user_id left join agency_table a1_0 on a1_0.agency_id=u1_1.agency_id where u1_0.group_id in(select g1_0.group_id from group_table g1_0 where g1_0.agency_id=?)"
|
||||
"select u1_0.group_id,u1_1.user_id,a1_0.agency_id,a1_0.agency_txt,u1_1.user_name from group_user u1_0 join user_table u1_1 on u1_1.user_id=u1_0.user_id left join agency_table a1_0 on a1_0.agency_id=u1_1.agency_id where u1_0.group_id in (select g1_0.group_id from group_table g1_0 where g1_0.agency_id=?)"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class DepthOneTest {
|
|||
);
|
||||
|
||||
assertThat( executedQueries.get( 3 ).toLowerCase() ).isEqualTo(
|
||||
"select u1_0.group_id,u1_1.user_id,a1_0.agency_id,a1_0.agency_txt,u1_1.user_name from group_user u1_0 join user_table u1_1 on u1_1.user_id=u1_0.user_id left join agency_table a1_0 on a1_0.agency_id=u1_1.agency_id where u1_0.group_id in(select g1_0.group_id from group_table g1_0 where g1_0.agency_id=?)"
|
||||
"select u1_0.group_id,u1_1.user_id,a1_0.agency_id,a1_0.agency_txt,u1_1.user_name from group_user u1_0 join user_table u1_1 on u1_1.user_id=u1_0.user_id left join agency_table a1_0 on a1_0.agency_id=u1_1.agency_id where u1_0.group_id in (select g1_0.group_id from group_table g1_0 where g1_0.agency_id=?)"
|
||||
);
|
||||
|
||||
assertThat( executedQueries.get( 4 ).toLowerCase() ).isEqualTo(
|
||||
|
|
|
@ -41,7 +41,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.uk in(" +
|
||||
"where p1_0.uk in (" +
|
||||
"select c1_0.child_uk " +
|
||||
"from children_uks c1_0 " +
|
||||
"where p1_0.uk=c1_0.owner_uk" +
|
||||
|
@ -65,7 +65,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.id in(" +
|
||||
"where p1_0.id in (" +
|
||||
"select c1_0.children_id " +
|
||||
"from PERSON_TABLE_PERSON_TABLE c1_0 " +
|
||||
"where p1_0.id=c1_0.Person_id" +
|
||||
|
@ -109,7 +109,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.parent_id in(" +
|
||||
"where p1_0.parent_id in (" +
|
||||
"select c1_1.id " +
|
||||
"from children_uks c1_0 " +
|
||||
"join PERSON_TABLE c1_1 on c1_1.uk=c1_0.child_uk " +
|
||||
|
@ -135,7 +135,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join PERSON_TABLE p2_0 on p2_0.uk=p1_0.parent_uk " +
|
||||
"where p2_0.id in(" +
|
||||
"where p2_0.id in (" +
|
||||
"select c1_0.children_id " +
|
||||
"from PERSON_TABLE_PERSON_TABLE c1_0 " +
|
||||
"where p1_0.id=c1_0.Person_id" +
|
||||
|
@ -159,7 +159,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.id in(" +
|
||||
"where p1_0.id in (" +
|
||||
"select e1_0.id " +
|
||||
"from PERSON_TABLE e1_0 " +
|
||||
"where p1_0.id=e1_0.supervisor_id" +
|
||||
|
@ -183,7 +183,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.id in(" +
|
||||
"where p1_0.id in (" +
|
||||
"select e1_0.id " +
|
||||
"from PERSON_TABLE e1_0 " +
|
||||
"where p1_0.uk=e1_0.supervisor_uk" +
|
||||
|
@ -294,7 +294,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join (children_uks c1_0 join PERSON_TABLE c1_1 on c1_1.uk=c1_0.child_uk) on p1_0.uk=c1_0.owner_uk " +
|
||||
"where c1_1.id in(select c2_0.children_id from PERSON_TABLE_PERSON_TABLE c2_0 where p1_0.id=c2_0.Person_id)",
|
||||
"where c1_1.id in (select c2_0.children_id from PERSON_TABLE_PERSON_TABLE c2_0 where p1_0.id=c2_0.Person_id)",
|
||||
statementInspector.getSqlQueries().get( 0 )
|
||||
);
|
||||
}
|
||||
|
@ -315,7 +315,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join PERSON_TABLE_PERSON_TABLE c1_0 on p1_0.id=c1_0.Person_id " +
|
||||
"where c1_0.children_id in(select c2_1.id from children_uks c2_0 join PERSON_TABLE c2_1 on c2_1.uk=c2_0.child_uk where p1_0.uk=c2_0.owner_uk)",
|
||||
"where c1_0.children_id in (select c2_1.id from children_uks c2_0 join PERSON_TABLE c2_1 on c2_1.uk=c2_0.child_uk where p1_0.uk=c2_0.owner_uk)",
|
||||
statementInspector.getSqlQueries().get( 0 )
|
||||
);
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.uk in(" +
|
||||
"where p1_0.uk in (" +
|
||||
"select p2_0.parent_uk " +
|
||||
"from PERSON_TABLE p2_0" +
|
||||
")",
|
||||
|
@ -358,7 +358,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"where p1_0.id in(" +
|
||||
"where p1_0.id in (" +
|
||||
"select p2_0.parent_id " +
|
||||
"from PERSON_TABLE p2_0" +
|
||||
")",
|
||||
|
|
Loading…
Reference in New Issue