HHH-10679 HHH-10712 : Fix subselect fetching in load plans
(cherry picked from commit 9c29ec22c5
)
This commit is contained in:
parent
5c9c0c22ce
commit
18df49306e
|
@ -45,7 +45,11 @@ public final class StringHelper {
|
|||
if ( length == 0 ) {
|
||||
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] );
|
||||
for ( int i = 1; i < length; i++ ) {
|
||||
buf.append( seperator ).append( strings[i] );
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
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.persister.collection.QueryableCollection;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -63,28 +64,14 @@ public class CollectionLoader extends AbstractLoadPlanBasedCollectionInitializer
|
|||
}
|
||||
|
||||
public CollectionLoader byKey() {
|
||||
final QueryBuildingParameters buildingParameters = new QueryBuildingParameters() {
|
||||
@Override
|
||||
public LoadQueryInfluencers getQueryInfluencers() {
|
||||
return influencers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return LockMode.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return new CollectionLoader( collectionPersister, buildingParameters ) ;
|
||||
// capture current values in a new instance of QueryBuildingParametersImpl
|
||||
final QueryBuildingParameters currentBuildingParameters = new QueryBuildingParametersImpl(
|
||||
influencers,
|
||||
batchSize,
|
||||
LockMode.NONE,
|
||||
null
|
||||
);
|
||||
return new CollectionLoader( collectionPersister, currentBuildingParameters ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.MappingException;
|
|||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -77,32 +78,18 @@ public class EntityLoader extends AbstractLoadPlanBasedEntityLoader {
|
|||
}
|
||||
|
||||
public EntityLoader byUniqueKey(String[] keyColumnNames, Type keyType) {
|
||||
// capture current values in a new instance of QueryBuildingParametersImpl
|
||||
return new EntityLoader(
|
||||
persister.getFactory(),
|
||||
persister,
|
||||
keyColumnNames,
|
||||
keyType,
|
||||
new QueryBuildingParameters() {
|
||||
@Override
|
||||
public LoadQueryInfluencers getQueryInfluencers() {
|
||||
return influencers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return lockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return lockOptions;
|
||||
}
|
||||
}
|
||||
new QueryBuildingParametersImpl(
|
||||
influencers,
|
||||
batchSize,
|
||||
lockMode,
|
||||
lockOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.hibernate.loader.plan.spi.EntityReturn;
|
|||
import org.hibernate.loader.plan.spi.FetchSource;
|
||||
import org.hibernate.loader.plan.spi.Return;
|
||||
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.AssociationAttributeDefinition;
|
||||
import org.hibernate.persister.walking.spi.AssociationKey;
|
||||
|
@ -833,9 +834,6 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
|
|||
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
|
||||
// todo : this seems to not be correct for one-to-one
|
||||
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
|
||||
if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ExpandingFetchSource currentSource = currentSource();
|
||||
currentSource.validateFetchPlan( fetchStrategy, attributeDefinition );
|
||||
|
@ -845,6 +843,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
|
|||
// for ANY mappings we need to build a Fetch:
|
||||
// 1) fetch type is SELECT
|
||||
// 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(
|
||||
attributeDefinition,
|
||||
fetchStrategy
|
||||
|
@ -852,11 +851,13 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
|
|||
return false;
|
||||
}
|
||||
else if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
|
||||
// regardless of the fetch style, build the fetch
|
||||
EntityFetch fetch = currentSource.buildEntityAttributeFetch(
|
||||
attributeDefinition,
|
||||
fetchStrategy
|
||||
);
|
||||
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) {
|
||||
if ( FetchStrategyHelper.isJoinFetched( fetchStrategy ) ) {
|
||||
// only push to the stack if join fetched
|
||||
pushToStack( (ExpandingFetchSource) fetch );
|
||||
return true;
|
||||
}
|
||||
|
@ -866,8 +867,13 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
|
|||
}
|
||||
else {
|
||||
// Collection
|
||||
CollectionAttributeFetch fetch = currentSource.buildCollectionAttributeFetch( attributeDefinition, fetchStrategy );
|
||||
if ( fetchStrategy.getStyle() == FetchStyle.JOIN ) {
|
||||
// regardless of the fetch style, build the fetch
|
||||
CollectionAttributeFetch fetch = currentSource.buildCollectionAttributeFetch(
|
||||
attributeDefinition,
|
||||
fetchStrategy
|
||||
);
|
||||
if ( FetchStrategyHelper.isJoinFetched( fetchStrategy ) ) {
|
||||
// only push to the stack if join fetched
|
||||
pushToCollectionStack( fetch );
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,16 @@ public abstract class AbstractCollectionLoadQueryDetails extends AbstractLoadQue
|
|||
return (CollectionReturn) getRootReturn();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSubselectLoadingEnabled(FetchStats fetchStats) {
|
||||
return fetchStats != null && fetchStats.hasSubselectFetches();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldUseOptionalEntityInstance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReaderCollector getReaderCollector() {
|
||||
return readerCollector;
|
||||
|
|
|
@ -173,11 +173,22 @@ public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
|
|||
this.sqlStatement = select.toStatementString();
|
||||
this.resultSetProcessor = new ResultSetProcessorImpl(
|
||||
loadPlan,
|
||||
queryProcessor.getAliasResolutionContext(),
|
||||
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 QuerySpace getRootQuerySpace();
|
||||
protected abstract String getRootTableAlias();
|
||||
|
|
|
@ -150,6 +150,18 @@ public class EntityLoadQueryDetails extends AbstractLoadQueryDetails {
|
|||
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
|
||||
protected ReaderCollector getReaderCollector() {
|
||||
return readerCollector;
|
||||
|
|
|
@ -443,10 +443,8 @@ public class LoadQueryJoinAndFetchProcessor {
|
|||
Fetch fetch,
|
||||
ReaderCollector readerCollector,
|
||||
FetchStatsImpl fetchStats) {
|
||||
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// process fetch even if it is not join fetched
|
||||
if ( EntityFetch.class.isInstance( fetch ) ) {
|
||||
final EntityFetch entityFetch = (EntityFetch) fetch;
|
||||
processEntityFetch(
|
||||
|
@ -494,6 +492,10 @@ public class LoadQueryJoinAndFetchProcessor {
|
|||
// }
|
||||
|
||||
fetchStats.processingFetch( fetch );
|
||||
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
|
||||
// not join fetched, so nothing else to do
|
||||
return;
|
||||
}
|
||||
|
||||
// First write out the SQL SELECT fragments
|
||||
final Joinable joinable = (Joinable) fetch.getEntityPersister();
|
||||
|
@ -540,8 +542,14 @@ public class LoadQueryJoinAndFetchProcessor {
|
|||
CollectionAttributeFetch fetch,
|
||||
ReaderCollector readerCollector,
|
||||
FetchStatsImpl fetchStats) {
|
||||
|
||||
fetchStats.processingFetch( fetch );
|
||||
|
||||
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
|
||||
// not join fetched, so nothing else to do
|
||||
return;
|
||||
}
|
||||
|
||||
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases(
|
||||
fetch.getQuerySpaceUid()
|
||||
);
|
||||
|
|
|
@ -15,13 +15,17 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
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.query.spi.NamedParameterContext;
|
||||
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
|
||||
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||
import org.hibernate.loader.plan.spi.EntityReference;
|
||||
import org.hibernate.loader.plan.spi.Fetch;
|
||||
|
@ -34,9 +38,12 @@ import org.hibernate.type.EntityType;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ResultSetProcessingContextImpl implements ResultSetProcessingContext {
|
||||
private static final Logger LOG = CoreLogging.logger( ResultSetProcessingContextImpl.class );
|
||||
|
||||
private final ResultSet resultSet;
|
||||
private final SessionImplementor session;
|
||||
private final LoadPlan loadPlan;
|
||||
private final AliasResolutionContext aliasResolutionContext;
|
||||
private final boolean readOnly;
|
||||
private final boolean shouldUseOptionalEntityInformation;
|
||||
private final boolean forceFetchLazyAttributes;
|
||||
|
@ -47,8 +54,9 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
|
||||
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
|
||||
|
||||
private Map<EntityPersister,Set<EntityKey>> subselectLoadableEntityKeyMap;
|
||||
private Map<EntityReference,Set<EntityKey>> subselectLoadableEntityKeyMap;
|
||||
private List<HydratedEntityRegistration> hydratedEntityRegistrationList;
|
||||
private int nRowsRead = 0;
|
||||
|
||||
/**
|
||||
* Builds a ResultSetProcessingContextImpl
|
||||
|
@ -61,6 +69,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
final ResultSet resultSet,
|
||||
final SessionImplementor session,
|
||||
final LoadPlan loadPlan,
|
||||
final AliasResolutionContext aliasResolutionContext,
|
||||
final boolean readOnly,
|
||||
final boolean shouldUseOptionalEntityInformation,
|
||||
final boolean forceFetchLazyAttributes,
|
||||
|
@ -71,6 +80,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
this.resultSet = resultSet;
|
||||
this.session = session;
|
||||
this.loadPlan = loadPlan;
|
||||
this.aliasResolutionContext = aliasResolutionContext;
|
||||
this.readOnly = readOnly;
|
||||
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
|
||||
this.forceFetchLazyAttributes = forceFetchLazyAttributes;
|
||||
|
@ -254,6 +264,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
* Package-protected
|
||||
*/
|
||||
void finishUpRow() {
|
||||
nRowsRead++;
|
||||
|
||||
if ( currentRowHydratedEntityRegistrationList == null ) {
|
||||
if ( identifierResolutionContextMap != null ) {
|
||||
identifierResolutionContextMap.clear();
|
||||
|
@ -272,15 +284,15 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
// managing the map forms needed for subselect fetch generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
if ( hadSubselectFetches ) {
|
||||
if ( subselectLoadableEntityKeyMap == null ) {
|
||||
subselectLoadableEntityKeyMap = new HashMap<EntityPersister, Set<EntityKey>>();
|
||||
subselectLoadableEntityKeyMap = new HashMap<EntityReference, Set<EntityKey>>();
|
||||
}
|
||||
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
|
||||
Set<EntityKey> entityKeys = subselectLoadableEntityKeyMap.get(
|
||||
registration.getEntityReference().getEntityPersister()
|
||||
registration.getEntityReference()
|
||||
);
|
||||
if ( entityKeys == null ) {
|
||||
entityKeys = new HashSet<EntityKey>();
|
||||
subselectLoadableEntityKeyMap.put( registration.getEntityReference().getEntityPersister(), entityKeys );
|
||||
subselectLoadableEntityKeyMap.put( registration.getEntityReference(), entityKeys );
|
||||
}
|
||||
entityKeys.add( registration.getKey() );
|
||||
}
|
||||
|
@ -314,24 +326,27 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
|
|||
}
|
||||
|
||||
private void createSubselects() {
|
||||
if ( subselectLoadableEntityKeyMap == null || subselectLoadableEntityKeyMap.size() <= 1 ) {
|
||||
// if we only returned one entity, query by key is more efficient; so do nothing here
|
||||
return;
|
||||
if ( subselectLoadableEntityKeyMap == null || nRowsRead <= 1 ) {
|
||||
LOG.tracef(
|
||||
"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 =
|
||||
ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext );
|
||||
|
||||
final String subselectQueryString = SubselectFetch.createSubselectFetchQueryFragment( queryParameters );
|
||||
for ( Map.Entry<EntityPersister, Set<EntityKey>> entry : subselectLoadableEntityKeyMap.entrySet() ) {
|
||||
if ( ! entry.getKey().hasSubselectLoadableCollections() ) {
|
||||
for ( Map.Entry<EntityReference, Set<EntityKey>> entry : subselectLoadableEntityKeyMap.entrySet() ) {
|
||||
if ( ! entry.getKey().getEntityPersister().hasSubselectLoadableCollections() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SubselectFetch subselectFetch = new SubselectFetch(
|
||||
subselectQueryString,
|
||||
null, // aliases[i],
|
||||
(Loadable) entry.getKey(),
|
||||
aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( entry.getKey().getQuerySpaceUid() ),
|
||||
(Loadable) entry.getKey().getEntityPersister(),
|
||||
queryParameters,
|
||||
entry.getValue(),
|
||||
namedParameterLocMap
|
||||
|
|
|
@ -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.ScrollableResultSetProcessor;
|
||||
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.LoadPlan;
|
||||
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 final LoadPlan loadPlan;
|
||||
private final AliasResolutionContext aliasResolutionContext;
|
||||
private final RowReader rowReader;
|
||||
|
||||
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.aliasResolutionContext = aliasResolutionContext;
|
||||
this.rowReader = rowReader;
|
||||
this.shouldUseOptionalEntityInstance = shouldUseOptionalEntityInstance;
|
||||
this.hadSubselectFetches = hadSubselectFetches;
|
||||
}
|
||||
|
||||
|
@ -80,12 +95,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
|
|||
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
|
||||
final boolean forceFetchLazyAttributes = false;
|
||||
|
||||
|
@ -93,6 +102,7 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
|
|||
resultSet,
|
||||
session,
|
||||
loadPlan,
|
||||
aliasResolutionContext,
|
||||
readOnly,
|
||||
shouldUseOptionalEntityInstance,
|
||||
forceFetchLazyAttributes,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ public final class FetchStrategyHelper {
|
|||
* @param type The association type
|
||||
* @param sessionFactory The session factory
|
||||
*
|
||||
* @return
|
||||
* @return the fetch style
|
||||
*/
|
||||
public static FetchStyle determineFetchStyleByMetadata(
|
||||
FetchMode mappingFetchMode,
|
||||
|
@ -90,15 +90,14 @@ public final class FetchStrategyHelper {
|
|||
return FetchStyle.JOIN;
|
||||
}
|
||||
|
||||
if ( mappingFetchMode == FetchMode.SELECT ) {
|
||||
return FetchStyle.SELECT;
|
||||
|
||||
}
|
||||
if ( type.isEntityType() ) {
|
||||
EntityPersister persister = (EntityPersister) type.getAssociatedJoinable( sessionFactory );
|
||||
if ( persister.isBatchLoadable() ) {
|
||||
return FetchStyle.BATCH;
|
||||
}
|
||||
else if ( mappingFetchMode == FetchMode.SELECT ) {
|
||||
return FetchStyle.SELECT;
|
||||
}
|
||||
else if ( !persister.hasProxy() ) {
|
||||
return FetchStyle.JOIN;
|
||||
}
|
||||
|
@ -113,7 +112,6 @@ public final class FetchStrategyHelper {
|
|||
return FetchStyle.BATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return FetchStyle.SELECT;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import org.hibernate.loader.entity.EntityJoinWalker;
|
|||
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
|
||||
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
|
||||
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.spi.LoadQueryDetails;
|
||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||
|
@ -66,27 +67,14 @@ public class LoadPlanStructureAssertionHelper {
|
|||
LoadQueryDetails details = BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
|
||||
plan,
|
||||
persister.getKeyColumnNames(),
|
||||
new QueryBuildingParameters() {
|
||||
@Override
|
||||
public LoadQueryInfluencers getQueryInfluencers() {
|
||||
return influencers;
|
||||
}
|
||||
new QueryBuildingParametersImpl(
|
||||
influencers,
|
||||
batchSize,
|
||||
lockMode,
|
||||
null
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return lockMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return null;
|
||||
}
|
||||
}, sf
|
||||
),
|
||||
sf
|
||||
);
|
||||
|
||||
compare( walker, details );
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.loader.plan.build.internal.FetchStyleLoadPlanBuildingAssociationVisitationStrategy;
|
||||
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
|
||||
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.spi.LoadQueryDetails;
|
||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||
|
@ -27,14 +28,21 @@ public class Helper implements QueryBuildingParameters {
|
|||
*/
|
||||
public static final Helper INSTANCE = new Helper();
|
||||
|
||||
private static final QueryBuildingParameters queryBuildingParameters = new QueryBuildingParametersImpl(
|
||||
LoadQueryInfluencers.NONE,
|
||||
1,
|
||||
LockMode.NONE,
|
||||
null
|
||||
);
|
||||
|
||||
private Helper() {
|
||||
}
|
||||
|
||||
public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister entityPersister) {
|
||||
final FetchStyleLoadPlanBuildingAssociationVisitationStrategy strategy = new FetchStyleLoadPlanBuildingAssociationVisitationStrategy(
|
||||
sf,
|
||||
LoadQueryInfluencers.NONE,
|
||||
LockMode.NONE
|
||||
queryBuildingParameters.getQueryInfluencers(),
|
||||
queryBuildingParameters.getLockMode()
|
||||
);
|
||||
return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
|
||||
}
|
||||
|
@ -50,28 +58,28 @@ public class Helper implements QueryBuildingParameters {
|
|||
return BatchingLoadQueryDetailsFactory.INSTANCE.makeEntityLoadQueryDetails(
|
||||
loadPlan,
|
||||
null,
|
||||
this,
|
||||
queryBuildingParameters,
|
||||
sf
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadQueryInfluencers getQueryInfluencers() {
|
||||
return LoadQueryInfluencers.NONE;
|
||||
return queryBuildingParameters.getQueryInfluencers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return 1;
|
||||
return queryBuildingParameters.getBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode getLockMode() {
|
||||
return null;
|
||||
return queryBuildingParameters.getLockMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return null;
|
||||
return queryBuildingParameters.getLockOptions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue