HHH-10679 HHH-10712 : Fix subselect fetching in load plans

(cherry picked from commit d444be1fdbf3618ce1f52b6ea8b8812fbfd795db)

Conflicts:
	hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java
This commit is contained in:
Gail Badner 2016-04-26 13:34:15 -07:00
parent 545930c39f
commit 62ac35213f
18 changed files with 1250 additions and 104 deletions

View File

@ -44,7 +44,11 @@ public final class StringHelper {
if ( length == 0 ) { if ( length == 0 ) {
return ""; return "";
} }
StringBuilder buf = new StringBuilder( length * strings[0].length() ) // Allocate space for length * firstStringLength;
// If strings[0] is null, then its length is defined as 4, since that's the
// length of "null".
final int firstStringLength = strings[0] != null ? strings[0].length() : 4;
StringBuilder buf = new StringBuilder( length * firstStringLength )
.append( strings[0] ); .append( strings[0] );
for ( int i = 1; i < length; i++ ) { for ( int i = 1; i < length; i++ ) {
buf.append( seperator ).append( strings[i] ); buf.append( seperator ).append( strings[i] );

View File

@ -12,6 +12,7 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -63,28 +64,14 @@ public class CollectionLoader extends AbstractLoadPlanBasedCollectionInitializer
} }
public CollectionLoader byKey() { public CollectionLoader byKey() {
final QueryBuildingParameters buildingParameters = new QueryBuildingParameters() { // capture current values in a new instance of QueryBuildingParametersImpl
@Override final QueryBuildingParameters currentBuildingParameters = new QueryBuildingParametersImpl(
public LoadQueryInfluencers getQueryInfluencers() { influencers,
return influencers; batchSize,
} LockMode.NONE,
null
@Override );
public int getBatchSize() { return new CollectionLoader( collectionPersister, currentBuildingParameters ) ;
return batchSize;
}
@Override
public LockMode getLockMode() {
return LockMode.NONE;
}
@Override
public LockOptions getLockOptions() {
return null;
}
};
return new CollectionLoader( collectionPersister, buildingParameters ) ;
} }
} }

View File

@ -12,6 +12,7 @@ import org.hibernate.MappingException;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -77,32 +78,18 @@ public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
} }
public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) { public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
// capture current values in a new instance of QueryBuildingParametersImpl
return new EntityLoader( return new EntityLoader(
persister.getFactory(), persister.getFactory(),
persister, persister,
keyColumnNames, keyColumnNames,
keyType, keyType,
new QueryBuildingParameters() { new QueryBuildingParametersImpl(
@Override influencers,
public LoadQueryInfluencers getQueryInfluencers() { batchSize,
return influencers; lockMode,
} lockOptions
)
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
}
); );
} }
} }

View File

