HHH-14877 - FetchMode.SUBSELECT ignored

This commit is contained in:
Steve Ebersole 2021-10-15 18:59:53 -05:00
parent b5558307a9
commit e8e62c4d6c
76 changed files with 1686 additions and 778 deletions

View File

@ -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<EntityPersister> entityPersisters = new ArrayList<>( 1 );
final MetamodelImplementor metamodel = executionContext.getSession()
.getFactory()
.getMetamodel();
final MetamodelImplementor metamodel = session.getFactory().getMetamodel();
if ( !( statement instanceof InsertStatement ) ) {
entityPersisters.add( metamodel.entityPersister( statement.getTarget().getEntityName() ) );
}
@ -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<String> affectedQueryables) {
final SharedSessionContractImplementor session = executionContext.getSession();
public static void schedule(SharedSessionContractImplementor session, Set<String> affectedQueryables) {
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, affectedQueryables );
if ( session.isEventSource() ) {
( (EventSource) session ).getActionQueue().addAction( action );

View File

@ -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;

View File

@ -458,4 +458,11 @@ public interface PersistentCollection<E> {
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() + ")";
}
}

View File

@ -1,8 +1,8 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.engine.spi;
@ -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:<ul>
* <li>entity and collection keys that are available for batch fetching</li>
* <li>details related to queries which load entities with sub-select-fetchable collections</li>
* </ul>
*
* @author Gavin King
* @author Steve Ebersole
@ -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()
);
}
}
/**

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.engine.spi;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -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<JdbcParameter> getLoadingJdbcParameters() {
// todo (6.0) : do not believe this is needed
// - see org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(org.hibernate.engine.spi.SubselectFetch)
return loadingJdbcParameters;
}
/**
* The SQL AST select from which the owner was loaded
*/
public QuerySpec getLoadingSqlAst() {
return loadingSqlAst;
}
/**
* The TableGroup for the owner within the {@link #getLoadingSqlAst()}
*/
public TableGroup getOwnerTableGroup() {
return ownerTableGroup;
}
public List<JdbcParameter> getLoadingJdbcParameters() {
return loadingJdbcParameters;
}
/**
* The JDBC parameter bindings related to {@link #getLoadingSqlAst()} for
* the specific execution that loaded the owners
*/
public JdbcParameterBindings getLoadingJdbcParameterBindings() {
return loadingJdbcParameterBindings;
}
/**
*The entity-keys of all owners loaded from a particular execution
*
* Used for "empty collection" handling mostly
*/
public Set<EntityKey> getResultingEntityKeys() {
return resultingEntityKeys;
}
@ -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<JdbcParameter> jdbcParameters,
JdbcParameterBindings jdbcParameterBindings) {
final SubselectFetch subselectFetch = new SubselectFetch(
null,
sqlAst.getQuerySpec(),
tableGroup,
jdbcParameters,
jdbcParameterBindings,
new HashSet<>()
);
return new StandardRegistrationHandler( batchFetchQueue, subselectFetch );
}
public static RegistrationHandler createRegistrationHandler(
BatchFetchQueue batchFetchQueue,
SelectStatement sqlAst,
List<JdbcParameter> jdbcParameters,
JdbcParameterBindings jdbcParameterBindings) {
final List<TableGroup> roots = sqlAst.getQuerySpec().getFromClause().getRoots();
if ( roots.isEmpty() ) {
// we allow this now
return NO_OP_REG_HANDLER;
}
return createRegistrationHandler( batchFetchQueue, sqlAst, roots.get( 0 ), jdbcParameters, jdbcParameterBindings );
}
public interface RegistrationHandler {
void addKey(EntityKey key);
}
private static final RegistrationHandler NO_OP_REG_HANDLER = new RegistrationHandler() {
@Override
public void addKey(EntityKey key) {
}
} ;
public static class StandardRegistrationHandler implements RegistrationHandler {
private final BatchFetchQueue batchFetchQueue;
private final SubselectFetch subselectFetch;
private StandardRegistrationHandler(BatchFetchQueue batchFetchQueue, SubselectFetch subselectFetch) {
this.batchFetchQueue = batchFetchQueue;
this.subselectFetch = subselectFetch;
}
public void addKey(EntityKey key) {
subselectFetch.resultingEntityKeys.add( key );
batchFetchQueue.addSubselect( key, subselectFetch );
}
}
}

View File

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

View File

