Skip to content

Commit

Permalink
HHH-14877 - FetchMode.SUBSELECT ignored
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Oct 21, 2021
1 parent b555830 commit e8e62c4
Show file tree
Hide file tree
Showing 76 changed files with 1,686 additions and 778 deletions.
Expand Up @@ -32,7 +32,6 @@
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.exec.spi.ExecutionContext;

/**
* An {@link org.hibernate.engine.spi.ActionQueue} {@link Executable} for ensuring
Expand Down Expand Up @@ -148,11 +147,9 @@ public BulkOperationCleanupAction(SharedSessionContractImplementor session, Set
this.affectedTableSpaces = spacesList.toArray( new String[ 0 ] );
}

public static void schedule(ExecutionContext executionContext, SqmDmlStatement<?> statement) {
public static void schedule(SharedSessionContractImplementor session, SqmDmlStatement<?> statement) {
final List<EntityPersister> entityPersisters = new ArrayList<>( 1 );
final MetamodelImplementor metamodel = executionContext.getSession()
.getFactory()
.getMetamodel();
final MetamodelImplementor metamodel = session.getFactory().getMetamodel();
if ( !( statement instanceof InsertStatement ) ) {
entityPersisters.add( metamodel.entityPersister( statement.getTarget().getEntityName() ) );
}
Expand All @@ -165,11 +162,10 @@ public static void schedule(ExecutionContext executionContext, SqmDmlStatement<?
}
}

schedule( executionContext, entityPersisters.toArray( new EntityPersister[0] ) );
schedule( session, entityPersisters.toArray( new EntityPersister[0] ) );
}

public static void schedule(ExecutionContext executionContext, EntityPersister... affectedQueryables) {
final SharedSessionContractImplementor session = executionContext.getSession();
public static void schedule(SharedSessionContractImplementor session, EntityPersister... affectedQueryables) {
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, affectedQueryables );
if ( session.isEventSource() ) {
( (EventSource) session ).getActionQueue().addAction( action );
Expand All @@ -179,8 +175,7 @@ public static void schedule(ExecutionContext executionContext, EntityPersister..
}
}

public static void schedule(ExecutionContext executionContext, Set<String> affectedQueryables) {
final SharedSessionContractImplementor session = executionContext.getSession();
public static void schedule(SharedSessionContractImplementor session, Set<String> affectedQueryables) {
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, affectedQueryables );
if ( session.isEventSource() ) {
( (EventSource) session ).getActionQueue().addAction( action );
Expand Down
Expand Up @@ -11,6 +11,7 @@
import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
import org.hibernate.sql.results.graph.DomainResultAssembler;
Expand Down
Expand Up @@ -458,4 +458,11 @@ default boolean isDirectlyProvidedCollection(Object collection) {
default boolean isNewlyInstantiated() {
return getKey() == null && !isDirty();
}

/**
* Like {@link #toString()} but without the silliness of rendering the elements
*/
default String render() {
return getRole() + "#" + getKey() + "(initialized: " + wasInitialized() + ")";
}
}
@@ -1,8 +1,8 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.engine.spi;

Expand All @@ -24,9 +24,10 @@
import org.jboss.logging.Logger;

