From e8e62c4d6c9a22a8a2d257abe9d8c608bcc22571 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 15 Oct 2021 18:59:53 -0500 Subject: [PATCH] HHH-14877 - FetchMode.SUBSELECT ignored --- .../internal/BulkOperationCleanupAction.java | 15 +- .../spi/CollectionInitializerProducer.java | 1 + .../collection/spi/PersistentCollection.java | 7 + .../hibernate/engine/spi/BatchFetchQueue.java | 21 +- .../engine/spi/PersistenceContext.java | 1 - .../hibernate/engine/spi/SubselectFetch.java | 93 +++- .../internal/CollectionLoaderBatchKey.java | 25 +- .../internal/CollectionLoaderSingleKey.java | 19 +- .../CollectionLoaderSubSelectFetch.java | 83 +++- .../ast/internal/LoaderSelectBuilder.java | 28 +- .../ast/internal/MultiIdLoaderStandard.java | 16 +- .../MultiNaturalIdLoadingBatcher.java | 21 +- .../SingleIdEntityLoaderDynamicBatch.java | 18 +- .../AbstractCollectionPersister.java | 2 +- ...DelegatingDomainQueryExecutionContext.java | 44 ++ .../spi/DomainQueryExecutionContext.java | 27 ++ .../query/spi/NonSelectQueryPlan.java | 4 +- .../hibernate/query/spi/SelectQueryPlan.java | 5 +- .../query/spi/SqlOmittingQueryOptions.java | 34 +- .../NativeNonSelectQueryPlanImpl.java | 9 +- .../query/sql/internal/NativeQueryImpl.java | 38 +- .../internal/NativeSelectQueryPlanImpl.java | 11 +- .../AggregatedSelectQueryPlanImpl.java | 5 +- .../internal/ConcreteSqmSelectQueryPlan.java | 101 +++- .../internal/MultiTableDeleteQueryPlan.java | 5 +- .../internal/MultiTableUpdateQueryPlan.java | 5 +- .../query/sqm/internal/QuerySqmImpl.java | 52 +-- .../sqm/internal/SimpleDeleteQueryPlan.java | 19 +- .../sqm/internal/SimpleInsertQueryPlan.java | 17 +- .../sqm/internal/SimpleUpdateQueryPlan.java | 13 +- .../SqmJdbcExecutionContextAdapter.java | 58 +++ .../query/sqm/mutation/internal/Handler.java | 3 +- .../internal/MatchingIdSelectionHelper.java | 14 +- .../internal/SqmMutationStrategyHelper.java | 2 +- .../cte/AbstractCteMutationHandler.java | 11 +- .../mutation/internal/cte/CteStrategy.java | 7 +- .../idtable/ExecuteWithIdTableHelper.java | 3 +- .../idtable/GlobalTemporaryTableStrategy.java | 7 +- .../idtable/LocalTemporaryTableStrategy.java | 6 +- .../idtable/PersistentTableStrategy.java | 6 +- .../RestrictedDeleteExecutionDelegate.java | 15 +- .../idtable/TableBasedDeleteHandler.java | 10 +- .../idtable/TableBasedUpdateHandler.java | 13 +- .../idtable/UpdateExecutionDelegate.java | 6 +- .../internal/inline/InlineDeleteHandler.java | 27 +- .../internal/inline/InlineStrategy.java | 7 +- .../internal/inline/InlineUpdateHandler.java | 7 +- .../spi/SqmMultiTableMutationStrategy.java | 5 +- .../result/internal/OutputsImpl.java | 1 + .../sql/exec/spi/ExecutionContext.java | 5 +- .../collection/CollectionLoadingLogger.java | 8 +- .../AbstractCollectionInitializer.java | 3 +- ...bstractImmediateCollectionInitializer.java | 14 +- .../internal/CollectionDomainResult.java | 2 + .../DelayedCollectionInitializer.java | 1 + .../internal/SetInitializerProducer.java | 13 + .../sql/results/internal/ResultsHelper.java | 1 - .../RowProcessingStateStandardImpl.java | 1 - .../FilterWitSubSelectFetchModeTest.java | 1 - .../collections}/subselectfetch/Child.java | 2 +- .../collections}/subselectfetch/Name.java | 2 +- .../collections}/subselectfetch/Parent.java | 2 +- .../SimpleEagerSubSelectFetchTests.java | 210 +++++++++ .../SimpleLazySubSelectFetchTests.java | 236 ++++++++++ ...SubselectFetchCollectionFromBatchTest.java | 431 +++++++++++++++++ .../subselectfetch/SubselectFetchTest.java | 103 +++-- .../SubselectFetchWithFormulaTest.java | 6 +- ...selectFetchWithFormulaTransactSqlTest.java | 6 +- .../collections}/subselectfetch/Value.java | 2 +- .../orm/test/query/hql/ParameterTests.java | 5 +- .../mutation/multitable/IdSelectionTests.java | 14 +- ...SubselectFetchCollectionFromBatchTest.java | 437 ------------------ .../subselectfetch/Name.hbm.xml | 4 +- .../subselectfetch/NameTransactSql.hbm.xml | 4 +- .../subselectfetch/ParentChild.hbm.xml | 2 +- .../subselectfetch/Value.hbm.xml | 2 +- 76 files changed, 1686 insertions(+), 778 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/internal/DelegatingDomainQueryExecutionContext.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/spi/DomainQueryExecutionContext.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmJdbcExecutionContextAdapter.java rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/Child.java (91%) rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/Name.java (94%) rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/Parent.java (93%) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleEagerSubSelectFetchTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleLazySubSelectFetchTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchCollectionFromBatchTest.java rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/SubselectFetchTest.java (82%) rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/SubselectFetchWithFormulaTest.java (95%) rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{test => orm/test/mapping/collections}/subselectfetch/Value.java (92%) delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java rename hibernate-core/src/test/resources/{org/hibernate/test => mappings}/subselectfetch/Name.hbm.xml (80%) rename hibernate-core/src/test/resources/{org/hibernate/test => mappings}/subselectfetch/NameTransactSql.hbm.xml (79%) rename hibernate-core/src/test/resources/{org/hibernate/test => mappings}/subselectfetch/ParentChild.hbm.xml (94%) rename hibernate-core/src/test/resources/{org/hibernate/test => mappings}/subselectfetch/Value.hbm.xml (86%) diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java index 5f386b6750..60fb43be1a 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/BulkOperationCleanupAction.java @@ -32,7 +32,6 @@ import org.hibernate.query.sqm.tree.SqmDmlStatement; 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 @@ -148,11 +147,9 @@ public class BulkOperationCleanupAction implements Executable, Serializable { 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 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() ) ); } @@ -165,11 +162,10 @@ public class BulkOperationCleanupAction implements Executable, Serializable { } } - 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 ); @@ -179,8 +175,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable { } } - public static void schedule(ExecutionContext executionContext, Set affectedQueryables) { - final SharedSessionContractImplementor session = executionContext.getSession(); + public static void schedule(SharedSessionContractImplementor session, Set affectedQueryables) { final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, affectedQueryables ); if ( session.isEventSource() ) { ( (EventSource) session ).getActionQueue().addAction( action ); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionInitializerProducer.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionInitializerProducer.java index 6ee0e7d04a..e4c85a7459 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionInitializerProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionInitializerProducer.java @@ -11,6 +11,7 @@ import java.util.function.Consumer; 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; diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java index 3a50a67e30..f1d41d94c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java @@ -458,4 +458,11 @@ public interface PersistentCollection { 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() + ")"; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java index 43d894eda2..a7aa1891b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java @@ -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 . + * 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; @@ -24,9 +24,10 @@ import org.hibernate.persister.entity.EntityPersister; 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:
    + *
  • entity and collection keys that are available for batch fetching
  • + *
  • details related to queries which load entities with sub-select-fetchable collections
  • + *
* * @author Gavin King * @author Steve Ebersole @@ -105,7 +106,15 @@ public class BatchFetchQueue { 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() + ); + } } /** diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 6c4fa5fcc2..cbca135f8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -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; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java index 07eceff931..3ef27937a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java @@ -1,11 +1,12 @@ /* * 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 . + * 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; @@ -13,11 +14,13 @@ 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; @@ -46,22 +49,39 @@ public class SubselectFetch { return entityModelPart; } + public List 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 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 getResultingEntityKeys() { return resultingEntityKeys; } @@ -70,4 +90,61 @@ public class SubselectFetch { public String toString() { return "SubselectFetch(" + entityModelPart.getEntityMappingType().getEntityName() + ")"; } + + public static RegistrationHandler createRegistrationHandler( + BatchFetchQueue batchFetchQueue, + SelectStatement sqlAst, + TableGroup tableGroup, + List 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 jdbcParameters, + JdbcParameterBindings jdbcParameterBindings) { + final List 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 ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderBatchKey.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderBatchKey.java index 79a6f3c799..edacdc1cda 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderBatchKey.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderBatchKey.java @@ -7,6 +7,7 @@ package org.hibernate.loader.ast.internal; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.hibernate.LockOptions; @@ -14,13 +15,14 @@ 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; @@ -32,6 +34,7 @@ import org.hibernate.sql.exec.spi.Callback; 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; @@ -167,7 +170,8 @@ public class CollectionLoaderBatchKey implements CollectionLoader { 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 ); @@ -185,6 +189,14 @@ public class CollectionLoaderBatchKey implements CollectionLoader { session ); } + assert offset == jdbcParameters.size(); + + final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext().getBatchFetchQueue(), + sqlAst, + Collections.emptyList(), + jdbcParameterBindings + ); jdbcServices.getJdbcSelectExecutor().list( jdbcSelect, @@ -205,6 +217,11 @@ public class CollectionLoaderBatchKey implements CollectionLoader { return sql; } + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchableKeysHandler.addKey( entityKey ); + } + @Override public QueryParameterBindings getQueryParameterBindings() { return QueryParameterBindings.NO_PARAM_BINDINGS; @@ -220,9 +237,6 @@ public class CollectionLoaderBatchKey implements CollectionLoader { ListResultsConsumer.UniqueSemantic.FILTER ); - - assert offset == jdbcParameters.size(); - // prepare for the next round... smallBatchStart += smallBatchLength; if ( smallBatchStart >= numberOfIds ) { @@ -232,4 +246,5 @@ public class CollectionLoaderBatchKey implements CollectionLoader { smallBatchLength = Math.min( numberOfIds - smallBatchStart, batchSize ); } } + } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java index 616b3d472e..114b46d7ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java @@ -14,9 +14,11 @@ 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.loader.ast.spi.CollectionLoader; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.spi.QueryOptions; @@ -30,6 +32,7 @@ import org.hibernate.sql.exec.spi.Callback; 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; @@ -103,9 +106,18 @@ public class CollectionLoaderSingleKey implements CollectionLoader { 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, @@ -120,6 +132,11 @@ public class CollectionLoaderSingleKey implements CollectionLoader { return collectionKey; } + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchableKeysHandler.addKey( entityKey ); + } + @Override public QueryOptions getQueryOptions() { return QueryOptions.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java index d840f694c3..5f8e9a6294 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java @@ -6,14 +6,23 @@ */ package org.hibernate.loader.ast.internal; +import java.util.ArrayList; +import java.util.Iterator; +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.BatchFetchQueue; import org.hibernate.engine.spi.CollectionKey; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.ast.spi.CollectionLoader; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.spi.QueryOptions; @@ -24,6 +33,7 @@ import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -63,18 +73,63 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader { } @Override - public PersistentCollection load(Object triggerKey, SharedSessionContractImplementor session) { + public PersistentCollection load(Object triggerKey, SharedSessionContractImplementor session) { + final CollectionKey collectionKey = new CollectionKey( attributeMapping.getCollectionDescriptor(), triggerKey ); + final SessionFactoryImplementor sessionFactory = session.getFactory(); final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + final PersistenceContext persistenceContext = session.getPersistenceContext(); - final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst ) - .translate( subselect.getLoadingJdbcParameterBindings(), QueryOptions.NONE ); + // try to find a registered SubselectFetch + final PersistentCollection collection = persistenceContext.getCollection( collectionKey ); + attributeMapping.getCollectionDescriptor().getCollectionType().getKeyOfOwner( collection.getOwner(), session ); + + final EntityEntry ownerEntry = persistenceContext.getEntry( collection.getOwner() ); + final BatchFetchQueue batchFetchQueue = persistenceContext.getBatchFetchQueue(); + final EntityKey triggerKeyOwnerKey = ownerEntry.getEntityKey(); + final SubselectFetch registeredFetch = batchFetchQueue.getSubselect( triggerKeyOwnerKey ); + List> subSelectFetchedCollections = null; + if ( registeredFetch != null ) { + batchFetchQueue.removeSubselect( triggerKeyOwnerKey ); + subSelectFetchedCollections = CollectionHelper.arrayList( registeredFetch.getResultingEntityKeys().size() ); + + // there was one, so we want to make sure to prepare the corresponding collection + // reference for reading + final Iterator itr = registeredFetch.getResultingEntityKeys().iterator(); + while ( itr.hasNext() ) { + final EntityKey key = itr.next(); + batchFetchQueue.removeSubselect( key ); + itr.remove(); + + final PersistentCollection containedCollection = persistenceContext.getCollection( + new CollectionKey( attributeMapping.getCollectionDescriptor(), key.getIdentifier() ) + ); + + if ( containedCollection != collection ) { + containedCollection.beginRead(); + containedCollection.beforeInitialize( getLoadable().getCollectionDescriptor(), -1 ); + + subSelectFetchedCollections.add( containedCollection ); + } + } + } + + final JdbcSelect jdbcSelect = sqlAstTranslatorFactory + .buildSelectTranslator( sessionFactory, sqlAst ) + .translate( this.subselect.getLoadingJdbcParameterBindings(), QueryOptions.NONE ); + + final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( + batchFetchQueue, + sqlAst, + this.subselect.getLoadingJdbcParameters(), + this.subselect.getLoadingJdbcParameterBindings() + ); jdbcServices.getJdbcSelectExecutor().list( jdbcSelect, - subselect.getLoadingJdbcParameterBindings(), + this.subselect.getLoadingJdbcParameterBindings(), new ExecutionContext() { @Override public SharedSessionContractImplementor getSession() { @@ -91,6 +146,11 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader { return sql; } + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchableKeysHandler.addKey( entityKey ); + } + @Override public QueryParameterBindings getQueryParameterBindings() { return QueryParameterBindings.NO_PARAM_BINDINGS; @@ -106,7 +166,18 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader { ListResultsConsumer.UniqueSemantic.FILTER ); - final CollectionKey collectionKey = new CollectionKey( attributeMapping.getCollectionDescriptor(), triggerKey ); - return session.getPersistenceContext().getCollection( collectionKey ); + if ( subSelectFetchedCollections != null && ! subSelectFetchedCollections.isEmpty() ) { + subSelectFetchedCollections.forEach( (c) -> { + if ( c.wasInitialized() ) { + return; + } + + c.initializeEmptyCollection( getLoadable().getCollectionDescriptor() ); + } ); + + subSelectFetchedCollections.clear(); + } + + return collection; } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index a343d92460..7bfde93af6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -20,13 +20,13 @@ import org.hibernate.LockOptions; import org.hibernate.collection.spi.BagSemantics; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.engine.profile.FetchProfile; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.FilterHelper; @@ -896,8 +896,23 @@ public class LoaderSelectBuilder { } private SelectStatement generateSelect(SubselectFetch subselect) { - // todo (6.0) : i think we may even be able to convert this to a join by piecing together - // parts from the subselect-fetch sql-ast.. + + // todo (6.0) : we could even convert this to a join by piecing together + // parts from the subselect-fetch sql-ast. e.g. today we do: + // select ... + // from collection_table c + // where c.fk in ( + // select o.id + // from owner_table o + // where + // ) + // but instead could do: + // select ... + // from owner_table o + // left join collection_table c on c.fk = o.id + // where + + // just like with other load-paths, bag-mappings can potentially be problematic here // todo (6.0) : ^^ another interesting idea is to use `partsToSelect` here relative to the owner // - so `loadable` is the owner entity-descriptor and the `partsToSelect` is the collection @@ -920,9 +935,6 @@ public class LoaderSelectBuilder { creationContext ); - // todo (6.0) : I think we want to continue to assign aliases to these table-references. we just want - // to control how that gets rendered in the walker - final TableGroup rootTableGroup = loadable.createRootTableGroup( true, rootNavigablePath, @@ -949,7 +961,9 @@ public class LoaderSelectBuilder { applyOrdering( rootTableGroup, attributeMapping ); // register the jdbc-parameters - subselect.getLoadingJdbcParameters().forEach( jdbcParameterConsumer ); + // todo (6.0) : analyzing the call paths, it seems like `jdbcParameterConsumer` + // never does anything for sub-select-fetch select building. + //subselect.getLoadingJdbcParameters().forEach( jdbcParameterConsumer ); return new SelectStatement( rootQuerySpec, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdLoaderStandard.java index 8fd6f98759..282bd992da 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdLoaderStandard.java @@ -23,6 +23,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LoadEvent; import org.hibernate.event.spi.LoadEventListener; @@ -297,7 +298,14 @@ public class MultiIdLoaderStandard implements MultiIdEntityLoader { loadingEntityCollector = null; } - return JdbcSelectExecutorStandardImpl.INSTANCE.list( + final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext().getBatchFetchQueue(), + sqlAst, + jdbcParameters, + jdbcParameterBindings + ); + + List list = JdbcSelectExecutorStandardImpl.INSTANCE.list( jdbcSelect, jdbcParameterBindings, new ExecutionContext() { @@ -328,14 +336,14 @@ public class MultiIdLoaderStandard implements MultiIdEntityLoader { @Override public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { - if ( loadingEntityCollector != null ) { - loadingEntityCollector.collectLoadingEntityKey( entityKey ); - } + subSelectFetchableKeysHandler.addKey( entityKey ); } }, RowTransformerPassThruImpl.instance(), ListResultsConsumer.UniqueSemantic.FILTER ); + + return list; } private List performUnorderedMultiLoad( diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java index b8f36eed09..7abfd77342 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java @@ -16,6 +16,7 @@ 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.CollectionHelper; import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -145,6 +146,7 @@ public class MultiNaturalIdLoadingBatcher { private List performLoad(JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session) { final LoadingEntityCollector loadingEntityCollector; + final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler; if ( entityDescriptor.getEntityPersister().hasSubselectLoadableCollections() ) { loadingEntityCollector = new LoadingEntityCollector( @@ -154,12 +156,23 @@ public class MultiNaturalIdLoadingBatcher { jdbcParamBindings, session.getPersistenceContext().getBatchFetchQueue() ); + + subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext().getBatchFetchQueue(), + sqlSelect, + jdbcParameters, + jdbcParamBindings + ); + + } else { loadingEntityCollector = null; + subSelectFetchableKeysHandler = null; } - return JdbcSelectExecutorStandardImpl.INSTANCE.list( + + final List result = JdbcSelectExecutorStandardImpl.INSTANCE.list( jdbcSelect, jdbcParamBindings, new ExecutionContext() { @@ -190,13 +203,15 @@ public class MultiNaturalIdLoadingBatcher { @Override public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { - if ( loadingEntityCollector != null ) { - loadingEntityCollector.collectLoadingEntityKey( entityKey ); + if ( subSelectFetchableKeysHandler != null ) { + subSelectFetchableKeysHandler.addKey( entityKey ); } } }, RowTransformerPassThruImpl.instance(), ListResultsConsumer.UniqueSemantic.FILTER ); + + return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderDynamicBatch.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderDynamicBatch.java index 07000cc5e8..f0bbb63838 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderDynamicBatch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderDynamicBatch.java @@ -16,6 +16,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.EntityKey; 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.metamodel.mapping.EntityMappingType; import org.hibernate.query.spi.QueryOptions; @@ -31,6 +32,7 @@ import org.hibernate.sql.exec.spi.Callback; 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; @@ -117,9 +119,18 @@ public class SingleIdEntityLoaderDynamicBatch extends SingleIdEntityLoaderSup ); } 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 + ); + JdbcSelectExecutorStandardImpl.INSTANCE.list( jdbcSelect, jdbcParameterBindings, @@ -144,6 +155,11 @@ public class SingleIdEntityLoaderDynamicBatch extends SingleIdEntityLoaderSup return sql; } + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchableKeysHandler.addKey( entityKey ); + } + @Override public QueryParameterBindings getQueryParameterBindings() { return QueryParameterBindings.NO_PARAM_BINDINGS; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 826036ff04..35e6fc0c7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -37,6 +37,7 @@ import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; @@ -49,7 +50,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.exception.spi.SQLExceptionConverter; import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.CoreMessageLogger; diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/DelegatingDomainQueryExecutionContext.java b/hibernate-core/src/main/java/org/hibernate/query/internal/DelegatingDomainQueryExecutionContext.java new file mode 100644 index 0000000000..f2081647c2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/DelegatingDomainQueryExecutionContext.java @@ -0,0 +1,44 @@ +/* + * 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 + */ +package org.hibernate.query.internal; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.sql.exec.spi.Callback; + +/** + * @author Steve Ebersole + */ +public class DelegatingDomainQueryExecutionContext implements DomainQueryExecutionContext { + private final DomainQueryExecutionContext delegate; + + public DelegatingDomainQueryExecutionContext(DomainQueryExecutionContext delegate) { + this.delegate = delegate; + } + + @Override + public QueryOptions getQueryOptions() { + return delegate.getQueryOptions(); + } + + @Override + public QueryParameterBindings getQueryParameterBindings() { + return delegate.getQueryParameterBindings(); + } + + @Override + public Callback getCallback() { + return delegate.getCallback(); + } + + @Override + public SharedSessionContractImplementor getSession() { + return delegate.getSession(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/DomainQueryExecutionContext.java b/hibernate-core/src/main/java/org/hibernate/query/spi/DomainQueryExecutionContext.java new file mode 100644 index 0000000000..a59a5dafa9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/DomainQueryExecutionContext.java @@ -0,0 +1,27 @@ +/* + * 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 + */ +package org.hibernate.query.spi; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.sql.exec.spi.Callback; + +/** + * Context for execution of {@link org.hibernate.query.Query}" + */ +public interface DomainQueryExecutionContext { + QueryOptions getQueryOptions(); + + QueryParameterBindings getQueryParameterBindings(); + + /** + * The callback reference + * @return + */ + Callback getCallback(); + + SharedSessionContractImplementor getSession(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/NonSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/spi/NonSelectQueryPlan.java index 610005db29..e4cee5be93 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/NonSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/NonSelectQueryPlan.java @@ -6,11 +6,9 @@ */ package org.hibernate.query.spi; -import org.hibernate.sql.exec.spi.ExecutionContext; - /** * @author Steve Ebersole */ public interface NonSelectQueryPlan extends QueryPlan { - int executeUpdate(ExecutionContext executionContext); + int executeUpdate(DomainQueryExecutionContext executionContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/spi/SelectQueryPlan.java index 99b013b971..50b48a1464 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/SelectQueryPlan.java @@ -11,7 +11,6 @@ import java.util.List; import org.hibernate.Incubating; import org.hibernate.ScrollMode; import org.hibernate.query.Query; -import org.hibernate.sql.exec.spi.ExecutionContext; /** * General contract for performing execution of a query returning results. These @@ -36,11 +35,11 @@ public interface SelectQueryPlan extends QueryPlan { /** * Perform (execute) the query returning a List */ - List performList(ExecutionContext executionContext); + List performList(DomainQueryExecutionContext executionContext); /** * Perform (execute) the query returning a ScrollableResults */ - ScrollableResultsImplementor performScroll(ScrollMode scrollMode, ExecutionContext executionContext); + ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java index 78ea425b0a..e9d18d1144 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java @@ -8,8 +8,6 @@ package org.hibernate.query.spi; import org.hibernate.LockOptions; import org.hibernate.query.Limit; -import org.hibernate.sql.exec.internal.DelegatingExecutionContext; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcSelect; /** @@ -26,35 +24,31 @@ public class SqlOmittingQueryOptions extends DelegatingQueryOptions { this.omitLocks = omitLocks; } - public static ExecutionContext omitSqlQueryOptions(ExecutionContext context) { - return omitSqlQueryOptions( context, true, true ); + public static QueryOptions omitSqlQueryOptions(QueryOptions originalOptions) { + return omitSqlQueryOptions( originalOptions, true, true ); } - public static ExecutionContext omitSqlQueryOptions(ExecutionContext context, JdbcSelect select) { - return omitSqlQueryOptions( context, !select.usesLimitParameters(), false ); + public static QueryOptions omitSqlQueryOptions(QueryOptions originalOptions, JdbcSelect select) { + return omitSqlQueryOptions( originalOptions, !select.usesLimitParameters(), false ); } - public static ExecutionContext omitSqlQueryOptions(ExecutionContext context, boolean omitLimit, boolean omitLocks) { - final QueryOptions originalQueryOptions = context.getQueryOptions(); - final Limit limit = originalQueryOptions.getLimit(); + public static QueryOptions omitSqlQueryOptions(QueryOptions originalOptions, boolean omitLimit, boolean omitLocks) { + final Limit limit = originalOptions.getLimit(); + // No need for a context when there are no options we use during SQL rendering - if ( originalQueryOptions.getLockOptions().isEmpty() ) { + if ( originalOptions.getLockOptions().isEmpty() ) { if ( !omitLimit || limit == null || limit.isEmpty() ) { - return context; + return originalOptions; } } - else if ( !omitLocks ) { + + if ( !omitLocks ) { if ( !omitLimit || limit == null || limit.isEmpty() ) { - return context; + return originalOptions; } } - final QueryOptions queryOptions = new SqlOmittingQueryOptions( originalQueryOptions, omitLimit, omitLocks ); - return new DelegatingExecutionContext( context ) { - @Override - public QueryOptions getQueryOptions() { - return queryOptions; - } - }; + + return new SqlOmittingQueryOptions( originalOptions, omitLimit, omitLocks ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java index 713fa4ba20..149984203e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java @@ -16,15 +16,16 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.model.domain.AllowableParameterType; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcMutation; import org.hibernate.sql.exec.spi.JdbcMutationExecutor; import org.hibernate.sql.exec.spi.JdbcParameterBinder; @@ -50,9 +51,9 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan { } @Override - public int executeUpdate(ExecutionContext executionContext) { + public int executeUpdate(DomainQueryExecutionContext executionContext) { executionContext.getSession().autoFlushIfRequired( affectedTableNames ); - BulkOperationCleanupAction.schedule( executionContext, affectedTableNames ); + BulkOperationCleanupAction.schedule( executionContext.getSession(), affectedTableNames ); final List jdbcParameterBinders; final JdbcParameterBindings jdbcParameterBindings; @@ -108,7 +109,7 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - executionContext + new SqmJdbcExecutionContextAdapter( executionContext ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 9764261b23..cd8b1b65d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -21,16 +21,6 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.Parameter; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.TemporalType; -import jakarta.persistence.Tuple; -import jakarta.persistence.metamodel.SingularAttribute; import org.hibernate.CacheMode; import org.hibernate.FlushMode; @@ -53,7 +43,6 @@ import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.jpa.spi.NativeQueryTupleTransformer; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.metamodel.model.domain.BasicDomainType; -import org.hibernate.persister.entity.Loadable; import org.hibernate.query.Limit; import org.hibernate.query.NativeQuery; import org.hibernate.query.ParameterMetadata; @@ -74,6 +63,7 @@ import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicResultBuilderEntityStandard; import org.hibernate.query.results.dynamic.DynamicResultBuilderInstantiation; import org.hibernate.query.spi.AbstractQuery; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.ParameterMetadataImplementor; @@ -92,12 +82,22 @@ import org.hibernate.query.sql.spi.ParameterInterpretation; import org.hibernate.query.sql.spi.SelectInterpretationsKey; import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.spi.Callback; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeReference; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Parameter; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Tuple; +import jakarta.persistence.metamodel.SingularAttribute; + import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE; /** @@ -106,7 +106,7 @@ import static org.hibernate.jpa.QueryHints.HINT_NATIVE_LOCKMODE; @SuppressWarnings("WeakerAccess") public class NativeQueryImpl extends AbstractQuery - implements NativeQueryImplementor, ExecutionContext, ResultSetMappingResolutionContext { + implements NativeQueryImplementor, DomainQueryExecutionContext, ResultSetMappingResolutionContext { private final String sqlString; private final ParameterMetadataImplementor parameterMetadata; @@ -440,13 +440,6 @@ public class NativeQueryImpl return callback; } - @Override - public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { - if ( callback != null ) { - callback.invokeAfterLoadActions( session, entity, persister ); - } - } - public SessionFactoryImplementor getSessionFactory() { return getSession().getFactory(); } @@ -504,11 +497,6 @@ public class NativeQueryImpl throw new IllegalStateException( "Illegal attempt to set lock mode on a native-query" ); } - @Override - public String getQueryIdentifier(String sql) { - return sql; - } - @Override public Query applyGraph(RootGraph graph, GraphSemantic semantic) { throw new HibernateException( "A native SQL query cannot use EntityGraphs" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java index b9ee21f053..a00a0dc8a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java @@ -19,16 +19,17 @@ import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.results.ResultSetMapping; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.sql.spi.NativeSelectQueryPlan; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcSelect; @@ -68,7 +69,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { } @Override - public List performList(ExecutionContext executionContext) { + public List performList(DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return Collections.emptyList(); } @@ -126,14 +127,14 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { return executor.list( jdbcSelect, jdbcParameterBindings, - executionContext, + new SqmJdbcExecutionContextAdapter( executionContext, executionContext.getQueryOptions() ), null, ListResultsConsumer.UniqueSemantic.NONE ); } @Override - public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, ExecutionContext executionContext) { + public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return EmptyScrollableResults.INSTANCE; } @@ -191,7 +192,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { jdbcSelect, scrollMode, jdbcParameterBindings, - executionContext, + new SqmJdbcExecutionContextAdapter( executionContext, jdbcSelect ), null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AggregatedSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AggregatedSelectQueryPlanImpl.java index c6437afc76..2f7fd8a0d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AggregatedSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AggregatedSelectQueryPlanImpl.java @@ -12,6 +12,7 @@ import java.util.List; import org.hibernate.ScrollMode; import org.hibernate.internal.EmptyScrollableResults; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.NotYetImplementedFor6Exception; @@ -28,7 +29,7 @@ public class AggregatedSelectQueryPlanImpl implements SelectQueryPlan { } @Override - public List performList(ExecutionContext executionContext) { + public List performList(DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return Collections.emptyList(); } @@ -42,7 +43,7 @@ public class AggregatedSelectQueryPlanImpl implements SelectQueryPlan { } @Override - public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, ExecutionContext executionContext) { + public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return EmptyScrollableResults.INSTANCE; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index 1cb12b2f8c..3a6950472b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -11,20 +11,20 @@ import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import jakarta.persistence.Tuple; -import jakarta.persistence.TupleElement; -import jakarta.persistence.criteria.CompoundSelection; import org.hibernate.ScrollMode; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.internal.EmptyScrollableResults; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.criteria.JpaSelection; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterImplementor; @@ -42,9 +42,10 @@ import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; -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.exec.spi.JdbcSelectExecutor; +import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; @@ -53,6 +54,12 @@ import org.hibernate.sql.results.internal.TupleMetadata; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.RowTransformer; +import jakarta.persistence.Tuple; +import jakarta.persistence.TupleElement; +import jakarta.persistence.criteria.CompoundSelection; + +import static org.hibernate.query.sqm.internal.QuerySqmImpl.CRITERIA_HQL_STRING; + /** * Standard Hibernate implementation of SelectQueryPlan for SQM-backed * {@link org.hibernate.query.Query} implementations, which means @@ -62,6 +69,7 @@ import org.hibernate.sql.results.spi.RowTransformer; */ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { private final SqmSelectStatement sqm; + private final String hql; private final DomainParameterXref domainParameterXref; private final RowTransformer rowTransformer; private final SqmInterpreter, Void> listInterpreter; @@ -72,22 +80,51 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { @SuppressWarnings("WeakerAccess") public ConcreteSqmSelectQueryPlan( SqmSelectStatement sqm, + String hql, DomainParameterXref domainParameterXref, Class resultType, QueryOptions queryOptions) { this.sqm = sqm; + this.hql = hql; this.domainParameterXref = domainParameterXref; this.rowTransformer = determineRowTransformer( sqm, resultType, queryOptions ); + this.listInterpreter = (unused, executionContext, sqmInterpretation, jdbcParameterBindings) -> { final SharedSessionContractImplementor session = executionContext.getSession(); final JdbcSelect jdbcSelect = sqmInterpretation.getJdbcSelect(); try { + final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler( + session.getPersistenceContext().getBatchFetchQueue(), + sqmInterpretation.selectStatement, + Collections.emptyList(), + jdbcParameterBindings + ); + session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); + return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, jdbcParameterBindings, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext, jdbcSelect ), + new SqmJdbcExecutionContextAdapter( executionContext, jdbcSelect ) { + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchKeyHandler.addKey( entityKey ); + } + + @Override + public String getQueryIdentifier(String sql) { + if ( CRITERIA_HQL_STRING.equals( hql ) ) { + return "[CRITERIA] " + sql; + } + return hql; + } + + @Override + public boolean hasQueryExecutionToBeAddedToStatistics() { + return !CRITERIA_HQL_STRING.equals( hql ); + } + }, rowTransformer, ListResultsConsumer.UniqueSemantic.FILTER ); @@ -96,15 +133,41 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { domainParameterXref.clearExpansions(); } }; + this.scrollInterpreter = (scrollMode, executionContext, sqmInterpretation, jdbcParameterBindings) -> { try { - return executionContext.getSession().getFactory().getJdbcServices().getJdbcSelectExecutor().scroll( + final SubselectFetch.RegistrationHandler subSelectFetchKeyHandler = SubselectFetch.createRegistrationHandler( + executionContext.getSession().getPersistenceContext().getBatchFetchQueue(), + sqmInterpretation.selectStatement, + Collections.emptyList(), + jdbcParameterBindings + ); + + final JdbcSelectExecutor jdbcSelectExecutor = executionContext.getSession() + .getFactory() + .getJdbcServices() + .getJdbcSelectExecutor(); + final ScrollableResultsImplementor result = jdbcSelectExecutor.scroll( sqmInterpretation.getJdbcSelect(), scrollMode, jdbcParameterBindings, - executionContext, + new SqmJdbcExecutionContextAdapter( executionContext ) { + final QueryOptions options = SqlOmittingQueryOptions.omitSqlQueryOptions( queryOptions, sqmInterpretation.jdbcSelect ); + + @Override + public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) { + subSelectFetchKeyHandler.addKey( entityKey ); + } + + @Override + public QueryOptions getQueryOptions() { + return options; + } + }, rowTransformer ); + + return result; } finally { domainParameterXref.clearExpansions(); @@ -202,7 +265,7 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { } @Override - public List performList(ExecutionContext executionContext) { + public List performList(DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return Collections.emptyList(); } @@ -210,14 +273,14 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { } @Override - public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, ExecutionContext executionContext) { + public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return EmptyScrollableResults.INSTANCE; } return withCacheableSqmInterpretation( executionContext, scrollMode, scrollInterpreter ); } - private T withCacheableSqmInterpretation(ExecutionContext executionContext, X context, SqmInterpreter interpreter) { + private T withCacheableSqmInterpretation(DomainQueryExecutionContext executionContext, X context, SqmInterpreter interpreter) { // NOTE : VERY IMPORTANT - intentional double-lock checking // The other option would be to leverage `java.util.concurrent.locks.ReadWriteLock` // to protect access. However, synchronized is much simpler here. We will verify @@ -260,13 +323,15 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { cacheableSqmInterpretation = localCopy; } } + if ( jdbcParameterBindings == null ) { jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); } + return interpreter.interpret( context, executionContext, localCopy, jdbcParameterBindings ); } - private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, ExecutionContext executionContext) { + private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, DomainQueryExecutionContext executionContext) { final SharedSessionContractImplementor session = executionContext.getSession(); final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), @@ -284,7 +349,7 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { private static CacheableSqmInterpretation buildCacheableSqmInterpretation( SqmSelectStatement sqm, DomainParameterXref domainParameterXref, - ExecutionContext executionContext) { + DomainQueryExecutionContext executionContext) { final SharedSessionContractImplementor session = executionContext.getSession(); final SessionFactoryImplementor sessionFactory = session.getFactory(); final QueryEngine queryEngine = sessionFactory.getQueryEngine(); @@ -296,7 +361,7 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), sessionFactory ); @@ -328,6 +393,7 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { final JdbcSelect jdbcSelect = selectTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); return new CacheableSqmInterpretation( + sqmInterpretation.getSqlAst(), jdbcSelect, tableGroupAccess, jdbcParamsXref, @@ -339,12 +405,13 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { private interface SqmInterpreter { T interpret( X context, - ExecutionContext executionContext, + DomainQueryExecutionContext executionContext, CacheableSqmInterpretation sqmInterpretation, JdbcParameterBindings jdbcParameterBindings); } private static class CacheableSqmInterpretation { + private final SelectStatement selectStatement; private final JdbcSelect jdbcSelect; private final FromClauseAccess tableGroupAccess; private final Map, Map>>> jdbcParamsXref; @@ -352,11 +419,13 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { private transient JdbcParameterBindings firstParameterBindings; CacheableSqmInterpretation( + SelectStatement selectStatement, JdbcSelect jdbcSelect, FromClauseAccess tableGroupAccess, Map, Map>>> jdbcParamsXref, Map sqmParameterMappingModelTypes, JdbcParameterBindings firstParameterBindings) { + this.selectStatement = selectStatement; this.jdbcSelect = jdbcSelect; this.tableGroupAccess = tableGroupAccess; this.jdbcParamsXref = jdbcParamsXref; @@ -364,6 +433,10 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { this.firstParameterBindings = firstParameterBindings; } + SelectStatement getSelectStatement() { + return selectStatement; + } + JdbcSelect getJdbcSelect() { return jdbcSelect; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableDeleteQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableDeleteQueryPlan.java index 203d33fc01..7155660e61 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableDeleteQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableDeleteQueryPlan.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.internal; import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; @@ -30,8 +31,8 @@ public class MultiTableDeleteQueryPlan implements NonSelectQueryPlan { } @Override - public int executeUpdate(ExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext, sqmDelete ); + public int executeUpdate(DomainQueryExecutionContext executionContext) { + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDelete ); return deleteStrategy.executeDelete( sqmDelete, domainParameterXref, executionContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableUpdateQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableUpdateQueryPlan.java index 9a874cef78..7564a3641e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableUpdateQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/MultiTableUpdateQueryPlan.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.internal; import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; @@ -30,8 +31,8 @@ public class MultiTableUpdateQueryPlan implements NonSelectQueryPlan { } @Override - public int executeUpdate(ExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext, sqmUpdate ); + public int executeUpdate(DomainQueryExecutionContext executionContext) { + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmUpdate ); return mutationStrategy.executeUpdate( sqmUpdate, domainParameterXref, executionContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 577d213732..7d172a4f9d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -15,10 +15,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import jakarta.persistence.LockModeType; -import jakarta.persistence.Parameter; -import jakarta.persistence.PersistenceException; -import jakarta.persistence.Tuple; import org.hibernate.HibernateException; import org.hibernate.LockMode; @@ -36,7 +32,6 @@ import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.Loadable; import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode; import org.hibernate.query.Query; import org.hibernate.query.QueryTypeMismatchException; @@ -44,10 +39,12 @@ import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl; import org.hibernate.query.hql.internal.QuerySplitter; import org.hibernate.query.hql.spi.HqlQueryImplementor; import org.hibernate.query.hql.spi.NamedHqlQueryMemento; +import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; import org.hibernate.query.internal.ParameterMetadataImpl; import org.hibernate.query.internal.QueryOptionsImpl; import org.hibernate.query.internal.QueryParameterBindingsImpl; import org.hibernate.query.spi.AbstractQuery; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.NonSelectQueryPlan; @@ -59,7 +56,6 @@ import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; -import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -79,10 +75,16 @@ import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.spi.Callback; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.jdbc.JdbcType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Parameter; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.Tuple; + +import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions; + /** * {@link Query} implementation based on an SQM * @@ -90,7 +92,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; */ public class QuerySqmImpl extends AbstractQuery - implements HqlQueryImplementor, ExecutionContext { + implements HqlQueryImplementor, DomainQueryExecutionContext { /** * The value used for {@link #getQueryString} for Criteria-based queries @@ -604,7 +606,8 @@ public class QuerySqmImpl queryOptions.getGraph() != null || hasLimit ); - final ExecutionContext executionContextToUse; + + final DomainQueryExecutionContext executionContextToUse; if ( hasLimit && containsCollectionFetches ) { boolean fail = getSessionFactory().getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled(); if (fail) { @@ -617,7 +620,15 @@ public class QuerySqmImpl else { LOG.firstOrMaxResultsSpecifiedWithCollectionFetch(); } - executionContextToUse = SqlOmittingQueryOptions.omitSqlQueryOptions( this, true, false ); + + final MutableQueryOptions originalQueryOptions = getQueryOptions(); + final QueryOptions normalizedQueryOptions = omitSqlQueryOptions( originalQueryOptions, true, false ); + if ( originalQueryOptions == normalizedQueryOptions ) { + executionContextToUse = this; + } + else { + executionContextToUse = new DelegatingDomainQueryExecutionContext( this ); + } } else { executionContextToUse = this; @@ -720,6 +731,7 @@ public class QuerySqmImpl QueryOptions queryOptions) { return new ConcreteSqmSelectQueryPlan<>( concreteSqmStatement, + hqlString, domainParameterXref, resultType, queryOptions @@ -841,21 +853,6 @@ public class QuerySqmImpl return callback; } - @Override - public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { - if ( callback != null ) { - callback.invokeAfterLoadActions( session, entity, persister ); - } - } - - @Override - public String getQueryIdentifier(String sql) { - if ( CRITERIA_HQL_STRING.equals( hqlString ) ) { - return "[CRITERIA] " + sql; - } - return hqlString; - } - @Override public NamedHqlQueryMemento toMemento(String name) { return new NamedHqlQueryMementoImpl( @@ -877,9 +874,4 @@ public class QuerySqmImpl ); } - @Override - public boolean hasQueryExecutionToBeAddedToStatistics() { - return !CRITERIA_HQL_STRING.equals( hqlString ); - } - } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java index 327f1485b8..73d1cb8f0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java @@ -17,10 +17,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.MappingModelHelper; import org.hibernate.query.NavigablePath; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; import org.hibernate.query.sqm.sql.SqmTranslation; import org.hibernate.query.sqm.sql.SqmTranslator; @@ -34,7 +34,6 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.select.QuerySpec; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcDelete; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.internal.SqlSelectionImpl; @@ -62,7 +61,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { this.domainParameterXref = domainParameterXref; } - private SqlAstTranslator createDeleteTranslator(ExecutionContext executionContext) { + private SqlAstTranslator createDeleteTranslator(DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final QueryEngine queryEngine = factory.getQueryEngine(); @@ -72,7 +71,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), factory ); @@ -88,8 +87,8 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { } @Override - public int executeUpdate(ExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext, sqmDelete ); + public int executeUpdate(DomainQueryExecutionContext executionContext) { + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDelete ); final SharedSessionContractImplementor session = executionContext.getSession(); final SessionFactoryImplementor factory = session.getFactory(); final JdbcServices jdbcServices = factory.getJdbcServices(); @@ -127,6 +126,8 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { assert jdbcParamsXref.isEmpty(); } + final SqmJdbcExecutionContextAdapter executionContextAdapter = new SqmJdbcExecutionContextAdapter( executionContext ); + SqmMutationStrategyHelper.cleanUpCollectionTables( entityDescriptor, (tableReference, attributeMapping) -> { @@ -162,8 +163,8 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { return new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false ); }, - missingRestriction ? JdbcParameterBindings.NO_BINDINGS : jdbcParameterBindings, - executionContext + ( missingRestriction ? JdbcParameterBindings.NO_BINDINGS : jdbcParameterBindings ), + executionContextAdapter ); return jdbcServices.getJdbcMutationExecutor().execute( @@ -174,7 +175,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContextAdapter ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleInsertQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleInsertQueryPlan.java index 6cc26c4e4f..6c479f28a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleInsertQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleInsertQueryPlan.java @@ -14,10 +14,10 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.sql.SqmTranslation; import org.hibernate.query.sqm.sql.SqmTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; @@ -27,7 +27,6 @@ import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.insert.InsertStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcInsert; import org.hibernate.sql.exec.spi.JdbcParameterBindings; @@ -50,7 +49,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan { this.domainParameterXref = domainParameterXref; } - private SqlAstTranslator createInsertTranslator(ExecutionContext executionContext) { + private SqlAstTranslator createInsertTranslator(DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final QueryEngine queryEngine = factory.getQueryEngine(); @@ -60,7 +59,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan { executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), factory ); @@ -75,13 +74,15 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan { this.paramTypeResolutions = sqmInterpretation.getSqmParameterMappingModelTypeResolutions(); - return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() + return factory.getJdbcServices() + .getJdbcEnvironment() + .getSqlAstTranslatorFactory() .buildInsertTranslator( factory, sqmInterpretation.getSqlAst() ); } @Override - public int executeUpdate(ExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext, sqmInsert ); + public int executeUpdate(DomainQueryExecutionContext executionContext) { + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmInsert ); final SharedSessionContractImplementor session = executionContext.getSession(); final SessionFactoryImplementor factory = session.getFactory(); final JdbcServices jdbcServices = factory.getJdbcServices(); @@ -122,7 +123,7 @@ public class SimpleInsertQueryPlan implements NonSelectQueryPlan { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + new SqmJdbcExecutionContextAdapter( executionContext ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java index 6013d91453..8b37c7c54f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleUpdateQueryPlan.java @@ -14,10 +14,10 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.sql.SqmTranslation; import org.hibernate.query.sqm.sql.SqmTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; @@ -27,7 +27,6 @@ import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.update.UpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcUpdate; @@ -51,8 +50,8 @@ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan { } @Override - public int executeUpdate(ExecutionContext executionContext) { - BulkOperationCleanupAction.schedule( executionContext, sqmUpdate ); + public int executeUpdate(DomainQueryExecutionContext executionContext) { + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmUpdate ); final SharedSessionContractImplementor session = executionContext.getSession(); final SessionFactoryImplementor factory = session.getFactory(); final JdbcServices jdbcServices = factory.getJdbcServices(); @@ -93,11 +92,11 @@ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + new SqmJdbcExecutionContextAdapter( executionContext ) ); } - private SqlAstTranslator createUpdateTranslator(ExecutionContext executionContext) { + private SqlAstTranslator createUpdateTranslator(DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final QueryEngine queryEngine = factory.getQueryEngine(); @@ -107,7 +106,7 @@ public class SimpleUpdateQueryPlan implements NonSelectQueryPlan { executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), factory ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmJdbcExecutionContextAdapter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmJdbcExecutionContextAdapter.java new file mode 100644 index 0000000000..6fc5d65589 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmJdbcExecutionContextAdapter.java @@ -0,0 +1,58 @@ +/* + * 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 + */ +package org.hibernate.query.sqm.internal; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.sql.exec.spi.Callback; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcSelect; + +import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions; + +/** + * @author Steve Ebersole + */ +public class SqmJdbcExecutionContextAdapter implements ExecutionContext { + private final DomainQueryExecutionContext sqmExecutionContext; + private final QueryOptions queryOptions; + + public SqmJdbcExecutionContextAdapter(DomainQueryExecutionContext sqmExecutionContext) { + this( sqmExecutionContext, omitSqlQueryOptions( sqmExecutionContext.getQueryOptions() ) ); + } + + public SqmJdbcExecutionContextAdapter(DomainQueryExecutionContext sqmExecutionContext, JdbcSelect jdbcSelect) { + this( sqmExecutionContext, omitSqlQueryOptions( sqmExecutionContext.getQueryOptions(), jdbcSelect ) ); + } + + public SqmJdbcExecutionContextAdapter(DomainQueryExecutionContext sqmExecutionContext, QueryOptions queryOptions) { + this.sqmExecutionContext = sqmExecutionContext; + this.queryOptions = queryOptions; + } + + @Override + public SharedSessionContractImplementor getSession() { + return sqmExecutionContext.getSession(); + } + + @Override + public QueryOptions getQueryOptions() { + return queryOptions; + } + + @Override + public QueryParameterBindings getQueryParameterBindings() { + return sqmExecutionContext.getQueryParameterBindings(); + } + + @Override + public Callback getCallback() { + return sqmExecutionContext.getCallback(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/Handler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/Handler.java index b5bd0302e8..bc6bfce2b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/Handler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/Handler.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.mutation.internal; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext; /** @@ -26,5 +27,5 @@ public interface Handler { * * @return The "number of rows affected" count */ - int execute(ExecutionContext executionContext); + int execute(DomainQueryExecutionContext executionContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index a6bb51fc9d..f8e0c11e45 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -21,8 +21,9 @@ import org.hibernate.internal.FilterHelper; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.persister.entity.Joinable; -import org.hibernate.query.spi.SqlOmittingQueryOptions; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -39,7 +40,6 @@ import org.hibernate.sql.ast.tree.predicate.FilterPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; -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.DomainResult; @@ -73,7 +73,7 @@ public class MatchingIdSelectionHelper { boolean queryRoot, Predicate restriction, MultiTableSqmMutationConverter sqmConverter, - ExecutionContext executionContext, + DomainQueryExecutionContext executionContext, SessionFactoryImplementor sessionFactory) { final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel(); if ( log.isTraceEnabled() ) { @@ -119,7 +119,7 @@ public class MatchingIdSelectionHelper { ); final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate( - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), (Joinable) targetEntityDescriptor.getEntityPersister(), mutatingTableGroup ); @@ -195,7 +195,7 @@ public class MatchingIdSelectionHelper { public static List selectMatchingIds( SqmDeleteOrUpdateStatement sqmMutationStatement, DomainParameterXref domainParameterXref, - ExecutionContext executionContext) { + DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final EntityMappingType entityDescriptor = factory.getDomainModel() @@ -206,7 +206,7 @@ public class MatchingIdSelectionHelper { sqmMutationStatement.getTarget().getExplicitAlias(), domainParameterXref, executionContext.getQueryOptions(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), factory ); @@ -275,7 +275,7 @@ public class MatchingIdSelectionHelper { return jdbcServices.getJdbcSelectExecutor().list( idSelectJdbcOperation, jdbcParameterBindings, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext, idSelectJdbcOperation ), + new SqmJdbcExecutionContextAdapter( executionContext ), row -> row, ListResultsConsumer.UniqueSemantic.FILTER ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java index 1554e85b2a..02b3c4c081 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java @@ -122,7 +122,7 @@ public class SqmMutationStrategyHelper { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java index 00fd9b587f..f86f677a98 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java @@ -21,9 +21,9 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.SqlExpressable; -import org.hibernate.metamodel.model.domain.BasicDomainType; -import org.hibernate.query.spi.SqlOmittingQueryOptions; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; @@ -53,7 +53,6 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectStatement; -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.DomainResult; @@ -102,7 +101,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler } @Override - public int execute(ExecutionContext executionContext) { + public int execute(DomainQueryExecutionContext executionContext) { final SqmDeleteOrUpdateStatement sqmMutationStatement = getSqmDeleteOrUpdateStatement(); final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final EntityMappingType entityDescriptor = getEntityDescriptor(); @@ -121,7 +120,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler explicitDmlTargetAlias, domainParameterXref, executionContext.getQueryOptions(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), factory ); @@ -207,7 +206,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler List list = jdbcServices.getJdbcSelectExecutor().list( select, jdbcParameterBindings, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext, select ), + new SqmJdbcExecutionContextAdapter( executionContext ), row -> row[0], ListResultsConsumer.UniqueSemantic.NONE ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteStrategy.java index 5a080cdd40..544533fda1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteStrategy.java @@ -14,14 +14,13 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.cte.SqmCteTable; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.sql.ast.tree.cte.CteTable; -import org.hibernate.sql.exec.spi.ExecutionContext; /** * @asciidoc @@ -97,7 +96,7 @@ public class CteStrategy implements SqmMultiTableMutationStrategy { public int executeDelete( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { checkMatch( sqmDelete ); return new CteDeleteHandler( cteTable, sqmDelete, domainParameterXref, this, sessionFactory ).execute( context ); } @@ -106,7 +105,7 @@ public class CteStrategy implements SqmMultiTableMutationStrategy { public int executeUpdate( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { checkMatch( sqmUpdate ); return new CteUpdateHandler( cteTable, sqmUpdate, domainParameterXref, this, sessionFactory ).execute( context ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java index dbd6d7dc1b..5f1202baeb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java @@ -21,7 +21,6 @@ import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.ComparisonOperator; import org.hibernate.query.NavigablePath; -import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstTranslatorFactory; @@ -154,7 +153,7 @@ public final class ExecuteWithIdTableHelper { .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java index c9864ec833..ff3bbd0885 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java @@ -11,17 +11,16 @@ import java.sql.SQLException; import java.util.function.Supplier; import org.hibernate.boot.TempTableDdlTransactionHandling; -import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.jboss.logging.Logger; @@ -64,7 +63,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate public int executeUpdate( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedUpdateHandler( sqmUpdate, domainParameterXref, @@ -84,7 +83,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate public int executeDelete( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedDeleteHandler( sqmDelete, domainParameterXref, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java index 89e118662c..d5109db02c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java @@ -17,11 +17,11 @@ import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.jboss.logging.Logger; @@ -76,7 +76,7 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg public int executeUpdate( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedUpdateHandler( sqmUpdate, domainParameterXref, @@ -96,7 +96,7 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg public int executeDelete( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedDeleteHandler( sqmDelete, domainParameterXref, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PersistentTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PersistentTableStrategy.java index 95bc0578dd..b0508be159 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PersistentTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PersistentTableStrategy.java @@ -14,11 +14,11 @@ import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.jboss.logging.Logger; @@ -68,7 +68,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy { public int executeUpdate( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedUpdateHandler( sqmUpdate, domainParameterXref, @@ -86,7 +86,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy { public int executeDelete( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { return new TableBasedDeleteHandler( sqmDelete, domainParameterXref, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/RestrictedDeleteExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/RestrictedDeleteExecutionDelegate.java index ade06d229e..030373c5d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/RestrictedDeleteExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/RestrictedDeleteExecutionDelegate.java @@ -31,10 +31,12 @@ import org.hibernate.metamodel.mapping.MappingModelHelper; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; @@ -118,8 +120,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle } @Override - public int execute(ExecutionContext executionContext) { - + public int execute(DomainQueryExecutionContext executionContext) { final EntityPersister entityDescriptor = sessionFactory.getDomainModel().getEntityDescriptor( sqmDelete.getTarget().getEntityName() ); final String hierarchyRootTableName = ( (Joinable) entityDescriptor ).getTableName(); @@ -166,7 +167,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle ); final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate( - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), (Joinable) entityDescriptor, deletingTableGroup ); @@ -177,13 +178,15 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle boolean needsIdTable = needsIdTableWrapper.get(); + final SqmJdbcExecutionContextAdapter executionContextAdapter = new SqmJdbcExecutionContextAdapter( executionContext ); + if ( needsIdTable ) { return executeWithIdTable( predicate, deletingTableGroup, parameterResolutions, paramTypeResolutions, - executionContext + executionContextAdapter ); } else { @@ -193,7 +196,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle parameterResolutions, paramTypeResolutions, converter.getSqlExpressionResolver(), - executionContext + executionContextAdapter ); } } @@ -387,7 +390,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java index e74bdd4c51..e43ac6e6c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java @@ -12,11 +12,11 @@ import java.util.function.Supplier; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.DeleteHandler; import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.jboss.logging.Logger; @@ -29,7 +29,7 @@ public class TableBasedDeleteHandler private static final Logger log = Logger.getLogger( TableBasedDeleteHandler.class ); public interface ExecutionDelegate { - int execute(ExecutionContext executionContext); + int execute(DomainQueryExecutionContext executionContext); } private final IdTable idTable; @@ -66,7 +66,7 @@ public class TableBasedDeleteHandler } @Override - public int execute(ExecutionContext executionContext) { + public int execute(DomainQueryExecutionContext executionContext) { if ( log.isTraceEnabled() ) { log.tracef( "Starting multi-table delete execution - %s", @@ -76,7 +76,7 @@ public class TableBasedDeleteHandler return resolveDelegate( executionContext ).execute( executionContext ); } - private ExecutionDelegate resolveDelegate(ExecutionContext executionContext) { + private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { return new RestrictedDeleteExecutionDelegate( getEntityDescriptor(), idTable, @@ -88,7 +88,7 @@ public class TableBasedDeleteHandler exporterSupplier, sessionUidAccess, executionContext.getQueryOptions(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), getSessionFactory() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedUpdateHandler.java index 3b2ea49584..061aff908e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedUpdateHandler.java @@ -25,7 +25,9 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.UpdateHandler; import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; @@ -100,7 +102,7 @@ public class TableBasedUpdateHandler @Override - public int execute(ExecutionContext executionContext) { + public int execute(DomainQueryExecutionContext executionContext) { if ( log.isTraceEnabled() ) { log.tracef( "Starting multi-table update execution - %s", @@ -108,10 +110,11 @@ public class TableBasedUpdateHandler ); } - return resolveDelegate( executionContext ).execute( executionContext ); + final SqmJdbcExecutionContextAdapter executionContextAdapter = new SqmJdbcExecutionContextAdapter( executionContext ); + return resolveDelegate( executionContext ).execute( executionContextAdapter ); } - private ExecutionDelegate resolveDelegate(ExecutionContext executionContext) { + private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) { final SessionFactoryImplementor sessionFactory = getSessionFactory(); final MappingMetamodel domainModel = sessionFactory.getDomainModel(); final EntityPersister entityDescriptor = domainModel.getEntityDescriptor( getSqmDeleteOrUpdateStatement().getTarget().getEntityName() ); @@ -126,7 +129,7 @@ public class TableBasedUpdateHandler getSqmDeleteOrUpdateStatement().getTarget().getExplicitAlias(), domainParameterXref, executionContext.getQueryOptions(), - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), sessionFactory ); @@ -193,7 +196,7 @@ public class TableBasedUpdateHandler } final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate( - executionContext.getLoadQueryInfluencers(), + executionContext.getSession().getLoadQueryInfluencers(), (Joinable) rootEntityDescriptor, updatingTableGroup ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UpdateExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UpdateExecutionDelegate.java index 504dbfa595..01bbc8b08b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UpdateExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UpdateExecutionDelegate.java @@ -22,7 +22,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.query.spi.SqlOmittingQueryOptions; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; @@ -83,7 +83,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio Predicate suppliedPredicate, Map>> parameterResolutions, Map paramTypeResolutions, - ExecutionContext executionContext) { + DomainQueryExecutionContext executionContext) { this.sqmUpdate = sqmUpdate; this.sqmConverter = sqmConverter; this.idTable = idTable; @@ -292,7 +292,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio .getStatementPreparer() .prepareStatement( sql ), (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java index 714d7d5190..b7acf38dfe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineDeleteHandler.java @@ -12,11 +12,14 @@ import java.util.function.Consumer; import java.util.function.Supplier; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.SelectableConsumer; +import org.hibernate.query.spi.DomainQueryExecutionContext; +import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.mutation.internal.DeleteHandler; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; @@ -25,7 +28,6 @@ import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcDelete; import org.hibernate.sql.exec.spi.JdbcMutationExecutor; import org.hibernate.sql.exec.spi.JdbcParameterBindings; @@ -44,7 +46,7 @@ public class InlineDeleteHandler implements DeleteHandler { private final SqmDeleteStatement sqmDeleteStatement; private final DomainParameterXref domainParameterXref; - private final ExecutionContext executionContext; + private final DomainQueryExecutionContext executionContext; private final SessionFactoryImplementor sessionFactory; private final SqlAstTranslatorFactory sqlAstTranslatorFactory; @@ -54,7 +56,7 @@ public class InlineDeleteHandler implements DeleteHandler { MatchingIdRestrictionProducer matchingIdsPredicateProducer, SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { this.sqmDeleteStatement = sqmDeleteStatement; this.domainParameterXref = domainParameterXref; @@ -68,7 +70,7 @@ public class InlineDeleteHandler implements DeleteHandler { } @Override - public int execute(ExecutionContext executionContext) { + public int execute(DomainQueryExecutionContext executionContext) { final List ids = MatchingIdSelectionHelper.selectMatchingIds( sqmDeleteStatement, domainParameterXref, @@ -140,7 +142,7 @@ public class InlineDeleteHandler implements DeleteHandler { Supplier> tableKeyColumnsVisitationSupplier, List ids, JdbcParameterBindings jdbcParameterBindings, - ExecutionContext executionContext) { + DomainQueryExecutionContext executionContext) { final TableReference targetTableReference = new TableReference( targetTableExpression, null, @@ -148,12 +150,21 @@ public class InlineDeleteHandler implements DeleteHandler { sessionFactory ); + final QueryOptions queryOptions = SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext.getQueryOptions() ); + + final SqmJdbcExecutionContextAdapter executionContextAdapter = new SqmJdbcExecutionContextAdapter( executionContext ) { + @Override + public QueryOptions getQueryOptions() { + return queryOptions; + } + }; + final Predicate matchingIdsPredicate = matchingIdsPredicateProducer.produceRestriction( ids, entityDescriptor, targetTableReference, tableKeyColumnsVisitationSupplier, - executionContext + executionContextAdapter ); final DeleteStatement deleteStatement = new DeleteStatement( targetTableReference, matchingIdsPredicate ); @@ -166,7 +177,7 @@ public class InlineDeleteHandler implements DeleteHandler { jdbcParameterBindings, this::prepareQueryStatement, (integer, preparedStatement) -> {}, - SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext ) + executionContextAdapter ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java index be3528af5b..9f89bb8df7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java @@ -8,14 +8,13 @@ package org.hibernate.query.sqm.mutation.internal.inline; import java.util.function.Function; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.dialect.Dialect; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; -import org.hibernate.sql.exec.spi.ExecutionContext; /** * Support for multi-table SQM mutation operations which select the matching id values from the database back into @@ -45,7 +44,7 @@ public class InlineStrategy implements SqmMultiTableMutationStrategy { public int executeUpdate( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { final InlineUpdateHandler handler = new InlineUpdateHandler( matchingIdsStrategy.apply( sqmUpdate ), sqmUpdate, @@ -59,7 +58,7 @@ public class InlineStrategy implements SqmMultiTableMutationStrategy { public int executeDelete( SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { final InlineDeleteHandler deleteHandler = new InlineDeleteHandler( matchingIdsStrategy.apply( sqmDelete ), sqmDelete, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java index da081439e5..ae67b18a1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.mutation.internal.inline; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.UpdateHandler; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; @@ -23,7 +24,7 @@ public class InlineUpdateHandler implements UpdateHandler { private final DomainParameterXref domainParameterXref; private final MatchingIdRestrictionProducer matchingIdsPredicateProducer; - private final ExecutionContext executionContext; + private final DomainQueryExecutionContext executionContext; private final SessionFactoryImplementor sessionFactory; private final SqlAstTranslatorFactory sqlAstTranslatorFactory; @@ -33,7 +34,7 @@ public class InlineUpdateHandler implements UpdateHandler { MatchingIdRestrictionProducer matchingIdsPredicateProducer, SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, - ExecutionContext context) { + DomainQueryExecutionContext context) { this.matchingIdsPredicateProducer = matchingIdsPredicateProducer; this.domainParameterXref = domainParameterXref; this.sqmUpdate = sqmUpdate; @@ -46,7 +47,7 @@ public class InlineUpdateHandler implements UpdateHandler { } @Override - public int execute(ExecutionContext executionContext) { + public int execute(DomainQueryExecutionContext executionContext) { throw new NotYetImplementedFor6Exception( getClass() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.java index a7340edc12..c7380ae106 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.java @@ -9,6 +9,7 @@ package org.hibernate.query.sqm.mutation.spi; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; @@ -56,7 +57,7 @@ public interface SqmMultiTableMutationStrategy { int executeUpdate( SqmUpdateStatement sqmUpdateStatement, DomainParameterXref domainParameterXref, - ExecutionContext context); + DomainQueryExecutionContext context); /** * Execute the multi-table update indicated by the passed SqmUpdateStatement @@ -66,5 +67,5 @@ public interface SqmMultiTableMutationStrategy { int executeDelete( SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, - ExecutionContext context); + DomainQueryExecutionContext context); } diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java index 9dead76b23..e928f369d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java @@ -19,6 +19,7 @@ import jakarta.persistence.ParameterMode; import org.hibernate.JDBCException; import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreLogging; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java index 80a0ae233b..b90df85214 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java @@ -17,7 +17,8 @@ import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; /** - * @author Steve Ebersole + * A context for execution of SQL statements expressed via + * SQL AST and JdbcOperation */ public interface ExecutionContext { @@ -34,7 +35,7 @@ public interface ExecutionContext { Callback getCallback(); default void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { - // No-op because by default there is callback + // No-op because by default there is no callback } default String getQueryIdentifier(String sql) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/CollectionLoadingLogger.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/CollectionLoadingLogger.java index 29fc529ae7..8df48d4e90 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/CollectionLoadingLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/CollectionLoadingLogger.java @@ -22,7 +22,13 @@ public interface CollectionLoadingLogger extends BasicLogger { /** * Static access to the logging instance */ - Logger INSTANCE = LoadingLogger.subLogger( LOCAL_NAME ); + Logger COLL_LOAD_LOGGER = LoadingLogger.subLogger( LOCAL_NAME ); + + /** + * @deprecated Use {@link #COLL_LOAD_LOGGER} + */ + @Deprecated + Logger INSTANCE = COLL_LOAD_LOGGER; boolean TRACE_ENABLED = INSTANCE.isTraceEnabled(); boolean DEBUG_ENABLED = INSTANCE.isDebugEnabled(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java index 2643012bec..90284f60a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java @@ -13,8 +13,8 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.DomainResultAssembler; -import org.hibernate.sql.results.graph.collection.CollectionInitializer; import org.hibernate.sql.results.graph.FetchParentAccess; +import org.hibernate.sql.results.graph.collection.CollectionInitializer; import org.hibernate.sql.results.graph.collection.CollectionLoadingLogger; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; @@ -69,6 +69,7 @@ public abstract class AbstractCollectionInitializer implements CollectionInitial else { collectionKeyValue = collectionKeyResultAssembler.assemble( rowProcessingState ); } + if ( collectionKeyValue != null ) { this.collectionKey = new CollectionKey( collectionAttributeMapping.getCollectionDescriptor(), diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java index d90f1b0608..d28ce4dbb2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java @@ -25,6 +25,8 @@ import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry; import org.hibernate.sql.results.internal.LoadingCollectionEntryImpl; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; +import static org.hibernate.sql.results.graph.collection.CollectionLoadingLogger.COLL_LOAD_LOGGER; + /** * Base support for CollectionInitializer implementations that represent * an immediate initialization of some sort (join, select, batch, sub-select) @@ -70,7 +72,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol } if ( CollectionLoadingLogger.TRACE_ENABLED ) { - CollectionLoadingLogger.INSTANCE.tracef( + COLL_LOAD_LOGGER.tracef( "(%s) Beginning Initializer#resolveInstance for collection : %s", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ) @@ -93,7 +95,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol collectionInstance = existingLoadingEntry.getCollectionInstance(); if ( CollectionLoadingLogger.DEBUG_ENABLED ) { - CollectionLoadingLogger.INSTANCE.debugf( + COLL_LOAD_LOGGER.debugf( "(%s) Found existing loading collection entry [%s]; using loading collection instance - %s", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ), @@ -108,7 +110,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol else { // the entity is already being loaded elsewhere if ( CollectionLoadingLogger.DEBUG_ENABLED ) { - CollectionLoadingLogger.INSTANCE.debugf( + COLL_LOAD_LOGGER.debugf( "(%s) Collection [%s] being loaded by another initializer [%s] - skipping processing", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ), @@ -130,7 +132,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol if ( collectionInstance.wasInitialized() ) { if ( CollectionLoadingLogger.DEBUG_ENABLED ) { - CollectionLoadingLogger.INSTANCE.debugf( + COLL_LOAD_LOGGER.debugf( "(%s) Found existing collection instance [%s] in Session; skipping processing - [%s]", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ), @@ -184,7 +186,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol ); if ( CollectionLoadingLogger.DEBUG_ENABLED ) { - CollectionLoadingLogger.INSTANCE.debugf( + COLL_LOAD_LOGGER.debugf( "(%s) Created new collection wrapper [%s] : %s", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ), @@ -203,7 +205,7 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol if ( responsibility != null ) { if ( CollectionLoadingLogger.DEBUG_ENABLED ) { - CollectionLoadingLogger.INSTANCE.debugf( + COLL_LOAD_LOGGER.debugf( "(%s) Responsible for loading collection [%s] : %s", getSimpleConcreteImplName(), LoggingHelper.toLoggableString( getNavigablePath(), collectionKey.getKey() ), diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java index d300bb075e..c3be2f4238 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java @@ -35,6 +35,7 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra private final PluralAttributeMapping loadingAttribute; private final String resultVariable; + private final TableGroup tableGroup; private final DomainResult fkResult; @@ -49,6 +50,7 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra this.loadingPath = loadingPath; this.loadingAttribute = loadingAttribute; this.resultVariable = resultVariable; + this.tableGroup = tableGroup; fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult( loadingPath, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java index 7a80267c95..3ead3fe48b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java @@ -15,6 +15,7 @@ import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializerProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializerProducer.java index ff91de27aa..5634527796 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializerProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializerProducer.java @@ -8,13 +8,18 @@ package org.hibernate.sql.results.graph.collection.internal; import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; +import org.hibernate.engine.FetchStyle; 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.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.collection.CollectionInitializer; +import org.hibernate.sql.results.graph.collection.CollectionLoadingLogger; + +import static org.hibernate.sql.results.graph.collection.CollectionLoadingLogger.COLL_LOAD_LOGGER; /** * @author Steve Ebersole @@ -23,11 +28,15 @@ public class SetInitializerProducer implements CollectionInitializerProducer { private final PluralAttributeMapping setDescriptor; private final Fetch elementFetch; + private final boolean isSubSelectFetchable; + public SetInitializerProducer( PluralAttributeMapping setDescriptor, Fetch elementFetch) { this.setDescriptor = setDescriptor; this.elementFetch = elementFetch; + + isSubSelectFetchable = setDescriptor.getMappedFetchOptions().getStyle() == FetchStyle.SUBSELECT; } @Override @@ -41,6 +50,10 @@ public class SetInitializerProducer implements CollectionInitializerProducer { AssemblerCreationState creationState) { final DomainResultAssembler elementAssembler = elementFetch.createAssembler( parentAccess, creationState ); + if ( isSubSelectFetchable && navigablePath.isRoot() ) { + COLL_LOAD_LOGGER.debugf( "Creating sub-select-fetch initializer : `%`", navigablePath ); + } + return new SetInitializer( navigablePath, setDescriptor, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java index 2312241fbe..6654653bbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java @@ -34,7 +34,6 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAstCreationContext; -import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.ResultsLogger; import org.hibernate.sql.results.graph.AssemblerCreationState; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java index c001b8248f..d7d76c5bb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java @@ -15,7 +15,6 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.spi.StatementOptions; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.collection.CollectionInitializer; import org.hibernate.sql.results.graph.entity.EntityFetch; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWitSubSelectFetchModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWitSubSelectFetchModeTest.java index 592e43be27..0896fb337c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWitSubSelectFetchModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/filter/FilterWitSubSelectFetchModeTest.java @@ -93,7 +93,6 @@ public class FilterWitSubSelectFetchModeTest { } @Test - @FailureExpected( reason = "subselect fetch mode not implemeneted correctly in v6" ) void testFiltersAreApplied(SessionFactoryScope scope) { scope.inTransaction( session -> { session.enableFilter( "ID" ).setParameter( "id", 3L ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Child.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Child.java similarity index 91% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Child.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Child.java index 9302b959be..141ad0e710 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Child.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Child.java @@ -6,7 +6,7 @@ */ //$Id: Child.java 6095 2005-03-17 05:57:29Z oneovthafew $ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.util.List; /** diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Name.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Name.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Name.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Name.java index 3969f591ce..fd72ebfa86 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Name.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Name.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.io.Serializable; import java.util.List; diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Parent.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Parent.java similarity index 93% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Parent.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Parent.java index 04cc092569..71033b3063 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Parent.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Parent.java @@ -6,7 +6,7 @@ */ //$Id: Parent.java 6095 2005-03-17 05:57:29Z oneovthafew $ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.util.ArrayList; import java.util.List; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleEagerSubSelectFetchTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleEagerSubSelectFetchTests.java new file mode 100644 index 0000000000..c768764396 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleEagerSubSelectFetchTests.java @@ -0,0 +1,210 @@ +/* + * 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 + */ +package org.hibernate.orm.test.mapping.collections.subselectfetch; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = { + SimpleEagerSubSelectFetchTests.Owner.class, + SimpleEagerSubSelectFetchTests.Thing.class +}) +@SessionFactory( statementInspectorClass = SQLStatementInspector.class ) +public class SimpleEagerSubSelectFetchTests { + + @Test + public void smokeTest(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + final List misspelled = session.createQuery( "from Owner o where o.name like 'Onwer%'", Owner.class ).list(); + assertThat( misspelled ).hasSize( 2 ); + + final Owner firstResult = misspelled.get( 0 ); + final Owner secondResult = misspelled.get( 1 ); + + // make sure we got the right owners + assertThat( firstResult.getId() ).isLessThanOrEqualTo( 2 ); + assertThat( secondResult.getId() ).isLessThanOrEqualTo( 2 ); + + // check that the one-to-many was loaded + assertThat( Hibernate.isInitialized( firstResult.getThings() ) ).isTrue(); + assertThat( Hibernate.isInitialized( secondResult.getThings() ) ).isTrue(); + + // the initial query + the "subselect" select + assertThat( statementInspector.getSqlQueries() ).hasSize( 2 ); + } ); + } + + @Test + public void baselineJoinFetchTest(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + final List misspelled = session.createQuery( "from Owner o left join fetch o.things where o.name like 'Onwer%'", Owner.class ).list(); + assertThat( misspelled ).hasSize( 2 ); + + final Owner firstResult = misspelled.get( 0 ); + final Owner secondResult = misspelled.get( 1 ); + + // make sure we got the right owners + assertThat( firstResult.getId() ).isLessThanOrEqualTo( 2 ); + assertThat( secondResult.getId() ).isLessThanOrEqualTo( 2 ); + + // both one-to-many should be initialized + assertThat( Hibernate.isInitialized( firstResult.getThings() ) ).isTrue(); + assertThat( Hibernate.isInitialized( secondResult.getThings() ) ).isTrue(); + + assertThat( firstResult.getThings().size() + secondResult.getThings().size() ).isEqualTo( 3 ); + + // the initial query with join + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getNumberOfJoins( 0 ) ).isEqualTo( 1 ); + } ); + } + + @BeforeEach + public void prepareTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + final Owner o1 = new Owner( 1, "Onwer 1" ); + final Owner o2 = new Owner( 2, "Onwer 2" ); + final Owner o3 = new Owner( 3, "Owner 3" ); + + final Thing thing1 = new Thing( 1, "first", o1 ); + final Thing thing2 = new Thing( 2, "second", o1 ); + final Thing thing3 = new Thing( 3, "third", o1 ); + final Thing thing4 = new Thing( 4, "fourth", o3 ); + final Thing thing5 = new Thing( 5, "fifth", o3 ); + + session.persist( o1 ); + session.persist( o2 ); + session.persist( o3 ); + + session.persist( thing1 ); + session.persist( thing2 ); + session.persist( thing3 ); + session.persist( thing4 ); + session.persist( thing5 ); + } ); + } + + @AfterEach + public void dropTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + session.createQuery( "delete Thing" ).executeUpdate(); + session.createQuery( "delete Owner" ).executeUpdate(); + } ); + } + + @Entity(name = "Owner") + @Table(name = "t_sub_fetch_owner") + public static class Owner { + @Id + private Integer id; + private String name; + + @OneToMany(mappedBy = "owner", fetch = FetchType.EAGER) + @Fetch(FetchMode.SUBSELECT) + private Set things = new HashSet<>(); + + private Owner() { + } + + public Owner(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getThings() { + return things; + } + + public void setThings(Set things) { + this.things = things; + } + } + + @Entity(name = "Thing") + @Table(name = "t_sub_fetch_thing") + public static class Thing { + @Id + private Integer id; + private String name; + + @ManyToOne + private Owner owner; + + private Thing() { + } + + public Thing(Integer id, String name, Owner owner) { + this.id = id; + this.name = name; + this.owner = owner; + owner.getThings().add( this ); + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Owner getOwner() { + return owner; + } + + public void setOwner(Owner owner) { + this.owner = owner; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleLazySubSelectFetchTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleLazySubSelectFetchTests.java new file mode 100644 index 0000000000..5232906938 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SimpleLazySubSelectFetchTests.java @@ -0,0 +1,236 @@ +/* + * 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 + */ +package org.hibernate.orm.test.mapping.collections.subselectfetch; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = { + SimpleLazySubSelectFetchTests.Owner.class, + SimpleLazySubSelectFetchTests.Thing.class +}) +@SessionFactory( statementInspectorClass = SQLStatementInspector.class ) +public class SimpleLazySubSelectFetchTests { + + @Test + public void smokeTest(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + final List misspelled = session.createQuery( "from Owner o where o.name like 'Onwer%'", Owner.class ).list(); + assertThat( misspelled ).hasSize( 2 ); + + final Owner firstResult = misspelled.get( 0 ); + final Owner secondResult = misspelled.get( 1 ); + + final Owner o1; + final Owner o2; + if ( firstResult.id == 1 ) { + o1 = firstResult; + o2 = secondResult; + } + else { + o1 = secondResult; + o2 = firstResult; + } + + // make sure we got the right owners + assertThat( o1.getId() ).isEqualTo( 1 ); + assertThat( o2.getId() ).isEqualTo( 2 ); + + // check that the one-to-many are not loaded (its lazy) + assertThat( Hibernate.isInitialized( firstResult.getThings() ) ).isFalse(); + assertThat( Hibernate.isInitialized( secondResult.getThings() ) ).isFalse(); + + // now trigger the initialization of one of them + // - the size() call should trigger the initialization + assertThat( o1.getThings().size() ).isEqualTo( 3 ); + + // now both should be initialized + assertThat( Hibernate.isInitialized( firstResult.getThings() ) ).isTrue(); + assertThat( Hibernate.isInitialized( secondResult.getThings() ) ).isTrue(); + + // the initial query + the "subselect" select + assertThat( statementInspector.getSqlQueries() ).hasSize( 2 ); + + // access the other + assertThat( o2.getThings().size() ).isEqualTo( 0 ); + + // make sure we did not get any extra SQL + assertThat( statementInspector.getSqlQueries() ).hasSize( 2 ); + } ); + } + + @Test + public void baselineJoinFetchTest(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + final List misspelled = session.createQuery( "from Owner o left join fetch o.things where o.name like 'Onwer%'", Owner.class ).list(); + assertThat( misspelled ).hasSize( 2 ); + + final Owner firstResult = misspelled.get( 0 ); + final Owner secondResult = misspelled.get( 1 ); + + // make sure we got the right owners + assertThat( firstResult.getId() ).isLessThanOrEqualTo( 2 ); + assertThat( secondResult.getId() ).isLessThanOrEqualTo( 2 ); + + // both one-to-many should be initialized + assertThat( Hibernate.isInitialized( firstResult.getThings() ) ).isTrue(); + assertThat( Hibernate.isInitialized( secondResult.getThings() ) ).isTrue(); + + assertThat( firstResult.getThings().size() + secondResult.getThings().size() ).isEqualTo( 3 ); + + // the initial query with join + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getNumberOfJoins( 0 ) ).isEqualTo( 1 ); + } ); + } + + + @BeforeEach + public void prepareTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + final Owner o1 = new Owner( 1, "Onwer 1" ); + final Owner o2 = new Owner( 2, "Onwer 2" ); + final Owner o3 = new Owner( 3, "Owner 3" ); + + final Thing thing1 = new Thing( 1, "first", o1 ); + final Thing thing2 = new Thing( 2, "second", o1 ); + final Thing thing3 = new Thing( 3, "third", o1 ); + final Thing thing4 = new Thing( 4, "fourth", o3 ); + final Thing thing5 = new Thing( 5, "fifth", o3 ); + + session.persist( o1 ); + session.persist( o2 ); + session.persist( o3 ); + + session.persist( thing1 ); + session.persist( thing2 ); + session.persist( thing3 ); + session.persist( thing4 ); + session.persist( thing5 ); + } ); + } + + @AfterEach + public void dropTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + session.createQuery( "delete Thing" ).executeUpdate(); + session.createQuery( "delete Owner" ).executeUpdate(); + } ); + } + + @Entity(name = "Owner") + @Table(name = "t_sub_fetch_owner") + public static class Owner { + @Id + private Integer id; + private String name; + + @OneToMany(mappedBy = "owner", fetch = FetchType.LAZY) + @Fetch(FetchMode.SUBSELECT) + private Set things = new HashSet<>(); + + private Owner() { + } + + public Owner(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getThings() { + return things; + } + + public void setThings(Set things) { + this.things = things; + } + } + + @Entity(name = "Thing") + @Table(name = "t_sub_fetch_thing") + public static class Thing { + @Id + private Integer id; + private String name; + + @ManyToOne + private Owner owner; + + private Thing() { + } + + public Thing(Integer id, String name, Owner owner) { + this.id = id; + this.name = name; + this.owner = owner; + owner.getThings().add( this ); + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Owner getOwner() { + return owner; + } + + public void setOwner(Owner owner) { + this.owner = owner; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchCollectionFromBatchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchCollectionFromBatchTest.java new file mode 100644 index 0000000000..fc9480fc19 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchCollectionFromBatchTest.java @@ -0,0 +1,431 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2006-2011, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.orm.test.mapping.collections.subselectfetch; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SessionFactoryImplementor; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.NotImplementedYet; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; + +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Stephen Fikes + * @author Gail Badner + */ +@ServiceRegistry( settings = @Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ) ) +@DomainModel( annotatedClasses = { SubselectFetchCollectionFromBatchTest.EmployeeGroup.class, SubselectFetchCollectionFromBatchTest.Employee.class } ) +@SessionFactory +public class SubselectFetchCollectionFromBatchTest { + + @Test + @TestForIssue( jiraKey = "HHH-10679") + @NotImplementedYet( strict = false, reason = "Need to check why these fail" ) + public void testSubselectFetchFromEntityBatch(SessionFactoryScope scope) { + final EmployeeGroup[] createdGroups = scope.fromTransaction( (s) -> { + EmployeeGroup group1 = new EmployeeGroup(); + Employee employee1 = new Employee("Jane"); + Employee employee2 = new Employee("Jeff"); + group1.addEmployee(employee1); + group1.addEmployee(employee2); + EmployeeGroup group2 = new EmployeeGroup(); + Employee employee3 = new Employee("Joan"); + Employee employee4 = new Employee("John"); + group2.addEmployee(employee3); + group2.addEmployee( employee4 ); + + s.save( group1 ); + s.save( group2 ); + + return new EmployeeGroup[] { group1, group2 }; + }); + + scope.getSessionFactory().getStatistics().clear(); + + scope.inTransaction( (s) -> { + EmployeeGroup[] loadedGroups = new EmployeeGroup[] { + s.load(EmployeeGroup.class, createdGroups[0].getId()), + s.load(EmployeeGroup.class, createdGroups[1].getId()) + }; + + // loadedGroups should only contain proxies + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + + for (EmployeeGroup group : loadedGroups) { + assertFalse( Hibernate.isInitialized( group ) ); + } + + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + + for ( EmployeeGroup group : loadedGroups ) { + // Both loadedGroups get initialized and are added to the PersistenceContext when i == 0; + // Still need to call Hibernate.initialize( loadedGroups[i] ) for i > 0 so that the entity + // in the PersistenceContext gets assigned to its respective proxy target (is this a + // bug???) + Hibernate.initialize( group ); + assertTrue( Hibernate.isInitialized( group ) ); + // the collections should be uninitialized + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); + } + + // both Group proxies should have been loaded in the same batch; + assertEquals( 1, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + scope.getSessionFactory().getStatistics().clear(); + + for ( EmployeeGroup group : loadedGroups ) { + assertTrue( Hibernate.isInitialized( group ) ); + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); + } + + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + + // now initialize the collection in the first; collections in both loadedGroups + // should get initialized + Hibernate.initialize( loadedGroups[0].getEmployees() ); + + assertEquals( 1, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + scope.getSessionFactory().getStatistics().clear(); + + // all collections should be initialized now + for (EmployeeGroup group : loadedGroups) { + assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); + } + + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + } ); + } + + @Test + public void testSubselectFetchFromQueryList(SessionFactoryScope scope) { + final Long[] createdIds = scope.fromTransaction( (s) -> { + EmployeeGroup group1 = new EmployeeGroup(); + Employee employee1 = new Employee("Jane"); + Employee employee2 = new Employee("Jeff"); + group1.addEmployee(employee1); + group1.addEmployee(employee2); + EmployeeGroup group2 = new EmployeeGroup(); + Employee employee3 = new Employee("Joan"); + Employee employee4 = new Employee("John"); + group2.addEmployee(employee3); + group2.addEmployee( employee4 ); + + s.save( group1 ); + s.save( group2 ); + + return new Long[] { group1.id, group2.id }; + } ); + + scope.getSessionFactory().getStatistics().clear(); + + scope.inTransaction( (s) -> { + List results = s + .createQuery( "from SubselectFetchCollectionFromBatchTest$EmployeeGroup where id in :groups" ) + .setParameterList( "groups", createdIds ) + .list(); + + assertEquals( 1, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + scope.getSessionFactory().getStatistics().clear(); + + for (EmployeeGroup group : results) { + assertTrue( Hibernate.isInitialized( group ) ); + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); + } + + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + + // now initialize the collection in the first; collections in both groups + // should get initialized + Hibernate.initialize( results.get( 0 ).getEmployees() ); + + assertEquals( 1, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + scope.getSessionFactory().getStatistics().clear(); + + // all collections should be initialized now + for (EmployeeGroup group : results) { + assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); + } + + assertEquals( 0, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); + } ); + + } + + @Test + @TestForIssue( jiraKey = "HHH-10679") + @NotImplementedYet( strict = false, reason = "Need to check why these fail" ) + public void testMultiSubselectFetchSamePersisterQueryList(SessionFactoryScope scope) { + final Long[] createdIds = scope.fromTransaction( (s) -> { + EmployeeGroup group1 = new EmployeeGroup(); + Employee employee1 = new Employee("Jane"); + Employee employee2 = new Employee("Jeff"); + group1.addEmployee( employee1 ); + group1.addEmployee( employee2 ); + group1.setManager( new Employee( "group1 manager" ) ); + group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#1" ) ); + group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#2" ) ); + group1.setLead( new Employee( "group1 lead" ) ); + group1.getLead().addCollaborator( new Employee( "group1 lead's collaborator#1" ) ); + EmployeeGroup group2 = new EmployeeGroup(); + Employee employee3 = new Employee("Joan"); + Employee employee4 = new Employee("John"); + group2.addEmployee( employee3 ); + group2.addEmployee( employee4 ); + group2.setManager( new Employee( "group2 manager" ) ); + group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#1" ) ); + group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#2" ) ); + group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#3" ) ); + group2.setLead( new Employee( "group2 lead" ) ); + group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#1" ) ); + group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#2" ) ); + + s.save( group1 ); + s.save( group2 ); + + return new Long[] { group1.id, group2.id }; + } ); + + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + sessionFactory.getStatistics().clear(); + + scope.inTransaction( (s) -> { + EmployeeGroup[] loadedGroups = new EmployeeGroup[] { + s.load(EmployeeGroup.class, createdIds[0]), + s.load(EmployeeGroup.class, createdIds[1]) + }; + + // loadedGroups should only contain proxies + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + for (EmployeeGroup group : loadedGroups) { + assertFalse( Hibernate.isInitialized( group ) ); + } + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + + for ( EmployeeGroup group : loadedGroups ) { + // Both loadedGroups get initialized and are added to the PersistenceContext when i == 0; + // Still need to call Hibernate.initialize( loadedGroups[i] ) for i > 0 so that the entity + // in the PersistenceContext gets assigned to its respective proxy target (is this a + // bug???) + Hibernate.initialize( group ); + assertTrue( Hibernate.isInitialized( group ) ); + assertTrue( Hibernate.isInitialized( group.getLead() ) ); + assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); + assertTrue( Hibernate.isInitialized( group.getManager() ) ); + assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); + // the collections should be uninitialized + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); + } + + // both Group proxies should have been loaded in the same batch; + assertEquals( 1, sessionFactory.getStatistics().getPrepareStatementCount() ); + sessionFactory.getStatistics().clear(); + + for ( EmployeeGroup group : loadedGroups ) { + assertTrue( Hibernate.isInitialized( group ) ); + assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); + } + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + // now initialize the collection in the first; collections in both loadedGroups + // should get initialized + Hibernate.initialize( loadedGroups[0].getEmployees() ); + + assertEquals( 1, sessionFactory.getStatistics().getPrepareStatementCount() ); + sessionFactory.getStatistics().clear(); + + // all EmployeeGroup#employees should be initialized now + for (EmployeeGroup group : loadedGroups) { + assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); + assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); + assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); + } + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + // now initialize loadedGroups[0].getLead().getCollaborators(); + // loadedGroups[1].getLead().getCollaborators() should also be initialized + Hibernate.initialize( loadedGroups[0].getLead().getCollaborators() ); + + assertEquals( 1, sessionFactory.getStatistics().getPrepareStatementCount() ); + sessionFactory.getStatistics().clear(); + + for (EmployeeGroup group : loadedGroups) { + assertTrue( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); + assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); + } + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + // now initialize loadedGroups[0].getManager().getCollaborators(); + // loadedGroups[1].getManager().getCollaborators() should also be initialized + Hibernate.initialize( loadedGroups[0].getManager().getCollaborators() ); + + assertEquals( 1, sessionFactory.getStatistics().getPrepareStatementCount() ); + sessionFactory.getStatistics().clear(); + + for (EmployeeGroup group : loadedGroups) { + assertTrue( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); + } + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + + assertEquals( loadedGroups[0].getLead().getCollaborators().size(), loadedGroups[0].getLead().getCollaborators().size() ); + assertEquals( loadedGroups[1].getLead().getCollaborators().size(), loadedGroups[1].getLead().getCollaborators().size() ); + assertEquals( loadedGroups[0].getManager().getCollaborators().size(), loadedGroups[0].getManager().getCollaborators().size() ); + assertEquals( loadedGroups[1].getManager().getCollaborators().size(), loadedGroups[1].getManager().getCollaborators().size() ); + + assertEquals( 0, sessionFactory.getStatistics().getPrepareStatementCount() ); + } ); + } + + @Entity + @Table(name = "EmployeeGroup") + @BatchSize(size = 1000) + public static class EmployeeGroup { + @Id + @GeneratedValue + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private Employee manager; + + @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private Employee lead; + + @OneToMany(cascade = CascadeType.ALL) + @Fetch(FetchMode.SUBSELECT) + @JoinTable(name="EmployeeGroup_employees") + private List employees = new ArrayList(); + + public EmployeeGroup(long id) { + this.id = id; + } + + @SuppressWarnings("unused") + protected EmployeeGroup() { + } + + public Employee getManager() { + return manager; + } + + public void setManager(Employee manager) { + this.manager = manager; + } + + public Employee getLead() { + return lead; + } + + public void setLead(Employee lead) { + this.lead = lead; + } + + public boolean addEmployee(Employee employee) { + return employees.add(employee); + } + + public List getEmployees() { + return employees; + } + + public long getId() { + return id; + } + + @Override + public String toString() { + return id.toString(); + } + } + + + @Entity + @Table(name = "Employee") + public static class Employee { + @Id + @GeneratedValue + private Long id; + private String name; + + @OneToMany(cascade = CascadeType.ALL) + @Fetch(FetchMode.SUBSELECT) + private List collaborators = new ArrayList(); + + public String getName() { + return name; + } + + @SuppressWarnings("unused") + private Employee() { + } + + public Employee(String name) { + this.name = name; + } + + public boolean addCollaborator(Employee employee) { + return collaborators.add(employee); + } + + public List getCollaborators() { + return collaborators; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchTest.java similarity index 82% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchTest.java index efc5a09217..c9c5466cfd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchTest.java @@ -4,21 +4,25 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.util.List; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Join; -import jakarta.persistence.criteria.JoinType; -import jakarta.persistence.criteria.Root; import org.hibernate.Hibernate; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.cfg.Configuration; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.NotImplementedYet; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Root; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -27,21 +31,21 @@ import static org.junit.Assert.assertTrue; /** * @author Gavin King */ -public class SubselectFetchTest extends BaseCoreFunctionalTestCase { - @Override - public String[] getMappings() { - return new String[] { "subselectfetch/ParentChild.hbm.xml" }; - } - - @Override - public void configure(Configuration cfg) { - cfg.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" ); - cfg.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" ); - } - +@ServiceRegistry( + settings = { + @Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ), + @Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" ) + } +) +@DomainModel( + xmlMappings = "/mappings/subselectfetch/ParentChild.hbm.xml" +) +@SessionFactory +@NotImplementedYet( strict = false, reason = "Need to check why these fail" ) +public class SubselectFetchTest { @Test - public void testSubselectFetchHql() { - inTransaction( + public void testSubselectFetchHql(SessionFactoryScope scope) { + scope.inTransaction( s -> { Parent p = new Parent( "foo" ); p.getChildren().add( new Child( "foo1" ) ); @@ -55,9 +59,9 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } ); - inTransaction( + scope.inTransaction( s -> { - sessionFactory().getStatistics().clear(); + scope.getSessionFactory().getStatistics().clear(); List parents = s.createQuery( "from Parent where name between 'bar' and 'foo' order by name desc" ) .list(); @@ -88,7 +92,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) ); - assertEquals( 3, sessionFactory().getStatistics().getPrepareStatementCount() ); + assertEquals( 3, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); Child c = (Child) p.getChildren().get( 0 ); c.getFriends().size(); @@ -100,8 +104,8 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } @Test - public void testSubselectFetchNamedParam() { - inTransaction( + public void testSubselectFetchNamedParam(SessionFactoryScope scope) { + scope.inTransaction( s -> { Parent p = new Parent( "foo" ); p.getChildren().add( new Child( "foo1" ) ); @@ -115,9 +119,9 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } ); - inTransaction( + scope.inTransaction( s -> { - sessionFactory().getStatistics().clear(); + scope.getSessionFactory().getStatistics().clear(); List parents = s.createQuery( "from Parent where name between :bar and :foo order by name desc" ) .setParameter( "bar", "bar" ) @@ -150,7 +154,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) ); - assertEquals( 3, sessionFactory().getStatistics().getPrepareStatementCount() ); + assertEquals( 3, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); Child c = (Child) p.getChildren().get( 0 ); c.getFriends().size(); @@ -162,8 +166,8 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } @Test - public void testSubselectFetchPosParam() { - inTransaction( + public void testSubselectFetchPosParam(SessionFactoryScope scope) { + scope.inTransaction( s -> { Parent p = new Parent( "foo" ); p.getChildren().add( new Child( "foo1" ) ); @@ -177,9 +181,9 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } ); - inTransaction( + scope.inTransaction( s -> { - sessionFactory().getStatistics().clear(); + scope.getSessionFactory().getStatistics().clear(); List parents = s.createQuery( "from Parent where name between ?1 and ?2 order by name desc" ) .setParameter( 1, "bar" ) @@ -212,7 +216,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) ); - assertEquals( 3, sessionFactory().getStatistics().getPrepareStatementCount() ); + assertEquals( 3, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); Child c = (Child) p.getChildren().get( 0 ); c.getFriends().size(); @@ -224,12 +228,13 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } @Test - public void testSubselectFetchWithLimit() { + public void testSubselectFetchWithLimit(SessionFactoryScope scope) { Parent parent = new Parent( "foo" ); Parent secondParent = new Parent( "bar" ); Parent thirdParent = new Parent( "aaa" ); - inTransaction( + + scope.inTransaction( s -> { parent.getChildren().add( new Child( "foo1" ) ); parent.getChildren().add( new Child( "foo2" ) ); @@ -242,9 +247,9 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } ); - inTransaction( + scope.inTransaction( s -> { - sessionFactory().getStatistics().clear(); + scope.getSessionFactory().getStatistics().clear(); List parents = s.createQuery( "from Parent order by name desc" ) .setMaxResults( 2 ) @@ -260,7 +265,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { assertTrue( Hibernate.isInitialized( q.getChildren() ) ); assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) ); - assertEquals( 3, sessionFactory().getStatistics().getPrepareStatementCount() ); + assertEquals( 3, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); Parent r = s.get( Parent.class, thirdParent.getName() ); assertTrue( Hibernate.isInitialized( r.getChildren() ) ); @@ -276,8 +281,8 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } @Test - public void testManyToManyCriteriaJoin() { - inTransaction( + public void testManyToManyCriteriaJoin(SessionFactoryScope scope) { + scope.inTransaction( s -> { Parent p = new Parent( "foo" ); p.getChildren().add( new Child( "foo1" ) ); @@ -291,7 +296,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } ); - inTransaction( + scope.inTransaction( s -> { CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); CriteriaQuery criteria = criteriaBuilder.createQuery( Parent.class ); @@ -329,8 +334,8 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { } @Test - public void testSubselectFetchCriteria() { - inTransaction( + public void testSubselectFetchCriteria(SessionFactoryScope scope) { + scope.inTransaction( s -> { Parent p = new Parent( "foo" ); p.getChildren().add( new Child( "foo1" ) ); @@ -345,9 +350,9 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { ); - inTransaction( + scope.inTransaction( s -> { - sessionFactory().getStatistics().clear(); + scope.getSessionFactory().getStatistics().clear(); CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); CriteriaQuery criteria = criteriaBuilder.createQuery( Parent.class ); @@ -387,7 +392,7 @@ public class SubselectFetchTest extends BaseCoreFunctionalTestCase { assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) ); - assertEquals( 3, sessionFactory().getStatistics().getPrepareStatementCount() ); + assertEquals( 3, scope.getSessionFactory().getStatistics().getPrepareStatementCount() ); Child c = (Child) p.getChildren().get( 0 ); c.getFriends().size(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTest.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTest.java index a2d208ea23..1d46fca1da 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.util.List; @@ -35,8 +35,8 @@ public class SubselectFetchWithFormulaTest extends BaseNonConfigCoreFunctionalTe @Override protected String[] getMappings() { return new String[] { - "org/hibernate/test/subselectfetch/Name.hbm.xml", - "org/hibernate/test/subselectfetch/Value.hbm.xml" + "mappings/subselectfetch/Name.hbm.xml", + "mappings/subselectfetch/Value.hbm.xml" }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java index f1f362b5a2..a80b3390f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/SubselectFetchWithFormulaTransactSqlTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.util.List; import jakarta.persistence.criteria.CriteriaBuilder; @@ -33,8 +33,8 @@ public class SubselectFetchWithFormulaTransactSqlTest extends BaseNonConfigCoreF @Override protected String[] getMappings() { return new String[] { - "org/hibernate/test/subselectfetch/NameTransactSql.hbm.xml", - "org/hibernate/test/subselectfetch/Value.hbm.xml" + "mappings/subselectfetch/NameTransactSql.hbm.xml", + "mappings/subselectfetch/Value.hbm.xml" }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Value.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Value.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Value.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Value.java index 9cd2d67486..971313b19d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/Value.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/subselectfetch/Value.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.subselectfetch; +package org.hibernate.orm.test.mapping.collections.subselectfetch; import java.io.Serializable; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ParameterTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ParameterTests.java index 680837e540..33825a56b4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ParameterTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ParameterTests.java @@ -21,6 +21,7 @@ import org.hibernate.Session; import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest; import org.hibernate.query.Query; import org.hibernate.query.SemanticException; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -95,7 +96,7 @@ public class ParameterTests extends BaseSqmUnitTest { query.setParameter( "start", Date.from( Instant.now().minus( 7, ChronoUnit.DAYS ) ), TemporalType.TIMESTAMP ); query.setParameter( "end", Date.from( Instant.now().plus( 7, ChronoUnit.DAYS ) ), TemporalType.TIMESTAMP ); - final QueryParameterBindings bindings = ( (ExecutionContext) query ).getQueryParameterBindings(); + final QueryParameterBindings bindings = ( (DomainQueryExecutionContext) query ).getQueryParameterBindings(); final QueryParameterBinding startBinding = bindings.getBinding( "start" ); assertThat( startBinding.getExplicitTemporalPrecision(), equalTo( TemporalType.TIMESTAMP ) ); @@ -113,7 +114,7 @@ public class ParameterTests extends BaseSqmUnitTest { query.setParameter( "start", Instant.now().minus( 7, ChronoUnit.DAYS ), TemporalType.DATE ); query.setParameter( "end", Instant.now().plus( 7, ChronoUnit.DAYS ), TemporalType.DATE ); - final QueryParameterBindings bindings = ( (ExecutionContext) query ).getQueryParameterBindings(); + final QueryParameterBindings bindings = ( (DomainQueryExecutionContext) query ).getQueryParameterBindings(); final QueryParameterBinding startBinding = bindings.getBinding( "start" ); assertThat( startBinding.getExplicitTemporalPrecision(), equalTo( TemporalType.DATE ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java index bd5da0f00b..79ca62ac28 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java @@ -12,13 +12,13 @@ import org.hibernate.orm.test.mapping.SecondaryTableTests; import org.hibernate.orm.test.mapping.inheritance.joined.JoinedInheritanceTest; import org.hibernate.query.internal.ParameterMetadataImpl; import org.hibernate.query.internal.QueryParameterBindingsImpl; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.sql.exec.spi.Callback; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.junit.DomainModel; @@ -68,7 +68,7 @@ public class IdSelectionTests { scope.inTransaction( session -> { - final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); + final DomainQueryExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext ); } @@ -93,7 +93,7 @@ public class IdSelectionTests { scope.inTransaction( session -> { - final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); + final DomainQueryExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext ); } @@ -118,7 +118,7 @@ public class IdSelectionTests { scope.inTransaction( session -> { - final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); + final DomainQueryExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext ); } @@ -143,7 +143,7 @@ public class IdSelectionTests { scope.inTransaction( session -> { - final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); + final DomainQueryExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext ); } @@ -168,14 +168,14 @@ public class IdSelectionTests { scope.inTransaction( session -> { - final ExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); + final DomainQueryExecutionContext executionContext = new TestExecutionContext( session, domainParamBindings ); MatchingIdSelectionHelper.selectMatchingIds( sqm, domainParameterXref, executionContext ); } ); } - private static class TestExecutionContext implements ExecutionContext { + private static class TestExecutionContext implements DomainQueryExecutionContext { private final SessionImplementor session; private final QueryParameterBindingsImpl domainParamBindings; diff --git a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java b/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java deleted file mode 100644 index 965a6e09e7..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchCollectionFromBatchTest.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2006-2011, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.test.subselectfetch; - -import java.util.ArrayList; -import java.util.List; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; - -import org.hibernate.Hibernate; -import org.hibernate.Session; -import org.hibernate.Transaction; -import org.hibernate.annotations.BatchSize; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; -import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; - -import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * @author Stephen Fikes - * @author Gail Badner - */ -public class SubselectFetchCollectionFromBatchTest extends BaseCoreFunctionalTestCase { - - @Override - public void configure(Configuration cfg) { - cfg.setProperty( Environment.GENERATE_STATISTICS, "true"); - } - - @Test - @TestForIssue( jiraKey = "HHH-10679") - public void testSubselectFetchFromEntityBatch() { - Session s = openSession(); - Transaction t = s.beginTransaction(); - EmployeeGroup group1 = new EmployeeGroup(); - Employee employee1 = new Employee("Jane"); - Employee employee2 = new Employee("Jeff"); - group1.addEmployee(employee1); - group1.addEmployee(employee2); - EmployeeGroup group2 = new EmployeeGroup(); - Employee employee3 = new Employee("Joan"); - Employee employee4 = new Employee("John"); - group2.addEmployee(employee3); - group2.addEmployee( employee4 ); - - s.save( group1 ); - s.save( group2 ); - s.flush(); - - s.clear(); - - sessionFactory().getStatistics().clear(); - - EmployeeGroup[] groups = new EmployeeGroup[] { - (EmployeeGroup) s.load(EmployeeGroup.class, group1.getId()), - (EmployeeGroup) s.load(EmployeeGroup.class, group2.getId()) - }; - - // groups should only contain proxies - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - for (EmployeeGroup group : groups) { - assertFalse( Hibernate.isInitialized( group ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - - for ( EmployeeGroup group : groups ) { - // Both groups get initialized and are added to the PersistenceContext when i == 0; - // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity - // in the PersistenceContext gets assigned to its respective proxy target (is this a - // bug???) - Hibernate.initialize( group ); - assertTrue( Hibernate.isInitialized( group ) ); - // the collections should be uninitialized - assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); - } - - // both Group proxies should have been loaded in the same batch; - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - for ( EmployeeGroup group : groups ) { - assertTrue( Hibernate.isInitialized( group ) ); - assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - // now initialize the collection in the first; collections in both groups - // should get initialized - Hibernate.initialize( groups[0].getEmployees() ); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - // all collections should be initialized now - for (EmployeeGroup group : groups) { - assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - t.rollback(); - s.close(); - } - - @Test - public void testSubselectFetchFromQueryList() { - Session s = openSession(); - Transaction t = s.beginTransaction(); - EmployeeGroup group1 = new EmployeeGroup(); - Employee employee1 = new Employee("Jane"); - Employee employee2 = new Employee("Jeff"); - group1.addEmployee(employee1); - group1.addEmployee(employee2); - EmployeeGroup group2 = new EmployeeGroup(); - Employee employee3 = new Employee("Joan"); - Employee employee4 = new Employee("John"); - group2.addEmployee(employee3); - group2.addEmployee( employee4 ); - - s.save( group1 ); - s.save( group2 ); - s.flush(); - - s.clear(); - - sessionFactory().getStatistics().clear(); - - List results = s.createQuery( - "from SubselectFetchCollectionFromBatchTest$EmployeeGroup where id in :groups" - ).setParameterList( - "groups", - new Long[] { group1.getId(), group2.getId() } - ).list(); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - for (EmployeeGroup group : results) { - assertTrue( Hibernate.isInitialized( group ) ); - assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - // now initialize the collection in the first; collections in both groups - // should get initialized - Hibernate.initialize( results.get( 0 ).getEmployees() ); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - // all collections should be initialized now - for (EmployeeGroup group : results) { - assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - t.rollback(); - s.close(); - } - - @Test - @TestForIssue( jiraKey = "HHH-10679") - public void testMultiSubselectFetchSamePersisterQueryList() { - Session s = openSession(); - Transaction t = s.beginTransaction(); - EmployeeGroup group1 = new EmployeeGroup(); - Employee employee1 = new Employee("Jane"); - Employee employee2 = new Employee("Jeff"); - group1.addEmployee( employee1 ); - group1.addEmployee( employee2 ); - group1.setManager( new Employee( "group1 manager" ) ); - group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#1" ) ); - group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#2" ) ); - group1.setLead( new Employee( "group1 lead" ) ); - group1.getLead().addCollaborator( new Employee( "group1 lead's collaborator#1" ) ); - EmployeeGroup group2 = new EmployeeGroup(); - Employee employee3 = new Employee("Joan"); - Employee employee4 = new Employee("John"); - group2.addEmployee( employee3 ); - group2.addEmployee( employee4 ); - group2.setManager( new Employee( "group2 manager" ) ); - group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#1" ) ); - group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#2" ) ); - group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#3" ) ); - group2.setLead( new Employee( "group2 lead" ) ); - group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#1" ) ); - group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#2" ) ); - s.save( group1 ); - s.save( group2 ); - s.flush(); - - s.clear(); - - sessionFactory().getStatistics().clear(); - - EmployeeGroup[] groups = new EmployeeGroup[] { - (EmployeeGroup) s.load(EmployeeGroup.class, group1.getId()), - (EmployeeGroup) s.load(EmployeeGroup.class, group2.getId()) - }; - - // groups should only contain proxies - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - for (EmployeeGroup group : groups) { - assertFalse( Hibernate.isInitialized( group ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - - for ( EmployeeGroup group : groups ) { - // Both groups get initialized and are added to the PersistenceContext when i == 0; - // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity - // in the PersistenceContext gets assigned to its respective proxy target (is this a - // bug???) - Hibernate.initialize( group ); - assertTrue( Hibernate.isInitialized( group ) ); - assertTrue( Hibernate.isInitialized( group.getLead() ) ); - assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); - assertTrue( Hibernate.isInitialized( group.getManager() ) ); - assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); - // the collections should be uninitialized - assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); - } - - // both Group proxies should have been loaded in the same batch; - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - for ( EmployeeGroup group : groups ) { - assertTrue( Hibernate.isInitialized( group ) ); - assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - // now initialize the collection in the first; collections in both groups - // should get initialized - Hibernate.initialize( groups[0].getEmployees() ); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - // all EmployeeGroup#employees should be initialized now - for (EmployeeGroup group : groups) { - assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); - assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); - assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - // now initialize groups[0].getLead().getCollaborators(); - // groups[1].getLead().getCollaborators() should also be initialized - Hibernate.initialize( groups[0].getLead().getCollaborators() ); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - for (EmployeeGroup group : groups) { - assertTrue( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); - assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - // now initialize groups[0].getManager().getCollaborators(); - // groups[1].getManager().getCollaborators() should also be initialized - Hibernate.initialize( groups[0].getManager().getCollaborators() ); - - assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); - sessionFactory().getStatistics().clear(); - - for (EmployeeGroup group : groups) { - assertTrue( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); - } - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - assertEquals( group1.getLead().getCollaborators().size(), groups[0].getLead().getCollaborators().size() ); - assertEquals( group2.getLead().getCollaborators().size(), groups[1].getLead().getCollaborators().size() ); - assertEquals( group1.getManager().getCollaborators().size(), groups[0].getManager().getCollaborators().size() ); - assertEquals( group2.getManager().getCollaborators().size(), groups[1].getManager().getCollaborators().size() ); - - assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); - - t.rollback(); - s.close(); - } - - public Class[] getAnnotatedClasses() { - return new Class[] { EmployeeGroup.class, Employee.class }; - } - - - @Entity - @Table(name = "EmployeeGroup") - @BatchSize(size = 1000) - private static class EmployeeGroup { - @Id - @GeneratedValue - private Long id; - - @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) - private Employee manager; - - @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) - private Employee lead; - - @OneToMany(cascade = CascadeType.ALL) - @Fetch(FetchMode.SUBSELECT) - @JoinTable(name="EmployeeGroup_employees") - private List employees = new ArrayList(); - - public EmployeeGroup(long id) { - this.id = id; - } - - @SuppressWarnings("unused") - protected EmployeeGroup() { - } - - public Employee getManager() { - return manager; - } - - public void setManager(Employee manager) { - this.manager = manager; - } - - public Employee getLead() { - return lead; - } - - public void setLead(Employee lead) { - this.lead = lead; - } - - public boolean addEmployee(Employee employee) { - return employees.add(employee); - } - - public List getEmployees() { - return employees; - } - - public long getId() { - return id; - } - - @Override - public String toString() { - return id.toString(); - } - } - - - @Entity - @Table(name = "Employee") - private static class Employee { - @Id - @GeneratedValue - private Long id; - private String name; - - @OneToMany(cascade = CascadeType.ALL) - @Fetch(FetchMode.SUBSELECT) - private List collaborators = new ArrayList(); - - public String getName() { - return name; - } - - @SuppressWarnings("unused") - private Employee() { - } - - public Employee(String name) { - this.name = name; - } - - public boolean addCollaborator(Employee employee) { - return collaborators.add(employee); - } - - public List getCollaborators() { - return collaborators; - } - - @Override - public String toString() { - return name; - } - } -} diff --git a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Name.hbm.xml b/hibernate-core/src/test/resources/mappings/subselectfetch/Name.hbm.xml similarity index 80% rename from hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Name.hbm.xml rename to hibernate-core/src/test/resources/mappings/subselectfetch/Name.hbm.xml index 8cc807bd91..14c84248f6 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Name.hbm.xml +++ b/hibernate-core/src/test/resources/mappings/subselectfetch/Name.hbm.xml @@ -11,7 +11,7 @@ - + @@ -19,7 +19,7 @@ - + diff --git a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/NameTransactSql.hbm.xml b/hibernate-core/src/test/resources/mappings/subselectfetch/NameTransactSql.hbm.xml similarity index 79% rename from hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/NameTransactSql.hbm.xml rename to hibernate-core/src/test/resources/mappings/subselectfetch/NameTransactSql.hbm.xml index b2d18faf19..9ca2815952 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/NameTransactSql.hbm.xml +++ b/hibernate-core/src/test/resources/mappings/subselectfetch/NameTransactSql.hbm.xml @@ -11,7 +11,7 @@ - + @@ -19,7 +19,7 @@ - + diff --git a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/ParentChild.hbm.xml b/hibernate-core/src/test/resources/mappings/subselectfetch/ParentChild.hbm.xml similarity index 94% rename from hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/ParentChild.hbm.xml rename to hibernate-core/src/test/resources/mappings/subselectfetch/ParentChild.hbm.xml index b2a41784d3..c6156237d0 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/ParentChild.hbm.xml +++ b/hibernate-core/src/test/resources/mappings/subselectfetch/ParentChild.hbm.xml @@ -10,7 +10,7 @@ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> + package="org.hibernate.orm.test.mapping.collections.subselectfetch"> diff --git a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Value.hbm.xml b/hibernate-core/src/test/resources/mappings/subselectfetch/Value.hbm.xml similarity index 86% rename from hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Value.hbm.xml rename to hibernate-core/src/test/resources/mappings/subselectfetch/Value.hbm.xml index f067f8882d..5fcbd344bf 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/subselectfetch/Value.hbm.xml +++ b/hibernate-core/src/test/resources/mappings/subselectfetch/Value.hbm.xml @@ -11,7 +11,7 @@ - +