HHH-16624 Do not create subselects when there are fewer than 2 results

This commit is contained in:
Andrea Boriero 2023-05-23 17:03:16 +02:00 committed by Andrea Boriero
parent 4c1d8a19bf
commit f8275f1a70
9 changed files with 41 additions and 28 deletions

View File

@ -13,15 +13,12 @@ import java.util.Map;
import java.util.Set;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcParametersList;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.graph.entity.internal.EntityResultInitializer;
/**
* Encapsulates details related to entities which contain sub-select-fetchable
@ -151,8 +148,7 @@ public class SubselectFetch {
public void addKey(EntityKey key, LoadingEntityEntry entry) {
if ( batchFetchQueue.getSession().getLoadQueryInfluencers()
.hasSubselectLoadableCollections( entry.getDescriptor() )
&& shouldAddSubselectFetch( entry ) ) {
.hasSubselectLoadableCollections( entry.getDescriptor() ) ) {
final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent(
entry.getEntityInitializer().getNavigablePath(),
navigablePath -> new SubselectFetch(
@ -169,23 +165,5 @@ public class SubselectFetch {
batchFetchQueue.addSubselect( key, subselectFetch );
}
}
private boolean shouldAddSubselectFetch(LoadingEntityEntry entry) {
if ( entry.getEntityInitializer() instanceof EntityResultInitializer ) {
return true;
}
else {
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;
}
}
return false;
}
}
}
}

View File

@ -60,10 +60,22 @@ public interface ExecutionContext {
return null;
}
/**
*
* @param entityKey
* @param entry
*
* @deprecated use {@link #registerSubselect(EntityKey, LoadingEntityEntry)} instead.
*/
@Deprecated
default void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
// by default do nothing
}
default void registerSubselect(EntityKey entityKey, LoadingEntityEntry entry) {
registerLoadingEntityEntry( entityKey, entry );
}
/**
* Hook to allow delaying calls to {@link LogicalConnectionImplementor#afterStatement()}.
* Mainly used in the case of batching and multi-table mutations

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;

View File

@ -83,7 +83,6 @@ public class StandardRowReader<T> implements RowReader<T> {
@Override
public T readRow(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
LoadingLogger.LOGGER.trace( "StandardRowReader#readRow" );
coordinateInitializers( rowProcessingState );
final Object[] resultRow = new Object[ assemblerCount ];
@ -112,6 +111,7 @@ public class StandardRowReader<T> implements RowReader<T> {
@Override
public void finishUp(JdbcValuesSourceProcessingState processingState) {
processingState.registerSubselect();
initializers.endLoading( processingState.getExecutionContext() );
}

View File

@ -20,6 +20,8 @@ import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
@ -36,6 +38,8 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState;
*/
public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSourceProcessingState {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JdbcValuesSourceProcessingStateStandardImpl.class );
private final ExecutionContext executionContext;
private final JdbcValuesSourceProcessingOptions processingOptions;
@ -97,7 +101,6 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
if ( loadingEntityMap == null ) {
loadingEntityMap = new HashMap<>();
}
executionContext.registerLoadingEntityEntry( entityKey, loadingEntry );
loadingEntityMap.put( entityKey, loadingEntry );
}
@ -137,6 +140,22 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo
return loadingCollectionMap.get( key );
}
@Override
public void registerSubselect() {
if ( loadingEntityMap != null && loadingEntityMap.size() > 1 ) {
loadingEntityMap.forEach(
(entityKey, loadingEntityEntry) ->
executionContext.registerSubselect( entityKey, loadingEntityEntry )
);
}
else {
LOG.tracef(
"Skipping create subselects because there are fewer than 2 results, so query by key is more efficient.",
getClass().getName()
);
}
}
@Override
public void registerLoadingCollection(CollectionKey key, LoadingCollectionEntry loadingCollectionEntry) {
if ( loadingCollectionMap == null ) {

View File

@ -80,5 +80,8 @@ public interface JdbcValuesSourceProcessingState {
CollectionKey collectionKey,
LoadingCollectionEntry loadingCollectionEntry);
default void registerSubselect() {
}
void finishUp();
}

View File

@ -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=?"
);
}

View File

@ -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=?"
);
assertThat( executedQueries.get( 4 ).toLowerCase() ).isEqualTo(

View File

@ -109,7 +109,7 @@ public class SubselectOneToManyTest {
assertThat( parent.getChildren() ).hasSize( 2 );
statementInspector.assertExecutedCount( 3 ); // 1 query for parent, 1 for grandparent, 1 for children
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 );
} );
}