@ -39,6 +39,7 @@ import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.FetchSource; import org.hibernate.loader.plan.spi.FetchSource;
import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.AnyMappingDefinition; import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AssociationKey;
@ -833,9 +834,6 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) { protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
// todo : this seems to not be correct for one-to-one // todo : this seems to not be correct for one-to-one
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition ); final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
return false;
}
final ExpandingFetchSource currentSource = currentSource(); final ExpandingFetchSource currentSource = currentSource();
currentSource.validateFetchPlan( fetchStrategy, attributeDefinition ); currentSource.validateFetchPlan( fetchStrategy, attributeDefinition );
@ -845,6 +843,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
// for ANY mappings we need to build a Fetch: // for ANY mappings we need to build a Fetch:
// 1) fetch type is SELECT // 1) fetch type is SELECT
// 2) (because the fetch cannot be a JOIN...) do not push it to the stack // 2) (because the fetch cannot be a JOIN...) do not push it to the stack
// regardless of the fetch style, build the fetch
currentSource.buildAnyAttributeFetch( currentSource.buildAnyAttributeFetch(
attributeDefinition, attributeDefinition,
fetchStrategy fetchStrategy
@ -852,11 +851,13 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
return false; return false;
} }
else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) { else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
// regardless of the fetch style, build the fetch
EntityFetch fetch = currentSource.buildEntityAttributeFetch( EntityFetch fetch = currentSource.buildEntityAttributeFetch(
attributeDefinition, attributeDefinition,
fetchStrategy fetchStrategy
); );
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) { if ( FetchStrategyHelper.isJoinFetched( fetchStrategy ) ) {
// only push to the stack if join fetched
pushToStack( (ExpandingFetchSource) fetch ); pushToStack( (ExpandingFetchSource) fetch );
return true; return true;
} }
@ -866,8 +867,13 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
} }
else { else {
// Collection // Collection
CollectionAttributeFetch fetch = currentSource.buildCollectionAttributeFetch( attributeDefinition, fetchStrategy ); // regardless of the fetch style, build the fetch
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) { CollectionAttributeFetch fetch = currentSource.buildCollectionAttributeFetch(
attributeDefinition,
fetchStrategy
);
if ( FetchStrategyHelper.isJoinFetched( fetchStrategy ) ) {
// only push to the stack if join fetched
pushToCollectionStack( fetch ); pushToCollectionStack( fetch );
return true; return true;
} }

View File

@ -91,6 +91,16 @@ public abstract class AbstractCollectionLoadQueryDetails extends AbstractLoadQue
return (CollectionReturn) getRootReturn(); return (CollectionReturn) getRootReturn();
} }
@Override
protected boolean isSubselectLoadingEnabled(FetchStats fetchStats) {
return fetchStats != null && fetchStats.hasSubselectFetches();
}
@Override
protected boolean shouldUseOptionalEntityInstance() {
return false;
}
@Override @Override
protected ReaderCollector getReaderCollector() { protected ReaderCollector getReaderCollector() {
return readerCollector; return readerCollector;

View File

@ -173,11 +173,22 @@ public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
this.sqlStatement = select.toStatementString(); this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl( this.resultSetProcessor = new ResultSetProcessorImpl(
loadPlan, loadPlan,
queryProcessor.getAliasResolutionContext(),
getReaderCollector().buildRowReader(), getReaderCollector().buildRowReader(),
fetchStats != null && fetchStats.hasSubselectFetches() shouldUseOptionalEntityInstance(),
isSubselectLoadingEnabled( fetchStats )
); );
} }
/**
* Is subselect loading enabled?
*
* @param fetchStats the fetch stats; may be null
* @return {@code true} if subselect loading is enabled; {@code false} otherwise.
*/
protected abstract boolean isSubselectLoadingEnabled(FetchStats fetchStats);
protected abstract boolean shouldUseOptionalEntityInstance();
protected abstract ReaderCollector getReaderCollector(); protected abstract ReaderCollector getReaderCollector();
protected abstract QuerySpace getRootQuerySpace(); protected abstract QuerySpace getRootQuerySpace();
protected abstract String getRootTableAlias(); protected abstract String getRootTableAlias();

View File

