HHH-8276 - Integrate LoadPlans into UniqueEntityLoader (PoC) - Initial reworking to remove SQL references (for reuse in Search, OGM, etc) and to split out conceptual "from clause" and "select clause" into different structures (see QuerySpaces)

This commit is contained in:
Steve Ebersole 2013-07-11 12:32:00 -05:00
parent dc7cdf9d88
commit 18079f346d
113 changed files with 7017 additions and 1628 deletions

View File

@ -32,12 +32,11 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.entity.UniqueEntityLoader;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.exec.spi.EntityLoadQueryDetails;
import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.build.LoadPlanBuilder;
import org.hibernate.loader.plan.spi.build.MetadataDrivenLoadPlanBuilder;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.NoOpLoadPlanAdvisor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
@ -58,7 +57,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
private final String entityName;
private final LoadPlan plan;
private final LoadQueryDetails staticLoadQuery;
private final EntityLoadQueryDetails staticLoadQuery;
private ColumnNameCache columnNameCache;
@ -78,12 +77,12 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
buildingParameters.getQueryInfluencers()
);
this.plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
this.staticLoadQuery = LoadQueryDetails.makeForBatching(
uniqueKeyColumnNames,
this.plan = MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister );
this.staticLoadQuery = EntityLoadQueryDetails.makeForBatching(
plan,
factory,
buildingParameters
uniqueKeyColumnNames,
buildingParameters,
factory
);
}
@ -91,7 +90,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return factory;
}
protected LoadQueryDetails getStaticLoadQuery() {
protected EntityLoadQueryDetails getStaticLoadQuery() {
return staticLoadQuery;
}
@ -197,7 +196,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
LoadQueryDetails loadQueryDetails,
EntityLoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
@ -214,7 +213,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected List executeLoad(
SessionImplementor session,
QueryParameters queryParameters,
LoadQueryDetails loadQueryDetails,
EntityLoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActions) throws SQLException {
@ -237,8 +236,6 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
try {
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
results = loadQueryDetails.getResultSetProcessor().extractResults(
// todo : hook in the JPA 2.1 entity graph advisor
NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(),
session,
queryParameters,
@ -248,7 +245,6 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
}
},
loadQueryDetails.getAliasResolutionContext(),
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,
@ -295,7 +291,7 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
protected Object doQueryAndLoadEntity(
SessionImplementor session,
QueryParameters queryParameters,
LoadQueryDetails loadQueryDetails,
EntityLoadQueryDetails loadQueryDetails,
boolean returnProxies,
ResultTransformer forcedResultTransformer) throws SQLException {
@ -305,7 +301,6 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
try {
final List results = loadQueryDetails.getResultSetProcessor().extractResults(
NoOpLoadPlanAdvisor.INSTANCE,
wrapper.getResultSet(),
session,
queryParameters,
@ -315,7 +310,6 @@ public abstract class AbstractLoadPlanBasedEntityLoader implements UniqueEntityL
return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name );
}
},
loadQueryDetails.getAliasResolutionContext(),
returnProxies,
queryParameters.isReadOnly(),
forcedResultTransformer,

View File

@ -0,0 +1,79 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.exec.internal;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return;
/**
* @author Steve Ebersole
*/
public class Helper {
/**
* Singleton access
*/
public static final Helper INSTANCE = new Helper();
/**
* Disallow direct instantiation
*/
private Helper() {
}
/**
* Extract the root return of the LoadPlan, assuming there is just one.
*
* @param loadPlan The LoadPlan from which to extract the root return
* @param returnType The Return type expected, passed as an argument
* @param <T> The parameterized type of the specific Return type expected
*
* @return The root Return
*
* @throws IllegalStateException If there is no root, more than one root or the single root
* is not of the expected type.
*/
@SuppressWarnings("unchecked")
public <T extends Return> T extractRootReturn(LoadPlan loadPlan, Class<T> returnType) {
if ( loadPlan.getReturns().size() == 0 ) {
throw new IllegalStateException( "LoadPlan contained no root returns" );
}
else if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "LoadPlan contained more than one root returns" );
}
final Return rootReturn = loadPlan.getReturns().get( 0 );
if ( !returnType.isInstance( rootReturn ) ) {
throw new IllegalStateException(
String.format(
"Unexpected LoadPlan root return; expecting %s, but found %s",
returnType.getName(),
rootReturn.getClass().getName()
)
);
}
return (T) rootReturn;
}
}

View File