/**
* Tracks entity and collection keys that are available for batch
* fetching, and the queries which were used to load entities, which
* can be re-used as a subquery for loading owned collections.
* Keeps track of:<ul>
* <li>entity and collection keys that are available for batch fetching</li>
* <li>details related to queries which load entities with sub-select-fetchable collections</li>
* </ul>
*
* @author Gavin King
* @author Steve Ebersole
Expand Down Expand Up @@ -105,7 +106,15 @@ public void addSubselect(EntityKey key, SubselectFetch subquery) {
if ( subselectsByEntityKey == null ) {
subselectsByEntityKey = CollectionHelper.mapOfSize( 12 );
}
subselectsByEntityKey.put( key, subquery );

final SubselectFetch previous = subselectsByEntityKey.put( key, subquery );
if ( previous != null && LOG.isDebugEnabled() ) {
LOG.debugf(
"SubselectFetch previously registered with BatchFetchQueue for `%s#s`",
key.getEntityName(),
key.getIdentifier()
);
}
}

/**
Expand Down
Expand Up @@ -7,7 +7,6 @@
package org.hibernate.engine.spi;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
Expand Down
@@ -1,23 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.engine.spi;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.metamodel.mapping.EntityValuedModelPart;
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;

/**
* @author Gavin King
* @author Steve Ebersole
* Encapsulates details related to entities which contain sub-select-fetchable
* collections and which were loaded in a Session so that those collections may
* be sub-select fetched later during initialization
*/
public class SubselectFetch {
private final EntityValuedModelPart entityModelPart;
Expand Down Expand Up @@ -46,22 +49,39 @@ public EntityValuedModelPart getEntityModelPart() {
return entityModelPart;
}

public List<JdbcParameter> getLoadingJdbcParameters() {
// todo (6.0) : do not believe this is needed
// - see org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(org.hibernate.engine.spi.SubselectFetch)
return loadingJdbcParameters;
}

/**
* The SQL AST select from which the owner was loaded
*/
public QuerySpec getLoadingSqlAst() {
return loadingSqlAst;
}

/**
* The TableGroup for the owner within the {@link #getLoadingSqlAst()}
*/
public TableGroup getOwnerTableGroup() {
return ownerTableGroup;
}

public List<JdbcParameter> getLoadingJdbcParameters() {
return loadingJdbcParameters;
}

/**
* The JDBC parameter bindings related to {@link #getLoadingSqlAst()} for
* the specific execution that loaded the owners
*/
public JdbcParameterBindings getLoadingJdbcParameterBindings() {
return loadingJdbcParameterBindings;
}

/**
*The entity-keys of all owners loaded from a particular execution
*
* Used for "empty collection" handling mostly
*/
public Set<EntityKey> getResultingEntityKeys() {
return resultingEntityKeys;
}
Expand All @@ -70,4 +90,61 @@ public Set<EntityKey> getResultingEntityKeys() {
public String toString() {
return "SubselectFetch(" + entityModelPart.getEntityMappingType().getEntityName() + ")";
}

public static RegistrationHandler createRegistrationHandler(
BatchFetchQueue batchFetchQueue,
SelectStatement sqlAst,
TableGroup tableGroup,
List<JdbcParameter> jdbcParameters,
JdbcParameterBindings jdbcParameterBindings) {
final SubselectFetch subselectFetch = new SubselectFetch(
null,
sqlAst.getQuerySpec(),
tableGroup,
jdbcParameters,
jdbcParameterBindings,
new HashSet<>()
);

return new StandardRegistrationHandler( batchFetchQueue, subselectFetch );
}

public static RegistrationHandler createRegistrationHandler(
BatchFetchQueue batchFetchQueue,
SelectStatement sqlAst,
List<JdbcParameter> jdbcParameters,
JdbcParameterBindings jdbcParameterBindings) {
final List<TableGroup> roots = sqlAst.getQuerySpec().getFromClause().getRoots();
if ( roots.isEmpty() ) {
// we allow this now
return NO_OP_REG_HANDLER;
}

return createRegistrationHandler( batchFetchQueue, sqlAst, roots.get( 0 ), jdbcParameters, jdbcParameterBindings );
}

public interface RegistrationHandler {
void addKey(EntityKey key);
}

private static final RegistrationHandler NO_OP_REG_HANDLER = new RegistrationHandler() {
@Override
public void addKey(EntityKey key) {
}
} ;

public static class StandardRegistrationHandler implements RegistrationHandler {
private final BatchFetchQueue batchFetchQueue;
private final SubselectFetch subselectFetch;

private StandardRegistrationHandler(BatchFetchQueue batchFetchQueue, SubselectFetch subselectFetch) {
this.batchFetchQueue = batchFetchQueue;
this.subselectFetch = subselectFetch;
}

public void addKey(EntityKey key) {
subselectFetch.resultingEntityKeys.add( key );
batchFetchQueue.addSubselect( key, subselectFetch );
}
}
}
Expand Up @@ -7,20 +7,22 @@
package org.hibernate.loader.ast.internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hibernate.LockOptions;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.ast.spi.CollectionLoader;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.sql.ast.Clause;
Expand All @@ -32,6 +34,7 @@
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;

Expand Down Expand Up @@ -167,7 +170,8 @@ private void batchLoad(
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();

final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst )
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory
.buildSelectTranslator( sessionFactory, sqlAst )
.translate( null, QueryOptions.NONE );

final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount * smallBatchLength );
Expand All @@ -185,6 +189,14 @@ private void batchLoad(
session
);
}
assert offset == jdbcParameters.size();

final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
session.getPersistenceContext().getBatchFetchQueue(),
sqlAst,
Collections.emptyList(),
jdbcParameterBindings
);

jdbcServices.getJdbcSelectExecutor().list(
jdbcSelect,
Expand All @@ -205,6 +217,11 @@ public String getQueryIdentifier(String sql) {
return sql;
}

@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchableKeysHandler.addKey( entityKey );
}

@Override
public QueryParameterBindings getQueryParameterBindings() {
return QueryParameterBindings.NO_PARAM_BINDINGS;
Expand All @@ -220,9 +237,6 @@ public Callback getCallback() {
ListResultsConsumer.UniqueSemantic.FILTER
);


assert offset == jdbcParameters.size();

// prepare for the next round...
smallBatchStart += smallBatchLength;
if ( smallBatchStart >= numberOfIds ) {
Expand All @@ -232,4 +246,5 @@ public Callback getCallback() {
smallBatchLength = Math.min( numberOfIds - smallBatchStart, batchSize );
}
}

}
Expand Up @@ -14,9 +14,11 @@
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.loader.ast.spi.CollectionLoader;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.spi.QueryOptions;
Expand All @@ -30,6 +32,7 @@
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;

Expand Down Expand Up @@ -103,9 +106,18 @@ public PersistentCollection load(Object key, SharedSessionContractImplementor se
session
);
assert offset == jdbcParameters.size();
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst )

final JdbcSelect jdbcSelect = sqlAstTranslatorFactory
.buildSelectTranslator( sessionFactory, sqlAst )
.translate( jdbcParameterBindings, QueryOptions.NONE );

final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
session.getPersistenceContext().getBatchFetchQueue(),
sqlAst,
jdbcParameters,
jdbcParameterBindings
);

jdbcServices.getJdbcSelectExecutor().list(
jdbcSelect,
jdbcParameterBindings,
Expand All @@ -120,6 +132,11 @@ public CollectionKey getCollectionKey() {
return collectionKey;
}

@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchableKeysHandler.addKey( entityKey );
}

@Override
public QueryOptions getQueryOptions() {
return QueryOptions.NONE;
Expand Down

0 comments on commit e8e62c4

Please sign in to comment.