@ -152,6 +152,18 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
protected void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder) { protected void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder) {
} }
@Override
protected boolean isSubselectLoadingEnabled(FetchStats fetchStats) {
return getQueryBuildingParameters().getBatchSize() > 1 &&
fetchStats != null &&
fetchStats.hasSubselectFetches();
}
@Override
protected boolean shouldUseOptionalEntityInstance() {
return getQueryBuildingParameters().getBatchSize() < 2;
}
@Override @Override
protected ReaderCollector getReaderCollector() { protected ReaderCollector getReaderCollector() {
return readerCollector; return readerCollector;

View File

@ -443,10 +443,8 @@ public class LoadQueryJoinAndFetchProcessor {
Fetch fetch, Fetch fetch,
ReaderCollector readerCollector, ReaderCollector readerCollector,
FetchStatsImpl fetchStats) { FetchStatsImpl fetchStats) {
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
return;
}
// process fetch even if it is not join fetched
if ( EntityFetch.class.isInstance( fetch ) ) { if ( EntityFetch.class.isInstance( fetch ) ) {
final EntityFetch entityFetch = (EntityFetch) fetch; final EntityFetch entityFetch = (EntityFetch) fetch;
processEntityFetch( processEntityFetch(
@ -494,6 +492,10 @@ public class LoadQueryJoinAndFetchProcessor {
// } // }
fetchStats.processingFetch( fetch ); fetchStats.processingFetch( fetch );
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
// not join fetched, so nothing else to do
return;
}
// First write out the SQL SELECT fragments // First write out the SQL SELECT fragments
final Joinable joinable = (Joinable) fetch.getEntityPersister(); final Joinable joinable = (Joinable) fetch.getEntityPersister();
@ -540,8 +542,14 @@ public class LoadQueryJoinAndFetchProcessor {
CollectionAttributeFetch fetch, CollectionAttributeFetch fetch,
ReaderCollector readerCollector, ReaderCollector readerCollector,
FetchStatsImpl fetchStats) { FetchStatsImpl fetchStats) {
fetchStats.processingFetch( fetch ); fetchStats.processingFetch( fetch );
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
// not join fetched, so nothing else to do
return;
}
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases( final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases(
fetch.getQuerySpaceUid() fetch.getQuerySpaceUid()
); );

View File

@ -15,13 +15,17 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext; import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.Fetch;
@ -34,9 +38,12 @@ import org.hibernate.type.EntityType;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ResultSetProcessingContextImpl implements ResultSetProcessingContext { public class ResultSetProcessingContextImpl implements ResultSetProcessingContext {
private static final Logger LOG = CoreLogging.logger( ResultSetProcessingContextImpl.class );
private final ResultSet resultSet; private final ResultSet resultSet;
private final SharedSessionContractImplementor session; private final SharedSessionContractImplementor session;
private final LoadPlan loadPlan; private final LoadPlan loadPlan;
private final AliasResolutionContext aliasResolutionContext;
private final boolean readOnly; private final boolean readOnly;
private final boolean shouldUseOptionalEntityInformation; private final boolean shouldUseOptionalEntityInformation;
private final boolean forceFetchLazyAttributes; private final boolean forceFetchLazyAttributes;
@ -47,8 +54,9 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList; private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap; private Map<EntityReference,Set<EntityKey>> subselectLoadableEntityKeyMap;
private List<HydratedEntityRegistration> hydratedEntityRegistrationList; private List<HydratedEntityRegistration> hydratedEntityRegistrationList;
private int nRowsRead = 0;
/** /**
* Builds a ResultSetProcessingContextImpl * Builds a ResultSetProcessingContextImpl
@ -61,6 +69,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
final ResultSet resultSet, final ResultSet resultSet,
final SharedSessionContractImplementor session, final SharedSessionContractImplementor session,
final LoadPlan loadPlan, final LoadPlan loadPlan,
final AliasResolutionContext aliasResolutionContext,
final boolean readOnly, final boolean readOnly,
final boolean shouldUseOptionalEntityInformation, final boolean shouldUseOptionalEntityInformation,
final boolean forceFetchLazyAttributes, final boolean forceFetchLazyAttributes,
@ -71,6 +80,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
this.resultSet = resultSet; this.resultSet = resultSet;
this.session = session; this.session = session;
this.loadPlan = loadPlan; this.loadPlan = loadPlan;
this.aliasResolutionContext = aliasResolutionContext;
this.readOnly = readOnly; this.readOnly = readOnly;
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation; this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
this.forceFetchLazyAttributes = forceFetchLazyAttributes; this.forceFetchLazyAttributes = forceFetchLazyAttributes;
@ -254,6 +264,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
* Package-protected * Package-protected
*/ */
void finishUpRow() { void finishUpRow() {
nRowsRead++;
if ( currentRowHydratedEntityRegistrationList == null ) { if ( currentRowHydratedEntityRegistrationList == null ) {
if ( identifierResolutionContextMap != null ) { if ( identifierResolutionContextMap != null ) {
identifierResolutionContextMap.clear(); identifierResolutionContextMap.clear();
@ -276,11 +288,11 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
} }
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) { for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get( Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get(
registration.getEntityReference().getEntityPersister() registration.getEntityReference()
); );
if ( entityKeys == null ) { if ( entityKeys == null ) {
entityKeys = new HashSet<>(); entityKeys = new HashSet<>();
subselectLoadableEntityKeyMap.put( registration.getEntityReference().getEntityPersister(), entityKeys ); subselectLoadableEntityKeyMap.put( registration.getEntityReference(), entityKeys );
} }
entityKeys.add( registration.getKey() ); entityKeys.add( registration.getKey() );
} }
@ -314,24 +326,27 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
} }
private void createSubselects() { private void createSubselects() {
if ( subselectLoadableEntityKeyMap == null || subselectLoadableEntityKeyMap.size() <= 1 ) { if ( subselectLoadableEntityKeyMap == null || nRowsRead <= 1 ) {
// if we only returned one entity, query by key is more efficient; so do nothing here LOG.tracef(
return; "Skipping create subselects because there are fewer than 2 results, so query by key is more efficient.",
getClass().getName()
);
return; // early return
} }
final Map<String, int[]> namedParameterLocMap = final Map<String, int[]> namedParameterLocMap =
ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext ); ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext );
final String subselectQueryString = SubselectFetch.createSubselectFetchQueryFragment( queryParameters ); final String subselectQueryString = SubselectFetch.createSubselectFetchQueryFragment( queryParameters );
for ( Map.Entry<EntityPersister, Set<EntityKey>> entry : subselectLoadableEntityKeyMap.entrySet() ) { for ( Map.Entry<EntityReference, Set<EntityKey>> entry : subselectLoadableEntityKeyMap.entrySet() ) {
if ( ! entry.getKey().hasSubselectLoadableCollections() ) { if ( ! entry.getKey().getEntityPersister().hasSubselectLoadableCollections() ) {
continue; continue;
} }
SubselectFetch subselectFetch = new SubselectFetch( SubselectFetch subselectFetch = new SubselectFetch(
subselectQueryString, subselectQueryString,
null, // aliases[i], aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( entry.getKey().getQuerySpaceUid() ),
(Loadable) entry.getKey(), (Loadable) entry.getKey().getEntityPersister(),
queryParameters, queryParameters,
entry.getValue(), entry.getValue(),
namedParameterLocMap namedParameterLocMap

View File

@ -21,6 +21,7 @@ import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.process.spi.RowReader; import org.hibernate.loader.plan.exec.process.spi.RowReader;
import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor; import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.CollectionReturn; import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.loader.spi.AfterLoadAction;
@ -37,13 +38,27 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class ); private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class );
private final LoadPlan loadPlan; private final LoadPlan loadPlan;
private final AliasResolutionContext aliasResolutionContext;
private final RowReader rowReader; private final RowReader rowReader;
private final boolean hadSubselectFetches; private final boolean hadSubselectFetches;
public ResultSetProcessorImpl(LoadPlan loadPlan, RowReader rowReader, boolean hadSubselectFetches) { private final boolean shouldUseOptionalEntityInstance;
// There are times when the "optional entity information" on QueryParameters should be used and
// times when they should be ignored. Loader uses its isSingleRowLoader method to allow
// subclasses to override that. Collection initializers, batch loaders, e.g. override that
// it to be false. The 'shouldUseOptionalEntityInstance' setting is meant to fill that same role.
public ResultSetProcessorImpl(
LoadPlan loadPlan,
AliasResolutionContext aliasResolutionContext,
RowReader rowReader,
boolean shouldUseOptionalEntityInstance,
boolean hadSubselectFetches) {
this.loadPlan = loadPlan; this.loadPlan = loadPlan;
this.aliasResolutionContext = aliasResolutionContext;
this.rowReader = rowReader; this.rowReader = rowReader;
this.shouldUseOptionalEntityInstance = shouldUseOptionalEntityInstance;
this.hadSubselectFetches = hadSubselectFetches; this.hadSubselectFetches = hadSubselectFetches;
} }
@ -80,12 +95,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
maxRows = Integer.MAX_VALUE; maxRows = Integer.MAX_VALUE;
} }
// There are times when the "optional entity information" on QueryParameters should be used and
// times when they should be ignored. Loader uses its isSingleRowLoader method to allow
// subclasses to override that. Collection initializers, batch loaders, e.g. override that
// it to be false. The 'shouldUseOptionalEntityInstance' setting is meant to fill that same role.
final boolean shouldUseOptionalEntityInstance = true;
// Handles the "FETCH ALL PROPERTIES" directive in HQL // Handles the "FETCH ALL PROPERTIES" directive in HQL
final boolean forceFetchLazyAttributes = false; final boolean forceFetchLazyAttributes = false;
@ -93,6 +102,7 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
resultSet, resultSet,
session, session,
loadPlan, loadPlan,
aliasResolutionContext,
readOnly, readOnly,
shouldUseOptionalEntityInstance, shouldUseOptionalEntityInstance,
forceFetchLazyAttributes, forceFetchLazyAttributes,

View File

@ -0,0 +1,53 @@
/*
* 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.loader.plan.exec.query.internal;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
/**
* @author Gail Badner
*/
public class QueryBuildingParametersImpl implements QueryBuildingParameters {
private final LoadQueryInfluencers loadQueryInfluencers;
private final int batchSize;
private final LockMode lockMode;
private final LockOptions lockOptions;
public QueryBuildingParametersImpl(
LoadQueryInfluencers loadQueryInfluencers,
int batchSize,
LockMode lockMode,
LockOptions lockOptions) {
this.loadQueryInfluencers = loadQueryInfluencers;
this.batchSize = batchSize;
this.lockMode = lockMode;
this.lockOptions = lockOptions;
}
@Override
public LoadQueryInfluencers getQueryInfluencers() {
return loadQueryInfluencers;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
}

View File

@ -76,7 +76,7 @@ public final class FetchStrategyHelper {
* @param type The association type * @param type The association type
* @param sessionFactory The session factory * @param sessionFactory The session factory
* *
* @return * @return the fetch style
*/ */
public static FetchStyle determineFetchStyleByMetadata( public static FetchStyle determineFetchStyleByMetadata(
FetchMode mappingFetchMode, FetchMode mappingFetchMode,
@ -90,15 +90,14 @@ public final class FetchStrategyHelper {
return FetchStyle.JOIN; return FetchStyle.JOIN;
} }
if ( mappingFetchMode == FetchMode.SELECT ) {
return FetchStyle.SELECT;
}
if ( type.isEntityType() ) { if ( type.isEntityType() ) {
EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory ); EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory );
if ( persister.isBatchLoadable() ) { if ( persister.isBatchLoadable() ) {
return FetchStyle.BATCH; return FetchStyle.BATCH;
} }
else if ( mappingFetchMode == FetchMode.SELECT ) {
return FetchStyle.SELECT;
}
else if ( !persister.hasProxy() ) { else if ( !persister.hasProxy() ) {
return FetchStyle.JOIN; return FetchStyle.JOIN;
} }
@ -113,7 +112,6 @@ public final class FetchStrategyHelper {
return FetchStyle.BATCH; return FetchStyle.BATCH;
} }
} }
return FetchStyle.SELECT; return FetchStyle.SELECT;
} }

View File

@ -0,0 +1,241 @@
/*
* 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.test.fetchstrategyhelper;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.ElementCollection;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.junit.Test;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.type.AssociationType;
import static org.junit.Assert.assertSame;
/**
* @author Gail Badner
*/
public class BatchFetchStrategyHelperTest extends BaseCoreFunctionalTestCase {
@Test
public void testManyToOneDefaultFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityDefault" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityDefault" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
// batch size is ignored with org.hibernate.FetchMode.JOIN
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneJoinFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityJoin" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityJoin" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
// batch size is ignored with org.hibernate.FetchMode.JOIN
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneSelectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntitySelect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntitySelect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.BATCH, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionDefaultFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsDefault" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsDefault" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.BATCH, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionJoinFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsJoin" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsJoin" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
// batch size is ignored with org.hibernate.FetchMode.JOIN
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testCollectionSelectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsSelect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsSelect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.BATCH, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionSubselectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsSubselect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsSubselect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
// Batch size is ignored with FetchMode.SUBSELECT
assertSame( FetchStyle.SUBSELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
private org.hibernate.FetchMode determineFetchMode(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return entityPersister.getFetchMode( index );
}
private AssociationType determineAssociationType(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return (AssociationType) entityPersister.getSubclassPropertyType( index );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class,
OtherEntity.class
};
}
@javax.persistence.Entity
@Table(name="entity")
public static class AnEntity {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private OtherEntity otherEntityDefault;
@ManyToOne
@Fetch(FetchMode.JOIN)
private OtherEntity otherEntityJoin;
@ManyToOne
@Fetch(FetchMode.SELECT)
private OtherEntity otherEntitySelect;
// @Fetch(FetchMode.SUBSELECT) is not allowed for ToOne associations
@ElementCollection
@BatchSize( size = 5)
private Set<String> colorsDefault = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.JOIN)
@BatchSize( size = 5)
private Set<String> colorsJoin = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.SELECT)
@BatchSize( size = 5)
private Set<String> colorsSelect = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.SUBSELECT)
@BatchSize( size = 5)
private Set<String> colorsSubselect = new HashSet<String>();
}
@javax.persistence.Entity
@Table(name="otherentity")
@BatchSize( size = 5)
public static class OtherEntity {
@Id
@GeneratedValue
private Long id;
}
}

View File

@ -0,0 +1,232 @@
/*
* 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.test.fetchstrategyhelper;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.ElementCollection;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.junit.Test;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.type.AssociationType;
import static org.junit.Assert.assertSame;
/**
* @author Gail Badner
*/
public class FetchStrategyHelperTest extends BaseCoreFunctionalTestCase {
@Test
public void testManyToOneDefaultFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityDefault" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityDefault" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneJoinFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityJoin" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityJoin" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneSelectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntitySelect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntitySelect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.SELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionDefaultFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsDefault" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsDefault" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.SELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionJoinFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsJoin" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsJoin" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testCollectionSelectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsSelect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsSelect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.SELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
@Test
public void testCollectionSubselectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "colorsSubselect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "colorsSubselect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.SUBSELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.DELAYED, fetchTiming );
}
private org.hibernate.FetchMode determineFetchMode(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return entityPersister.getFetchMode( index );
}
private AssociationType determineAssociationType(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return (AssociationType) entityPersister.getSubclassPropertyType( index );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class,
OtherEntity.class
};
}
@javax.persistence.Entity
@Table(name="entity")
public static class AnEntity {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private OtherEntity otherEntityDefault;
@ManyToOne
@Fetch(FetchMode.JOIN)
private OtherEntity otherEntityJoin;
@ManyToOne
@Fetch(FetchMode.SELECT)
private OtherEntity otherEntitySelect;
// @Fetch(FetchMode.SUBSELECT) is not allowed for ToOne associations
@ElementCollection
private Set<String> colorsDefault = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.JOIN)
private Set<String> colorsJoin = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.SELECT)
private Set<String> colorsSelect = new HashSet<String>();
@ElementCollection
@Fetch(FetchMode.SUBSELECT)
private Set<String> colorsSubselect = new HashSet<String>();
}
@javax.persistence.Entity
@Table(name="otherentity")
public static class OtherEntity {
@Id
@GeneratedValue
private Long id;
}
}