@ -21,17 +21,36 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.exec.query.internal;
package org.hibernate.loader.plan.exec.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityIdentifierReader;
import org.hibernate.loader.plan.exec.process.internal.EntityIdentifierReaderImpl;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.OneToOneFetchReader;
import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.exec.spi.ReaderCollector;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch;
@ -41,17 +60,15 @@ import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;
/**
* Helper for implementors of entity and collection based query building based on LoadPlans providing common
@ -60,16 +77,74 @@ import org.hibernate.type.AssociationType;
* @author Steve Ebersole
*/
public class LoadQueryBuilderHelper {
private static final Logger log = CoreLogging.logger( LoadQueryBuilderHelper.class );
private LoadQueryBuilderHelper() {
}
public static void applyJoinFetches(
// used to collect information about fetches. For now that is only whether there were subselect fetches
public static interface FetchStats {
public boolean hasSubselectFetches();
}
private static class FetchStatsImpl implements FetchStats {
private boolean hasSubselectFetch;
public void processingFetch(Fetch fetch) {
if ( ! hasSubselectFetch ) {
if ( fetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT
&& fetch.getFetchStrategy().getTiming() != FetchTiming.IMMEDIATE ) {
hasSubselectFetch = true;
}
}
}
@Override
public boolean hasSubselectFetches() {
return hasSubselectFetch;
}
}
public static void applyIdentifierJoinFetches(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
FetchOwner fetchOwner,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector) {
}
public static FetchStats applyJoinFetches(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
FetchOwner fetchOwner,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector) {
final JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
final FetchStatsImpl stats = new FetchStatsImpl();
// if the fetch owner is an entityReference, we should also walk its identifier fetches here...
//
// what if fetchOwner is a composite fetch (as it would be in the case of a key-many-to-one)?
if ( EntityReference.class.isInstance( fetchOwner ) ) {
final EntityReference fetchOwnerAsEntityReference = (EntityReference) fetchOwner;
for ( Fetch fetch : fetchOwnerAsEntityReference.getIdentifierDescription().getFetches() ) {
processFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
fetch,
buildingParameters,
aliasResolutionContext,
readerCollector,
stats
);
}
}
processJoinFetches(
selectStatementBuilder,
@ -77,22 +152,30 @@ public class LoadQueryBuilderHelper {
joinFragment,
fetchOwner,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
selectStatementBuilder.setOuterJoins(
joinFragment.toFromFragmentString(),
joinFragment.toWhereFragmentString()
);
return stats;
}
private static void processJoinFetches(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
FetchStatsImpl stats) {
for ( Fetch fetch : fetchOwner.getFetches() ) {
processFetch(
selectStatementBuilder,
@ -101,7 +184,9 @@ public class LoadQueryBuilderHelper {
fetchOwner,
fetch,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
}
}
@ -113,41 +198,40 @@ public class LoadQueryBuilderHelper {
FetchOwner fetchOwner,
Fetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
FetchStatsImpl stats) {
if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
return;
}
if ( EntityFetch.class.isInstance( fetch ) ) {
final EntityFetch entityFetch = (EntityFetch) fetch;
processEntityFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
(EntityFetch) fetch,
entityFetch,
buildingParameters,
aliasResolutionContext
);
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
(EntityFetch) fetch,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
}
else if ( CollectionFetch.class.isInstance( fetch ) ) {
final CollectionFetch collectionFetch = (CollectionFetch) fetch;
processCollectionFetch(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
(CollectionFetch) fetch,
collectionFetch,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
final CollectionFetch collectionFetch = (CollectionFetch) fetch;
if ( collectionFetch.getIndexGraph() != null ) {
processJoinFetches(
selectStatementBuilder,
@ -155,7 +239,9 @@ public class LoadQueryBuilderHelper {
joinFragment,
collectionFetch.getIndexGraph(),
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
}
if ( collectionFetch.getElementGraph() != null ) {
@ -165,7 +251,9 @@ public class LoadQueryBuilderHelper {
joinFragment,
collectionFetch.getElementGraph(),
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
}
}
@ -179,13 +267,97 @@ public class LoadQueryBuilderHelper {
joinFragment,
(FetchOwner) fetch,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
readerCollector,
stats
);
}
}
}
private static void processEntityFetch(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
FetchOwner fetchOwner,
EntityFetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
FetchStatsImpl stats) {
if ( BidirectionalEntityFetch.class.isInstance( fetch ) ) {
log.tracef( "Skipping bi-directional entity fetch [%s]", fetch );
return;
}
stats.processingFetch( fetch );
// write the fragments for this fetch to the in-flight SQL builder
final EntityReferenceAliases aliases = renderSqlFragments(
selectStatementBuilder,
factory,
joinFragment,
fetchOwner,
fetch,
buildingParameters,
aliasResolutionContext
);
// now we build readers as follows:
// 1) readers for any fetches that are part of the identifier
final EntityIdentifierReader identifierReader = buildIdentifierReader(
selectStatementBuilder,
factory,
joinFragment,
fetch,
buildingParameters,
aliasResolutionContext,
readerCollector,
aliases,
stats
);
// 2) a reader for this fetch itself
// todo : not sure this distinction really matters aside form the whole "register nullable property" stuff,
// but not sure we need a distinct class for just that
if ( fetch.getFetchedType().isOneToOne() ) {
readerCollector.addReader(
new OneToOneFetchReader( fetch, aliases, identifierReader, (EntityReference) fetchOwner )
);
}
else {
readerCollector.addReader(
new EntityReferenceReader( fetch, aliases, identifierReader )
);
}
// 3) and then readers for all fetches not part of the identifier
processJoinFetches(
selectStatementBuilder,
factory,
joinFragment,
fetch,
buildingParameters,
aliasResolutionContext,
readerCollector,
stats
);
}
/**
* Renders the pieces indicated by the incoming EntityFetch reference into the in-flight SQL statement builder.
*
* @param selectStatementBuilder The builder containing the in-flight SQL query definition.
* @param factory The SessionFactory SPI
* @param joinFragment The in-flight SQL JOIN definition.
* @param fetchOwner The owner of {@code fetch}
* @param fetch The fetch which indicates the information to be rendered.
* @param buildingParameters The settings/options for SQL building
* @param aliasResolutionContext The reference cache for entity/collection aliases
*
* @return The used aliases
*/
private static EntityReferenceAliases renderSqlFragments(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
@ -193,7 +365,9 @@ public class LoadQueryBuilderHelper {
EntityFetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final String rhsAlias = aliasResolutionContext.resolveAliases( fetch ).getTableAlias();
final EntityReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
final String rhsAlias = aliases.getTableAlias();
final String[] rhsColumnNames = JoinHelper.getRHSColumnNames( fetch.getFetchedType(), factory );
final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
@ -227,11 +401,125 @@ public class LoadQueryBuilderHelper {
null,
null,
rhsAlias,
aliasResolutionContext.resolveAliases( fetch ).getColumnAliases().getSuffix(),
aliases.getColumnAliases().getSuffix(),
null,
true
)
);
return aliases;
}
private static EntityIdentifierReader buildIdentifierReader(
SelectStatementBuilder selectStatementBuilder,
SessionFactoryImplementor factory,
JoinFragment joinFragment,
EntityReference entityReference,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
EntityReferenceAliases aliases,
FetchStatsImpl stats) {
final List<EntityReferenceReader> identifierFetchReaders = new ArrayList<EntityReferenceReader>();
final ReaderCollector identifierFetchReaderCollector = new ReaderCollector() {
@Override
public void addReader(CollectionReferenceReader collectionReferenceReader) {
throw new IllegalStateException( "Identifier cannot contain collection fetches" );
}
@Override
public void addReader(EntityReferenceReader entityReferenceReader) {
identifierFetchReaders.add( entityReferenceReader );
}
};
for ( Fetch fetch : entityReference.getIdentifierDescription().getFetches() ) {
processFetch(
selectStatementBuilder,
factory,
joinFragment,
(FetchOwner) entityReference,
fetch,
buildingParameters,
aliasResolutionContext,
identifierFetchReaderCollector,
stats
);
}
return new EntityIdentifierReaderImpl(
entityReference,
aliases,
identifierFetchReaders
);
}
private static List<EntityReferenceReader> collectIdentifierFetchReaders(
EntityReference entityReference,
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector) {
final Type identifierType = entityReference.getEntityPersister().getIdentifierType();
if ( ! identifierType.isComponentType() ) {
return Collections.emptyList();
}
final Fetch[] fetches = entityReference.getIdentifierDescription().getFetches();
if ( fetches == null || fetches.length == 0 ) {
return Collections.emptyList();
}
final List<EntityReferenceReader> readers = new ArrayList<EntityReferenceReader>();
for ( Fetch fetch : fetches ) {
collectIdentifierFetchReaders( aliasResolutionContext, readers, entityReference, fetch, readerCollector );
}
return readers;
}
private static void collectIdentifierFetchReaders(
AliasResolutionContext aliasResolutionContext,
List<EntityReferenceReader> readers,
EntityReference entityReference,
Fetch fetch,
ReaderCollector readerCollector) {
if ( CompositeFetch.class.isInstance( fetch ) ) {
for ( Fetch subFetch : ( (CompositeFetch) fetch).getFetches() ) {
collectIdentifierFetchReaders( aliasResolutionContext, readers, entityReference, subFetch, readerCollector );
}
}
else if ( ! EntityReference.class.isInstance( fetch ) ) {
throw new IllegalStateException(
String.format(
"Non-entity (and non-composite) fetch [%s] was found as part of entity identifier : %s",
fetch,
entityReference.getEntityPersister().getEntityName()
)
);
}
else {
// todo : add a mapping here from EntityReference -> EntityReferenceReader
//
// need to be careful here about bi-directionality, just not sure how to best check for bi-directionality here.
//
final EntityReference fetchedEntityReference = (EntityReference) fetch;
final EntityReferenceAliases fetchedAliases = aliasResolutionContext.resolveAliases( fetchedEntityReference );
if ( BidirectionalEntityFetch.class.isInstance( fetchedEntityReference ) ) {
return;
}
final EntityReferenceReader reader = new EntityReferenceReader(
fetchedEntityReference,
aliasResolutionContext.resolveAliases( fetchedEntityReference ),
new EntityIdentifierReaderImpl(
fetchedEntityReference,
fetchedAliases,
Collections.<EntityReferenceReader>emptyList()
)
);
readerCollector.addReader( reader );
// readers.add( reader );
}
}
private static String[] resolveAliasedLhsJoinColumns(
@ -331,19 +619,25 @@ public class LoadQueryBuilderHelper {
FetchOwner fetchOwner,
CollectionFetch fetch,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();
AliasResolutionContext aliasResolutionContext,
ReaderCollector readerCollector,
FetchStatsImpl stats) {
stats.processingFetch( fetch );
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
if ( fetch.getCollectionPersister().isManyToMany() ) {
final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();
// for many-to-many we have 3 table aliases. By way of example, consider a normal m-n: User<->Role
// where User is the FetchOwner and Role (User.roles) is the Fetch. We'd have:
// 1) the owner's table : user
final String ownerTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
// 2) the m-n table : user_role
final String collectionTableAlias = aliasResolutionContext.resolveAliases( fetch ).getCollectionTableAlias();
final String collectionTableAlias = aliases.getCollectionTableAlias();
// 3) the element table : role
final String elementTableAlias = aliasResolutionContext.resolveAliases( fetch ).getElementTableAlias();
final String elementTableAlias = aliases.getElementTableAlias();
{
// add join fragments from the owner table -> collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -371,8 +665,8 @@ public class LoadQueryBuilderHelper {
(Joinable) queryableCollection.getElementPersister(),
ownerTableAlias,
collectionTableAlias,
aliasResolutionContext.resolveAliases( fetch ).getEntityElementColumnAliases().getSuffix(),
aliasResolutionContext.resolveAliases( fetch ).getCollectionColumnAliases().getSuffix(),
aliases.getEntityElementColumnAliases().getSuffix(),
aliases.getCollectionColumnAliases().getSuffix(),
true
)
);
@ -419,7 +713,6 @@ public class LoadQueryBuilderHelper {
);
// add select fragments from the element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
selectStatementBuilder.appendSelectClauseFragment(
elementPersister.selectFragment(
aliases.getElementTableAlias(),
@ -437,9 +730,44 @@ public class LoadQueryBuilderHelper {
if ( StringHelper.isNotEmpty( ordering ) ) {
selectStatementBuilder.appendOrderByFragment( ordering );
}
final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliases() {
@Override
public String getTableAlias() {
return aliases.getElementTableAlias();
}
@Override
public EntityAliases getColumnAliases() {
return aliases.getEntityElementColumnAliases();
}
};
final EntityReference elementEntityReference = (EntityReference) fetch.getElementGraph();
readerCollector.addReader(
new EntityReferenceReader(
elementEntityReference,
entityReferenceAliases,
buildIdentifierReader(
selectStatementBuilder,
factory,
joinFragment,
elementEntityReference,
buildingParameters,
aliasResolutionContext,
readerCollector,
entityReferenceAliases,
stats
)
)
);
}
else {
final String rhsTableAlias = aliasResolutionContext.resolveAliases( fetch ).getElementTableAlias();
final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();
final String rhsTableAlias = aliases.getElementTableAlias();
final String[] rhsColumnNames = JoinHelper.getRHSColumnNames( fetch.getFetchedType(), factory );
final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
@ -469,13 +797,12 @@ public class LoadQueryBuilderHelper {
selectStatementBuilder.appendSelectClauseFragment(
queryableCollection.selectFragment(
rhsTableAlias,
aliasResolutionContext.resolveAliases( fetch ).getCollectionColumnAliases().getSuffix()
aliases.getCollectionColumnAliases().getSuffix()
)
);
if ( fetch.getCollectionPersister().isOneToMany() ) {
// if the collection elements are entities, select the entity columns as well
final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch );
final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
selectStatementBuilder.appendSelectClauseFragment(
elementPersister.selectFragment(
@ -483,6 +810,37 @@ public class LoadQueryBuilderHelper {
aliases.getEntityElementColumnAliases().getSuffix()
)
);
final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliases() {
@Override
public String getTableAlias() {
return aliases.getElementTableAlias();
}
@Override
public EntityAliases getColumnAliases() {
return aliases.getEntityElementColumnAliases();
}
};
final EntityReference elementEntityReference = (EntityReference) fetch.getElementGraph();
readerCollector.addReader(
new EntityReferenceReader(
elementEntityReference,
entityReferenceAliases,
buildIdentifierReader(
selectStatementBuilder,
factory,
joinFragment,
elementEntityReference,
buildingParameters,
aliasResolutionContext,
readerCollector,
entityReferenceAliases,
stats
)
)
);
}
final String ordering = queryableCollection.getSQLOrderByString( rhsTableAlias );
@ -491,6 +849,7 @@ public class LoadQueryBuilderHelper {
}
}
readerCollector.addReader( new CollectionReferenceReader( fetch, aliases ) );
}
private static Joinable extractJoinable(FetchOwner fetchOwner) {

View File

@ -0,0 +1,4 @@
/**
* This package supports converting a LoadPlan to SQL and generating readers for the resulting ResultSet
*/
package org.hibernate.loader.plan.exec;

View File

@ -44,16 +44,14 @@ public class CollectionReferenceReader {
private static final Logger log = CoreLogging.logger( CollectionReferenceReader.class );
private final CollectionReference collectionReference;
private final CollectionReferenceAliases aliases;
public CollectionReferenceReader(CollectionReference collectionReference) {
public CollectionReferenceReader(CollectionReference collectionReference, CollectionReferenceAliases aliases) {
this.collectionReference = collectionReference;
this.aliases = aliases;
}
public void finishUpRow(ResultSet resultSet, ResultSetProcessingContextImpl context) {
final CollectionReferenceAliases aliases = context.getAliasResolutionContext().resolveAliases(
collectionReference
);
try {
// read the collection key for this reference for the current row.
final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();

View File

@ -30,6 +30,7 @@ import java.sql.SQLException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan.spi.CollectionReturn;
/**
@ -38,8 +39,8 @@ import org.hibernate.loader.plan.spi.CollectionReturn;
public class CollectionReturnReader extends CollectionReferenceReader implements ReturnReader {
private final CollectionReturn collectionReturn;
public CollectionReturnReader(CollectionReturn collectionReturn) {
super( collectionReturn );
public CollectionReturnReader(CollectionReturn collectionReturn, CollectionReferenceAliases aliases) {
super( collectionReturn, aliases );
this.collectionReturn = collectionReturn;
}

View File

@ -26,30 +26,25 @@ package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.Type;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.*;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
/**
* Encapsulates the logic for reading a single entity identifier from a JDBC ResultSet, including support for fetches
@ -57,12 +52,12 @@ import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingCont
*
* @author Steve Ebersole
*/
class EntityIdentifierReaderImpl implements EntityIdentifierReader {
public class EntityIdentifierReaderImpl implements EntityIdentifierReader {
private static final Logger log = CoreLogging.logger( EntityIdentifierReaderImpl.class );
private final EntityReference entityReference;
private List<EntityReferenceReader> identifierFetchReaders;
private final EntityReferenceAliases aliases;
private final List<EntityReferenceReader> identifierFetchReaders;
private final boolean isReturn;
private final Type identifierType;
@ -72,49 +67,15 @@ class EntityIdentifierReaderImpl implements EntityIdentifierReader {
*
* @param entityReference The entity reference for which we will be reading the identifier.
*/
EntityIdentifierReaderImpl(EntityReference entityReference) {
public EntityIdentifierReaderImpl(
EntityReference entityReference,
EntityReferenceAliases aliases,
List<EntityReferenceReader> identifierFetchReaders) {
this.entityReference = entityReference;
this.aliases = aliases;
this.isReturn = EntityReturn.class.isInstance( entityReference );
this.identifierType = entityReference.getEntityPersister().getIdentifierType();
identifierFetchReaders = collectIdentifierFetchReaders();
}
private List<EntityReferenceReader> collectIdentifierFetchReaders() {
if ( ! identifierType.isComponentType() ) {
return Collections.emptyList();
}
final Fetch[] fetches = entityReference.getIdentifierDescription().getFetches();
if ( fetches == null || fetches.length == 0 ) {
return Collections.emptyList();
}
final List<EntityReferenceReader> readers = new ArrayList<EntityReferenceReader>();
for ( Fetch fetch : fetches ) {
collectIdentifierFetchReaders( readers, fetch );
}
return readers;
}
private void collectIdentifierFetchReaders(List<EntityReferenceReader> readers, Fetch fetch) {
if ( CompositeFetch.class.isInstance( fetch ) ) {
for ( Fetch subFetch : ( (CompositeFetch) fetch).getFetches() ) {
collectIdentifierFetchReaders( readers, subFetch );
}
}
else if ( ! EntityFetch.class.isInstance( fetch ) ) {
throw new IllegalStateException(
String.format(
"non-entity (and non-composite) fetch [%s] was found as part of entity identifier : %s",
fetch,
entityReference.getEntityPersister().getEntityName()
)
);
}
else {
final EntityReference fetchedEntityReference = (EntityReference) fetch;
readers.add( new EntityReferenceReader( fetchedEntityReference ) );
}
this.identifierFetchReaders = identifierFetchReaders;
}
@Override
@ -196,10 +157,7 @@ class EntityIdentifierReaderImpl implements EntityIdentifierReader {
}
}
else {
columnNames = context.getAliasResolutionContext()
.resolveAliases( entityReference )
.getColumnAliases()
.getSuffixedKeyAliases();
columnNames = aliases.getColumnAliases().getSuffixedKeyAliases();
}
try {
@ -282,13 +240,13 @@ class EntityIdentifierReaderImpl implements EntityIdentifierReader {
public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// resolve fetched state from the identifier first
for ( EntityReferenceReader reader : identifierFetchReaders ) {
reader.resolveEntityKey( resultSet, context );
}
for ( EntityReferenceReader reader : identifierFetchReaders ) {
reader.hydrateEntityState( resultSet, context );
}
// // resolve fetched state from the identifier first
// for ( EntityReferenceReader reader : identifierFetchReaders ) {
// reader.resolveEntityKey( resultSet, context );
// }
// for ( EntityReferenceReader reader : identifierFetchReaders ) {
// reader.hydrateEntityState( resultSet, context );
// }
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );

View File

@ -47,7 +47,6 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
@ -61,22 +60,22 @@ public class EntityReferenceReader {
private static final Logger log = CoreLogging.logger( EntityReferenceReader.class );
private final EntityReference entityReference;
private final EntityReferenceAliases entityReferenceAliases;
private final EntityIdentifierReader identifierReader;
private final boolean isReturn;
protected EntityReferenceReader(EntityReference entityReference, EntityIdentifierReader identifierReader) {
public EntityReferenceReader(
EntityReference entityReference,
EntityReferenceAliases entityReferenceAliases,
EntityIdentifierReader identifierReader) {
this.entityReference = entityReference;
this.entityReferenceAliases = entityReferenceAliases;
this.identifierReader = identifierReader;
this.isReturn = EntityReturn.class.isInstance( entityReference );
}
public EntityReferenceReader(EntityReference entityReference) {
this( entityReference, new EntityIdentifierReaderImpl( entityReference ) );
}
public EntityReference getEntityReference() {
return entityReference;
}
@ -243,7 +242,6 @@ public class EntityReferenceReader {
final EntityPersister rootEntityPersister = context.getSession().getFactory().getEntityPersister(
concreteEntityPersister.getRootEntityName()
);
final EntityReferenceAliases aliases = context.getAliasResolutionContext().resolveAliases( entityReference );
final Object[] values;
try {
values = concreteEntityPersister.hydrate(
@ -252,8 +250,8 @@ public class EntityReferenceReader {
entityInstance,
(Loadable) entityReference.getEntityPersister(),
concreteEntityPersister == rootEntityPersister
? aliases.getColumnAliases().getSuffixedPropertyAliases()
: aliases.getColumnAliases().getSuffixedPropertyAliases( concreteEntityPersister ),
? entityReferenceAliases.getColumnAliases().getSuffixedPropertyAliases()
: entityReferenceAliases.getColumnAliases().getSuffixedPropertyAliases( concreteEntityPersister ),
context.getLoadPlan().areLazyAttributesForceFetched(),
context.getSession()
);
@ -269,7 +267,9 @@ public class EntityReferenceReader {
final Object rowId;
try {
rowId = concreteEntityPersister.hasRowId() ? resultSet.getObject( aliases.getColumnAliases().getRowIdAlias() ) : null;
rowId = concreteEntityPersister.hasRowId()
? resultSet.getObject( entityReferenceAliases.getColumnAliases().getRowIdAlias() )
: null;
}
catch (SQLException e) {
throw context.getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
@ -332,7 +332,7 @@ public class EntityReferenceReader {
try {
discriminatorValue = loadable.getDiscriminatorType().nullSafeGet(
resultSet,
context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases().getSuffixedDiscriminatorAlias(),
entityReferenceAliases.getColumnAliases().getSuffixedDiscriminatorAlias(),
context.getSession(),
null
);
@ -377,7 +377,7 @@ public class EntityReferenceReader {
context.getSession(),
resultSet,
entityReference.getEntityPersister(),
context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases(),
entityReferenceAliases.getColumnAliases(),
entityKey,
existing
);

View File

@ -30,6 +30,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.proxy.HibernateProxy;
@ -41,8 +42,8 @@ import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingCont
public class EntityReturnReader extends EntityReferenceReader implements ReturnReader {
private final EntityReturn entityReturn;
public EntityReturnReader(EntityReturn entityReturn) {
super( entityReturn );
public EntityReturnReader(EntityReturn entityReturn, EntityReferenceAliases aliases, EntityIdentifierReader identifierReader) {
super( entityReturn, aliases, identifierReader );
this.entityReturn = entityReturn;
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.loader.plan.exec.process.internal;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
@ -32,15 +33,12 @@ import org.hibernate.loader.plan.spi.EntityReference;
public class OneToOneFetchReader extends EntityReferenceReader {
private final EntityReference ownerEntityReference;
public OneToOneFetchReader(EntityFetch entityFetch, EntityReference ownerEntityReference) {
super( entityFetch, new OneToOneFetchIdentifierReader( entityFetch, ownerEntityReference ) );
public OneToOneFetchReader(
EntityFetch entityFetch,
EntityReferenceAliases aliases,
EntityIdentifierReader identifierReader,
EntityReference ownerEntityReference) {
super( entityFetch, aliases, identifierReader );
this.ownerEntityReference = ownerEntityReference;
}
private static class OneToOneFetchIdentifierReader extends EntityIdentifierReaderImpl {
public OneToOneFetchIdentifierReader(EntityFetch oneToOne, EntityReference ownerEntityReference) {
super( oneToOne );
}
}
}

View File

@ -92,7 +92,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
private final boolean shouldReturnProxies;
private final QueryParameters queryParameters;
private final NamedParameterContext namedParameterContext;
private final AliasResolutionContext aliasResolutionContext;
private final boolean hadSubselectFetches;
private List<HydratedEntityRegistration> currentRowHydratedEntityRegistrationList;
@ -121,7 +120,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
* @param shouldReturnProxies
* @param queryParameters
* @param namedParameterContext
* @param aliasResolutionContext
* @param hadSubselectFetches
*/
public ResultSetProcessingContextImpl(
@ -134,7 +132,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
boolean shouldReturnProxies,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
AliasResolutionContext aliasResolutionContext,
boolean hadSubselectFetches) {
this.resultSet = resultSet;
this.session = session;
@ -145,7 +142,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
this.shouldReturnProxies = shouldReturnProxies;
this.queryParameters = queryParameters;
this.namedParameterContext = namedParameterContext;
this.aliasResolutionContext = aliasResolutionContext;
this.hadSubselectFetches = hadSubselectFetches;
if ( shouldUseOptionalEntityInformation ) {
@ -318,405 +314,400 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
);
}
@Override
public AliasResolutionContext getAliasResolutionContext() {
return aliasResolutionContext;
}
@Override
public void checkVersion(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) {
final Object version = session.getPersistenceContext().getEntry( entityInstance ).getVersion();
if ( version != null ) {
//null version means the object is in the process of being loaded somewhere else in the ResultSet
VersionType versionType = persister.getVersionType();
final Object currentVersion;
try {
currentVersion = versionType.nullSafeGet(
resultSet,
entityAliases.getSuffixedVersionAliases(),
session,
null
);
}
catch (SQLException e) {
throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read version value from result set"
);
}
if ( !versionType.isEqual( version, currentVersion ) ) {
if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
session.getFactory().getStatisticsImplementor().optimisticFailure( persister.getEntityName() );
}
throw new StaleObjectStateException( persister.getEntityName(), entityKey.getIdentifier() );
}
}
}
@Override
public String getConcreteEntityTypeName(
final ResultSet rs,
final EntityPersister persister,
final EntityAliases entityAliases,
final EntityKey entityKey) {
final Loadable loadable = (Loadable) persister;
if ( ! loadable.hasSubclasses() ) {
return persister.getEntityName();
}
final Object discriminatorValue;
try {
discriminatorValue = loadable.getDiscriminatorType().nullSafeGet(
rs,
entityAliases.getSuffixedDiscriminatorAlias(),
session,
null
);
}
catch (SQLException e) {
throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read discriminator value from ResultSet"
);
}
final String result = loadable.getSubclassForDiscriminatorValue( discriminatorValue );
if ( result == null ) {
// whoops! we got an instance of another class hierarchy branch
throw new WrongClassException(
"Discriminator: " + discriminatorValue,
entityKey.getIdentifier(),
persister.getEntityName()
);
}
return result;
}
@Override
public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext) {
final Object existing = getSession().getEntityUsingInterceptor( entityKey );
if ( existing != null ) {
if ( !entityKeyContext.getEntityPersister().isInstance( existing ) ) {
throw new WrongClassException(
"loaded object was of wrong class " + existing.getClass(),
entityKey.getIdentifier(),
entityKeyContext.getEntityPersister().getEntityName()
);
}
final LockMode requestedLockMode = entityKeyContext.getLockMode() == null
? LockMode.NONE
: entityKeyContext.getLockMode();
if ( requestedLockMode != LockMode.NONE ) {
final LockMode currentLockMode = getSession().getPersistenceContext().getEntry( existing ).getLockMode();
final boolean isVersionCheckNeeded = entityKeyContext.getEntityPersister().isVersioned()
&& currentLockMode.lessThan( requestedLockMode );
// we don't need to worry about existing version being uninitialized because this block isn't called
// by a re-entrant load (re-entrant loads *always* have lock mode NONE)
if ( isVersionCheckNeeded ) {
//we only check the version when *upgrading* lock modes
checkVersion(
resultSet,
entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey,
existing
);
//we need to upgrade the lock mode to the mode requested
getSession().getPersistenceContext().getEntry( existing ).setLockMode( requestedLockMode );
}
}
return existing;
}
else {
final String concreteEntityTypeName = getConcreteEntityTypeName(
resultSet,
entityKeyContext.getEntityPersister(),
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
entityKey
);
final Object entityInstance;
// if ( suppliedOptionalEntityKey != null && entityKey.equals( suppliedOptionalEntityKey ) ) {
// // its the given optional object
// entityInstance = queryParameters.getOptionalObject();
// @Override
// public void checkVersion(
// ResultSet resultSet,
// EntityPersister persister,
// EntityAliases entityAliases,
// EntityKey entityKey,
// Object entityInstance) {
// final Object version = session.getPersistenceContext().getEntry( entityInstance ).getVersion();
//
// if ( version != null ) {
// //null version means the object is in the process of being loaded somewhere else in the ResultSet
// VersionType versionType = persister.getVersionType();
// final Object currentVersion;
// try {
// currentVersion = versionType.nullSafeGet(
// resultSet,
// entityAliases.getSuffixedVersionAliases(),
// session,
// null
// );
// }
// else {
// instantiate a new instance
entityInstance = session.instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
// catch (SQLException e) {
// throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
// e,
// "Could not read version value from result set"
// );
// }
FetchStrategy fetchStrategy = null;
final EntityReference entityReference = entityKeyContext.getEntityReference();
if ( EntityFetch.class.isInstance( entityReference ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
fetchStrategy = fetch.getFetchStrategy();
}
//need to hydrate it.
// grab its state from the ResultSet and keep it in the Session
// (but don't yet initialize the object itself)
// note that we acquire LockMode.READ even if it was not requested
final LockMode requestedLockMode = entityKeyContext.getLockMode() == null
? LockMode.NONE
: entityKeyContext.getLockMode();
final LockMode acquiredLockMode = requestedLockMode == LockMode.NONE
? LockMode.READ
: requestedLockMode;
loadFromResultSet(
resultSet,
entityInstance,
concreteEntityTypeName,
entityKey,
aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
acquiredLockMode,
entityKeyContext.getEntityPersister(),
fetchStrategy,
true,
entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
);
// materialize associations (and initialize the object) later
registerHydratedEntity( entityKeyContext.getEntityReference(), entityKey, entityInstance );
return entityInstance;
}
}
@Override
public void loadFromResultSet(
ResultSet resultSet,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
EntityAliases entityAliases,
LockMode acquiredLockMode,
EntityPersister rootPersister,
FetchStrategy fetchStrategy,
boolean eagerFetch,
EntityType associationType) {
final Serializable id = entityKey.getIdentifier();
// Get the persister for the _subclass_
final Loadable persister = (Loadable) getSession().getFactory().getEntityPersister( concreteEntityTypeName );
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
"Initializing object from ResultSet: {0}",
MessageHelper.infoString(
persister,
id,
getSession().getFactory()
)
);
}
// add temp entry so that the next step is circular-reference
// safe - only needed because some types don't take proper
// advantage of two-phase-load (esp. components)
TwoPhaseLoad.addUninitializedEntity(
entityKey,
entityInstance,
persister,
acquiredLockMode,
!forceFetchLazyAttributes,
session
);
// This is not very nice (and quite slow):
final String[][] cols = persister == rootPersister ?
entityAliases.getSuffixedPropertyAliases() :
entityAliases.getSuffixedPropertyAliases(persister);
final Object[] values;
try {
values = persister.hydrate(
resultSet,
id,
entityInstance,
(Loadable) rootPersister,
cols,
loadPlan.areLazyAttributesForceFetched(),
session
);
}
catch (SQLException e) {
throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read entity state from ResultSet : " + entityKey
);
}
final Object rowId;
try {
rowId = persister.hasRowId() ? resultSet.getObject( entityAliases.getRowIdAlias() ) : null;
}
catch (SQLException e) {
throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not read entity row-id from ResultSet : " + entityKey
);
}
if ( associationType != null ) {
String ukName = associationType.getRHSUniqueKeyPropertyName();
if ( ukName != null ) {
final int index = ( (UniqueKeyLoadable) persister ).getPropertyIndex( ukName );
final Type type = persister.getPropertyTypes()[index];
// polymorphism not really handled completely correctly,
// perhaps...well, actually its ok, assuming that the
// entity name used in the lookup is the same as the
// the one used here, which it will be
EntityUniqueKey euk = new EntityUniqueKey(
rootPersister.getEntityName(), //polymorphism comment above
ukName,
type.semiResolve( values[index], session, entityInstance ),
type,
persister.getEntityMode(),
session.getFactory()
);
session.getPersistenceContext().addEntity( euk, entityInstance );
}
}
TwoPhaseLoad.postHydrate(
persister,
id,
values,
rowId,
entityInstance,
acquiredLockMode,
!loadPlan.areLazyAttributesForceFetched(),
session
);
}
public void readCollectionElements(final Object[] row) {
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void handleCollectionReturn(CollectionReturn rootCollectionReturn) {
readCollectionElement(
null,
null,
rootCollectionReturn.getCollectionPersister(),
aliasResolutionContext.resolveAliases( rootCollectionReturn ).getCollectionColumnAliases(),
resultSet,
session
);
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
// TODO: determine which element is the owner.
final Object owner = row[ 0 ];
readCollectionElement(
owner,
collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
collectionFetch.getCollectionPersister(),
aliasResolutionContext.resolveAliases( collectionFetch ).getCollectionColumnAliases(),
resultSet,
session
);
}
private void readCollectionElement(
final Object optionalOwner,
final Serializable optionalKey,
final CollectionPersister persister,
final CollectionAliases descriptor,
final ResultSet rs,
final SessionImplementor session) {
try {
final PersistenceContext persistenceContext = session.getPersistenceContext();
final Serializable collectionRowKey = (Serializable) persister.readKey(
rs,
descriptor.getSuffixedKeyAliases(),
session
);
if ( collectionRowKey != null ) {
// we found a collection element in the result set
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Found row of collection: %s",
MessageHelper.collectionInfoString( persister, collectionRowKey, session.getFactory() ) );
}
Object owner = optionalOwner;
if ( owner == null ) {
owner = persistenceContext.getCollectionOwner( collectionRowKey, persister );
if ( owner == null ) {
//TODO: This is assertion is disabled because there is a bug that means the
// original owner of a transient, uninitialized collection is not known
// if the collection is re-referenced by a different object associated
// with the current Session
//throw new AssertionFailure("bug loading unowned collection");
}
}
PersistentCollection rowCollection = persistenceContext.getLoadContexts()
.getCollectionLoadContext( rs )
.getLoadingCollection( persister, collectionRowKey );
if ( rowCollection != null ) {
rowCollection.readFrom( rs, persister, descriptor, owner );
}
}
else if ( optionalKey != null ) {
// we did not find a collection element in the result set, so we
// ensure that a collection is created with the owner's identifier,
// since what we have is an empty collection
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Result set contains (possibly empty) collection: %s",
MessageHelper.collectionInfoString( persister, optionalKey, session.getFactory() ) );
}
persistenceContext.getLoadContexts()
.getCollectionLoadContext( rs )
.getLoadingCollection( persister, optionalKey ); // handle empty collection
}
// else no collection element, but also no owner
}
catch ( SQLException sqle ) {
// TODO: would be nice to have the SQL string that failed...
throw session.getFactory().getSQLExceptionHelper().convert(
sqle,
"could not read next row of results"
);
}
}
}
);
}
//
// if ( !versionType.isEqual( version, currentVersion ) ) {
// if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
// session.getFactory().getStatisticsImplementor().optimisticFailure( persister.getEntityName() );
// }
// throw new StaleObjectStateException( persister.getEntityName(), entityKey.getIdentifier() );
// }
// }
// }
//
// @Override
// public String getConcreteEntityTypeName(
// final ResultSet rs,
// final EntityPersister persister,
// final EntityAliases entityAliases,
// final EntityKey entityKey) {
//
// final Loadable loadable = (Loadable) persister;
// if ( ! loadable.hasSubclasses() ) {
// return persister.getEntityName();
// }
//
// final Object discriminatorValue;
// try {
// discriminatorValue = loadable.getDiscriminatorType().nullSafeGet(
// rs,
// entityAliases.getSuffixedDiscriminatorAlias(),
// session,
// null
// );
// }
// catch (SQLException e) {
// throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
// e,
// "Could not read discriminator value from ResultSet"
// );
// }
//
// final String result = loadable.getSubclassForDiscriminatorValue( discriminatorValue );
//
// if ( result == null ) {
// // whoops! we got an instance of another class hierarchy branch
// throw new WrongClassException(
// "Discriminator: " + discriminatorValue,
// entityKey.getIdentifier(),
// persister.getEntityName()
// );
// }
//
// return result;
// }
//
// @Override
// public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext) {
// final Object existing = getSession().getEntityUsingInterceptor( entityKey );
//
// if ( existing != null ) {
// if ( !entityKeyContext.getEntityPersister().isInstance( existing ) ) {
// throw new WrongClassException(
// "loaded object was of wrong class " + existing.getClass(),
// entityKey.getIdentifier(),
// entityKeyContext.getEntityPersister().getEntityName()
// );
// }
//
// final LockMode requestedLockMode = entityKeyContext.getLockMode() == null
// ? LockMode.NONE
// : entityKeyContext.getLockMode();
//
// if ( requestedLockMode != LockMode.NONE ) {
// final LockMode currentLockMode = getSession().getPersistenceContext().getEntry( existing ).getLockMode();
// final boolean isVersionCheckNeeded = entityKeyContext.getEntityPersister().isVersioned()
// && currentLockMode.lessThan( requestedLockMode );
//
// // we don't need to worry about existing version being uninitialized because this block isn't called
// // by a re-entrant load (re-entrant loads *always* have lock mode NONE)
// if ( isVersionCheckNeeded ) {
// //we only check the version when *upgrading* lock modes
// checkVersion(
// resultSet,
// entityKeyContext.getEntityPersister(),
// aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
// entityKey,
// existing
// );
// //we need to upgrade the lock mode to the mode requested
// getSession().getPersistenceContext().getEntry( existing ).setLockMode( requestedLockMode );
// }
// }
//
// return existing;
// }
// else {
// final String concreteEntityTypeName = getConcreteEntityTypeName(
// resultSet,
// entityKeyContext.getEntityPersister(),
// aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
// entityKey
// );
//
// final Object entityInstance;
//// if ( suppliedOptionalEntityKey != null && entityKey.equals( suppliedOptionalEntityKey ) ) {
//// // its the given optional object
//// entityInstance = queryParameters.getOptionalObject();
//// }
//// else {
// // instantiate a new instance
// entityInstance = session.instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
//// }
//
// FetchStrategy fetchStrategy = null;
// final EntityReference entityReference = entityKeyContext.getEntityReference();
// if ( EntityFetch.class.isInstance( entityReference ) ) {
// final EntityFetch fetch = (EntityFetch) entityReference;
// fetchStrategy = fetch.getFetchStrategy();
// }
//
// //need to hydrate it.
//
// // grab its state from the ResultSet and keep it in the Session
// // (but don't yet initialize the object itself)
// // note that we acquire LockMode.READ even if it was not requested
// final LockMode requestedLockMode = entityKeyContext.getLockMode() == null
// ? LockMode.NONE
// : entityKeyContext.getLockMode();
// final LockMode acquiredLockMode = requestedLockMode == LockMode.NONE
// ? LockMode.READ
// : requestedLockMode;
//
// loadFromResultSet(
// resultSet,
// entityInstance,
// concreteEntityTypeName,
// entityKey,
// aliasResolutionContext.resolveAliases( entityKeyContext.getEntityReference() ).getColumnAliases(),
// acquiredLockMode,
// entityKeyContext.getEntityPersister(),
// fetchStrategy,
// true,
// entityKeyContext.getEntityPersister().getEntityMetamodel().getEntityType()
// );
//
// // materialize associations (and initialize the object) later
// registerHydratedEntity( entityKeyContext.getEntityReference(), entityKey, entityInstance );
//
// return entityInstance;
// }
// }
//
// @Override
// public void loadFromResultSet(
// ResultSet resultSet,
// Object entityInstance,
// String concreteEntityTypeName,
// EntityKey entityKey,
// EntityAliases entityAliases,
// LockMode acquiredLockMode,
// EntityPersister rootPersister,
// FetchStrategy fetchStrategy,
// boolean eagerFetch,
// EntityType associationType) {
//
// final Serializable id = entityKey.getIdentifier();
//
// // Get the persister for the _subclass_
// final Loadable persister = (Loadable) getSession().getFactory().getEntityPersister( concreteEntityTypeName );
//
// if ( LOG.isTraceEnabled() ) {
// LOG.tracev(
// "Initializing object from ResultSet: {0}",
// MessageHelper.infoString(
// persister,
// id,
// getSession().getFactory()
// )
// );
// }
//
// // add temp entry so that the next step is circular-reference
// // safe - only needed because some types don't take proper
// // advantage of two-phase-load (esp. components)
// TwoPhaseLoad.addUninitializedEntity(
// entityKey,
// entityInstance,
// persister,
// acquiredLockMode,
// !forceFetchLazyAttributes,
// session
// );
//
// // This is not very nice (and quite slow):
// final String[][] cols = persister == rootPersister ?
// entityAliases.getSuffixedPropertyAliases() :
// entityAliases.getSuffixedPropertyAliases(persister);
//
// final Object[] values;
// try {
// values = persister.hydrate(
// resultSet,
// id,
// entityInstance,
// (Loadable) rootPersister,
// cols,
// loadPlan.areLazyAttributesForceFetched(),
// session
// );
// }
// catch (SQLException e) {
// throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
// e,
// "Could not read entity state from ResultSet : " + entityKey
// );
// }
//
// final Object rowId;
// try {
// rowId = persister.hasRowId() ? resultSet.getObject( entityAliases.getRowIdAlias() ) : null;
// }
// catch (SQLException e) {
// throw getSession().getFactory().getJdbcServices().getSqlExceptionHelper().convert(
// e,
// "Could not read entity row-id from ResultSet : " + entityKey
// );
// }
//
// if ( associationType != null ) {
// String ukName = associationType.getRHSUniqueKeyPropertyName();
// if ( ukName != null ) {
// final int index = ( (UniqueKeyLoadable) persister ).getPropertyIndex( ukName );
// final Type type = persister.getPropertyTypes()[index];
//
// // polymorphism not really handled completely correctly,
// // perhaps...well, actually its ok, assuming that the
// // entity name used in the lookup is the same as the
// // the one used here, which it will be
//
// EntityUniqueKey euk = new EntityUniqueKey(
// rootPersister.getEntityName(), //polymorphism comment above
// ukName,
// type.semiResolve( values[index], session, entityInstance ),
// type,
// persister.getEntityMode(),
// session.getFactory()
// );
// session.getPersistenceContext().addEntity( euk, entityInstance );
// }
// }
//
// TwoPhaseLoad.postHydrate(
// persister,
// id,
// values,
// rowId,
// entityInstance,
// acquiredLockMode,
// !loadPlan.areLazyAttributesForceFetched(),
// session
// );
//
// }
//
// public void readCollectionElements(final Object[] row) {
// LoadPlanVisitor.visit(
// loadPlan,
// new LoadPlanVisitationStrategyAdapter() {
// @Override
// public void handleCollectionReturn(CollectionReturn rootCollectionReturn) {
// readCollectionElement(
// null,
// null,
// rootCollectionReturn.getCollectionPersister(),
// aliasResolutionContext.resolveAliases( rootCollectionReturn ).getCollectionColumnAliases(),
// resultSet,
// session
// );
// }
//
// @Override
// public void startingCollectionFetch(CollectionFetch collectionFetch) {
// // TODO: determine which element is the owner.
// final Object owner = row[ 0 ];
// readCollectionElement(
// owner,
// collectionFetch.getCollectionPersister().getCollectionType().getKeyOfOwner( owner, session ),
// collectionFetch.getCollectionPersister(),
// aliasResolutionContext.resolveAliases( collectionFetch ).getCollectionColumnAliases(),
// resultSet,
// session
// );
// }
//
// private void readCollectionElement(
// final Object optionalOwner,
// final Serializable optionalKey,
// final CollectionPersister persister,
// final CollectionAliases descriptor,
// final ResultSet rs,
// final SessionImplementor session) {
//
// try {
// final PersistenceContext persistenceContext = session.getPersistenceContext();
//
// final Serializable collectionRowKey = (Serializable) persister.readKey(
// rs,
// descriptor.getSuffixedKeyAliases(),
// session
// );
//
// if ( collectionRowKey != null ) {
// // we found a collection element in the result set
//
// if ( LOG.isDebugEnabled() ) {
// LOG.debugf( "Found row of collection: %s",
// MessageHelper.collectionInfoString( persister, collectionRowKey, session.getFactory() ) );
// }
//
// Object owner = optionalOwner;
// if ( owner == null ) {
// owner = persistenceContext.getCollectionOwner( collectionRowKey, persister );
// if ( owner == null ) {
// //TODO: This is assertion is disabled because there is a bug that means the
// // original owner of a transient, uninitialized collection is not known
// // if the collection is re-referenced by a different object associated
// // with the current Session
// //throw new AssertionFailure("bug loading unowned collection");
// }
// }
//
// PersistentCollection rowCollection = persistenceContext.getLoadContexts()
// .getCollectionLoadContext( rs )
// .getLoadingCollection( persister, collectionRowKey );
//
// if ( rowCollection != null ) {
// rowCollection.readFrom( rs, persister, descriptor, owner );
// }
//
// }
// else if ( optionalKey != null ) {
// // we did not find a collection element in the result set, so we
// // ensure that a collection is created with the owner's identifier,
// // since what we have is an empty collection
//
// if ( LOG.isDebugEnabled() ) {
// LOG.debugf( "Result set contains (possibly empty) collection: %s",
// MessageHelper.collectionInfoString( persister, optionalKey, session.getFactory() ) );
// }
//
// persistenceContext.getLoadContexts()
// .getCollectionLoadContext( rs )
// .getLoadingCollection( persister, optionalKey ); // handle empty collection
//
// }
//
// // else no collection element, but also no owner
// }
// catch ( SQLException sqle ) {
// // TODO: would be nice to have the SQL string that failed...
// throw session.getFactory().getSQLExceptionHelper().convert(
// sqle,
// "could not read next row of results"
// );
// }
// }
//
// }
// );
// }
@Override
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance) {

View File

@ -33,31 +33,16 @@ import org.jboss.logging.Logger;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.ScalarReturn;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadPlanAdvisor;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.process.spi.ScrollableResultSetProcessor;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
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.RowReader;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.transform.ResultTransformer;
@ -68,40 +53,19 @@ import org.hibernate.transform.ResultTransformer;
public class ResultSetProcessorImpl implements ResultSetProcessor {
private static final Logger LOG = Logger.getLogger( ResultSetProcessorImpl.class );
private final LoadPlan baseLoadPlan;
private final LoadPlan loadPlan;
private final RowReader rowReader;
private final boolean shouldUseOptionalEntityInstance;
private final boolean hadSubselectFetches;
public ResultSetProcessorImpl(
LoadPlan loadPlan,
boolean shouldUseOptionalEntityInstance) {
this.baseLoadPlan = loadPlan;
this.rowReader = buildRowReader( loadPlan );
this.shouldUseOptionalEntityInstance = shouldUseOptionalEntityInstance;
LocalVisitationStrategy strategy = new LocalVisitationStrategy();
LoadPlanVisitor.visit( loadPlan, strategy );
this.hadSubselectFetches = strategy.hadSubselectFetches;
public ResultSetProcessorImpl(LoadPlan loadPlan, RowReader rowReader, boolean hadSubselectFetches) {
this.loadPlan = loadPlan;
this.rowReader = rowReader;
this.hadSubselectFetches = hadSubselectFetches;
}
private RowReader buildRowReader(LoadPlan loadPlan) {
switch ( loadPlan.getDisposition() ) {
case MIXED: {
return new MixedReturnRowReader( loadPlan );
}
case ENTITY_LOADER: {
return new EntityLoaderRowReader( loadPlan );
}
case COLLECTION_INITIALIZER: {
return new CollectionInitializerRowReader( loadPlan );
}
default: {
throw new IllegalStateException( "Unrecognized LoadPlan Return dispostion : " + loadPlan.getDisposition() );
}
}
public RowReader getRowReader() {
return rowReader;
}
@Override
@ -112,22 +76,15 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
@Override
public List extractResults(
LoadPlanAdvisor loadPlanAdvisor,
ResultSet resultSet,
final SessionImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
AliasResolutionContext aliasResolutionContext,
boolean returnProxies,
boolean readOnly,
ResultTransformer forcedResultTransformer,
List<AfterLoadAction> afterLoadActionList) throws SQLException {
final LoadPlan loadPlan = loadPlanAdvisor.advise( this.baseLoadPlan );
if ( loadPlan == null ) {
throw new IllegalStateException( "LoadPlanAdvisor returned null" );
}
handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session );
final int maxRows;
@ -159,7 +116,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
returnProxies,
queryParameters,
namedParameterContext,
aliasResolutionContext,
hadSubselectFetches
);
@ -221,323 +177,125 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
}
private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
private boolean hadSubselectFetches = false;
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
// only collections are currently supported for subselect fetching.
// hadSubselectFetches = hadSubselectFetches
// || entityFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
hadSubselectFetches = hadSubselectFetches
|| collectionFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
}
}
private static interface RowReader {
Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException;
}
private static abstract class AbstractRowReader implements RowReader {
@Override
public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final List<EntityReferenceReader> entityReferenceReaders = getEntityReferenceReaders();
final List<CollectionReferenceReader> collectionReferenceReaders = getCollectionReferenceReaders();
final boolean hasEntityReferenceReaders = entityReferenceReaders != null && entityReferenceReaders.size() > 0;
final boolean hasCollectionReferenceReaders = collectionReferenceReaders != null && collectionReferenceReaders.size() > 0;
if ( hasEntityReferenceReaders ) {
// 1) allow entity references to resolve identifiers (in 2 steps)
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateIdentifier( resultSet, context );
}
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.resolveEntityKey( resultSet, context );
}
// 2) allow entity references to resolve their hydrated state and entity instance
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateEntityState( resultSet, context );
}
}
// 3) read the logical row
Object logicalRow = readLogicalRow( resultSet, context );
// 4) allow entities and collection to read their elements
if ( hasEntityReferenceReaders ) {
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.finishUpRow( resultSet, context );
}
}
if ( hasCollectionReferenceReaders ) {
for ( CollectionReferenceReader collectionReferenceReader : collectionReferenceReaders ) {
collectionReferenceReader.finishUpRow( resultSet, context );
}
}
return logicalRow;
}
protected abstract List<EntityReferenceReader> getEntityReferenceReaders();
protected abstract List<CollectionReferenceReader> getCollectionReferenceReaders();
protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context)
throws SQLException;
}
private class MixedReturnRowReader extends AbstractRowReader implements RowReader {
private final List<ReturnReader> returnReaders;
private List<EntityReferenceReader> entityReferenceReaders = new ArrayList<EntityReferenceReader>();
private List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
private final int numberOfReturns;
public MixedReturnRowReader(LoadPlan loadPlan) {
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
final List<ReturnReader> readers = new ArrayList<ReturnReader>();
for ( Return rtn : loadPlan.getReturns() ) {
final ReturnReader returnReader = buildReturnReader( rtn );
if ( EntityReferenceReader.class.isInstance( returnReader ) ) {
entityReferenceReaders.add( (EntityReferenceReader) returnReader );
}
readers.add( returnReader );
}
this.returnReaders = readers;
this.numberOfReturns = readers.size();
}
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
Object[] logicalRow = new Object[ numberOfReturns ];
int pos = 0;
for ( ReturnReader reader : returnReaders ) {
logicalRow[pos] = reader.read( resultSet, context );
pos++;
}
return logicalRow;
}
}
private static ReturnReader buildReturnReader(Return rtn) {
if ( ScalarReturn.class.isInstance( rtn ) ) {
return new ScalarReturnReader( (ScalarReturn) rtn );
}
else if ( EntityReturn.class.isInstance( rtn ) ) {
return new EntityReturnReader( (EntityReturn) rtn );
}
else if ( CollectionReturn.class.isInstance( rtn ) ) {
return new CollectionReturnReader( (CollectionReturn) rtn );
}
else {
throw new IllegalStateException( "Unknown Return type : " + rtn );
}
}
private static interface EntityReferenceReaderListBuildingAccess {
public void add(EntityReferenceReader reader);
}
private static interface CollectionReferenceReaderListBuildingAccess {
public void add(CollectionReferenceReader reader);
}
private class EntityLoaderRowReader extends AbstractRowReader implements RowReader {
private final EntityReturnReader returnReader;
private final List<EntityReferenceReader> entityReferenceReaders = new ArrayList<EntityReferenceReader>();
private List<CollectionReferenceReader> collectionReferenceReaders = null;
public EntityLoaderRowReader(LoadPlan loadPlan) {
final EntityReturn entityReturn = (EntityReturn) loadPlan.getReturns().get( 0 );
this.returnReader = (EntityReturnReader) buildReturnReader( entityReturn );
// final EntityReferenceReaderListBuildingAccess entityReaders = new EntityReferenceReaderListBuildingAccess() {
// @Override
// public void add(EntityReferenceReader reader) {
// entityReferenceReaders.add( reader );
// }
// };
// private class LocalVisitationStrategy extends LoadPlanVisitationStrategyAdapter {
// private boolean hadSubselectFetches = false;
//
// final CollectionReferenceReaderListBuildingAccess collectionReaders = new CollectionReferenceReaderListBuildingAccess() {
// @Override
// public void add(CollectionReferenceReader reader) {
// if ( collectionReferenceReaders == null ) {
// collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
// }
// collectionReferenceReaders.add( reader );
// }
// };
//
// buildFetchReaders( entityReaders, collectionReaders, entityReturn, returnReader );
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
if ( collectionReferenceReaders == null ) {
collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
}
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
entityReferenceReaders.add( returnReader );
}
// private void buildFetchReaders(
// EntityReferenceReaderListBuildingAccess entityReaders,
// CollectionReferenceReaderListBuildingAccess collectionReaders,
// FetchOwner fetchOwner,
// EntityReferenceReader entityReferenceReader) {
// for ( Fetch fetch : fetchOwner.getFetches() ) {
// if ( CollectionFetch.class.isInstance( fetch ) ) {
// final CollectionFetch collectionFetch = (CollectionFetch) fetch;
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// collectionFetch.getIndexGraph(),
// null
// );
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// collectionFetch.getElementGraph(),
// null
// );
// collectionReaders.add( new CollectionReferenceReader( collectionFetch ) );
// }
// else if ( CompositeFetch.class.isInstance( fetch ) ) {
// buildFetchReaders(
// entityReaders,
// collectionReaders,
// (CompositeFetch) fetch,
// entityReferenceReader
// );
// }
// else {
// final EntityFetch entityFetch = (EntityFetch) fetch;
// if ( entityFetch.getFetchedType().isOneToOne() ) {
// // entityReferenceReader should reference the owner still...
// if ( entityReferenceReader == null ) {
// throw new IllegalStateException( "Entity reader for one-to-one fetch owner not known" );
// }
// final EntityReferenceReader fetchReader = new OneToOneFetchReader(
// entityFetch,
// entityReferenceReader.getEntityReference()
// );
// }
// else {
//
// }
// }
// }
// //To change body of created methods use File | Settings | File Templates.
// @Override
// public void startingEntityFetch(EntityFetch entityFetch) {
// // only collections are currently supported for subselect fetching.
// // hadSubselectFetches = hadSubselectFetches
// // || entityFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
// }
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return returnReader.read( resultSet, context );
}
}
private class CollectionInitializerRowReader extends AbstractRowReader implements RowReader {
private final CollectionReturnReader returnReader;
private List<EntityReferenceReader> entityReferenceReaders = null;
private final List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
public CollectionInitializerRowReader(LoadPlan loadPlan) {
returnReader = (CollectionReturnReader) buildReturnReader( loadPlan.getReturns().get( 0 ) );
LoadPlanVisitor.visit(
loadPlan,
new LoadPlanVisitationStrategyAdapter() {
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
if ( entityReferenceReaders == null ) {
entityReferenceReaders = new ArrayList<EntityReferenceReader>();
}
entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
}
}
);
collectionReferenceReaders.add( returnReader );
}
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return returnReader.read( resultSet, context );
}
}
//
// @Override
// public void startingCollectionFetch(CollectionFetch collectionFetch) {
// hadSubselectFetches = hadSubselectFetches
// || collectionFetch.getFetchStrategy().getStyle() == FetchStyle.SUBSELECT;
// }
// }
//
// private class MixedReturnRowReader extends AbstractRowReader implements RowReader {
// private final List<ReturnReader> returnReaders;
// private List<EntityReferenceReader> entityReferenceReaders = new ArrayList<EntityReferenceReader>();
// private List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
//
// private final int numberOfReturns;
//
// public MixedReturnRowReader(LoadPlan loadPlan) {
// LoadPlanVisitor.visit(
// loadPlan,
// new LoadPlanVisitationStrategyAdapter() {
// @Override
// public void startingEntityFetch(EntityFetch entityFetch) {
// entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
// }
//
// @Override
// public void startingCollectionFetch(CollectionFetch collectionFetch) {
// collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
// }
// }
// );
//
// final List<ReturnReader> readers = new ArrayList<ReturnReader>();
//
// for ( Return rtn : loadPlan.getReturns() ) {
// final ReturnReader returnReader = buildReturnReader( rtn );
// if ( EntityReferenceReader.class.isInstance( returnReader ) ) {
// entityReferenceReaders.add( (EntityReferenceReader) returnReader );
// }
// readers.add( returnReader );
// }
//
// this.returnReaders = readers;
// this.numberOfReturns = readers.size();
// }
//
// @Override
// protected List<EntityReferenceReader> getEntityReferenceReaders() {
// return entityReferenceReaders;
// }
//
// @Override
// protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
// return collectionReferenceReaders;
// }
//
// @Override
// protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
// Object[] logicalRow = new Object[ numberOfReturns ];
// int pos = 0;
// for ( ReturnReader reader : returnReaders ) {
// logicalRow[pos] = reader.read( resultSet, context );
// pos++;
// }
// return logicalRow;
// }
// }
//
// private class CollectionInitializerRowReader extends AbstractRowReader implements RowReader {
// private final CollectionReturnReader returnReader;
//
// private List<EntityReferenceReader> entityReferenceReaders = null;
// private final List<CollectionReferenceReader> collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
//
// public CollectionInitializerRowReader(LoadPlan loadPlan) {
// returnReader = (CollectionReturnReader) buildReturnReader( loadPlan.getReturns().get( 0 ) );
//
// LoadPlanVisitor.visit(
// loadPlan,
// new LoadPlanVisitationStrategyAdapter() {
// @Override
// public void startingEntityFetch(EntityFetch entityFetch) {
// if ( entityReferenceReaders == null ) {
// entityReferenceReaders = new ArrayList<EntityReferenceReader>();
// }
// entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) );
// }
//
// @Override
// public void startingCollectionFetch(CollectionFetch collectionFetch) {
// collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) );
// }
// }
// );
//
// collectionReferenceReaders.add( returnReader );
// }
//
// @Override
// protected List<EntityReferenceReader> getEntityReferenceReaders() {
// return entityReferenceReaders;
// }
//
// @Override
// protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
// return collectionReferenceReaders;
// }
//
// @Override
// protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
// return returnReader.read( resultSet, context );
// }
// }
}

View File

@ -35,16 +35,18 @@ import org.hibernate.loader.plan.spi.ScalarReturn;
*/
public class ScalarReturnReader implements ReturnReader {
private final ScalarReturn scalarReturn;
private final String[] aliases;
public ScalarReturnReader(ScalarReturn scalarReturn) {
public ScalarReturnReader(ScalarReturn scalarReturn, String[] aliases) {
this.scalarReturn = scalarReturn;
this.aliases = aliases;
}
@Override
public Object read(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return scalarReturn.getType().nullSafeGet(
resultSet,
context.getAliasResolutionContext().resolveScalarColumnAliases( scalarReturn ),
aliases,
context.getSession(),
null
);

View File

@ -0,0 +1,91 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.exec.process.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan.exec.spi.RowReader;
/**
* @author Steve Ebersole
*/
public abstract class AbstractRowReader implements RowReader {
@Override
public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final List<EntityReferenceReader> entityReferenceReaders = getEntityReferenceReaders();
final List<CollectionReferenceReader> collectionReferenceReaders = getCollectionReferenceReaders();
final boolean hasEntityReferenceReaders = entityReferenceReaders != null && entityReferenceReaders.size() > 0;
final boolean hasCollectionReferenceReaders = collectionReferenceReaders != null && collectionReferenceReaders.size() > 0;
if ( hasEntityReferenceReaders ) {
// 1) allow entity references to resolve identifiers (in 2 steps)
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateIdentifier( resultSet, context );
}
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.resolveEntityKey( resultSet, context );
}
// 2) allow entity references to resolve their hydrated state and entity instance
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.hydrateEntityState( resultSet, context );
}
}
// 3) read the logical row
Object logicalRow = readLogicalRow( resultSet, context );
// 4) allow entities and collection to read their elements
if ( hasEntityReferenceReaders ) {
for ( EntityReferenceReader entityReferenceReader : entityReferenceReaders ) {
entityReferenceReader.finishUpRow( resultSet, context );
}
}
if ( hasCollectionReferenceReaders ) {
for ( CollectionReferenceReader collectionReferenceReader : collectionReferenceReaders ) {
collectionReferenceReader.finishUpRow( resultSet, context );
}
}
return logicalRow;
}
protected abstract List<EntityReferenceReader> getEntityReferenceReaders();
protected abstract List<CollectionReferenceReader> getCollectionReferenceReaders();
protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context)
throws SQLException;
}

View File

@ -129,8 +129,6 @@ public interface ResultSetProcessingContext extends LockModeResolver {
public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch);
public AliasResolutionContext getAliasResolutionContext();
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance);
public static interface EntityKeyResolutionContext {
@ -139,33 +137,33 @@ public interface ResultSetProcessingContext extends LockModeResolver {
public EntityReference getEntityReference();
}
public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext);
// public Object resolveEntityKey(EntityKey entityKey, EntityKeyResolutionContext entityKeyContext);
// should be able to get rid of the methods below here from the interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void checkVersion(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) throws SQLException;
public String getConcreteEntityTypeName(
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey) throws SQLException;
public void loadFromResultSet(
ResultSet resultSet,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
EntityAliases entityAliases,
LockMode acquiredLockMode,
EntityPersister persister,
FetchStrategy fetchStrategy,
boolean eagerFetch,
EntityType associationType) throws SQLException;
// public void checkVersion(
// ResultSet resultSet,
// EntityPersister persister,
// EntityAliases entityAliases,
// EntityKey entityKey,
// Object entityInstance) throws SQLException;
//
// public String getConcreteEntityTypeName(
// ResultSet resultSet,
// EntityPersister persister,
// EntityAliases entityAliases,
// EntityKey entityKey) throws SQLException;
//
// public void loadFromResultSet(
// ResultSet resultSet,
// Object entityInstance,
// String concreteEntityTypeName,
// EntityKey entityKey,
// EntityAliases entityAliases,
// LockMode acquiredLockMode,
// EntityPersister persister,
// FetchStrategy fetchStrategy,
// boolean eagerFetch,
// EntityType associationType) throws SQLException;
}

View File

@ -30,7 +30,6 @@ import java.util.List;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.loader.spi.LoadPlanAdvisor;
import org.hibernate.transform.ResultTransformer;
@ -52,7 +51,6 @@ public interface ResultSetProcessor {
*
* Semi-copy of {@link org.hibernate.loader.Loader#doQuery}, with focus on just the ResultSet processing bit.
*
* @param loadPlanAdvisor A dynamic advisor on the load plan.
* @param resultSet The result set being processed.
* @param session The originating session
* @param queryParameters The "parameters" used to build the query
@ -65,12 +63,10 @@ public interface ResultSetProcessor {
* @throws java.sql.SQLException Indicates a problem access the JDBC ResultSet
*/
public List extractResults(
LoadPlanAdvisor loadPlanAdvisor,
ResultSet resultSet,
SessionImplementor session,
QueryParameters queryParameters,
NamedParameterContext namedParameterContext,
AliasResolutionContext aliasResolutionContext,
boolean returnProxies,
boolean readOnly,
ResultTransformer forcedResultTransformer,

View File

@ -27,7 +27,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Handles reading results from a JDBC ResultSet relating to a single Return object.
* Handles reading a single root Return object
*
* @author Steve Ebersole
*/

View File

@ -23,15 +23,18 @@
*/
package org.hibernate.loader.plan.exec.query.internal;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.internal.Helper;
import org.hibernate.loader.plan.exec.internal.LoadQueryBuilderHelper;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceReader;
import org.hibernate.loader.plan.exec.query.spi.EntityLoadQueryBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.ReaderCollector;
import org.hibernate.loader.plan.exec.spi.RowReader;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.ConditionFragment;
@ -53,7 +56,7 @@ public class EntityLoadQueryBuilderImpl implements EntityLoadQueryBuilder {
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final EntityReturn rootReturn = extractRootReturn( loadPlan );
final EntityReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, EntityReturn.class );
return generateSql(
( (Queryable) rootReturn.getEntityPersister() ).getKeyColumnNames(),
@ -64,28 +67,6 @@ public class EntityLoadQueryBuilderImpl implements EntityLoadQueryBuilder {
);
}
private static EntityReturn extractRootReturn(LoadPlan loadPlan) {
if ( loadPlan.getReturns().size() == 0 ) {
throw new IllegalStateException( "LoadPlan contained no root returns" );
}
else if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "LoadPlan contained more than one root returns" );
}
final Return rootReturn = loadPlan.getReturns().get( 0 );
if ( !EntityReturn.class.isInstance( rootReturn ) ) {
throw new IllegalStateException(
String.format(
"Unexpected LoadPlan root return; expecting %s, but found %s",
EntityReturn.class.getName(),
rootReturn.getClass().getName()
)
);
}
return (EntityReturn) rootReturn;
}
@Override
public String generateSql(
String[] keyColumnNames,
@ -93,10 +74,14 @@ public class EntityLoadQueryBuilderImpl implements EntityLoadQueryBuilder {
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final EntityReturn rootReturn = extractRootReturn( loadPlan );
final EntityReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, EntityReturn.class );
final String[] keyColumnNamesToUse = keyColumnNames != null
? keyColumnNames
: ( (Queryable) rootReturn.getEntityPersister() ).getIdentifierColumnNames();
return generateSql(
keyColumnNames,
keyColumnNamesToUse,
rootReturn,
factory,
buildingParameters,
@ -113,14 +98,31 @@ public class EntityLoadQueryBuilderImpl implements EntityLoadQueryBuilder {
final SelectStatementBuilder select = new SelectStatementBuilder( factory.getDialect() );
// apply root entity return specifics
applyRootReturnSpecifics( select, keyColumnNames, rootReturn, factory, buildingParameters, aliasResolutionContext );
applyRootReturnSpecifics(
select,
keyColumnNames,
rootReturn,
factory,
buildingParameters,
aliasResolutionContext
);
LoadQueryBuilderHelper.applyJoinFetches(
select,
factory,
rootReturn,
buildingParameters,
aliasResolutionContext
aliasResolutionContext,
new ReaderCollector() {
@Override
public void addReader(CollectionReferenceReader collectionReferenceReader) {
}
@Override
public void addReader(EntityReferenceReader entityReferenceReader) {
}
}
);
return select.toStatementString();

View File

@ -23,8 +23,6 @@
*/
package org.hibernate.loader.plan.exec.query.spi;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.LoadPlan;

View File

@ -0,0 +1,315 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.exec.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan.exec.internal.Helper;
import org.hibernate.loader.plan.exec.internal.LoadQueryBuilderHelper;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityIdentifierReaderImpl;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityReturnReader;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.process.spi.AbstractRowReader;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
import static org.hibernate.loader.plan.exec.internal.LoadQueryBuilderHelper.FetchStats;
/**
* Handles interpreting a LoadPlan (for loading of an entity) by:<ul>
* <li>generating the SQL query to perform</li>
* <li>creating the readers needed to read the results from the SQL's ResultSet</li>
* </ul>
*
* @author Steve Ebersole
*/
public class EntityLoadQueryDetails implements LoadQueryDetails {
// todo : keep around the LoadPlan? Any benefit?
private final LoadPlan loadPlan;
private final String sqlStatement;
private final ResultSetProcessor resultSetProcessor;
/**
* Constructs a EntityLoadQueryDetails object from the given inputs.
*
* @param loadPlan The load plan
* @param keyColumnNames The columns to load the entity by (the PK columns or some other unique set of columns)
* @param buildingParameters And influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
* @param factory The SessionFactory
*
* @return The EntityLoadQueryDetails
*/
public static EntityLoadQueryDetails makeForBatching(
LoadPlan loadPlan,
String[] keyColumnNames,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
final int batchSize = buildingParameters.getBatchSize();
final boolean shouldUseOptionalEntityInformation = batchSize == 1;
return new EntityLoadQueryDetails(
loadPlan,
keyColumnNames,
shouldUseOptionalEntityInformation,
buildingParameters,
factory
);
}
protected EntityLoadQueryDetails(
LoadPlan loadPlan,
String[] keyColumnNames,
boolean shouldUseOptionalEntityInformation,
QueryBuildingParameters buildingParameters,
SessionFactoryImplementor factory) {
this.loadPlan = loadPlan;
final SelectStatementBuilder select = new SelectStatementBuilder( factory.getDialect() );
final EntityReturn rootReturn = Helper.INSTANCE.extractRootReturn( loadPlan, EntityReturn.class );
final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( factory );
final ReaderCollectorImpl readerCollector = new ReaderCollectorImpl();
final String[] keyColumnNamesToUse = keyColumnNames != null
? keyColumnNames
: ( (Queryable) rootReturn.getEntityPersister() ).getIdentifierColumnNames();
// apply root entity return specifics
applyRootReturnSpecifics(
select,
keyColumnNamesToUse,
rootReturn,
factory,
buildingParameters,
aliasResolutionContext
);
readerCollector.addReader(
new EntityReturnReader(
rootReturn,
aliasResolutionContext.resolveAliases( rootReturn ),
new EntityIdentifierReaderImpl(
rootReturn,
aliasResolutionContext.resolveAliases( rootReturn ),
Collections.<EntityReferenceReader>emptyList()
)
)
);
FetchStats fetchStats = LoadQueryBuilderHelper.applyJoinFetches(
select,
factory,
rootReturn,
buildingParameters,
aliasResolutionContext,
readerCollector
);
this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl(
loadPlan,
readerCollector.buildRowReader(),
fetchStats.hasSubselectFetches()
);
}
protected void applyRootReturnSpecifics(
SelectStatementBuilder select,
String[] keyColumnNames,
EntityReturn rootReturn,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters,
AliasResolutionContext aliasResolutionContext) {
final String rootAlias = aliasResolutionContext.resolveAliases( rootReturn ).getTableAlias();
final OuterJoinLoadable rootLoadable = (OuterJoinLoadable) rootReturn.getEntityPersister();
final Queryable rootQueryable = (Queryable) rootReturn.getEntityPersister();
applyKeyRestriction( select, rootAlias, keyColumnNames, buildingParameters.getBatchSize() );
select.appendRestrictions(
rootQueryable.filterFragment(
rootAlias,
buildingParameters.getQueryInfluencers().getEnabledFilters()
)
);
select.appendRestrictions( rootLoadable.whereJoinFragment( rootAlias, true, true ) );
select.appendSelectClauseFragment(
rootLoadable.selectFragment(
rootAlias,
aliasResolutionContext.resolveAliases( rootReturn ).getColumnAliases().getSuffix()
)
);
final String fromTableFragment;
if ( buildingParameters.getLockOptions() != null ) {
fromTableFragment = factory.getDialect().appendLockHint(
buildingParameters.getLockOptions(),
rootLoadable.fromTableFragment( rootAlias )
);
select.setLockOptions( buildingParameters.getLockOptions() );
}
else if ( buildingParameters.getLockMode() != null ) {
fromTableFragment = factory.getDialect().appendLockHint(
buildingParameters.getLockMode(),
rootLoadable.fromTableFragment( rootAlias )
);
select.setLockMode( buildingParameters.getLockMode() );
}
else {
fromTableFragment = rootLoadable.fromTableFragment( rootAlias );
}
select.appendFromClauseFragment( fromTableFragment + rootLoadable.fromJoinFragment( rootAlias, true, true ) );
}
private void applyKeyRestriction(SelectStatementBuilder select, String alias, String[] keyColumnNames, int batchSize) {
if ( keyColumnNames.length==1 ) {
// NOT A COMPOSITE KEY
// for batching, use "foo in (?, ?, ?)" for batching
// for no batching, use "foo = ?"
// (that distinction is handled inside InFragment)
final InFragment in = new InFragment().setColumn( alias, keyColumnNames[0] );
for ( int i = 0; i < batchSize; i++ ) {
in.addValue( "?" );
}
select.appendRestrictions( in.toFragmentString() );
}
else {
// A COMPOSITE KEY...
final ConditionFragment keyRestrictionBuilder = new ConditionFragment()
.setTableAlias( alias )
.setCondition( keyColumnNames, "?" );
final String keyRestrictionFragment = keyRestrictionBuilder.toFragmentString();
StringBuilder restrictions = new StringBuilder();
if ( batchSize==1 ) {
// for no batching, use "foo = ? and bar = ?"
restrictions.append( keyRestrictionFragment );
}
else {
// for batching, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )"
restrictions.append( '(' );
DisjunctionFragment df = new DisjunctionFragment();
for ( int i=0; i<batchSize; i++ ) {
df.addCondition( keyRestrictionFragment );
}
restrictions.append( df.toFragmentString() );
restrictions.append( ')' );
}
select.appendRestrictions( restrictions.toString() );
}
}
@Override
public String getSqlStatement() {
return sqlStatement;
}
@Override
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
private static class ReaderCollectorImpl implements ReaderCollector {
private EntityReturnReader rootReturnReader;
private List<EntityReferenceReader> entityReferenceReaders;
private List<CollectionReferenceReader> collectionReferenceReaders;
@Override
public void addReader(CollectionReferenceReader collectionReferenceReader) {
if ( collectionReferenceReaders == null ) {
collectionReferenceReaders = new ArrayList<CollectionReferenceReader>();
}
collectionReferenceReaders.add( collectionReferenceReader );
}
@Override
public void addReader(EntityReferenceReader entityReferenceReader) {
if ( EntityReturnReader.class.isInstance( entityReferenceReader ) ) {
if ( rootReturnReader != null ) {
throw new IllegalStateException( "Root return reader already set" );
}
rootReturnReader = (EntityReturnReader) entityReferenceReader;
}
if ( entityReferenceReaders == null ) {
entityReferenceReaders = new ArrayList<EntityReferenceReader>();
}
entityReferenceReaders.add( entityReferenceReader );
}
public RowReader buildRowReader() {
return new EntityLoaderRowReader( rootReturnReader, entityReferenceReaders, collectionReferenceReaders );
}
}
public static class EntityLoaderRowReader extends AbstractRowReader implements RowReader {
private final EntityReturnReader rootReturnReader;
private final List<EntityReferenceReader> entityReferenceReaders;
private final List<CollectionReferenceReader> collectionReferenceReaders;
public EntityLoaderRowReader(
EntityReturnReader rootReturnReader,
List<EntityReferenceReader> entityReferenceReaders,
List<CollectionReferenceReader> collectionReferenceReaders) {
this.rootReturnReader = rootReturnReader;
this.entityReferenceReaders = entityReferenceReaders != null
? entityReferenceReaders
: Collections.<EntityReferenceReader>emptyList();
this.collectionReferenceReaders = collectionReferenceReaders != null
? collectionReferenceReaders
: Collections.<CollectionReferenceReader>emptyList();
}
@Override
protected List<EntityReferenceReader> getEntityReferenceReaders() {
return entityReferenceReaders;
}
@Override
protected List<CollectionReferenceReader> getCollectionReferenceReaders() {
return collectionReferenceReaders;
}
@Override
protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
return rootReturnReader.read( resultSet, context );
}
}
}

View File

@ -23,89 +23,14 @@
*/
package org.hibernate.loader.plan.exec.spi;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.query.internal.EntityLoadQueryBuilderImpl;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.spi.LoadPlan;
/**
* Wraps a LoadPlan (for an entity load) and exposes details about the query and its execution.
*
* @author Steve Ebersole
*/
public class LoadQueryDetails {
private final SessionFactoryImplementor factory;
private final LoadPlan loadPlan;
public interface LoadQueryDetails {
public String getSqlStatement();
private final AliasResolutionContext aliasResolutionContext;
private final String sqlStatement;
private final ResultSetProcessor resultSetProcessor;
public ResultSetProcessor getResultSetProcessor();
/**
* Constructs a LoadQueryDetails object from the given inputs.
*
*
* @param uniqueKeyColumnNames
* @param loadPlan The load plan
* @param factory The SessionFactory
* @param buildingParameters And influencers that would affect the generated SQL (mostly we are concerned with those
* that add additional joins here)
*
* @return The LoadQueryDetails
*/
public static LoadQueryDetails makeForBatching(
String[] uniqueKeyColumnNames,
LoadPlan loadPlan,
SessionFactoryImplementor factory,
QueryBuildingParameters buildingParameters) {
final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( factory );
final ResultSetProcessor resultSetProcessor = new ResultSetProcessorImpl( loadPlan, false );
final String sqlStatement = EntityLoadQueryBuilderImpl.INSTANCE.generateSql(
uniqueKeyColumnNames,
loadPlan,
factory,
buildingParameters,
aliasResolutionContext
);
return new LoadQueryDetails( factory, loadPlan, aliasResolutionContext, resultSetProcessor, sqlStatement );
}
private LoadQueryDetails(
SessionFactoryImplementor factory,
LoadPlan loadPlan,
AliasResolutionContext aliasResolutionContext,
ResultSetProcessor resultSetProcessor,
String sqlStatement) {
this.factory = factory;
this.loadPlan = loadPlan;
this.aliasResolutionContext = aliasResolutionContext;
this.resultSetProcessor = resultSetProcessor;
this.sqlStatement = sqlStatement;
}
public SessionFactoryImplementor getFactory() {
return factory;
}
public LoadPlan getLoadPlan() {
return loadPlan;
}
public AliasResolutionContext getAliasResolutionContext() {
return aliasResolutionContext;
}
public String getSqlStatement() {
return sqlStatement;
}
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.exec.spi;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceReader;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceReader;
/**
* Used as a callback mechanism while building the SQL statement to collect the needed ResultSet readers
*
* @author Steve Ebersole
*/
public interface ReaderCollector {
public void addReader(CollectionReferenceReader collectionReferenceReader);
public void addReader(EntityReferenceReader entityReferenceReader);
}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.exec.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessingContextImpl;
/**
* @author Steve Ebersole
*/
public interface RowReader {
Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException;
}

View File

@ -0,0 +1,64 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.internal;
import org.hibernate.loader.plan.spi.EntityReturn2;
import org.hibernate.loader.plan.spi.IdentifierDescription;
import org.hibernate.loader.plan.spi.build.IdentifierDescriptionInjectable;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Steve Ebersole
*/
public class EntityReturnImpl implements EntityReturn2, IdentifierDescriptionInjectable {
private final String entityQuerySpaceUid;
private final EntityPersister entityPersister;
private IdentifierDescription identifierDescription;
public EntityReturnImpl(String entityQuerySpaceUid, EntityPersister entityPersister) {
this.entityQuerySpaceUid = entityQuerySpaceUid;
this.entityPersister = entityPersister;
}
@Override
public String getQuerySpaceUid() {
return entityQuerySpaceUid;
}
@Override
public EntityPersister getEntityPersister() {
return entityPersister;
}
@Override
public IdentifierDescription getIdentifierDescription() {
return identifierDescription;
}
@Override
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
this.identifierDescription = identifierDescription;
}
}

View File

@ -47,14 +47,35 @@ public class LoadPlanImpl implements LoadPlan {
this.areLazyAttributesForceFetched = areLazyAttributesForceFetched;
}
/**
* Creates a {@link Disposition#ENTITY_LOADER} LoadPlan.
*
* @param rootReturn The EntityReturn representation of the entity being loaded.
*/
public LoadPlanImpl(EntityReturn rootReturn) {
this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
}
/**
* Creates a {@link Disposition#COLLECTION_INITIALIZER} LoadPlan.
*
* @param rootReturn The CollectionReturn representation of the collection being initialized.
*/
public LoadPlanImpl(CollectionReturn rootReturn) {
this( Collections.singletonList( rootReturn ), Disposition.ENTITY_LOADER, false );
this( Collections.singletonList( rootReturn ), Disposition.COLLECTION_INITIALIZER, false );
}
/**
* Creates a {@link Disposition#MIXED} LoadPlan.
*
* @param returns The mixed Return references
* @param areLazyAttributesForceFetched Should lazy attributes (bytecode enhanced laziness) be fetched also? This
* effects the eventual SQL SELECT-clause which is why we have it here. Currently this is "all-or-none"; you
* can request that all lazy properties across all entities in the loadplan be force fetched or none. There is
* no entity-by-entity option. {@code FETCH ALL PROPERTIES} is the way this is requested in HQL. Would be nice to
* consider this entity-by-entity, as opposed to all-or-none. For example, "fetch the LOB value for the Item.image
* attribute, but no others (leave them lazy)". Not too concerned about having it at the attribute level.
*/
public LoadPlanImpl(List<? extends Return> returns, boolean areLazyAttributesForceFetched) {
this( returns, Disposition.MIXED, areLazyAttributesForceFetched );
}

View File

@ -0,0 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.internal;
/**
* @author Steve Ebersole
*/
public class ScalarReturnImpl {
}

View File

@ -115,20 +115,20 @@ public class AnyFetch extends AbstractPlanNode implements Fetch {
return propertyPath;
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
throw new NotYetImplementedException();
}
@Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
throw new NotYetImplementedException();
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
throw new NotYetImplementedException();
}
// @Override
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// throw new NotYetImplementedException();
// }
//
// @Override
// public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// throw new NotYetImplementedException();
// }
//
// @Override
// public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
// throw new NotYetImplementedException();
// }
@Override
public AnyFetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {

View File

@ -27,9 +27,22 @@ package org.hibernate.loader.plan.spi;
* Represents the circular side of a bi-directional entity fetch. Wraps a reference to an EntityReference
* as an EntityFetch. We can use the special type as a trigger in AliasResolutionContext, etc to lookup information
* based on the wrapped reference.
* <p/>
* This relies on reference lookups against the EntityReference instances, therefore this allows representation of the
* circularity but with a little protection against potential stack overflows. This is unfortunately still a cyclic
* graph. An alternative approach is to make the graph acyclic (DAG) would be to follow the process I adopted in the
* original HQL Antlr v3 work with regard to always applying an alias to the "persister reference", even where that
* meant creating a generated, unique identifier as the alias. That allows other parts of the tree to refer to the
* "persister reference" by that alias without the need for potentially cyclic graphs (think ALIAS_REF in the current
* ORM parser). Those aliases can then be mapped/catalogued against the "persister reference" for retrieval as needed.
*
* @author Steve Ebersole
*/
public interface BidirectionalEntityFetch {
/**
* Get the targeted EntityReference
*
* @return The targeted EntityReference
*/
public EntityReference getTargetEntityReference();
}

View File

@ -23,24 +23,13 @@
*/
package org.hibernate.loader.plan.spi;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.plan.exec.process.internal.Helper;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.CollectionType;
/**
@ -110,92 +99,92 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc
return fetchStrategy;
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
return null;
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final Serializable collectionRowKey = (Serializable) getCollectionPersister().readKey(
resultSet,
context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases().getSuffixedKeyAliases(),
context.getSession()
);
final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();
if ( collectionRowKey == null ) {
// we did not find a collection element in the result set, so we
// ensure that a collection is created with the owner's identifier,
// since what we have is an empty collection
final EntityKey ownerEntityKey = findOwnerEntityKey( context );
if ( ownerEntityKey == null ) {
// should not happen
throw new IllegalStateException(
"Could not locate owner's EntityKey during attempt to read collection element fro JDBC row : " +
getPropertyPath().getFullPath()
);
}
if ( log.isDebugEnabled() ) {
log.debugf(
"Result set contains (possibly empty) collection: %s",
MessageHelper.collectionInfoString(
getCollectionPersister(),
ownerEntityKey,
context.getSession().getFactory()
)
);
}
persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( getCollectionPersister(), ownerEntityKey );
}
else {
// we found a collection element in the result set
if ( log.isDebugEnabled() ) {
log.debugf(
"Found row of collection: %s",
MessageHelper.collectionInfoString(
getCollectionPersister(),
collectionRowKey,
context.getSession().getFactory()
)
);
}
PersistentCollection rowCollection = persistenceContext.getLoadContexts()
.getCollectionLoadContext( resultSet )
.getLoadingCollection( getCollectionPersister(), collectionRowKey );
final CollectionAliases descriptor = context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases();
if ( rowCollection != null ) {
final Object element = rowCollection.readFrom( resultSet, getCollectionPersister(), descriptor, owner );
if ( getElementGraph() != null ) {
for ( Fetch fetch : getElementGraph().getFetches() ) {
fetch.read( resultSet, context, element );
}
}
}
}
}
private EntityKey findOwnerEntityKey(ResultSetProcessingContext context) {
return context.getProcessingState( findOwnerEntityReference( getOwner() ) ).getEntityKey();
}
private EntityReference findOwnerEntityReference(FetchOwner owner) {
return Helper.INSTANCE.findOwnerEntityReference( owner );
}
// @Override
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// //To change body of implemented methods use File | Settings | File Templates.
// }
//
// @Override
// public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// return null;
// }
//
// @Override
// public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
// final Serializable collectionRowKey = (Serializable) getCollectionPersister().readKey(
// resultSet,
// context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases().getSuffixedKeyAliases(),
// context.getSession()
// );
//
// final PersistenceContext persistenceContext = context.getSession().getPersistenceContext();
//
// if ( collectionRowKey == null ) {
// // we did not find a collection element in the result set, so we
// // ensure that a collection is created with the owner's identifier,
// // since what we have is an empty collection
// final EntityKey ownerEntityKey = findOwnerEntityKey( context );
// if ( ownerEntityKey == null ) {
// // should not happen
// throw new IllegalStateException(
// "Could not locate owner's EntityKey during attempt to read collection element fro JDBC row : " +
// getPropertyPath().getFullPath()
// );
// }
//
// if ( log.isDebugEnabled() ) {
// log.debugf(
// "Result set contains (possibly empty) collection: %s",
// MessageHelper.collectionInfoString(
// getCollectionPersister(),
// ownerEntityKey,
// context.getSession().getFactory()
// )
// );
// }
//
// persistenceContext.getLoadContexts()
// .getCollectionLoadContext( resultSet )
// .getLoadingCollection( getCollectionPersister(), ownerEntityKey );
// }
// else {
// // we found a collection element in the result set
// if ( log.isDebugEnabled() ) {
// log.debugf(
// "Found row of collection: %s",
// MessageHelper.collectionInfoString(
// getCollectionPersister(),
// collectionRowKey,
// context.getSession().getFactory()
// )
// );
// }
//
// PersistentCollection rowCollection = persistenceContext.getLoadContexts()
// .getCollectionLoadContext( resultSet )
// .getLoadingCollection( getCollectionPersister(), collectionRowKey );
//
// final CollectionAliases descriptor = context.getAliasResolutionContext().resolveAliases( this ).getCollectionColumnAliases();
//
// if ( rowCollection != null ) {
// final Object element = rowCollection.readFrom( resultSet, getCollectionPersister(), descriptor, owner );
//
// if ( getElementGraph() != null ) {
// for ( Fetch fetch : getElementGraph().getFetches() ) {
// fetch.read( resultSet, context, element );
// }
// }
// }
// }
// }
//
// private EntityKey findOwnerEntityKey(ResultSetProcessingContext context) {
// return context.getProcessingState( findOwnerEntityReference( getOwner() ) ).getEntityKey();
// }
//
// private EntityReference findOwnerEntityReference(FetchOwner owner) {
// return Helper.INSTANCE.findOwnerEntityReference( owner );
// }
@Override
public CollectionFetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {

View File

@ -92,29 +92,29 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
return getOwner().retrieveFetchSourcePersister();
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// anything to do?
}
@Override
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// anything to do?
return null;
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final EntityReference ownerEntityReference = Helper.INSTANCE.findOwnerEntityReference( this );
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
ownerEntityReference
);
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
final Object entity = context.resolveEntityKey( entityKey, Helper.INSTANCE.findOwnerEntityReference( (FetchOwner) ownerEntityReference ) );
for ( Fetch fetch : getFetches() ) {
fetch.read( resultSet, context, entity );
}
}
// @Override
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// // anything to do?
// }
//
// @Override
// public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// // anything to do?
// return null;
// }
//
// @Override
// public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
// final EntityReference ownerEntityReference = Helper.INSTANCE.findOwnerEntityReference( this );
// final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
// ownerEntityReference
// );
// final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
// final Object entity = context.resolveEntityKey( entityKey, Helper.INSTANCE.findOwnerEntityReference( (FetchOwner) ownerEntityReference ) );
// for ( Fetch fetch : getFetches() ) {
// fetch.read( resultSet, context, entity );
// }
// }
@Override
public CompositeFetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {

View File

@ -146,143 +146,143 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit
this.identifierDescription = identifierDescription;
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
identifierDescription.hydrate( resultSet, context );
// @Override
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// identifierDescription.hydrate( resultSet, context );
//
// for ( Fetch fetch : getFetches() ) {
// fetch.hydrate( resultSet, context );
// }
// }
//
// @Override
// public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
// this
// );
// EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
// if ( entityKey == null ) {
// entityKey = identifierDescription.resolve( resultSet, context );
// if ( entityKey == null ) {
// // register the non-existence (though only for one-to-one associations)
// if ( getFetchedType().isOneToOne() ) {
// // first, find our owner's entity-key...
// final EntityKey ownersEntityKey = context.getProcessingState( (EntityReference) getOwner() ).getEntityKey();
// if ( ownersEntityKey != null ) {
// context.getSession().getPersistenceContext()
// .addNullProperty( ownersEntityKey, getFetchedType().getPropertyName() );
// }
// }
// }
//
// entityReferenceProcessingState.registerEntityKey( entityKey );
//
// for ( Fetch fetch : getFetches() ) {
// fetch.resolve( resultSet, context );
// }
// }
//
// return entityKey;
// }
//
// @Override
// public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
// final EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState( this );
// final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
// if ( entityKey == null ) {
// return;
// }
// final Object entity = context.resolveEntityKey( entityKey, this );
// for ( Fetch fetch : getFetches() ) {
// fetch.read( resultSet, context, entity );
// }
// }
for ( Fetch fetch : getFetches() ) {
fetch.hydrate( resultSet, context );
}
}
@Override
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState(
this
);
EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
if ( entityKey == null ) {
entityKey = identifierDescription.resolve( resultSet, context );
if ( entityKey == null ) {
// register the non-existence (though only for one-to-one associations)
if ( getFetchedType().isOneToOne() ) {
// first, find our owner's entity-key...
final EntityKey ownersEntityKey = context.getProcessingState( (EntityReference) getOwner() ).getEntityKey();
if ( ownersEntityKey != null ) {
context.getSession().getPersistenceContext()
.addNullProperty( ownersEntityKey, getFetchedType().getPropertyName() );
}
}
}
entityReferenceProcessingState.registerEntityKey( entityKey );
for ( Fetch fetch : getFetches() ) {
fetch.resolve( resultSet, context );
}
}
return entityKey;
}
@Override
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException {
final EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState( this );
final EntityKey entityKey = entityReferenceProcessingState.getEntityKey();
if ( entityKey == null ) {
return;
}
final Object entity = context.resolveEntityKey( entityKey, this );
for ( Fetch fetch : getFetches() ) {
fetch.read( resultSet, context, entity );
}
}
/**
* Resolve any fetches required to resolve the identifier as well
* as the entity key for this fetch..
*
* @param resultSet - the result set.
* @param context - the result set processing context.
* @return the entity key for this fetch.
*
* @throws SQLException
*/
public EntityKey resolveInIdentifier(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// todo : may not need to do this if entitykey is already part of the resolution context
final EntityKey entityKey = resolve( resultSet, context );
final Object existing = context.getSession().getEntityUsingInterceptor( entityKey );
if ( existing != null ) {
if ( !persister.isInstance( existing ) ) {
throw new WrongClassException(
"loaded object was of wrong class " + existing.getClass(),
entityKey.getIdentifier(),
persister.getEntityName()
);
}
if ( getLockMode() != null && getLockMode() != LockMode.NONE ) {
final boolean isVersionCheckNeeded = persister.isVersioned()
&& context.getSession().getPersistenceContext().getEntry( existing ).getLockMode().lessThan( getLockMode() );
// we don't need to worry about existing version being uninitialized because this block isn't called
// by a re-entrant load (re-entrant loads _always_ have lock mode NONE)
if ( isVersionCheckNeeded ) {
//we only check the version when _upgrading_ lock modes
context.checkVersion(
resultSet,
persister,
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey,
existing
);
//we need to upgrade the lock mode to the mode requested
context.getSession().getPersistenceContext().getEntry( existing ).setLockMode( getLockMode() );
}
}
}
else {
final String concreteEntityTypeName = context.getConcreteEntityTypeName(
resultSet,
persister,
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
entityKey
);
final Object entityInstance = context.getSession().instantiate(
concreteEntityTypeName,
entityKey.getIdentifier()
);
//need to hydrate it.
// grab its state from the ResultSet and keep it in the Session
// (but don't yet initialize the object itself)
// note that we acquire LockMode.READ even if it was not requested
LockMode acquiredLockMode = getLockMode() == LockMode.NONE ? LockMode.READ : getLockMode();
context.loadFromResultSet(
resultSet,
entityInstance,
concreteEntityTypeName,
entityKey,
context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
acquiredLockMode,
persister,
getFetchStrategy(),
true,
getFetchedType()
);
// materialize associations (and initialize the object) later
context.registerHydratedEntity( this, entityKey, entityInstance );
}
return entityKey;
}
// /**
// * Resolve any fetches required to resolve the identifier as well
// * as the entity key for this fetch..
// *
// * @param resultSet - the result set.
// * @param context - the result set processing context.
// * @return the entity key for this fetch.
// *
// * @throws SQLException
// */
// public EntityKey resolveInIdentifier(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
// // todo : may not need to do this if entitykey is already part of the resolution context
//
// final EntityKey entityKey = resolve( resultSet, context );
//
// final Object existing = context.getSession().getEntityUsingInterceptor( entityKey );
//
// if ( existing != null ) {
// if ( !persister.isInstance( existing ) ) {
// throw new WrongClassException(
// "loaded object was of wrong class " + existing.getClass(),
// entityKey.getIdentifier(),
// persister.getEntityName()
// );
// }
//
// if ( getLockMode() != null && getLockMode() != LockMode.NONE ) {
// final boolean isVersionCheckNeeded = persister.isVersioned()
// && context.getSession().getPersistenceContext().getEntry( existing ).getLockMode().lessThan( getLockMode() );
//
// // we don't need to worry about existing version being uninitialized because this block isn't called
// // by a re-entrant load (re-entrant loads _always_ have lock mode NONE)
// if ( isVersionCheckNeeded ) {
// //we only check the version when _upgrading_ lock modes
// context.checkVersion(
// resultSet,
// persister,
// context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
// entityKey,
// existing
// );
// //we need to upgrade the lock mode to the mode requested
// context.getSession().getPersistenceContext().getEntry( existing ).setLockMode( getLockMode() );
// }
// }
// }
// else {
// final String concreteEntityTypeName = context.getConcreteEntityTypeName(
// resultSet,
// persister,
// context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
// entityKey
// );
//
// final Object entityInstance = context.getSession().instantiate(
// concreteEntityTypeName,
// entityKey.getIdentifier()
// );
//
// //need to hydrate it.
//
// // grab its state from the ResultSet and keep it in the Session
// // (but don't yet initialize the object itself)
// // note that we acquire LockMode.READ even if it was not requested
// LockMode acquiredLockMode = getLockMode() == LockMode.NONE ? LockMode.READ : getLockMode();
//
// context.loadFromResultSet(
// resultSet,
// entityInstance,
// concreteEntityTypeName,
// entityKey,
// context.getAliasResolutionContext().resolveAliases( this ).getColumnAliases(),
// acquiredLockMode,
// persister,
// getFetchStrategy(),
// true,
// getFetchedType()
// );
//
// // materialize associations (and initialize the object) later
// context.registerHydratedEntity( this, entityKey, entityInstance );
// }
//
// return entityKey;
// }
@Override
public String toString() {

View File

@ -25,6 +25,7 @@ package org.hibernate.loader.plan.spi;
import org.hibernate.LockMode;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.spi.build.IdentifierDescriptionInjectable;
import org.hibernate.persister.entity.EntityPersister;
/**

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.spi;
import org.hibernate.persister.entity.EntityPersister;
/**
* Represents a reference to an entity either as a return or as a fetch
*
* @author Steve Ebersole
*/
public interface EntityReference2 {
/**
* Retrieves the EntityPersister describing the entity associated with this Return.
*
* @return The EntityPersister.
*/
public EntityPersister getEntityPersister();
/**
* Get the description of this entity's identifier.
*
* @return The identifier description.
*/
public IdentifierDescription getIdentifierDescription();
}

View File

@ -0,0 +1,31 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan.spi;
/**
* @author Steve Ebersole
*/
public interface EntityReturn2 extends EntityReference2 {
public String getQuerySpaceUid();
}

View File

@ -78,11 +78,11 @@ public interface Fetch extends CopyableFetch {
*/
public String[] toSqlSelectFragments(String alias);
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException;
// public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
//
// public Object resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
//
// public void read(ResultSet resultSet, ResultSetProcessingContext context, Object owner) throws SQLException;
@Override
public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy);

View File

@ -23,22 +23,15 @@
*/
package org.hibernate.loader.plan.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
/**
* @author Steve Ebersole
*/
public interface IdentifierDescription {
/**
* Obtain fetches that are specific to the identifier. These will only be either of type EntityFetch
* (many-key-to-one) or CompositeFetch (composite ids, possibly with nested CompositeFetches and EntityFetches).
*
* @return This identifier's fetches.
*/
public Fetch[] getFetches();
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException;
HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch);
}

View File

@ -29,8 +29,11 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* We can use the special type as a trigger in AliasResolutionContext, etc to lookup information based on
* the wrapped reference. E.g.
* Represents a key-many-to-one fetch that is bi-directionally join fetched.
* <p/>
* For example, consider an Order entity whose primary key is partially made up of the Customer entity to which
* it is associated. When we join fetch Customer -> Order(s) and then Order -> Customer we have a bi-directional
* fetch. This class would be used to represent the Order -> Customer part of that link.
*
* @author Steve Ebersole
*/

View File

@ -30,26 +30,58 @@ import java.util.List;
*
* Generally speaking there are 3 forms of load plans:<ul>
* <li>
* An entity load plan for handling get/load handling. This form will typically have a single
* return (of type {@link EntityReturn}) defined by {@link #getReturns()}, possibly defining fetches.
* {@link org.hibernate.loader.plan.spi.LoadPlan.Disposition#ENTITY_LOADER} - An entity load plan for
* handling get/load handling. This form will typically have a single return (of type {@link EntityReturn})
* defined by {@link #getReturns()}, possibly defining fetches.
* </li>
* <li>
* A collection initializer, used to load the contents of a collection. This form will typically have a
* single return (of type {@link CollectionReturn} defined by {@link #getReturns()}, possibly defining fetches
* {@link org.hibernate.loader.plan.spi.LoadPlan.Disposition#COLLECTION_INITIALIZER} - A collection initializer,
* used to load the contents of a collection. This form will typically have a single return (of
* type {@link CollectionReturn}) defined by {@link #getReturns()}, possibly defining fetches
* </li>
* <li>
* A query load plan which can contain multiple returns of mixed type (though implementing {@link Return}).
* Again, may possibly define fetches.
* {@link org.hibernate.loader.plan.spi.LoadPlan.Disposition#MIXED} - A query load plan which can contain
* multiple returns of mixed type (though all implementing {@link Return}). Again, may possibly define fetches.
* </li>
* </ul>
* <p/>
* todo : would also like to see "call back" style access for handling "subsequent actions" such as...<ul>
* <li>follow-on locking</li>
* <li>join fetch conversions to subselect fetches</li>
* </ul>
*
* @author Steve Ebersole
*/
public interface LoadPlan {
public List<? extends Return> getReturns();
/**
* What is the disposition of this LoadPlan, in terms of its returns.
*
* @return The LoadPlan's disposition
*/
public Disposition getDisposition();
/**
* Get the returns indicated by this LoadPlan.<ul>
* <li>
* A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link EntityReturn}.
* </li>
* <li>
* A {@link Disposition#COLLECTION_INITIALIZER} LoadPlan would have just a single Return of type
* {@link CollectionReturn}.
* </li>
* <li>
* A {@link Disposition#MIXED} LoadPlan would contain a mix of {@link EntityReturn} and
* {@link ScalarReturn} elements, but no {@link CollectionReturn}.
* </li>
* </ul>
*
* @return The Returns for this LoadPlan.
*
* @see Disposition
*/
public List<? extends Return> getReturns();
/**
* Does this load plan indicate that lazy attributes are to be force fetched?
* <p/>
@ -93,8 +125,4 @@ public interface LoadPlan {
*/
MIXED
}
// todo : would also like to see "call back" style access for handling "subsequent actions" such as:
// 1) follow-on locking
// 2) join fetch conversions to subselect fetches
}

View File

@ -1,5 +1,5 @@
/*
* jDocBook, processing of DocBook sources
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
@ -23,9 +23,6 @@
*/
package org.hibernate.loader.plan.spi.build;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
@ -35,20 +32,21 @@ import org.jboss.logging.MDC;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.AbstractFetchOwner;
import org.hibernate.loader.plan.spi.AnyFetch;
import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.CopyContext;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityPersisterBasedSqlSelectFragmentResolver;
import org.hibernate.loader.plan.spi.EntityReference;
@ -59,13 +57,12 @@ import org.hibernate.loader.plan.spi.IdentifierDescription;
import org.hibernate.loader.plan.spi.KeyManyToOneBidirectionalEntityFetch;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.plan.spi.SqlSelectFragmentResolver;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.spi.HydratedCompoundValueHandler;
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;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
@ -419,6 +416,138 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
);
}
private Map<AssociationKey,FetchOwner> fetchedAssociationKeyOwnerMap = new HashMap<AssociationKey, FetchOwner>();
@Override
public void associationKeyRegistered(AssociationKey associationKey) {
// todo : use this information to maintain a map of AssociationKey->FetchOwner mappings (associationKey + current fetchOwner stack entry)
// that mapping can then be used in #foundCircularAssociationKey to build the proper BiDirectionalEntityFetch
// based on the mapped owner
fetchedAssociationKeyOwnerMap.put( associationKey, currentFetchOwner() );
}
@Override
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
// todo : use this information to create the BiDirectionalEntityFetch instances
final FetchOwner fetchOwner = fetchedAssociationKeyOwnerMap.get( associationKey );
if ( fetchOwner == null ) {
throw new IllegalStateException(
String.format(
"Expecting AssociationKey->FetchOwner mapping for %s",
associationKey.toString()
)
);
}
currentFetchOwner().addFetch( new CircularFetch( currentFetchOwner(), fetchOwner, attributeDefinition ) );
}
public static class CircularFetch implements BidirectionalEntityFetch, EntityReference, Fetch {
private final FetchOwner circularFetchOwner;
private final FetchOwner associationOwner;
private final AttributeDefinition attributeDefinition;
private final EntityReference targetEntityReference;
private final FetchStrategy fetchStrategy = new FetchStrategy(
FetchTiming.IMMEDIATE,
FetchStyle.JOIN
);
public CircularFetch(FetchOwner circularFetchOwner, FetchOwner associationOwner, AttributeDefinition attributeDefinition) {
this.circularFetchOwner = circularFetchOwner;
this.associationOwner = associationOwner;
this.attributeDefinition = attributeDefinition;
this.targetEntityReference = resolveEntityReference( associationOwner );
}
@Override
public EntityReference getTargetEntityReference() {
return targetEntityReference;
}
protected static EntityReference resolveEntityReference(FetchOwner owner) {
if ( EntityReference.class.isInstance( owner ) ) {
return (EntityReference) owner;
}
if ( CompositeFetch.class.isInstance( owner ) ) {
return resolveEntityReference( ( (CompositeFetch) owner ).getOwner() );
}
// todo : what others?
throw new UnsupportedOperationException(
"Unexpected FetchOwner type [" + owner + "] encountered trying to build circular fetch"
);
}
@Override
public FetchOwner getOwner() {
return circularFetchOwner;
}
@Override
public PropertyPath getPropertyPath() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Type getFetchedType() {
return attributeDefinition.getType();
}
@Override
public FetchStrategy getFetchStrategy() {
return fetchStrategy;
}
@Override
public boolean isNullable() {
return attributeDefinition.isNullable();
}
@Override
public String getAdditionalJoinConditions() {
return null;
}
@Override
public String[] toSqlSelectFragments(String alias) {
return new String[0];
}
@Override
public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {
// todo : will need this implemented
return null;
}
@Override
public LockMode getLockMode() {
return targetEntityReference.getLockMode();
}
@Override
public EntityReference getEntityReference() {
return targetEntityReference;
}
@Override
public EntityPersister getEntityPersister() {
return targetEntityReference.getEntityPersister();
}
@Override
public IdentifierDescription getIdentifierDescription() {
return targetEntityReference.getIdentifierDescription();
}
@Override
public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
throw new IllegalStateException( "IdentifierDescription should never be injected from circular fetch side" );
}
}
@Override
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition) {
// for ANY mappings we need to build a Fetch:
@ -545,7 +674,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
public void poppedFromStack();
}
protected static abstract class AbstractIdentifierAttributeCollector extends AbstractFetchOwner
protected static abstract class AbstractIdentifierAttributeCollector
extends AbstractFetchOwner
implements FetchOwner, EntityReference, FetchStackAware {
protected final EntityReference entityReference;
private final EntityPersisterBasedSqlSelectFragmentResolver sqlSelectFragmentResolver;
@ -771,118 +901,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
public Fetch[] getFetches() {
return identifierFetches;
}
@Override
public HydratedCompoundValueHandler getHydratedStateHandler(Fetch fetch) {
return fetchToHydratedStateExtractorMap == null ? null : fetchToHydratedStateExtractorMap.get( fetch );
}
@Override
public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getProcessingState( entityReference );
final Object ownerIdentifierHydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
if ( ownerIdentifierHydratedState != null ) {
for ( Fetch fetch : identifierFetches ) {
if ( fetch instanceof EntityFetch ) {
final ResultSetProcessingContext.EntityReferenceProcessingState fetchEntityReferenceProcessingState =
context.getProcessingState( (EntityFetch) fetch );
// if the identifier was already hydrated, nothing to do
if ( fetchEntityReferenceProcessingState.getIdentifierHydratedForm() != null ) {
continue;
}
// try to extract the sub-hydrated value from the owners tuple array
if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) {
Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch )
.extract( ownerIdentifierHydratedState );
fetchEntityReferenceProcessingState.registerIdentifierHydratedForm( extracted );
continue;
}
// if we can't, then read from result set
fetch.hydrate( resultSet, context );
}
else {
throw new NotYetImplementedException( "Cannot hydrate identifier Fetch that is not an EntityFetch" );
}
}
return;
}
final String[] columnNames;
if ( EntityFetch.class.isInstance( entityReference )
&& !FetchStrategyHelper.isJoinFetched( ((EntityFetch) entityReference).getFetchStrategy() ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
final FetchOwner fetchOwner = fetch.getOwner();
if ( EntityReference.class.isInstance( fetchOwner ) ) {
throw new NotYetImplementedException();
// final EntityReference ownerEntityReference = (EntityReference) fetchOwner;
// final EntityAliases ownerEntityAliases = context.getAliasResolutionContext()
// .resolveEntityColumnAliases( ownerEntityReference );
// final int propertyIndex = ownerEntityReference.getEntityPersister()
// .getEntityMetamodel()
// .getPropertyIndex( fetch.getOwnerPropertyName() );
// columnNames = ownerEntityAliases.getSuffixedPropertyAliases()[ propertyIndex ];
}
else {
// todo : better message here...
throw new WalkingException( "Cannot locate association column names" );
}
}
else {
columnNames = context.getAliasResolutionContext()
.resolveAliases( entityReference )
.getColumnAliases()
.getSuffixedKeyAliases();
}
final Object hydratedIdentifierState = entityReference.getEntityPersister().getIdentifierType().hydrate(
resultSet,
columnNames,
context.getSession(),
null
);
context.getProcessingState( entityReference ).registerIdentifierHydratedForm( hydratedIdentifierState );
}
@Override
public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException {
for ( Fetch fetch : identifierFetches ) {
resolveIdentifierFetch( resultSet, context, fetch );
}
final ResultSetProcessingContext.EntityReferenceProcessingState ownerEntityReferenceProcessingState =
context.getProcessingState( entityReference );
Object hydratedState = ownerEntityReferenceProcessingState.getIdentifierHydratedForm();
Serializable resolvedId = (Serializable) entityReference.getEntityPersister()
.getIdentifierType()
.resolve( hydratedState, context.getSession(), null );
return context.getSession().generateEntityKey( resolvedId, entityReference.getEntityPersister() );
}
}
private static void resolveIdentifierFetch(
ResultSet resultSet,
ResultSetProcessingContext context,
Fetch fetch) throws SQLException {
if ( fetch instanceof EntityFetch ) {
EntityFetch entityFetch = (EntityFetch) fetch;
final ResultSetProcessingContext.EntityReferenceProcessingState entityReferenceProcessingState =
context.getProcessingState( entityFetch );
if ( entityReferenceProcessingState.getEntityKey() != null ) {
return;
}
EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context );
entityReferenceProcessingState.registerEntityKey( fetchKey );
}
else if ( fetch instanceof CompositeFetch ) {
for ( Fetch subFetch : ( (CompositeFetch) fetch ).getFetches() ) {
resolveIdentifierFetch( resultSet, context, subFetch );
}
}
}
public static class MDCStack {
private ArrayDeque<PropertyPath> pathStack = new ArrayDeque<PropertyPath>();

View File

@ -21,10 +21,12 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan.spi;
package org.hibernate.loader.plan.spi.build;
import org.hibernate.loader.plan.spi.IdentifierDescription;
/**
* Ugh
* Ugh.
*
* @author Steve Ebersole
*/

View File

@ -29,13 +29,14 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
/**
* Coordinates building of a {@link org.hibernate.loader.plan.spi.LoadPlan} between the
* {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and
* {@link LoadPlanBuilderStrategy}
* A metadata-driven builder of LoadPlans. Coordinates between the {@link MetadataDrivenModelGraphVisitor} and
* {@link LoadPlanBuilderStrategy}.
*
* @author Steve Ebersole
*
* @see MetadataDrivenModelGraphVisitor
*/
public class LoadPlanBuilder {
public class MetadataDrivenLoadPlanBuilder {
/**
* Coordinates building a LoadPlan that defines just a single root entity return (may have fetches).
* <p/>

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
/**
* A LoadPlan building strategy for cascade processing; meaning, it builds the LoadPlan for loading related to
* cascading a particular action across associations
*
* @author Steve Ebersole
*/
public class CascadeLoadPlanBuilderStrategy extends StandardFetchBasedLoadPlanBuilderStrategy {
private static final FetchStrategy EAGER = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
private static final FetchStrategy DELAYED = new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT );
private final CascadingAction cascadeActionToMatch;
public CascadeLoadPlanBuilderStrategy(
CascadingAction cascadeActionToMatch,
SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers) {
super( sessionFactory, loadQueryInfluencers );
this.cascadeActionToMatch = cascadeActionToMatch;
}
@Override
protected FetchStrategy determineFetchStrategy(AssociationAttributeDefinition attributeDefinition) {
return attributeDefinition.determineCascadeStyle().doCascade( cascadeActionToMatch ) ? EAGER : DELAYED;
}
}

View File

@ -0,0 +1,252 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.AbstractQuerySpace;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.CompositeQuerySpace;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType;
/**
* @author Steve Ebersole
*/
public class CollectionQuerySpaceImpl extends AbstractQuerySpace implements CollectionQuerySpace {
private final CollectionPersister persister;
public CollectionQuerySpaceImpl(
CollectionPersister persister,
String uid,
QuerySpacesImpl querySpaces,
SessionFactoryImplementor sessionFactory) {
super( uid, Disposition.COLLECTION, querySpaces, sessionFactory );
this.persister = persister;
}
@Override
public CollectionPersister getCollectionPersister() {
return persister;
}
public static interface CollectionIndexEntityJoin extends Join {
public CollectionQuerySpaceImpl getCollectionQuerySpace();
@Override
CollectionQuerySpaceImpl getLeftHandSide();
@Override
EntityQuerySpaceImpl getRightHandSide();
}
public CollectionIndexEntityJoin addIndexEntityJoin(
EntityPersister indexPersister,
LoadPlanBuildingContext context) {
final String entityQuerySpaceUid = getQuerySpaces().generateImplicitUid();
final EntityQuerySpaceImpl entityQuerySpace = new EntityQuerySpaceImpl(
indexPersister,
entityQuerySpaceUid,
getQuerySpaces(),
sessionFactory()
);
( (QuerySpacesImpl) context.getQuerySpaces() ).registerQuerySpace( entityQuerySpace );
final CollectionIndexEntityJoin join = new CollectionIndexEntityJoin() {
@Override
public CollectionQuerySpaceImpl getCollectionQuerySpace() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CollectionQuerySpaceImpl getLeftHandSide() {
return CollectionQuerySpaceImpl.this;
}
@Override
public EntityQuerySpaceImpl getRightHandSide() {
return entityQuerySpace;
}
@Override
public boolean isRightHandSideOptional() {
return false;
}
};
internalGetJoins().add( join );
return join;
}
public static interface CollectionIndexCompositeJoin extends Join {
public CollectionQuerySpaceImpl getCollectionQuerySpace();
@Override
CollectionQuerySpaceImpl getLeftHandSide();
@Override
CompositeQuerySpaceImpl getRightHandSide();
}
public CollectionIndexCompositeJoin addIndexCompositeJoin(
CompositeType compositeType,
LoadPlanBuildingContext context) {
final String compositeQuerySpaceUid = getQuerySpaces().generateImplicitUid();
final CompositeQuerySpaceImpl compositeQuerySpace = new CompositeQuerySpaceImpl(
CollectionQuerySpaceImpl.this.getUid(),
compositeType,
compositeQuerySpaceUid,
Disposition.COMPOSITE,
getQuerySpaces(),
sessionFactory()
);
( (QuerySpacesImpl) context.getQuerySpaces() ).registerQuerySpace( compositeQuerySpace );
final CollectionIndexCompositeJoin join = new CollectionIndexCompositeJoin() {
@Override
public CollectionQuerySpaceImpl getCollectionQuerySpace() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CollectionQuerySpaceImpl getLeftHandSide() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CompositeQuerySpaceImpl getRightHandSide() {
return compositeQuerySpace;
}
@Override
public boolean isRightHandSideOptional() {
return false;
}
};
internalGetJoins().add( join );
return join;
}
public static interface CollectionElementEntityJoin extends Join {
public CollectionQuerySpaceImpl getCollectionQuerySpace();
@Override
CollectionQuerySpaceImpl getLeftHandSide();
@Override
EntityQuerySpaceImpl getRightHandSide();
}
public CollectionElementEntityJoin addElementEntityJoin(
EntityPersister elementPersister,
LoadPlanBuildingContext context) {
final String entityQuerySpaceUid = getQuerySpaces().generateImplicitUid();
final EntityQuerySpaceImpl entityQuerySpace = new EntityQuerySpaceImpl(
elementPersister,
entityQuerySpaceUid,
getQuerySpaces(),
sessionFactory()
);
( (QuerySpacesImpl) context.getQuerySpaces() ).registerQuerySpace( entityQuerySpace );
final CollectionElementEntityJoin join = new CollectionElementEntityJoin() {
@Override
public CollectionQuerySpaceImpl getCollectionQuerySpace() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CollectionQuerySpaceImpl getLeftHandSide() {
return CollectionQuerySpaceImpl.this;
}
@Override
public EntityQuerySpaceImpl getRightHandSide() {
return entityQuerySpace;
}
@Override
public boolean isRightHandSideOptional() {
return false;
}
};
internalGetJoins().add( join );
return join;
}
public static interface CollectionElementCompositeJoin extends Join {
public CollectionQuerySpaceImpl getCollectionQuerySpace();
@Override
CollectionQuerySpaceImpl getLeftHandSide();
@Override
CompositeQuerySpaceImpl getRightHandSide();
}
public CollectionElementCompositeJoin addElementCompositeJoin(
CompositeType compositeType,
LoadPlanBuildingContext context) {
final String compositeQuerySpaceUid = getQuerySpaces().generateImplicitUid();
final CompositeQuerySpaceImpl compositeQuerySpace = new CompositeQuerySpaceImpl(
CollectionQuerySpaceImpl.this.getUid(),
compositeType,
compositeQuerySpaceUid,
Disposition.COMPOSITE,
getQuerySpaces(),
sessionFactory()
);
( (QuerySpacesImpl) context.getQuerySpaces() ).registerQuerySpace( compositeQuerySpace );
final CollectionElementCompositeJoin join = new CollectionElementCompositeJoin() {
@Override
public CollectionQuerySpaceImpl getCollectionQuerySpace() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CollectionQuerySpaceImpl getLeftHandSide() {
return CollectionQuerySpaceImpl.this;
}
@Override
public CompositeQuerySpaceImpl getRightHandSide() {
return compositeQuerySpace;
}
@Override
public boolean isRightHandSideOptional() {
return false;
}
};
internalGetJoins().add( join );
return join;
}
}

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.AbstractQuerySpace;
import org.hibernate.loader.plan2.build.spi.ExpandingQuerySpace;
import org.hibernate.loader.plan2.spi.CompositeQuerySpace;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.CompositeType;
/**
* @author Steve Ebersole
*/
public class CompositeQuerySpaceImpl extends AbstractQuerySpace implements CompositeQuerySpace, ExpandingQuerySpace {
private final String ownerUid;
private final CompositeType compositeType;
public CompositeQuerySpaceImpl(
String ownerUid,
CompositeType compositeType,
String uid,
Disposition disposition,
QuerySpacesImpl querySpaces,
SessionFactoryImplementor sessionFactory) {
super( uid, disposition, querySpaces, sessionFactory );
this.ownerUid = ownerUid;
this.compositeType = compositeType;
}
@Override
public JoinDefinedByMetadata addCompositeJoin(
CompositionDefinition compositionDefinition, String querySpaceUid) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public JoinDefinedByMetadata addEntityJoin(
AttributeDefinition attributeDefinition,
EntityPersister persister,
String querySpaceUid,
boolean optional) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public JoinDefinedByMetadata addCollectionJoin(
AttributeDefinition attributeDefinition, CollectionPersister collectionPersister, String querySpaceUid) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,200 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.ExpandingEntityIdentifierDescription;
import org.hibernate.loader.plan2.build.spi.ExpandingQuerySpace;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.internal.EntityFetchImpl;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
*/
public class EntityIdentifierDescriptionImpl implements ExpandingEntityIdentifierDescription {
private final EntityReference entityReference;
private final PropertyPath propertyPath;
private List<Fetch> fetches;
public EntityIdentifierDescriptionImpl(EntityReference entityReference) {
this.entityReference = entityReference;
this.propertyPath = entityReference.getPropertyPath().append( "<id>" );
}
// IdentifierDescription impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
public Fetch[] getFetches() {
return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] );
}
// FetchContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
if ( attributeDefinition.getType().isCollectionType() ) {
throw new WalkingException(
"Encountered collection attribute in identifier fetches: " + attributeDefinition.getSource() +
"." + attributeDefinition.getName()
);
}
// todo : allow bi-directional key-many-to-one fetches?
// those do cause problems in Loader; question is whether those are indicative of that situation or
// of Loaders ability to handle it.
}
@Override
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
// we have a key-many-to-one
//
// IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch
// impl. We collect them there and later build the IdentifierDescription
// if `this` is a fetch and its owner is "the same" (bi-directionality) as the attribute to be join fetched
// we should wrap our FetchOwner as an EntityFetch. That should solve everything except for the alias
// context lookups because of the different instances (because of wrapping). So somehow the consumer of this
// needs to be able to unwrap it to do the alias lookup, and would have to know to do that.
//
//
// we are processing the EntityReference(Address) identifier. we come across its key-many-to-one reference
// to Person. Now, if EntityReference(Address) is an instance of EntityFetch(Address) there is a strong
// likelihood that we have a bi-directionality and need to handle that specially.
//
// how to best (a) find the bi-directionality and (b) represent that?
final EntityType fetchedType = (EntityType) attributeDefinition.getType();
final EntityPersister fetchedPersister = loadPlanBuildingContext.getSessionFactory().getEntityPersister(
fetchedType.getAssociatedEntityName()
);
if ( fetchedPersister == null ) {
throw new WalkingException(
String.format(
"Unable to locate EntityPersister [%s] for fetch [%s]",
fetchedType.getAssociatedEntityName(),
attributeDefinition.getName()
)
);
}
if ( EntityFetch.class.isInstance( entityReference ) ) {
// // we just confirmed that EntityReference(Address) is an instance of EntityFetch(Address),
// final EntityFetch entityFetch = (EntityFetch) entityReference;
// final FetchSource entityFetchSource = entityFetch.getSource();
// // so at this point we need to see if entityFetchSource and attributeDefinition refer to the
// // "same thing". "same thing" == "same type" && "same column(s)"?
// //
// // i make assumptions here that that the attribute type is the EntityType, is that always valid?
//
// final boolean sameType = fetchedPersister.getEntityName().equals(
// entityFetchSource.retrieveFetchSourcePersister().getEntityName()
// );
//
// if ( sameType ) {
// // check same columns as well?
//
// return new KeyManyToOneBidirectionalEntityFetch(
// sessionFactory(),
// //ugh
// LockMode.READ,
// this,
// attributeDefinition,
// (EntityReference) entityFetchSource,
// fetchStrategy
// );
// }
}
final ExpandingQuerySpace leftHandSide = (ExpandingQuerySpace) loadPlanBuildingContext.getQuerySpaces().findQuerySpaceByUid(
entityReference.getQuerySpaceUid()
);
Join join = leftHandSide.addEntityJoin(
attributeDefinition,
fetchedPersister,
loadPlanBuildingContext.getQuerySpaces().generateImplicitUid(),
attributeDefinition.isNullable()
);
final EntityFetch fetch = new EntityFetchImpl(
this,
attributeDefinition,
fetchedPersister,
fetchStrategy,
join
);
addFetch( fetch );
// this.sqlSelectFragmentResolver = new EntityPersisterBasedSqlSelectFragmentResolver( (Queryable) persister );
return fetch;
}
private void addFetch(EntityFetch fetch) {
if ( fetches == null ) {
fetches = new ArrayList<Fetch>();
}
fetches.add( fetch );
}
@Override
public CompositeFetch buildCompositeFetch(
CompositionDefinition attributeDefinition,
LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
throw new WalkingException( "Entity identifier cannot contain persistent collections" );
}
}

View File

@ -0,0 +1,107 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.AbstractQuerySpace;
import org.hibernate.loader.plan2.build.spi.ExpandingQuerySpace;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.CompositeType;
/**
* @author Steve Ebersole
*/
public class EntityQuerySpaceImpl extends AbstractQuerySpace implements ExpandingQuerySpace, EntityQuerySpace {
private final EntityPersister persister;
public EntityQuerySpaceImpl(
EntityPersister persister,
String uid,
QuerySpacesImpl querySpaces,
SessionFactoryImplementor sessionFactory) {
super( uid, Disposition.ENTITY, querySpaces, sessionFactory );
this.persister = persister;
}
@Override
public EntityPersister getEntityPersister() {
return persister;
}
@Override
public JoinDefinedByMetadata addCompositeJoin(CompositionDefinition compositionDefinition, String querySpaceUid) {
final CompositeQuerySpaceImpl rhs = new CompositeQuerySpaceImpl(
getUid(),
compositionDefinition.getType(),
querySpaceUid,
Disposition.COMPOSITE,
getQuerySpaces(),
sessionFactory()
);
final JoinDefinedByMetadata join = new JoinImpl( this, compositionDefinition, rhs, compositionDefinition.isNullable() );
internalGetJoins().add( join );
getQuerySpaces().registerQuerySpace( rhs );
return join;
}
public JoinDefinedByMetadata addEntityJoin(
AttributeDefinition attribute,
EntityPersister persister,
String querySpaceUid,
boolean optional) {
final EntityQuerySpaceImpl rhs = new EntityQuerySpaceImpl(
persister,
querySpaceUid,
getQuerySpaces(),
sessionFactory()
);
final JoinDefinedByMetadata join = new JoinImpl( this, attribute, rhs, optional );
internalGetJoins().add( join );
getQuerySpaces().registerQuerySpace( rhs );
return join;
}
@Override
public JoinDefinedByMetadata addCollectionJoin(
AttributeDefinition attributeDefinition,
CollectionPersister collectionPersister,
String querySpaceUid) {
final CollectionQuerySpaceImpl rhs = new CollectionQuerySpaceImpl(
collectionPersister,
querySpaceUid,
getQuerySpaces(),
sessionFactory()
);
final JoinDefinedByMetadata join = new JoinImpl( this, attributeDefinition, rhs, attributeDefinition.isNullable() );
internalGetJoins().add( join );
getQuerySpaces().registerQuerySpace( rhs );
return join;
}
}

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* @author Steve Ebersole
*/
public class JoinImpl implements JoinDefinedByMetadata {
private final QuerySpace leftHandSide;
private final AttributeDefinition attributeDefinition;
private final QuerySpace rightHandSide;
private final boolean rightHandSideOptional;
public JoinImpl(
QuerySpace leftHandSide,
AttributeDefinition attributeDefinition,
QuerySpace rightHandSide,
boolean rightHandSideOptional) {
this.leftHandSide = leftHandSide;
this.attributeDefinition = attributeDefinition;
this.rightHandSide = rightHandSide;
this.rightHandSideOptional = rightHandSideOptional;
}
@Override
public QuerySpace getLeftHandSide() {
return leftHandSide;
}
@Override
public AttributeDefinition getAttributeDefiningJoin() {
return attributeDefinition;
}
@Override
public QuerySpace getRightHandSide() {
return rightHandSide;
}
@Override
public boolean isRightHandSideOptional() {
return rightHandSideOptional;
}
}

View File

@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.loader.plan2.spi.Return;
/**
* @author Steve Ebersole
*/
public class LoadPlanImpl implements LoadPlan {
private final List<? extends Return> returns;
private final QuerySpaces querySpaces;
private final Disposition disposition;
private final boolean areLazyAttributesForceFetched;
protected LoadPlanImpl(
List<? extends Return> returns,
QuerySpaces querySpaces,
Disposition disposition,
boolean areLazyAttributesForceFetched) {
this.returns = returns;
this.querySpaces = querySpaces;
this.disposition = disposition;
this.areLazyAttributesForceFetched = areLazyAttributesForceFetched;
}
/**
* Creates a {@link Disposition#ENTITY_LOADER} LoadPlan.
*
* @param rootReturn The EntityReturn representation of the entity being loaded.
*/
public LoadPlanImpl(EntityReturn rootReturn, QuerySpaces querySpaces) {
this( Collections.singletonList( rootReturn ), querySpaces, Disposition.ENTITY_LOADER, false );
}
/**
* Creates a {@link Disposition#COLLECTION_INITIALIZER} LoadPlan.
*
* @param rootReturn The CollectionReturn representation of the collection being initialized.
*/
public LoadPlanImpl(CollectionReturn rootReturn, QuerySpaces querySpaces) {
this( Collections.singletonList( rootReturn ), querySpaces, Disposition.COLLECTION_INITIALIZER, false );
}
/**
* Creates a {@link Disposition#MIXED} LoadPlan.
*
* @param returns The mixed Return references
* @param areLazyAttributesForceFetched Should lazy attributes (bytecode enhanced laziness) be fetched also? This
* effects the eventual SQL SELECT-clause which is why we have it here. Currently this is "all-or-none"; you
* can request that all lazy properties across all entities in the loadplan be force fetched or none. There is
* no entity-by-entity option. {@code FETCH ALL PROPERTIES} is the way this is requested in HQL. Would be nice to
* consider this entity-by-entity, as opposed to all-or-none. For example, "fetch the LOB value for the Item.image
* attribute, but no others (leave them lazy)". Not too concerned about having it at the attribute level.
*/
public LoadPlanImpl(List<? extends Return> returns, QuerySpaces querySpaces, boolean areLazyAttributesForceFetched) {
this( returns, querySpaces, Disposition.MIXED, areLazyAttributesForceFetched );
}
@Override
public List<? extends Return> getReturns() {
return returns;
}
@Override
public QuerySpaces getQuerySpaces() {
return querySpaces;
}
@Override
public Disposition getDisposition() {
return disposition;
}
@Override
public boolean areLazyAttributesForceFetched() {
return areLazyAttributesForceFetched;
}
@Override
public boolean hasAnyScalarReturns() {
return disposition == Disposition.MIXED;
}
}

View File

@ -0,0 +1,121 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.build.spi.ExpandingQuerySpaces;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Steve Ebersole
*/
public class QuerySpacesImpl implements QuerySpaces, ExpandingQuerySpaces {
private final SessionFactoryImplementor sessionFactory;
private final List<QuerySpace> roots = new ArrayList<QuerySpace>();
private final Map<String,QuerySpace> querySpaceByUid = new ConcurrentHashMap<String, QuerySpace>();
public QuerySpacesImpl(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
// QuerySpaces impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Iterable<QuerySpace> getRootQuerySpaces() {
return roots;
}
@Override
public QuerySpace findQuerySpaceByUid(String uid) {
return querySpaceByUid.get( uid );
}
// ExpandingQuerySpaces impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private int implicitUidBase = 0;
@Override
public String generateImplicitUid() {
return "<gen:" + implicitUidBase++ + ">";
}
@Override
public EntityQuerySpace makeEntityQuerySpace(String uid, EntityPersister entityPersister) {
if ( querySpaceByUid.containsKey( uid ) ) {
throw new IllegalStateException( "Encountered duplicate QuerySpace uid : " + uid );
}
final EntityQuerySpaceImpl space = new EntityQuerySpaceImpl(
entityPersister,
uid,
this,
sessionFactory
);
roots.add( space );
return space;
}
@Override
public CollectionQuerySpaceImpl makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister) {
if ( querySpaceByUid.containsKey( uid ) ) {
throw new IllegalStateException( "Encountered duplicate QuerySpace uid : " + uid );
}
final CollectionQuerySpaceImpl space = new CollectionQuerySpaceImpl(
collectionPersister,
uid,
this,
sessionFactory
);
roots.add( space );
return space;
}
/**
* Feeds a QuerySpace into this spaces group.
*
* @param querySpace The space
*/
protected void registerQuerySpace(QuerySpace querySpace) {
final QuerySpace previous = querySpaceByUid.put( querySpace.getUid(), querySpace );
if ( previous != null ) {
throw new IllegalStateException( "Encountered duplicate QuerySpace uid : " + querySpace.getUid() );
}
}
}

View File

@ -0,0 +1,153 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.internal;
import org.hibernate.HibernateException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.AbstractMetamodelWalkingLoadPlanBuilderStrategy;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuilderStrategy;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.Return;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* LoadPlanBuilderStrategy implementation used for building LoadPlans based on metamodel-defined fetching. Built
* LoadPlans contain a single root return object, either an {@link EntityReturn} or a {@link CollectionReturn}.
*
* @author Steve Ebersole
*/
public class StandardFetchBasedLoadPlanBuilderStrategy
extends AbstractMetamodelWalkingLoadPlanBuilderStrategy
implements LoadPlanBuilderStrategy {
private final LoadQueryInfluencers loadQueryInfluencers;
private Return rootReturn;
private PropertyPath propertyPath = new PropertyPath( "" );
public StandardFetchBasedLoadPlanBuilderStrategy(
SessionFactoryImplementor sessionFactory,
LoadQueryInfluencers loadQueryInfluencers) {
super( sessionFactory );
this.loadQueryInfluencers = loadQueryInfluencers;
}
@Override
protected boolean supportsRootEntityReturns() {
return true;
}
@Override
protected boolean supportsRootCollectionReturns() {
return true;
}
@Override
protected void addRootReturn(Return rootReturn) {
if ( this.rootReturn != null ) {
throw new HibernateException( "Root return already identified" );
}
this.rootReturn = rootReturn;
}
@Override
public LoadPlan buildLoadPlan() {
if ( EntityReturn.class.isInstance( rootReturn ) ) {
return new LoadPlanImpl( (EntityReturn) rootReturn, getQuerySpaces() );
}
else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
return new LoadPlanImpl( (CollectionReturn) rootReturn, getQuerySpaces() );
}
else {
throw new IllegalStateException( "Unexpected root Return type : " + rootReturn );
}
}
@Override
protected FetchStrategy determineFetchStrategy(AssociationAttributeDefinition attributeDefinition) {
FetchStrategy fetchStrategy = attributeDefinition.determineFetchPlan( loadQueryInfluencers, propertyPath );
if ( fetchStrategy.getTiming() == FetchTiming.IMMEDIATE && fetchStrategy.getStyle() == FetchStyle.JOIN ) {
// see if we need to alter the join fetch to another form for any reason
fetchStrategy = adjustJoinFetchIfNeeded( attributeDefinition, fetchStrategy );
}
return fetchStrategy;
}
protected FetchStrategy adjustJoinFetchIfNeeded(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy) {
if ( currentDepth() > sessionFactory().getSettings().getMaximumFetchDepth() ) {
return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT );
}
if ( attributeDefinition.getType().isCollectionType() && isTooManyCollections() ) {
// todo : have this revert to batch or subselect fetching once "sql gen redesign" is in place
return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT );
}
return fetchStrategy;
}
@Override
protected boolean isTooManyCollections() {
return false;
}
@Override
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
//To change body of implemented methods use File | Settings | File Templates.
}
// @Override
// protected EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition) {
// final String entityName = entityDefinition.getEntityPersister().getEntityName();
// return new EntityReturn(
// sessionFactory(),
// LockMode.NONE, // todo : for now
// entityName
// );
// }
//
// @Override
// protected CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition) {
// final CollectionPersister persister = collectionDefinition.getCollectionPersister();
// final String collectionRole = persister.getRole();
// return new CollectionReturn(
// sessionFactory(),
// LockMode.NONE, // todo : for now
// persister.getOwnerEntityPersister().getEntityName(),
// StringHelper.unqualify( collectionRole )
// );
// }
}

