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:
parent
545930c39f
commit
62ac35213f
|
@ -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] );
|
||||||
|
|
|
@ -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 ) ;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.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 );
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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