View File

@ -0,0 +1,139 @@
/*
* 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.test.fetchstrategyhelper;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.junit.Test;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Proxy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.type.AssociationType;
import static org.junit.Assert.assertSame;
/**
* @author Gail Badner
*/
public class NoProxyFetchStrategyHelperTest extends BaseCoreFunctionalTestCase {
@Test
public void testManyToOneDefaultFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityDefault" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityDefault" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneJoinFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntityJoin" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntityJoin" );
assertSame( org.hibernate.FetchMode.JOIN, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.JOIN, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
@Test
public void testManyToOneSelectFetch() {
final AssociationType associationType = determineAssociationType( AnEntity.class, "otherEntitySelect" );
final org.hibernate.FetchMode fetchMode = determineFetchMode( AnEntity.class, "otherEntitySelect" );
assertSame( org.hibernate.FetchMode.SELECT, fetchMode );
final FetchStyle fetchStyle = FetchStrategyHelper.determineFetchStyleByMetadata(
fetchMode,
associationType,
sessionFactory()
);
assertSame( FetchStyle.SELECT, fetchStyle );
final FetchTiming fetchTiming = FetchStrategyHelper.determineFetchTiming(
fetchStyle,
associationType,
sessionFactory()
);
// Proxies are not allowed, so it should be FetchTiming.IMMEDIATE
assertSame( FetchTiming.IMMEDIATE, fetchTiming );
}
private org.hibernate.FetchMode determineFetchMode(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return entityPersister.getFetchMode( index );
}
private AssociationType determineAssociationType(Class<?> entityClass, String path) {
OuterJoinLoadable entityPersister = (OuterJoinLoadable) sessionFactory().getEntityPersister( entityClass.getName() );
int index = ( (UniqueKeyLoadable) entityPersister ).getPropertyIndex( path );
return (AssociationType) entityPersister.getSubclassPropertyType( index );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class,
OtherEntity.class
};
}
@javax.persistence.Entity
@Table(name="entity")
public static class AnEntity {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private OtherEntity otherEntityDefault;
@ManyToOne
@Fetch(FetchMode.JOIN)
private OtherEntity otherEntityJoin;
@ManyToOne
@Fetch(FetchMode.SELECT)
private OtherEntity otherEntitySelect;
// @Fetch(FetchMode.SUBSELECT) is not allowed for ToOne associations
}
@javax.persistence.Entity
@Table(name="otherentity")
@Proxy(lazy = false)
public static class OtherEntity {
@Id
@GeneratedValue
private Long id;
}
}