View File

@ -0,0 +1,700 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import org.jboss.logging.Logger;
import org.jboss.logging.MDC;
import org.hibernate.HibernateException;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.internal.QuerySpacesImpl;
import org.hibernate.loader.plan2.internal.CollectionReturnImpl;
import org.hibernate.loader.plan2.internal.EntityReturnImpl;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CollectionFetchableElement;
import org.hibernate.loader.plan2.spi.CollectionFetchableIndex;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.Return;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.Type;
/**
* A LoadPlanBuilderStrategy is a strategy for building a LoadPlan. LoadPlanBuilderStrategy is also a
* AssociationVisitationStrategy, which is used in conjunction with visiting associations via walking
* metamodel definitions.
* <p/>
* So this strategy defines a AssociationVisitationStrategy that walks the metamodel defined associations after
* which is can then build a LoadPlan based on the visited associations. {@link #determineFetchStrategy} Is the
* main decision point
*
* @author Steve Ebersole
*
* @see org.hibernate.loader.plan.spi.build.LoadPlanBuilderStrategy
* @see org.hibernate.persister.walking.spi.AssociationVisitationStrategy
*/
public abstract class AbstractMetamodelWalkingLoadPlanBuilderStrategy implements LoadPlanBuilderStrategy, LoadPlanBuildingContext {
private static final Logger log = Logger.getLogger( AbstractMetamodelWalkingLoadPlanBuilderStrategy.class );
private static final String MDC_KEY = "hibernateLoadPlanWalkPath";
private final SessionFactoryImplementor sessionFactory;
private final QuerySpacesImpl querySpaces;
private final ArrayDeque<ExpandingFetchSource> fetchSourceStack = new ArrayDeque<ExpandingFetchSource>();
protected AbstractMetamodelWalkingLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
this.querySpaces = new QuerySpacesImpl( sessionFactory );
}
public SessionFactoryImplementor sessionFactory() {
return sessionFactory;
}
@Override
public ExpandingQuerySpaces getQuerySpaces() {
return querySpaces;
}
// stack management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static interface FetchStackAware {
public void poppedFromStack();
}
private void pushToStack(ExpandingFetchSource fetchSource) {
log.trace( "Pushing fetch source to stack : " + fetchSource );
mdcStack().push( fetchSource.getPropertyPath() );
fetchSourceStack.addFirst( fetchSource );
}
private MDCStack mdcStack() {
return (MDCStack) MDC.get( MDC_KEY );
}
private ExpandingFetchSource popFromStack() {
final ExpandingFetchSource last = fetchSourceStack.removeFirst();
log.trace( "Popped fetch owner from stack : " + last );
mdcStack().pop();
if ( FetchStackAware.class.isInstance( last ) ) {
( (FetchStackAware) last ).poppedFromStack();
}
return last;
}
private ExpandingFetchSource currentSource() {
return fetchSourceStack.peekFirst();
}
// top-level AssociationVisitationStrategy hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void start() {
if ( ! fetchSourceStack.isEmpty() ) {
throw new WalkingException(
"Fetch owner stack was not empty on start; " +
"be sure to not use LoadPlanBuilderStrategy instances concurrently"
);
}
MDC.put( MDC_KEY, new MDCStack() );
}
@Override
public void finish() {
MDC.remove( MDC_KEY );
fetchSourceStack.clear();
}
protected abstract void addRootReturn(Return rootReturn);
// Entity-level AssociationVisitationStrategy hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected boolean supportsRootEntityReturns() {
return true;
}
@Override
public void startingEntity(EntityDefinition entityDefinition) {
log.tracef(
"%s Starting entity : %s",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
entityDefinition.getEntityPersister().getEntityName()
);
// see if the EntityDefinition is a root...
final boolean isRoot = fetchSourceStack.isEmpty();
if ( ! isRoot ) {
// if not, this call should represent a fetch which should have been handled in #startingAttribute
return;
}
// if we get here, it is a root
if ( !supportsRootEntityReturns() ) {
throw new HibernateException( "This strategy does not support root entity returns" );
}
final EntityReturnImpl entityReturn = new EntityReturnImpl( entityDefinition, this );
addRootReturn( entityReturn );
pushToStack( entityReturn );
}
@Override
public void finishingEntity(EntityDefinition entityDefinition) {
// pop the current fetch owner, and make sure what we just popped represents this entity
final ExpandingFetchSource fetchSource = popFromStack();
if ( ! EntityReference.class.isInstance( fetchSource ) ) {
throw new WalkingException( "Mismatched FetchSource from stack on pop" );
}
final EntityReference entityReference = (EntityReference) fetchSource;
// NOTE : this is not the most exhaustive of checks because of hierarchical associations (employee/manager)
if ( ! entityReference.getEntityPersister().equals( entityDefinition.getEntityPersister() ) ) {
throw new WalkingException( "Mismatched FetchSource from stack on pop" );
}
log.tracef(
"%s Finished entity : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
entityDefinition.getEntityPersister().getEntityName()
);
}
@Override
public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) {
log.tracef(
"%s Starting entity identifier : %s",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName()
);
final EntityReference entityReference = (EntityReference) currentSource();
// perform some stack validation
if ( ! entityReference.getEntityPersister().equals( entityIdentifierDefinition.getEntityDefinition().getEntityPersister() ) ) {
throw new WalkingException(
String.format(
"Encountered unexpected fetch owner [%s] in stack while processing entity identifier for [%s]",
entityReference.getEntityPersister().getEntityName(),
entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName()
)
);
}
pushToStack( (ExpandingEntityIdentifierDescription) entityReference.getIdentifierDescription() );
}
@Override
public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition) {
final ExpandingFetchSource popped = popFromStack();
// perform some stack validation on exit, first on the current stack element we want to pop
if ( ! ExpandingEntityIdentifierDescription.class.isInstance( popped ) ) {
throw new WalkingException( "Unexpected state in ProcessNode stack" );
}
final ExpandingEntityIdentifierDescription identifierDescription = (ExpandingEntityIdentifierDescription) popped;
// and then on the node before it (which should be the entity that owns the identifier being described)
final ExpandingFetchSource entitySource = currentSource();
if ( ! EntityReference.class.isInstance( entitySource ) ) {
throw new WalkingException( "Unexpected state in ProcessNode stack" );
}
final EntityReference entityReference = (EntityReference) entitySource;
if ( entityReference.getIdentifierDescription() != identifierDescription ) {
throw new WalkingException(
String.format(
"Encountered unexpected fetch owner [%s] in stack while processing entity identifier for [%s]",
entityReference.getEntityPersister().getEntityName(),
entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName()
)
);
}
log.tracef(
"%s Finished entity identifier : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getEntityName()
);
}
// Collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private ArrayDeque<CollectionReference> collectionReferenceStack = new ArrayDeque<CollectionReference>();
private void pushToCollectionStack(CollectionReference collectionReference) {
log.trace( "Pushing collection reference to stack : " + collectionReference );
mdcStack().push( collectionReference.getPropertyPath() );
collectionReferenceStack.addFirst( collectionReference );
}
private CollectionReference popFromCollectionStack() {
final CollectionReference last = collectionReferenceStack.removeFirst();
log.trace( "Popped collection reference from stack : " + last );
mdcStack().pop();
if ( FetchStackAware.class.isInstance( last ) ) {
( (FetchStackAware) last ).poppedFromStack();
}
return last;
}
@Override
public void startingCollection(CollectionDefinition collectionDefinition) {
log.tracef(
"%s Starting collection : %s",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
collectionDefinition.getCollectionPersister().getRole()
);
// see if the EntityDefinition is a root...
final boolean isRoot = fetchSourceStack.isEmpty();
if ( ! isRoot ) {
// if not, this call should represent a fetch which should have been handled in #startingAttribute
return;
}
// if we get here, it is a root
if ( ! supportsRootCollectionReturns() ) {
throw new HibernateException( "This strategy does not support root collection returns" );
}
final CollectionReturn collectionReturn = new CollectionReturnImpl( collectionDefinition, this );
collectionReferenceStack.addFirst( collectionReturn );
addRootReturn( collectionReturn );
}
protected boolean supportsRootCollectionReturns() {
return true;
}
@Override
public void finishingCollection(CollectionDefinition collectionDefinition) {
// pop the current fetch owner, and make sure what we just popped represents this collection
final CollectionReference collectionReference = popFromCollectionStack();
if ( ! collectionReference.getCollectionPersister().equals( collectionDefinition.getCollectionPersister() ) ) {
throw new WalkingException( "Mismatched FetchSource from stack on pop" );
}
log.tracef(
"%s Finished collection : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
collectionDefinition.getCollectionPersister().getRole()
);
}
@Override
public void startingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) {
final Type indexType = collectionIndexDefinition.getType();
if ( indexType.isAssociationType() || indexType.isComponentType() ) {
final CollectionReference collectionReference = collectionReferenceStack.peekFirst();
final CollectionFetchableIndex indexGraph = collectionReference.getIndexGraph();
if ( indexGraph == null ) {
throw new WalkingException( "Collection reference did not return index handler" );
}
pushToStack( (ExpandingFetchSource) indexGraph );
}
}
@Override
public void finishingCollectionIndex(CollectionIndexDefinition collectionIndexDefinition) {
// nothing to do here
// - the element graph pushed while starting would be popped in finishingEntity/finishingComposite
}
@Override
public void startingCollectionElements(CollectionElementDefinition elementDefinition) {
if ( elementDefinition.getType().isAssociationType() || elementDefinition.getType().isComponentType() ) {
final CollectionReference collectionReference = collectionReferenceStack.peekFirst();
final CollectionFetchableElement elementGraph = collectionReference.getElementGraph();
if ( elementGraph == null ) {
throw new WalkingException( "Collection reference did not return element handler" );
}
pushToStack( (ExpandingFetchSource) elementGraph );
}
}
@Override
public void finishingCollectionElements(CollectionElementDefinition elementDefinition) {
// nothing to do here
// - the element graph pushed while starting would be popped in finishing/Entity/finishingComposite
}
@Override
public void startingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
log.tracef(
"%s Starting composite collection element for (%s)",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
);
}
@Override
public void finishingCompositeCollectionElement(CompositeCollectionElementDefinition compositeElementDefinition) {
// pop the current fetch owner, and make sure what we just popped represents this composition
final ExpandingFetchSource popped = popFromStack();
if ( ! CollectionFetchableElement.class.isInstance( popped ) ) {
throw new WalkingException( "Mismatched FetchSource from stack on pop" );
}
// NOTE : not much else we can really check here atm since on the walking spi side we do not have path
log.tracef(
"%s Finished composite element for : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole()
);
}
@Override
public void startingComposite(CompositionDefinition compositionDefinition) {
log.tracef(
"%s Starting composition : %s",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
compositionDefinition.getName()
);
if ( fetchSourceStack.isEmpty() ) {
throw new HibernateException( "A component cannot be the root of a walk nor a graph" );
}
}
@Override
public void finishingComposite(CompositionDefinition compositionDefinition) {
// pop the current fetch owner, and make sure what we just popped represents this composition
final ExpandingFetchSource popped = popFromStack();
if ( ! CompositeFetch.class.isInstance( popped ) ) {
throw new WalkingException( "Mismatched FetchSource from stack on pop" );
}
// NOTE : not much else we can really check here atm since on the walking spi side we do not have path
log.tracef(
"%s Finished composition : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
compositionDefinition.getName()
);
}
@Override
public boolean startingAttribute(AttributeDefinition attributeDefinition) {
log.tracef(
"%s Starting attribute %s",
StringHelper.repeat( ">>", fetchSourceStack.size() ),
attributeDefinition
);
final Type attributeType = attributeDefinition.getType();
final boolean isComponentType = attributeType.isComponentType();
final boolean isAssociationType = attributeType.isAssociationType();
final boolean isBasicType = ! ( isComponentType || isAssociationType );
if ( isBasicType ) {
return true;
}
else if ( isAssociationType ) {
return handleAssociationAttribute( (AssociationAttributeDefinition) attributeDefinition );
}
else {
return handleCompositeAttribute( (CompositionDefinition) attributeDefinition );
}
}
@Override
public void finishingAttribute(AttributeDefinition attributeDefinition) {
log.tracef(
"%s Finishing up attribute : %s",
StringHelper.repeat( "<<", fetchSourceStack.size() ),
attributeDefinition
);
}
private Map<AssociationKey,FetchSource> fetchedAssociationKeyOwnerMap = new HashMap<AssociationKey, FetchSource>();
@Override
public void associationKeyRegistered(AssociationKey associationKey) {
// todo : use this information to maintain a map of AssociationKey->FetchOwner mappings (associationKey + current fetchOwner stack entry)
// that mapping can then be used in #foundCircularAssociationKey to build the proper BiDirectionalEntityFetch
// based on the mapped owner
fetchedAssociationKeyOwnerMap.put( associationKey, currentSource() );
}
//
// @Override
// public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
// // todo : use this information to create the BiDirectionalEntityFetch instances
// final FetchOwner fetchOwner = fetchedAssociationKeyOwnerMap.get( associationKey );
// if ( fetchOwner == null ) {
// throw new IllegalStateException(
// String.format(
// "Expecting AssociationKey->FetchOwner mapping for %s",
// associationKey.toString()
// )
// );
// }
//
// currentFetchOwner().addFetch( new CircularFetch( currentFetchOwner(), fetchOwner, attributeDefinition ) );
// }
//
// public static class CircularFetch implements BidirectionalEntityFetch, EntityReference, Fetch {
// private final FetchOwner circularFetchOwner;
// private final FetchOwner associationOwner;
// private final AttributeDefinition attributeDefinition;
//
// private final EntityReference targetEntityReference;
//
// private final FetchStrategy fetchStrategy = new FetchStrategy(
// FetchTiming.IMMEDIATE,
// FetchStyle.JOIN
// );
//
// public CircularFetch(FetchOwner circularFetchOwner, FetchOwner associationOwner, AttributeDefinition attributeDefinition) {
// this.circularFetchOwner = circularFetchOwner;
// this.associationOwner = associationOwner;
// this.attributeDefinition = attributeDefinition;
// this.targetEntityReference = resolveEntityReference( associationOwner );
// }
//
// @Override
// public EntityReference getTargetEntityReference() {
// return targetEntityReference;
// }
//
// protected static EntityReference resolveEntityReference(FetchOwner owner) {
// if ( EntityReference.class.isInstance( owner ) ) {
// return (EntityReference) owner;
// }
// if ( CompositeFetch.class.isInstance( owner ) ) {
// return resolveEntityReference( ( (CompositeFetch) owner ).getOwner() );
// }
// // todo : what others?
//
// throw new UnsupportedOperationException(
// "Unexpected FetchOwner type [" + owner + "] encountered trying to build circular fetch"
// );
//
// }
//
// @Override
// public FetchOwner getSource() {
// return circularFetchOwner;
// }
//
// @Override
// public PropertyPath getPropertyPath() {
// return null; //To change body of implemented methods use File | Settings | File Templates.
// }
//
// @Override
// public Type getFetchedType() {
// return attributeDefinition.getType();
// }
//
// @Override
// public FetchStrategy getFetchStrategy() {
// return fetchStrategy;
// }
//
// @Override
// public boolean isNullable() {
// return attributeDefinition.isNullable();
// }
//
// @Override
// public String getAdditionalJoinConditions() {
// return null;
// }
//
// @Override
// public String[] toSqlSelectFragments(String alias) {
// return new String[0];
// }
//
// @Override
// public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {
// // todo : will need this implemented
// return null;
// }
//
// @Override
// public LockMode getLockMode() {
// return targetEntityReference.getLockMode();
// }
//
// @Override
// public EntityReference getEntityReference() {
// return targetEntityReference;
// }
//
// @Override
// public EntityPersister getEntityPersister() {
// return targetEntityReference.getEntityPersister();
// }
//
// @Override
// public IdentifierDescription getIdentifierDescription() {
// return targetEntityReference.getIdentifierDescription();
// }
//
// @Override
// public void injectIdentifierDescription(IdentifierDescription identifierDescription) {
// throw new IllegalStateException( "IdentifierDescription should never be injected from circular fetch side" );
// }
// }
@Override
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition) {
// for ANY mappings we need to build a Fetch:
// 1) fetch type is SELECT, timing might be IMMEDIATE or DELAYED depending on whether it was defined as lazy
// 2) (because the fetch cannot be a JOIN...) do not push it to the stack
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
// final FetchOwner fetchOwner = currentFetchOwner();
// fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
//
// fetchOwner.buildAnyFetch(
// attributeDefinition,
// anyDefinition,
// fetchStrategy,
// this
// );
}
protected boolean handleCompositeAttribute(CompositionDefinition attributeDefinition) {
final ExpandingFetchSource currentSource = currentSource();
final CompositeFetch fetch = currentSource.buildCompositeFetch( attributeDefinition, this );
pushToStack( (ExpandingFetchSource) fetch );
return true;
}
protected boolean handleAssociationAttribute(AssociationAttributeDefinition attributeDefinition) {
// todo : this seems to not be correct for one-to-one
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
return false;
}
// if ( fetchStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
// return false;
// }
final ExpandingFetchSource currentSource = currentSource();
currentSource.validateFetchPlan( fetchStrategy, attributeDefinition );
final AssociationAttributeDefinition.AssociationNature nature = attributeDefinition.getAssociationNature();
if ( nature == AssociationAttributeDefinition.AssociationNature.ANY ) {
return false;
}
if ( nature == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
EntityFetch fetch = currentSource.buildEntityFetch(
attributeDefinition,
fetchStrategy,
this
);
pushToStack( (ExpandingFetchSource) fetch );
}
else {
// Collection
CollectionFetch fetch = currentSource.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
pushToCollectionStack( fetch );
}
return true;
}
protected abstract FetchStrategy determineFetchStrategy(AssociationAttributeDefinition attributeDefinition);
protected int currentDepth() {
return fetchSourceStack.size();
}
protected boolean isTooManyCollections() {
return false;
}
// protected abstract EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition);
//
// protected abstract CollectionReturn buildRootCollectionReturn(CollectionDefinition collectionDefinition);
// LoadPlanBuildingContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory();
}
/**
* Used as the MDC object for logging purposes. Because of the recursive calls it is often useful (while debugging)
* to be able to see the "property path" as part of the logging output. This class helps fulfill that role
* here by acting as the object that gets put into the logging libraries underlying MDC.
*/
public static class MDCStack {
private ArrayDeque<PropertyPath> pathStack = new ArrayDeque<PropertyPath>();
public void push(PropertyPath path) {
pathStack.addFirst( path );
}
public void pop() {
pathStack.removeFirst();
}
public String toString() {
final PropertyPath path = pathStack.peekFirst();
return path == null ? "<no-path>" : path.getFullPath();
}
}
}

View File

@ -0,0 +1,95 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.AbstractPlanNode;
import org.hibernate.loader.plan2.build.internal.QuerySpacesImpl;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.loader.plan2.spi.QuerySpace;
/**
* Convenience base class for QuerySpace implementations.
*
* @author Steve Ebersole
*/
public abstract class AbstractQuerySpace extends AbstractPlanNode implements QuerySpace {
private final String uid;
private final Disposition disposition;
private final QuerySpacesImpl querySpaces;
private List<Join> joins;
public AbstractQuerySpace(
String uid,
Disposition disposition,
QuerySpacesImpl querySpaces,
SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
this.uid = uid;
this.disposition = disposition;
this.querySpaces = querySpaces;
}
// todo : copy ctor - that depends how graph copying works here...
/**
* Provides subclasses access to the spaces to which this space belongs.
*
* @return The query spaces
*/
protected QuerySpacesImpl getQuerySpaces() {
return querySpaces;
}
@Override
public String getUid() {
return uid;
}
@Override
public Disposition getDisposition() {
return disposition;
}
@Override
public Iterable<Join> getJoins() {
return joins == null
? Collections.<Join>emptyList()
: joins;
}
protected List<Join> internalGetJoins() {
if ( joins == null ) {
joins = new ArrayList<Join>();
}
return joins;
}
}