@ -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;

View File

@ -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<PersistentCollection<?>> 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<EntityKey> 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;
}
}

View File

@ -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 <original restriction>
// )
// but instead could do:
// select ...
// from owner_table o
// left join collection_table c on c.fk = o.id
// where <original restriction>
// 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,

View File

@ -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<T> implements MultiIdEntityLoader<T> {
loadingEntityCollector = null;
}
return JdbcSelectExecutorStandardImpl.INSTANCE.list(
final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler = SubselectFetch.createRegistrationHandler(
session.getPersistenceContext().getBatchFetchQueue(),
sqlAst,
jdbcParameters,
jdbcParameterBindings
);
List<T> list = JdbcSelectExecutorStandardImpl.INSTANCE.list(
jdbcSelect,
jdbcParameterBindings,
new ExecutionContext() {
@ -328,14 +336,14 @@ public class MultiIdLoaderStandard<T> implements MultiIdEntityLoader<T> {
@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<T> performUnorderedMultiLoad(

View File

@ -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 <E> List<E> 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<E> 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;
}
}

View File

@ -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<T> 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<T> extends SingleIdEntityLoaderSup
return sql;
}
@Override
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
subSelectFetchableKeysHandler.addKey( entityKey );
}
@Override
public QueryParameterBindings getQueryParameterBindings() {
return QueryParameterBindings.NO_PARAM_BINDINGS;

View File

@ -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;

View File

@ -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 <R> 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();
}
}

View File

@ -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();
}

View File

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

View File

@ -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<R> extends QueryPlan {
/**
* Perform (execute) the query returning a List
*/
List<R> performList(ExecutionContext executionContext);
List<R> performList(DomainQueryExecutionContext executionContext);
/**
* Perform (execute) the query returning a ScrollableResults
*/
ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext);
ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext);
}

View File

@ -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

View File

@ -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<JdbcParameterBinder> jdbcParameterBinders;
final JdbcParameterBindings jdbcParameterBindings;
@ -108,7 +109,7 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {},
executionContext
new SqmJdbcExecutionContextAdapter( executionContext )
);
}
}

View File

@ -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<R>
extends AbstractQuery<R>
implements NativeQueryImplementor<R>, ExecutionContext, ResultSetMappingResolutionContext {
implements NativeQueryImplementor<R>, DomainQueryExecutionContext, ResultSetMappingResolutionContext {
private final String sqlString;
private final ParameterMetadataImplementor parameterMetadata;
@ -440,13 +440,6 @@ public class NativeQueryImpl<R>
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<R>
throw new IllegalStateException( "Illegal attempt to set lock mode on a native-query" );
}
@Override
public String getQueryIdentifier(String sql) {
return sql;
}
@Override
public Query<R> applyGraph(RootGraph graph, GraphSemantic semantic) {
throw new HibernateException( "A native SQL query cannot use EntityGraphs" );

View File

@ -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<R> implements NativeSelectQueryPlan<R> {
}
@Override
public List<R> performList(ExecutionContext executionContext) {
public List<R> performList(DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return Collections.emptyList();
}
@ -126,14 +127,14 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
return executor.list(
jdbcSelect,
jdbcParameterBindings,
executionContext,
new SqmJdbcExecutionContextAdapter( executionContext, executionContext.getQueryOptions() ),
null,
ListResultsConsumer.UniqueSemantic.NONE
);
}
@Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return EmptyScrollableResults.INSTANCE;
}
@ -191,7 +192,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
jdbcSelect,
scrollMode,
jdbcParameterBindings,
executionContext,
new SqmJdbcExecutionContextAdapter( executionContext, jdbcSelect ),
null
);
}

View File

@ -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<R> implements SelectQueryPlan<R> {
}
@Override
public List<R> performList(ExecutionContext executionContext) {
public List<R> performList(DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return Collections.emptyList();
}
@ -42,7 +43,7 @@ public class AggregatedSelectQueryPlanImpl<R> implements SelectQueryPlan<R> {
}
@Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return EmptyScrollableResults.INSTANCE;
}

View File

@ -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<R> implements SelectQueryPlan<R> {
private final SqmSelectStatement sqm;
private final String hql;
private final DomainParameterXref domainParameterXref;
private final RowTransformer<R> rowTransformer;
private final SqmInterpreter<List<R>, Void> listInterpreter;
@ -72,22 +80,51 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
@SuppressWarnings("WeakerAccess")
public ConcreteSqmSelectQueryPlan(
SqmSelectStatement sqm,
String hql,
DomainParameterXref domainParameterXref,
Class<R> 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<R> implements SelectQueryPlan<R> {
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<R> 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<R> implements SelectQueryPlan<R> {
}
@Override
public List<R> performList(ExecutionContext executionContext) {
public List<R> performList(DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return Collections.emptyList();
}
@ -210,14 +273,14 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
}
@Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
return EmptyScrollableResults.INSTANCE;
}
return withCacheableSqmInterpretation( executionContext, scrollMode, scrollInterpreter );
}
private <T, X> T withCacheableSqmInterpretation(ExecutionContext executionContext, X context, SqmInterpreter<T, X> interpreter) {
private <T, X> T withCacheableSqmInterpretation(DomainQueryExecutionContext executionContext, X context, SqmInterpreter<T, X> 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<R> implements SelectQueryPlan<R> {
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<R> implements SelectQueryPlan<R> {
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<R> implements SelectQueryPlan<R> {
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
executionContext.getLoadQueryInfluencers(),
executionContext.getSession().getLoadQueryInfluencers(),
sessionFactory
);
@ -328,6 +393,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
final JdbcSelect jdbcSelect = selectTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
return new CacheableSqmInterpretation(
sqmInterpretation.getSqlAst(),
jdbcSelect,
tableGroupAccess,
jdbcParamsXref,
@ -339,12 +405,13 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
private interface SqmInterpreter<T, X> {
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<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref;
@ -352,11 +419,13 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
private transient JdbcParameterBindings firstParameterBindings;
CacheableSqmInterpretation(
SelectStatement selectStatement,
JdbcSelect jdbcSelect,
FromClauseAccess tableGroupAccess,
Map<QueryParameterImplementor<?>, Map<SqmParameter, List<List<JdbcParameter>>>> jdbcParamsXref,
Map<SqmParameter,MappingModelExpressable> sqmParameterMappingModelTypes,
JdbcParameterBindings firstParameterBindings) {
this.selectStatement = selectStatement;
this.jdbcSelect = jdbcSelect;
this.tableGroupAccess = tableGroupAccess;
this.jdbcParamsXref = jdbcParamsXref;
@ -364,6 +433,10 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
this.firstParameterBindings = firstParameterBindings;
}
SelectStatement getSelectStatement() {
return selectStatement;
}
JdbcSelect getJdbcSelect() {
return jdbcSelect;
}

View File

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

View File

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

View File

@ -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<R>
extends AbstractQuery<R>
implements HqlQueryImplementor<R>, ExecutionContext {
implements HqlQueryImplementor<R>, DomainQueryExecutionContext {
/**
* The value used for {@link #getQueryString} for Criteria-based queries
@ -604,7 +606,8 @@ public class QuerySqmImpl<R>
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<R>
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<R>
QueryOptions queryOptions) {
return new ConcreteSqmSelectQueryPlan<>(
concreteSqmStatement,
hqlString,
domainParameterXref,
resultType,
queryOptions
@ -841,21 +853,6 @@ public class QuerySqmImpl<R>
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<R>
);
}
@Override
public boolean hasQueryExecutionToBeAddedToStatistics() {
return !CRITERIA_HQL_STRING.equals( hqlString );
}
}

View File

@ -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<JdbcDelete> createDeleteTranslator(ExecutionContext executionContext) {
private SqlAstTranslator<JdbcDelete> 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
);
}
}

View File

@ -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<JdbcInsert> createInsertTranslator(ExecutionContext executionContext) {
private SqlAstTranslator<JdbcInsert> 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 )
);
}
}

View File

@ -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<JdbcUpdate> createUpdateTranslator(ExecutionContext executionContext) {
private SqlAstTranslator<JdbcUpdate> 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
);

View File

@ -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();
}
}

View File

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

View File

@ -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<Object> 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
);

View File

@ -122,7 +122,7 @@ public class SqmMutationStrategyHelper {
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {},
SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext )
executionContext
);
}
}

View File

@ -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<Object> list = jdbcServices.getJdbcSelectExecutor().list(
select,
jdbcParameterBindings,
SqlOmittingQueryOptions.omitSqlQueryOptions( executionContext, select ),
new SqmJdbcExecutionContextAdapter( executionContext ),
row -> row[0],
ListResultsConsumer.UniqueSemantic.NONE
);

View File

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

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

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

View File

@ -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()
);

View File

@ -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
);

View File

@ -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<SqmParameter, List<List<JdbcParameter>>> parameterResolutions,
Map<SqmParameter, MappingModelExpressable> 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
);
}
}

View File

@ -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<Object> ids = MatchingIdSelectionHelper.selectMatchingIds(
sqmDeleteStatement,
domainParameterXref,
@ -140,7 +142,7 @@ public class InlineDeleteHandler implements DeleteHandler {
Supplier<Consumer<SelectableConsumer>> tableKeyColumnsVisitationSupplier,
List<Object> 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
);
}

View File

@ -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,

View File

@ -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() );
}
}

View File

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

View File

@ -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;

View File

@ -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) {

View File

@ -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();

View File

@ -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(),

View File

@ -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() ),

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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 );

View File

@ -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;
/**

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.subselectfetch;
package org.hibernate.orm.test.mapping.collections.subselectfetch;
import java.io.Serializable;
import java.util.List;

View File

@ -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;

View File

@ -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<Owner> 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<Owner> 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<Thing> 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<Thing> getThings() {
return things;
}
public void setThings(Set<Thing> 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;
}
}
}

View File

@ -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<Owner> 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<Owner> 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<Thing> 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<Thing> getThings() {
return things;
}
public void setThings(Set<Thing> 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;
}
}
}

View File

@ -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<EmployeeGroup> 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<Employee> employees = new ArrayList<Employee>();
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<Employee> 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<Employee> collaborators = new ArrayList<Employee>();
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<Employee> getCollaborators() {
return collaborators;
}
@Override
public String toString() {
return name;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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" };
@ServiceRegistry(
settings = {
@Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ),
@Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" )
}
@Override
public void configure(Configuration cfg) {
cfg.setProperty( AvailableSettings.GENERATE_STATISTICS, "true" );
cfg.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "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<Parent> 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<Parent> 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();

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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"
};
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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"
};
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.subselectfetch;
package org.hibernate.orm.test.mapping.collections.subselectfetch;
import java.io.Serializable;

View File

@ -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 ) );

View File

@ -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;

View File

@ -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<EmployeeGroup> 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<Employee> employees = new ArrayList<Employee>();
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<Employee> 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<Employee> collaborators = new ArrayList<Employee>();
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<Employee> getCollaborators() {
return collaborators;
}
@Override
public String toString() {
return name;
}
}
}

View File

@ -11,7 +11,7 @@
<hibernate-mapping>
<class name="org.hibernate.test.subselectfetch.Name" table="T_Name">
<class name="org.hibernate.orm.test.mapping.collections.subselectfetch.Name" table="T_Name">
<id name="id" column="id"/>
<property name="name" column="c_name"/>
@ -19,7 +19,7 @@
<bag name="values" inverse="true" lazy="false" fetch="subselect">
<key column="name_id"/>
<one-to-many class="org.hibernate.test.subselectfetch.Value"/>
<one-to-many class="org.hibernate.orm.test.mapping.collections.subselectfetch.Value"/>
</bag>
</class>

View File

@ -11,7 +11,7 @@
<hibernate-mapping>
<class name="org.hibernate.test.subselectfetch.Name" table="T_Name">
<class name="org.hibernate.orm.test.mapping.collections.subselectfetch.Name" table="T_Name">
<id name="id" column="id"/>
<property name="name" column="c_name"/>
@ -19,7 +19,7 @@
<bag name="values" inverse="true" lazy="false" fetch="subselect">
<key column="name_id"/>
<one-to-many class="org.hibernate.test.subselectfetch.Value"/>
<one-to-many class="org.hibernate.orm.test.mapping.collections.subselectfetch.Value"/>
</bag>
</class>

View File

@ -10,7 +10,7 @@
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="org.hibernate.test.subselectfetch">
package="org.hibernate.orm.test.mapping.collections.subselectfetch">
<class name="Child">
<id name="name"/>

View File

@ -11,7 +11,7 @@
<hibernate-mapping>
<class name="org.hibernate.test.subselectfetch.Value" table="T_Value">
<class name="org.hibernate.orm.test.mapping.collections.subselectfetch.Value" table="T_Value">
<id name="id" column="id"/>
<property name="value" column="c_value"/>
<many-to-one name="name" column="name_id" insert="false" update="false" />