View File

@ -16,6 +16,7 @@ import org.hibernate.loader.entity.EntityJoinWalker;
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy; import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder; import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory; import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory;
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
@ -66,27 +67,14 @@ public class LoadPlanStructureAssertionHelper {
LoadQueryDetails details = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails( LoadQueryDetails details = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
plan, plan,
persister.getKeyColumnNames(), persister.getKeyColumnNames(),
new QueryBuildingParameters() { new QueryBuildingParametersImpl(
@Override influencers,
public LoadQueryInfluencers getQueryInfluencers() { batchSize,
return influencers; lockMode,
} null
@Override ),
public int getBatchSize() { sf
return batchSize;
}
@Override
public LockMode getLockMode() {
return lockMode;
}
@Override
public LockOptions getLockOptions() {
return null;
}
}, sf
); );
compare( walker, details ); compare( walker, details );

View File

@ -13,6 +13,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy; import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder; import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory; import org.hibernate.loader.plan.exec.internal.BatchingLoadQueryDetailsFactory;
import org.hibernate.loader.plan.exec.query.internal.QueryBuildingParametersImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.spi.LoadPlan; import org.hibernate.loader.plan.spi.LoadPlan;
@ -27,14 +28,21 @@ public class Helper implements QueryBuildingParameters {
*/ */
public static final Helper INSTANCE = new Helper(); public static final Helper INSTANCE = new Helper();
private static final QueryBuildingParameters queryBuildingParameters = new QueryBuildingParametersImpl(
LoadQueryInfluencers.NONE,
1,
LockMode.NONE,
null
);
private Helper() { private Helper() {
} }
public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister entityPersister) { public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister entityPersister) {
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy( final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
sf, sf,
LoadQueryInfluencers.NONE, queryBuildingParameters.getQueryInfluencers(),
LockMode.NONE queryBuildingParameters.getLockMode()
); );
return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
} }
@ -50,28 +58,28 @@ public class Helper implements QueryBuildingParameters {
return BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails( return BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
loadPlan, loadPlan,
null, null,
this, queryBuildingParameters,
sf sf
); );
} }
@Override @Override
public LoadQueryInfluencers getQueryInfluencers() { public LoadQueryInfluencers getQueryInfluencers() {
return LoadQueryInfluencers.NONE; return queryBuildingParameters.getQueryInfluencers();
} }
@Override @Override
public int getBatchSize() { public int getBatchSize() {
return 1; return queryBuildingParameters.getBatchSize();
} }
@Override @Override
public LockMode getLockMode() { public LockMode getLockMode() {
return null; return queryBuildingParameters.getLockMode();
} }
@Override @Override
public LockOptions getLockOptions() { public LockOptions getLockOptions() {
return null; return queryBuildingParameters.getLockOptions();
} }
} }

View File

@ -0,0 +1,437 @@
/*
* 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 javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.junit.Test;
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 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 (int i = 0 ; i < groups.length; i++ ) {
// 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( groups[ i ] );
assertTrue( Hibernate.isInitialized( groups[i] ) );
// the collections should be uninitialized
assertFalse( Hibernate.isInitialized( groups[i].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 (int i = 0 ; i < groups.length; i++ ) {
// 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( groups[ i ] );
assertTrue( Hibernate.isInitialized( groups[i] ) );
assertTrue( Hibernate.isInitialized( groups[i].getLead() ) );
assertFalse( Hibernate.isInitialized( groups[i].getLead().getCollaborators() ) );
assertTrue( Hibernate.isInitialized( groups[i].getManager() ) );
assertFalse( Hibernate.isInitialized( groups[i].getManager().getCollaborators() ) );
// the collections should be uninitialized
assertFalse( Hibernate.isInitialized( groups[i].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;
}
}
}