View File

@ -0,0 +1,33 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.loader.plan2.spi.EntityIdentifierDescription;
/**
* @author Steve Ebersole
*/
public interface ExpandingEntityIdentifierDescription extends EntityIdentifierDescription, ExpandingFetchSource {
}

View File

@ -0,0 +1,64 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
/**
* Describes the internal contract for things which can contain fetches. Used to request building
* the different types of fetches.
*
* @author Steve Ebersole
*/
public interface ExpandingFetchSource extends FetchSource {
/**
* Is the asserted plan valid from this owner to a fetch?
*
* @param fetchStrategy The type of fetch to validate
* @param attributeDefinition The attribute to be fetched
*/
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition);
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext);
public CompositeFetch buildCompositeFetch(
CompositionDefinition attributeDefinition,
LoadPlanBuildingContext loadPlanBuildingContext);
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext);
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.loader.plan2.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
/**
* @author Steve Ebersole
*/
public interface ExpandingQuerySpace extends QuerySpace {
public JoinDefinedByMetadata addCompositeJoin(CompositionDefinition compositionDefinition, String querySpaceUid);
public JoinDefinedByMetadata addEntityJoin(
AttributeDefinition attributeDefinition,
EntityPersister persister,
String querySpaceUid,
boolean optional);
public JoinDefinedByMetadata addCollectionJoin(
AttributeDefinition attributeDefinition,
CollectionPersister collectionPersister,
String querySpaceUid);
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Steve Ebersole
*/
public interface ExpandingQuerySpaces extends QuerySpaces {
public String generateImplicitUid();
public EntityQuerySpace makeEntityQuerySpace(String uid, EntityPersister entityPersister);
public CollectionQuerySpaceImpl makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister);
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
/**
* Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for
* building {@link org.hibernate.loader.plan.spi.LoadPlan} instances.
*
* @author Steve Ebersole
*/
public interface LoadPlanBuilderStrategy extends AssociationVisitationStrategy {
/**
* After visitation is done, build the load plan.
*
* @return The load plan
*/
public LoadPlan buildLoadPlan();
}

View File

@ -0,0 +1,43 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.spi.QuerySpaces;
/**
* Provides access to context needed in building a LoadPlan.
*
* @author Steve Ebersole
*/
public interface LoadPlanBuildingContext {
/**
* Access to the SessionFactory
*
* @return The SessionFactory
*/
public SessionFactoryImplementor getSessionFactory();
public ExpandingQuerySpaces getQuerySpaces();
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.build.spi;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor;
/**
* A metadata-driven builder of LoadPlans. Coordinates between the {@link org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor} and
* {@link LoadPlanBuilderStrategy}.
*
* @author Steve Ebersole
*
* @see org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor
*/
public class MetadataDrivenLoadPlanBuilder {
/**
* Coordinates building a LoadPlan that defines just a single root entity return (may have fetches).
* <p/>
* Typically this includes building load plans for entity loading or cascade loading.
*
* @param strategy The strategy defining the load plan shaping
* @param persister The persister for the entity forming the root of the load plan.
*
* @return The built load plan.
*/
public static LoadPlan buildRootEntityLoadPlan(LoadPlanBuilderStrategy strategy, EntityPersister persister) {
MetadataDrivenModelGraphVisitor.visitEntity( strategy, persister );
return strategy.buildLoadPlan();
}
/**
* Coordinates building a LoadPlan that defines just a single root collection return (may have fetches).
*
* @param strategy The strategy defining the load plan shaping
* @param persister The persister for the collection forming the root of the load plan.
*
* @return The built load plan.
*/
public static LoadPlan buildRootCollectionLoadPlan(LoadPlanBuilderStrategy strategy, CollectionPersister persister) {
MetadataDrivenModelGraphVisitor.visitCollection( strategy, persister );
return strategy.buildLoadPlan();
}
}

View File

@ -0,0 +1,4 @@
/**
* Defines the SPI for building a metamodel-driven LoadPlan
*/
package org.hibernate.loader.plan2.build.spi;

View File

@ -0,0 +1,147 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeIndexGraph;
import org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionFetchableElement;
import org.hibernate.loader.plan2.spi.CollectionFetchableIndex;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionElementCompositeJoin;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionElementEntityJoin;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionIndexCompositeJoin;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionIndexEntityJoin;
/**
* @author Steve Ebersole
*/
public abstract class AbstractCollectionReference implements CollectionReference {
private final CollectionQuerySpaceImpl collectionQuerySpace;
private final PropertyPath propertyPath;
private final CollectionFetchableIndex index;
private final CollectionFetchableElement element;
protected AbstractCollectionReference(
CollectionQuerySpaceImpl collectionQuerySpace,
PropertyPath propertyPath,
LoadPlanBuildingContext loadPlanBuildingContext) {
this.collectionQuerySpace = collectionQuerySpace;
this.propertyPath = propertyPath;
this.index = buildIndexGraph( collectionQuerySpace, loadPlanBuildingContext );
this.element = buildElementGraph( collectionQuerySpace, loadPlanBuildingContext );
}
private CollectionFetchableIndex buildIndexGraph(
CollectionQuerySpaceImpl collectionQuerySpace,
LoadPlanBuildingContext loadPlanBuildingContext) {
final CollectionPersister persister = collectionQuerySpace.getCollectionPersister();
if ( persister.hasIndex() ) {
final Type type = persister.getIndexType();
if ( type.isAssociationType() ) {
if ( type.isEntityType() ) {
final EntityPersister indexPersister = persister.getFactory().getEntityPersister(
( (EntityType) type ).getAssociatedEntityName()
);
final CollectionIndexEntityJoin join = collectionQuerySpace.addIndexEntityJoin(
indexPersister,
loadPlanBuildingContext
);
return new CollectionFetchableIndexEntityGraph( this, indexPersister, join );
}
}
else if ( type.isComponentType() ) {
final CollectionIndexCompositeJoin join = collectionQuerySpace.addIndexCompositeJoin(
(CompositeType) type,
loadPlanBuildingContext
);
return new CollectionFetchableIndexCompositeGraph( this, join );
}
}
return null;
}
private CollectionFetchableElement buildElementGraph(
CollectionQuerySpaceImpl collectionQuerySpace,
LoadPlanBuildingContext loadPlanBuildingContext) {
final CollectionPersister persister = collectionQuerySpace.getCollectionPersister();
final Type type = persister.getElementType();
if ( type.isAssociationType() ) {
if ( type.isEntityType() ) {
final EntityPersister indexPersister = persister.getFactory().getEntityPersister(
( (EntityType) type ).getAssociatedEntityName()
);
final CollectionElementEntityJoin join = collectionQuerySpace.addElementEntityJoin( indexPersister, loadPlanBuildingContext );
return new CollectionFetchableElementEntityGraph( this, indexPersister, join );
}
}
else if ( type.isComponentType() ) {
final CollectionElementCompositeJoin join = collectionQuerySpace.addElementCompositeJoin(
(CompositeType) type,
loadPlanBuildingContext
);
return new CollectionFetchableElementCompositeGraph( this, join );
}
return null;
}
@Override
public String getQuerySpaceUid() {
return collectionQuerySpace.getUid();
}
@Override
public CollectionPersister getCollectionPersister() {
return collectionQuerySpace.getCollectionPersister();
}
@Override
public CollectionFetchableIndex getIndexGraph() {
return index;
}
@Override
public CollectionFetchableElement getElementGraph() {
return element;
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
}

View File

@ -0,0 +1,126 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import java.util.List;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public abstract class AbstractCompositeFetch implements CompositeFetch, ExpandingFetchSource {
private static final FetchStrategy FETCH_STRATEGY = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
private final CompositeType compositeType;
private final PropertyPath propertyPath;
private List<Fetch> fetches;
protected AbstractCompositeFetch(CompositeType compositeType, PropertyPath propertyPath) {
this.compositeType = compositeType;
this.propertyPath = propertyPath;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
// anything to do here?
}
@Override
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
// todo : implement
return null;
}
@Override
public CompositeFetch buildCompositeFetch(
CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) {
// todo : implement
return null;
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
// todo : implement
return null;
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
public FetchStrategy getFetchStrategy() {
return FETCH_STRATEGY;
}
@Override
public Type getFetchedType() {
return compositeType;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public String getAdditionalJoinConditions() {
return null;
}
@Override
public Fetch[] getFetches() {
return (fetches == null) ? NO_FETCHES : fetches.toArray( new Fetch[fetches.size()] );
}
// this is being removed to be more ogm/search friendly
@Override
public String[] toSqlSelectFragments(String alias) {
return new String[0]; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,110 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import java.util.List;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.internal.EntityIdentifierDescriptionImpl;
import org.hibernate.loader.plan2.build.spi.ExpandingEntityIdentifierDescription;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
/**
* @author Steve Ebersole
*/
public abstract class AbstractEntityReference implements EntityReference, ExpandingFetchSource {
private final EntityPersister entityPersister;
private final PropertyPath propertyPath;
private ExpandingEntityIdentifierDescription identifierDescription;
private List<Fetch> fetches;
public AbstractEntityReference(EntityPersister entityPersister, PropertyPath propertyPath) {
this.entityPersister = entityPersister;
this.propertyPath = propertyPath;
this.identifierDescription = new EntityIdentifierDescriptionImpl( this );
}
protected abstract EntityQuerySpace getEntityQuerySpace();
@Override
public String getQuerySpaceUid() {
return getEntityQuerySpace().getUid();
}
@Override
public EntityPersister getEntityPersister() {
return entityPersister;
}
@Override
public ExpandingEntityIdentifierDescription getIdentifierDescription() {
return identifierDescription;
}
@Override
public PropertyPath getPropertyPath() {
return propertyPath;
}
@Override
public Fetch[] getFetches() {
return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] );
}
@Override
public EntityFetch buildEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public CompositeFetch buildCompositeFetch(
CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CollectionFetchableElement;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionElementCompositeJoin;
/**
* @author Steve Ebersole
*/
public class CollectionFetchableElementCompositeGraph
extends AbstractCompositeFetch
implements CompositeFetch, CollectionFetchableElement {
private final CollectionReference collectionReference;
private final CollectionElementCompositeJoin compositeJoin;
public CollectionFetchableElementCompositeGraph(
CollectionReference collectionReference,
CollectionElementCompositeJoin compositeJoin) {
super(
(CompositeType) compositeJoin.getCollectionQuerySpace().getCollectionPersister().getIndexType(),
collectionReference.getPropertyPath().append( "<element>" )
);
this.collectionReference = collectionReference;
this.compositeJoin = compositeJoin;
}
@Override
public CollectionReference getCollectionReference() {
return collectionReference;
}
@Override
public FetchSource getSource() {
return collectionReference.getIndexGraph();
}
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
throw new WalkingException( "Encountered collection as part of fetched Collection composite-element" );
}
}

View File

@ -0,0 +1,65 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.spi.CollectionFetchableElement;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* @author Steve Ebersole
*/
public class CollectionFetchableElementEntityGraph extends AbstractEntityReference implements CollectionFetchableElement {
private final CollectionReference collectionReference;
private final EntityQuerySpace entityQuerySpace;
public CollectionFetchableElementEntityGraph(
CollectionReference collectionReference,
EntityPersister elementPersister,
Join entityJoin) {
super( elementPersister, collectionReference.getPropertyPath().append( "<elements>" ) );
this.collectionReference = collectionReference;
this.entityQuerySpace = (EntityQuerySpace) entityJoin.getRightHandSide();
}
@Override
protected EntityQuerySpace getEntityQuerySpace() {
return entityQuerySpace;
}
@Override
public CollectionReference getCollectionReference() {
return collectionReference;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
//To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@ -0,0 +1,95 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.CollectionFetchableIndex;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
import static org.hibernate.loader.plan2.build.internal.CollectionQuerySpaceImpl.CollectionIndexCompositeJoin;
/**
* @author Steve Ebersole
*/
public class CollectionFetchableIndexCompositeGraph
extends AbstractCompositeFetch
implements CompositeFetch, CollectionFetchableIndex {
private final CollectionReference collectionReference;
private final CollectionIndexCompositeJoin compositeJoin;
public CollectionFetchableIndexCompositeGraph(
CollectionReference collectionReference,
CollectionIndexCompositeJoin compositeJoin) {
super(
(CompositeType) compositeJoin.getCollectionQuerySpace().getCollectionPersister().getIndexType(),
collectionReference.getPropertyPath().append( "<index>" )
);
this.collectionReference = collectionReference;
this.compositeJoin = compositeJoin;
}
@Override
public CollectionReference getCollectionReference() {
return collectionReference;
}
@Override
public FetchSource getSource() {
return collectionReference.getIndexGraph();
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
// metamodel should already disallow collections to be defined as part of a collection composite-index
// so, nothing to do here
super.validateFetchPlan( fetchStrategy, attributeDefinition );
}
// todo : override buildCompositeFetch as well to account for nested composites?
// the idea would be to find nested composites attempting to define a collection. but again, the metramodel
// is supposed to disallow that anyway.
@Override
public CollectionFetch buildCollectionFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
LoadPlanBuildingContext loadPlanBuildingContext) {
throw new WalkingException( "Encountered collection as part of the Map composite-index" );
}
}

View File

@ -0,0 +1,64 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.spi.CollectionFetchableIndex;
import org.hibernate.loader.plan2.spi.CollectionReference;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* @author Steve Ebersole
*/
public class CollectionFetchableIndexEntityGraph extends AbstractEntityReference implements CollectionFetchableIndex {
private final CollectionReference collectionReference;
private final EntityQuerySpace entityQuerySpace;
public CollectionFetchableIndexEntityGraph(
CollectionReference collectionReference,
EntityPersister indexPersister,
Join entityJoin) {
super( indexPersister, collectionReference.getPropertyPath().append( "<index>" ) );
this.collectionReference = collectionReference;
this.entityQuerySpace = (EntityQuerySpace) entityJoin.getRightHandSide();
}
@Override
public CollectionReference getCollectionReference() {
return collectionReference;
}
@Override
protected EntityQuerySpace getEntityQuerySpace() {
return entityQuerySpace;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.persister.walking.spi.CollectionDefinition;
/**
* @author Steve Ebersole
*/
public class CollectionReturnImpl extends AbstractCollectionReference implements CollectionReturn {
public CollectionReturnImpl(CollectionDefinition collectionDefinition, LoadPlanBuildingContext context) {
super(
context.getQuerySpaces().makeCollectionQuerySpace(
context.getQuerySpaces().generateImplicitUid(),
collectionDefinition.getCollectionPersister()
),
new PropertyPath( "[" + collectionDefinition.getCollectionPersister().getRole() + "]" ),
context
);
}
}

View File

@ -0,0 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.WalkingException;
import org.hibernate.type.EntityType;
/**
* @author Steve Ebersole
*/
public class EntityFetchImpl extends AbstractEntityReference implements EntityFetch {
private final FetchSource fetchSource;
private final AttributeDefinition fetchedAttribute;
private final EntityPersister fetchedPersister;
private final FetchStrategy fetchStrategy;
private final Join fetchedJoin;
public EntityFetchImpl(
ExpandingFetchSource fetchSource,
AssociationAttributeDefinition fetchedAttribute,
EntityPersister fetchedPersister,
FetchStrategy fetchStrategy,
Join fetchedJoin) {
super(
fetchedPersister,
fetchSource.getPropertyPath().append( fetchedAttribute.getName() )
);
this.fetchSource = fetchSource;
this.fetchedAttribute = fetchedAttribute;
this.fetchedPersister = fetchedPersister;
this.fetchStrategy = fetchStrategy;
this.fetchedJoin = fetchedJoin;
}
@Override
protected EntityQuerySpace getEntityQuerySpace() {
return (EntityQuerySpace) fetchedJoin.getRightHandSide();
}
@Override
public FetchSource getSource() {
return fetchSource;
}
@Override
public FetchStrategy getFetchStrategy() {
return fetchStrategy;
}
@Override
public EntityType getFetchedType() {
return (EntityType) fetchedAttribute.getType();
}
@Override
public boolean isNullable() {
return fetchedAttribute.isNullable();
}
@Override
public String getAdditionalJoinConditions() {
return null;
}
@Override
public String[] toSqlSelectFragments(String alias) {
return new String[0]; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
if ( attributeDefinition.getType().isCollectionType() ) {
throw new WalkingException(
"Encountered collection attribute in identifier fetches: " + attributeDefinition.getSource() +
"." + attributeDefinition.getName()
);
}
// todo : allow bi-directional key-many-to-one fetches?
// those do cause problems in Loader; question is whether those are indicative of that situation or
// of Loaders ability to handle it.
}
}

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
/**
* @author Steve Ebersole
*/
public class EntityReturnImpl extends AbstractEntityReference implements EntityReturn, ExpandingFetchSource {
private final EntityQuerySpace entityQuerySpace;
public EntityReturnImpl(EntityDefinition entityDefinition, LoadPlanBuildingContext loadPlanBuildingContext) {
super(
entityDefinition.getEntityPersister(),
new PropertyPath( entityDefinition.getEntityPersister().getEntityName() )
);
this.entityQuerySpace = loadPlanBuildingContext.getQuerySpaces().makeEntityQuerySpace(
loadPlanBuildingContext.getQuerySpaces().generateImplicitUid(),
entityDefinition.getEntityPersister()
);
}
@Override
protected EntityQuerySpace getEntityQuerySpace() {
return entityQuerySpace;
}
@Override
public void validateFetchPlan(FetchStrategy fetchStrategy, AttributeDefinition attributeDefinition) {
// nothing to do here really
}
}

View File

@ -0,0 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.internal;
/**
* @author Steve Ebersole
*/
public class ScalarReturnImpl {
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.type.CollectionType;
/**
* Models the requested fetching of a persistent collection attribute.
*
* @author Steve Ebersole
*/
public interface CollectionFetch extends Fetch, CollectionReference {
@Override
public CollectionType getFetchedType();
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* A collection element which can be a FetchSource.
*
* @author Steve Ebersole
*/
public interface CollectionFetchableElement extends FetchSource {
/**
* Reference back to the collection to which this element belongs
*
* @return the collection reference.
*/
public CollectionReference getCollectionReference();
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* A collection index which can be a FetchSource.
*
* @author Steve Ebersole
*/
public interface CollectionFetchableIndex extends FetchSource {
/**
* Reference back to the collection to which this index belongs
*
* @return the collection reference.
*/
public CollectionReference getCollectionReference();
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.persister.collection.CollectionPersister;
/**
* Models a QuerySpace for a persistent collection.
* <p/>
* It's {@link #getDisposition()} result will be {@link Disposition#COLLECTION}
*
* @author Steve Ebersole
*/
public interface CollectionQuerySpace extends QuerySpace {
/**
* Retrieve the collection persister this QuerySpace refers to.
*
* @return The collection persister.
*/
public CollectionPersister getCollectionPersister();
}

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister;
/**
* Represents a reference to a persistent collection either as a Return or as a Fetch
*
* @author Steve Ebersole
*/
public interface CollectionReference {
/**
* Obtain the UID of the QuerySpace (specifically a {@link CollectionQuerySpace}) that this CollectionReference
* refers to.
*
* @return The UID
*/
public String getQuerySpaceUid();
/**
* Retrieves the CollectionPersister describing the collection associated with this Return.
*
* @return The CollectionPersister.
*/
public CollectionPersister getCollectionPersister();
/**
* Retrieve the metadata about the index of this collection *as a FetchSource*. Will return
* {@code null} when:<ul>
* <li>the collection is not indexed</li>
* <li>the index is not a composite or entity (cannot act as a FetchSource)</li>
* </ul>
* <p/>
* Works only for map keys, since a List index (int type) cannot act as a FetchSource.
* <p/>
*
* @return The collection index metadata as a FetchSource, or {@code null}.
*/
public CollectionFetchableIndex getIndexGraph();
/**
* Retrieve the metadata about the elements of this collection *as a FetchSource*. Will return
* {@code null} when the element is not a composite or entity (cannot act as a FetchSource).
* Works only for map keys, since a List index cannot be anything other than an int which cannot be a FetchSource.
* <p/>
*
* @return The collection element metadata as a FetchSource, or {@code null}.
*/
public CollectionFetchableElement getElementGraph();
/**
* Retrieve the PropertyPath to this reference.
*
* @return The PropertyPath
*/
public PropertyPath getPropertyPath();
}

View File

@ -0,0 +1,33 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Models the a persistent collection as root {@link Return}. Pertinent to collection initializer
* ({@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#COLLECTION_INITIALIZER}) LoadPlans only,
*
* @author Steve Ebersole
*/
public interface CollectionReturn extends CollectionReference, Return {
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Models the requested fetching of a composite attribute.
*
* @author Steve Ebersole
*/
public interface CompositeFetch extends Fetch, FetchSource {
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Models a QuerySpace for a composition (component/embeddable).
* <p/>
* It's {@link #getDisposition()} result will be {@link Disposition#COMPOSITE}
*
* @author Steve Ebersole
*/
public interface CompositeQuerySpace extends QuerySpace {
}

View File

@ -0,0 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* @author Steve Ebersole
*/
public interface EntityFetch extends Fetch, EntityReference {
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Descriptor for the identifier of an entity as a FetchSource (which allows for key-many-to-one handling).
*
* @author Steve Ebersole
*/
public interface EntityIdentifierDescription extends FetchSource {
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.persister.entity.EntityPersister;
/**
* Models a QuerySpace specific to an entity (EntityPersister).
* <p/>
* It's {@link #getDisposition()} result will be {@link Disposition#ENTITY}
*
* @author Steve Ebersole
*/
public interface EntityQuerySpace extends QuerySpace {
/**
* Retrieve the EntityPersister that this QuerySpace refers to.
*
* @return The entity persister
*/
public EntityPersister getEntityPersister();
}

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.persister.entity.EntityPersister;
/**
* Represents a reference to an entity either as a return or as a fetch
*
* @author Steve Ebersole
*/
public interface EntityReference extends FetchSource {
/**
* Obtain the UID of the QuerySpace (specifically a {@link CollectionQuerySpace}) that this CollectionReference
* refers to.
*
* @return The UID
*/
public String getQuerySpaceUid();
/**
* Retrieves the EntityPersister describing the entity associated with this Return.
*
* @return The EntityPersister.
*/
public EntityPersister getEntityPersister();
/**
* Get the description of this entity's identifier.
*
* @return The identifier description.
*/
public EntityIdentifierDescription getIdentifierDescription();
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Models the an entity as root {@link Return}. Pertinent to entity loader
* ({@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#ENTITY_LOADER}) and mixed
* ({@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#MIXED}) LoadPlans
*
* @author Steve Ebersole
*/
public interface EntityReturn extends EntityReference, Return {
}

View File

@ -0,0 +1,85 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.type.Type;
/**
* Contract for associations that are being fetched.
* <p/>
* NOTE : can represent components/embeddables
*
* @author Steve Ebersole
*/
public interface Fetch {
/**
* Obtain the owner of this fetch.
*
* @return The fetch owner.
*/
public FetchSource getSource();
/**
* Get the property path to this fetch
*
* @return The property path
*/
public PropertyPath getPropertyPath();
/**
* Gets the fetch strategy for this fetch.
*
* @return the fetch strategy for this fetch.
*/
public FetchStrategy getFetchStrategy();
/**
* Get the Hibernate Type that describes the fetched attribute
*
* @return The Type of the fetched attribute
*/
public Type getFetchedType();
/**
* Is this fetch nullable?
*
* @return true, if this fetch is nullable; false, otherwise.
*/
public boolean isNullable();
// Hoping to make these go away ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public String getAdditionalJoinConditions();
/**
* Generates the SQL select fragments for this fetch. A select fragment is the column and formula references.
*
* @return the select fragments
*/
public String[] toSqlSelectFragments(String alias);
}

View File

@ -0,0 +1,121 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan.spi.SqlSelectFragmentResolver;
import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.type.Type;
/**
* Contract for a FetchSource (aka, the thing that owns the fetched attribute).
*
*
* @author Steve Ebersole
*/
public interface FetchSource {
/**
* Convenient constant for returning no fetches from {@link #getFetches()}
*/
public static final Fetch[] NO_FETCHES = new Fetch[0];
/**
* Get the property path to this fetch owner
*
* @return The property path
*/
public PropertyPath getPropertyPath();
/**
* Retrieve the fetches owned by this return.
*
* @return The owned fetches.
*/
public Fetch[] getFetches();
// Stuff I can hopefully remove ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The idea of addFetch() below has moved to {@link org.hibernate.loader.plan2.build.spi.ExpandingFetchSource}.
* <p/>
* Most of the others are already part of Fetch
*/
//
//
//
// /**
// * Returns the type of the specified fetch.
// *
// * @param fetch - the owned fetch.
// *
// * @return the type of the specified fetch.
// */
// public Type getType(Fetch fetch);
//
// /**
// * Is the specified fetch nullable?
// *
// * @param fetch - the owned fetch.
// *
// * @return true, if the fetch is nullable; false, otherwise.
// */
// public boolean isNullable(Fetch fetch);
//
// /**
// * Generates the SQL select fragments for the specified fetch. A select fragment is the column and formula
// * references.
// *
// * @param fetch - the owned fetch.
// * @param alias The table alias to apply to the fragments (used to qualify column references)
// *
// * @return the select fragments
// */
// public String[] toSqlSelectFragments(Fetch fetch, String alias);
//
// /**
// * Contract to add fetches to this owner. Care should be taken in calling this method; it is intended
// * for Hibernate usage
// *
// * @param fetch The fetch to add
// */
// public void addFetch(Fetch fetch);
//
//
// public SqlSelectFragmentResolver toSqlSelectFragmentResolver();
//
//
//
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Represents a join in the QuerySpace-sense. In HQL/JP-QL, this would be an implicit/explicit join; in
* metamodel-driven LoadPlans, this would be joins indicated by the metamodel.
*/
public interface Join {
// todo : would be good to have the SQL alias info here because we know it when we would be building this Join,
// and to do it afterwards would require lot of logic to recreate.
// But we do want this model to be workable in Search/OGM as well, plus the HQL parser has shown time-and-again
// that it is best to put off resolving and injecting physical aliases etc until as-late-as-possible.
// todo : do we know enough here to declare the "owner" side? aka, the "fk direction"
// and if we do ^^, is that enough to figure out the SQL aliases more easily (see above)?
public QuerySpace getLeftHandSide();
public QuerySpace getRightHandSide();
public boolean isRightHandSideOptional();
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* Specialization of a Join that is defined by the metadata of a persistent attribute.
*
* @author Steve Ebersole
*/
public interface JoinDefinedByMetadata extends Join {
/**
* Get a reference to the attribute whose metadata defines this join.
*
* @return The attribute defining the join.
*/
public AttributeDefinition getAttributeDefiningJoin();
}

View File

@ -0,0 +1,139 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import java.util.List;
/**
* Describes a plan for performing a load of results.
*
* Generally speaking there are 3 forms of load plans:<ul>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#ENTITY_LOADER} - An entity load plan for
* handling get/load handling. This form will typically have a single return (of type {@link org.hibernate.loader.plan.spi.EntityReturn})
* defined by {@link #getReturns()}, possibly defining fetches.
* </li>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#COLLECTION_INITIALIZER} - A collection initializer,
* used to load the contents of a collection. This form will typically have a single return (of
* type {@link org.hibernate.loader.plan.spi.CollectionReturn}) defined by {@link #getReturns()}, possibly defining fetches
* </li>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#MIXED} - A query load plan which can contain
* multiple returns of mixed type (though all implementing {@link org.hibernate.loader.plan.spi.Return}). Again, may possibly define fetches.
* </li>
* </ul>
* <p/>
* todo : would also like to see "call back" style access for handling "subsequent actions" such as...<ul>
* <li>follow-on locking</li>
* <li>join fetch conversions to subselect fetches</li>
* </ul>
*
* @author Steve Ebersole
*/
public interface LoadPlan {
/**
* What is the disposition of this LoadPlan, in terms of its returns.
*
* @return The LoadPlan's disposition
*/
public Disposition getDisposition();
/**
* Get the returns indicated by this LoadPlan.<ul>
* <li>
* A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link org.hibernate.loader.plan.spi.EntityReturn}.
* </li>
* <li>
* A {@link Disposition#COLLECTION_INITIALIZER} LoadPlan would have just a single Return of type
* {@link org.hibernate.loader.plan.spi.CollectionReturn}.
* </li>
* <li>
* A {@link Disposition#MIXED} LoadPlan would contain a mix of {@link org.hibernate.loader.plan.spi.EntityReturn} and
* {@link org.hibernate.loader.plan.spi.ScalarReturn} elements, but no {@link org.hibernate.loader.plan.spi.CollectionReturn}.
* </li>
* </ul>
*
* @return The Returns for this LoadPlan.
*
* @see Disposition
*/
public List<? extends Return> getReturns();
/**
* todo : document this...
* <p/>
* this is the stuff that was added in this plan2 package... splitting the "from clause" and "select clause"
* graphs and removing all (started) SQL references. QuerySpaces represents the "from clause". The
* "select clause" is represented by {@link #getReturns()}.
*
* @return The QuerySpaces
*/
public QuerySpaces getQuerySpaces();
/**
* Does this load plan indicate that lazy attributes are to be force fetched?
* <p/>
* Here we are talking about laziness in regards to the legacy bytecode enhancement which adds support for
* partial selects of an entity's state (e.g., skip loading a lob initially, wait until/if it is needed)
* <p/>
* This one would effect the SQL that needs to get generated as well as how the result set would be read.
* Therefore we make this part of the LoadPlan contract.
* <p/>
* NOTE that currently this is only relevant for HQL loaders when the HQL has specified the {@code FETCH ALL PROPERTIES}
* key-phrase. In all other cases, this returns false.
* @return Whether or not to
*/
public boolean areLazyAttributesForceFetched();
/**
* Convenient form of checking {@link #getReturns()} for scalar root returns.
*
* @return {@code true} if {@link #getReturns()} contained any scalar returns; {@code false} otherwise.
*/
public boolean hasAnyScalarReturns();
/**
* Enumerated possibilities for describing the disposition of this LoadPlan.
*/
public static enum Disposition {
/**
* This is an "entity loader" load plan, which describes a plan for loading one or more entity instances of
* the same entity type. There is a single return, which will be of type {@link org.hibernate.loader.plan.spi.EntityReturn}
*/
ENTITY_LOADER,
/**
* This is a "collection initializer" load plan, which describes a plan for loading one or more entity instances of
* the same collection type. There is a single return, which will be of type {@link org.hibernate.loader.plan.spi.CollectionReturn}
*/
COLLECTION_INITIALIZER,
/**
* We have a mixed load plan, which will have one or more returns of {@link org.hibernate.loader.plan.spi.EntityReturn} and {@link org.hibernate.loader.plan.spi.ScalarReturn}
* (NOT {@link org.hibernate.loader.plan.spi.CollectionReturn}).
*/
MIXED
}
}

View File

@ -0,0 +1,82 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Defines a persister reference (either entity or collection) or a composite reference. JPA terms this
* an "abstract schema type" when discussing JPQL or JPA Criteria queries. This models a single source of attributes
* (and fetches).
*
* @author Steve Ebersole
*/
public interface QuerySpace {
/**
* The uid/alias which uniquely identifies this QuerySpace. Can be used to uniquely reference this
* QuerySpace elsewhere.
*
* @return The uid
*
* @see QuerySpaces#findQuerySpaceByUid(java.lang.String)
*/
public String getUid();
/**
* Enumeration of the different types of QuerySpaces we can have.
*/
public static enum Disposition {
/**
* We have an entity-based QuerySpace. It is castable to {@link EntityQuerySpace} for more details.
*/
ENTITY,
/**
* We have a collection-based QuerySpace. It is castable to {@link CollectionQuerySpace} for more details.
*/
COLLECTION,
/**
* We have a composition-based QuerySpace. It is castable to {@link CompositeQuerySpace} for more details.
*/
COMPOSITE
}
/**
* What type of QuerySpace (more-specific) is this?
*
* @return The enum value representing the more-specific type of QuerySpace
*/
public Disposition getDisposition();
/**
* Obtain all joins which originate from this QuerySpace, in other words, all the joins which this QuerySpace is
* the right-hand-side of.
* <p/>
* For all the joins returned here, {@link Join#getRightHandSide()} should point back to this QuerySpace such that
* <code>
* space.getJoins().forEach{ join -> join.getRightHandSide() == space }
* </code>
* is true for all.
*
* @return The joins which originate from this query space.
*/
public Iterable<Join> getJoins();
}

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Models a collection of {@link QuerySpace} references and exposes the ability to find a {@link QuerySpace} by its UID
* <p/>
* todo : make this hierarchical... that would be needed to truly work for hql parser
*
* @author Steve Ebersole
*/
public interface QuerySpaces {
/**
* Gets the root QuerySpace references.
*
* @return The roots
*/
public Iterable<QuerySpace> getRootQuerySpaces();
/**
* Locate a QuerySpace by its uid.
*
* @param uid The QuerySpace uid to match
*
* @return The match, {@code null} is returned if no match.
*
* @see QuerySpace#getUid()
*/
public QuerySpace findQuerySpaceByUid(String uid);
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
/**
* Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
* <p/>
* Return is distinctly different from a {@link org.hibernate.loader.plan.spi.Fetch} and so modeled as completely separate hierarchy.
*
* @see ScalarReturn
* @see EntityReturn
* @see CollectionReturn
*
* @author Steve Ebersole
*/
public interface Return {
}

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.loader.plan2.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.AbstractPlanNode;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.type.Type;
/**
* Represent a simple scalar return within a query result. Generally this would be values of basic (String, Integer,
* etc) or composite types.
*
* @author Steve Ebersole
*/
public class ScalarReturn extends AbstractPlanNode implements Return {
private final Type type;
public ScalarReturn(SessionFactoryImplementor factory, Type type) {
super( factory );
this.type = type;
}
public Type getType() {
return type;
}
}

View File

@ -2052,8 +2052,8 @@ public abstract class AbstractCollectionPersister
}
@Override
public Type getType() {
return getElementType();
public CompositeType getType() {
return (CompositeType) getElementType();
}
@Override

View File

@ -225,8 +225,8 @@ public class CompositionSingularSubAttributesHelper {
}
@Override
public Type getType() {
return type;
public CompositeType getType() {
return (CompositeType) type;
}
@Override

View File

@ -31,6 +31,7 @@ import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinitio
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
/**
@ -40,9 +41,11 @@ public class EntityIdentifierDefinitionHelper {
public static EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition(final AbstractEntityPersister entityPersister) {
return new EncapsulatedEntityIdentifierDefinition() {
private final AttributeDefinitionAdapter attr = new AttributeDefinitionAdapter( entityPersister);
@Override
public AttributeDefinition getAttributeDefinition() {
return new AttributeDefinitionAdapter( entityPersister);
return attr;
}
@Override
@ -61,9 +64,11 @@ public class EntityIdentifierDefinitionHelper {
final AbstractEntityPersister entityPersister) {
return new EncapsulatedEntityIdentifierDefinition() {
private final CompositionDefinitionAdapter compositionDefinition = new CompositionDefinitionAdapter( entityPersister );
@Override
public AttributeDefinition getAttributeDefinition() {
return new CompositionDefinitionAdapter( entityPersister );
return compositionDefinition;
}
@Override
@ -149,6 +154,11 @@ public class EntityIdentifierDefinitionHelper {
return "<identifier-property:" + getName() + ">";
}
@Override
public CompositeType getType() {
return (CompositeType) super.getType();
}
@Override
public Iterable<AttributeDefinition> getAttributes() {
return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( getEntityPersister() );

View File

@ -25,29 +25,63 @@ package org.hibernate.persister.walking.spi;
import java.util.Arrays;
import org.hibernate.internal.util.StringHelper;
/**
* Used to uniquely identify a foreign key, so that we don't join it more than once creating circularities.
* Used to uniquely identify a foreign key, so that we don't join it more than once creating circularities. Note
* that the table+columns refers to the association owner. These are used to help detect bi-directional associations
* since the Hibernate runtime metamodel (persisters) do not inherently know this information. For example, consider
* the Order -> Customer and Customer -> Order(s) bi-directional association; both would be mapped to the
* {@code ORDER_TABLE.CUST_ID} column. That is the purpose of this struct.
* <p/>
* bit of a misnomer to call this an association attribute. But this follows the legacy use of AssociationKey
* Bit of a misnomer to call this an association attribute. But this follows the legacy use of AssociationKey
* from old JoinWalkers to denote circular join detection
*
* @author Steve Ebersole
* @author Gail Badner
* @author Gavin King
*/
public class AssociationKey {
private final String table;
private final String[] columns;
/**
* Create the AssociationKey.
*
* @param table The table part of the association key
* @param columns The columns that define the association key
*/
public AssociationKey(String table, String[] columns) {
this.table = table;
this.columns = columns;
}
@Override
public boolean equals(Object other) {
AssociationKey that = (AssociationKey) other;
return that.table.equals(table) && Arrays.equals( columns, that.columns );
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final AssociationKey that = (AssociationKey) o;
return table.equals( that.table ) && Arrays.equals( columns, that.columns );
}
@Override
public int hashCode() {
return table.hashCode(); //TODO: inefficient
return table.hashCode();
}
private String str;
@Override
public String toString() {
if ( str == null ) {
str = "AssociationKey[table=" + table + ", columns={" + StringHelper.join( ",", columns ) + "}]";
}
return str;
}
}

View File

@ -24,6 +24,14 @@
package org.hibernate.persister.walking.spi;
/**
* Strategy for walking associations as defined by the Hibernate metamodel.
* <p/>
* {@link #start()} and {@link #finish()} are called at the start and at the finish of the process.
* <p/>
* Walking might start with an entity or a collection depending on where the walker is asked to start. When starting
* with an entity, {@link #startingEntity}/{@link #finishingEntity} ()} will be the outer set of calls. When starting
* with a collection, {@link #startingCollection}/{@link #finishingCollection} will be the outer set of calls.
*
* @author Steve Ebersole
*/
public interface AssociationVisitationStrategy {
@ -37,10 +45,32 @@ public interface AssociationVisitationStrategy {
*/
public void finish();
/**
* Notification we are starting to walk an entity.
*
* @param entityDefinition The entity we are starting to walk
*/
public void startingEntity(EntityDefinition entityDefinition);
/**
* Notification we are finishing walking an entity.
*
* @param entityDefinition The entity we are finishing walking.
*/
public void finishingEntity(EntityDefinition entityDefinition);
/**
* Notification we are starting to walk the identifier of an entity.
*
* @param entityIdentifierDefinition The identifier we are starting to walk
*/
public void startingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition);
/**
* Notification we are finishing walking an entity.
*
* @param entityIdentifierDefinition The identifier we are finishing walking.
*/
public void finishingEntityIdentifier(EntityIdentifierDefinition entityIdentifierDefinition);
public void startingCollection(CollectionDefinition collectionDefinition);
@ -62,4 +92,7 @@ public interface AssociationVisitationStrategy {
public void finishingAttribute(AttributeDefinition attributeDefinition);
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition);
public void associationKeyRegistered(AssociationKey associationKey);
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition);
}

View File

@ -23,8 +23,12 @@
*/
package org.hibernate.persister.walking.spi;
import org.hibernate.type.CompositeType;
/**
* @author Steve Ebersole
*/
public interface CompositionDefinition extends AttributeDefinition, AttributeSource {
@Override
CompositeType getType();
}

View File

@ -118,15 +118,17 @@ public class MetadataDrivenModelGraphVisitor {
final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() );
log.debug( "Visiting attribute path : " + subPath.getFullPath() );
final boolean continueWalk;
if ( attributeDefinition.getType().isAssociationType() &&
isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) ) {
log.debug( "Property path deemed to be circular : " + subPath.getFullPath() );
continueWalk = false;
}
else {
continueWalk = strategy.startingAttribute( attributeDefinition );
if ( attributeDefinition.getType().isAssociationType() ) {
final AssociationKey associationKey = ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey();
if ( isDuplicateAssociationKey( associationKey ) ) {
log.debug( "Property path deemed to be circular : " + subPath.getFullPath() );
strategy.foundCircularAssociationKey( associationKey, attributeDefinition );
// EARLY EXIT!!!
return;
}
}
final boolean continueWalk = strategy.startingAttribute( attributeDefinition );
if ( continueWalk ) {
final PropertyPath old = currentPropertyPath;
currentPropertyPath = subPath;
@ -148,6 +150,8 @@ public class MetadataDrivenModelGraphVisitor {
private void visitAssociation(AssociationAttributeDefinition attribute) {
// todo : do "too deep" checks; but see note about adding depth to PropertyPath
//
// may also need to better account for "composite fetches" in terms of "depth".
addAssociationKey( attribute.getAssociationKey() );
@ -247,6 +251,7 @@ public class MetadataDrivenModelGraphVisitor {
String.format( "Association has already been visited: %s", associationKey )
);
}
strategy.associationKeyRegistered( associationKey );
}
/**

Some files were not shown because too many files have changed in this diff Show More