diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/AbstractLoadPlanBasedEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/AbstractLoadPlanBasedEntityLoader.java index 9f66629638..eba9cdab2b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/AbstractLoadPlanBasedEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/plan/AbstractLoadPlanBasedEntityLoader.java @@ -32,12 +32,11 @@ 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 AbstractLoadPlanBasedEntityLoader( 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 @@ protected SessionFactoryImplementor getFactory() { return factory; } - protected LoadQueryDetails getStaticLoadQuery() { + protected EntityLoadQueryDetails getStaticLoadQuery() { return staticLoadQuery; } @@ -197,7 +196,7 @@ public Object load(Serializable id, Object optionalObject, SessionImplementor se protected List executeLoad( SessionImplementor session, QueryParameters queryParameters, - LoadQueryDetails loadQueryDetails, + EntityLoadQueryDetails loadQueryDetails, boolean returnProxies, ResultTransformer forcedResultTransformer) throws SQLException { final List afterLoadActions = new ArrayList(); @@ -214,7 +213,7 @@ protected List executeLoad( protected List executeLoad( SessionImplementor session, QueryParameters queryParameters, - LoadQueryDetails loadQueryDetails, + EntityLoadQueryDetails loadQueryDetails, boolean returnProxies, ResultTransformer forcedResultTransformer, List afterLoadActions) throws SQLException { @@ -237,8 +236,6 @@ protected List executeLoad( 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 int[] getNamedParameterLocations(String name) { return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name ); } }, - loadQueryDetails.getAliasResolutionContext(), returnProxies, queryParameters.isReadOnly(), forcedResultTransformer, @@ -295,7 +291,7 @@ else if ( results.size() == 1 ) { protected Object doQueryAndLoadEntity( SessionImplementor session, QueryParameters queryParameters, - LoadQueryDetails loadQueryDetails, + EntityLoadQueryDetails loadQueryDetails, boolean returnProxies, ResultTransformer forcedResultTransformer) throws SQLException { @@ -305,7 +301,6 @@ protected Object doQueryAndLoadEntity( try { final List results = loadQueryDetails.getResultSetProcessor().extractResults( - NoOpLoadPlanAdvisor.INSTANCE, wrapper.getResultSet(), session, queryParameters, @@ -315,7 +310,6 @@ public int[] getNamedParameterLocations(String name) { return AbstractLoadPlanBasedEntityLoader.this.getNamedParameterLocs( name ); } }, - loadQueryDetails.getAliasResolutionContext(), returnProxies, queryParameters.isReadOnly(), forcedResultTransformer, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/Helper.java new file mode 100644 index 0000000000..ee6a466615 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/Helper.java @@ -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 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 extractRootReturn(LoadPlan loadPlan, Class 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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/LoadQueryBuilderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/LoadQueryBuilderHelper.java similarity index 55% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/LoadQueryBuilderHelper.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/LoadQueryBuilderHelper.java index 0f99cb8596..25e4751371 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/LoadQueryBuilderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/LoadQueryBuilderHelper.java @@ -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.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 @@ * @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 static void applyJoinFetches( 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 @@ private static void processJoinFetches( fetchOwner, fetch, buildingParameters, - aliasResolutionContext + aliasResolutionContext, + readerCollector, + stats ); } } @@ -113,41 +198,40 @@ private static void processFetch( 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 @@ else if ( CollectionFetch.class.isInstance( fetch ) ) { joinFragment, collectionFetch.getIndexGraph(), buildingParameters, - aliasResolutionContext + aliasResolutionContext, + readerCollector, + stats ); } if ( collectionFetch.getElementGraph() != null ) { @@ -165,7 +251,9 @@ else if ( CollectionFetch.class.isInstance( fetch ) ) { joinFragment, collectionFetch.getElementGraph(), buildingParameters, - aliasResolutionContext + aliasResolutionContext, + readerCollector, + stats ); } } @@ -179,13 +267,97 @@ else if ( CollectionFetch.class.isInstance( fetch ) ) { 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 @@ private static void processEntityFetch( 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 @@ private static void processEntityFetch( 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 identifierFetchReaders = new ArrayList(); + 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 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 readers = new ArrayList(); + for ( Fetch fetch : fetches ) { + collectIdentifierFetchReaders( aliasResolutionContext, readers, entityReference, fetch, readerCollector ); + } + return readers; + } + + + private static void collectIdentifierFetchReaders( + AliasResolutionContext aliasResolutionContext, + List 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.emptyList() + ) + ); + + readerCollector.addReader( reader ); +// readers.add( reader ); + } } private static String[] resolveAliasedLhsJoinColumns( @@ -331,19 +619,25 @@ private static void processCollectionFetch( 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 @@ private static void processCollectionFetch( (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 @@ else if ( "".equals( additionalJoinConditions ) ) { ); // add select fragments from the element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - final CollectionReferenceAliases aliases = aliasResolutionContext.resolveAliases( fetch ); selectStatementBuilder.appendSelectClauseFragment( elementPersister.selectFragment( aliases.getElementTableAlias(), @@ -437,9 +730,44 @@ else if ( "".equals( additionalJoinConditions ) ) { 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 @@ else if ( "".equals( additionalJoinConditions ) ) { 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 @@ else if ( "".equals( additionalJoinConditions ) ) { 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 @@ else if ( "".equals( additionalJoinConditions ) ) { } } + readerCollector.addReader( new CollectionReferenceReader( fetch, aliases ) ); } private static Joinable extractJoinable(FetchOwner fetchOwner) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/package-info.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/package-info.java new file mode 100644 index 0000000000..57302dcd75 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/package-info.java @@ -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; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReferenceReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReferenceReader.java index bf340f853f..f838560805 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReferenceReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReferenceReader.java @@ -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(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReturnReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReturnReader.java index c71bfbec54..f78f38af5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReturnReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/CollectionReturnReader.java @@ -30,6 +30,7 @@ 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 @@ 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; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityIdentifierReaderImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityIdentifierReaderImpl.java index 254612bebf..c308c27a55 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityIdentifierReaderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityIdentifierReaderImpl.java @@ -26,30 +26,25 @@ 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 @@ * * @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 identifierFetchReaders; + private final EntityReferenceAliases aliases; + private final List 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 identifierFetchReaders) { this.entityReference = entityReference; + this.aliases = aliases; this.isReturn = EntityReturn.class.isInstance( entityReference ); this.identifierType = entityReference.getEntityPersister().getIdentifierType(); - - identifierFetchReaders = collectIdentifierFetchReaders(); - } - - private List collectIdentifierFetchReaders() { - if ( ! identifierType.isComponentType() ) { - return Collections.emptyList(); - } - final Fetch[] fetches = entityReference.getIdentifierDescription().getFetches(); - if ( fetches == null || fetches.length == 0 ) { - return Collections.emptyList(); - } - - final List readers = new ArrayList(); - for ( Fetch fetch : fetches ) { - collectIdentifierFetchReaders( readers, fetch ); - } - return readers; - } - - private void collectIdentifierFetchReaders(List 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 @@ private Object readIdentifierHydratedState(ResultSet resultSet, ResultSetProcess } } else { - columnNames = context.getAliasResolutionContext() - .resolveAliases( entityReference ) - .getColumnAliases() - .getSuffixedKeyAliases(); + columnNames = aliases.getColumnAliases().getSuffixedKeyAliases(); } try { @@ -282,13 +240,13 @@ private Object readIdentifierHydratedState(ResultSet resultSet, ResultSetProcess 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 ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReferenceReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReferenceReader.java index 1fe9c8daf3..fff80a7353 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReferenceReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReferenceReader.java @@ -47,7 +47,6 @@ 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 @@ private void loadFromResultSet( 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 @@ private void loadFromResultSet( 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 @@ private void loadFromResultSet( 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 @@ private String getConcreteEntityTypeName( try { discriminatorValue = loadable.getDiscriminatorType().nullSafeGet( resultSet, - context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases().getSuffixedDiscriminatorAlias(), + entityReferenceAliases.getColumnAliases().getSuffixedDiscriminatorAlias(), context.getSession(), null ); @@ -377,7 +377,7 @@ private void checkVersion( context.getSession(), resultSet, entityReference.getEntityPersister(), - context.getAliasResolutionContext().resolveAliases( entityReference ).getColumnAliases(), + entityReferenceAliases.getColumnAliases(), entityKey, existing ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReturnReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReturnReader.java index 0d5edb86af..4362af7248 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReturnReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReturnReader.java @@ -30,6 +30,7 @@ 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 @@ 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; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/OneToOneFetchReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/OneToOneFetchReader.java index 82bad7b476..beb4aad260 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/OneToOneFetchReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/OneToOneFetchReader.java @@ -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 @@ 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 ); - } - - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java index 30fad0710d..0381191073 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java @@ -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 currentRowHydratedEntityRegistrationList; @@ -121,7 +120,6 @@ public LockMode resolveLockMode(EntityReference entityReference) { * @param shouldReturnProxies * @param queryParameters * @param namedParameterContext - * @param aliasResolutionContext * @param hadSubselectFetches */ public ResultSetProcessingContextImpl( @@ -134,7 +132,6 @@ public ResultSetProcessingContextImpl( boolean shouldReturnProxies, QueryParameters queryParameters, NamedParameterContext namedParameterContext, - AliasResolutionContext aliasResolutionContext, boolean hadSubselectFetches) { this.resultSet = resultSet; this.session = session; @@ -145,7 +142,6 @@ public ResultSetProcessingContextImpl( this.shouldReturnProxies = shouldReturnProxies; this.queryParameters = queryParameters; this.namedParameterContext = namedParameterContext; - this.aliasResolutionContext = aliasResolutionContext; this.hadSubselectFetches = hadSubselectFetches; if ( shouldUseOptionalEntityInformation ) { @@ -318,405 +314,400 @@ else if ( CompositeFetch.class.isInstance( fetchOwner ) ) { ); } - @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) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java index 60a77179be..cbe2f66855 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java @@ -33,31 +33,16 @@ 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 @@ 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 ScrollableResultSetProcessor toOnDemandForm() { @Override public List extractResults( - LoadPlanAdvisor loadPlanAdvisor, ResultSet resultSet, final SessionImplementor session, QueryParameters queryParameters, NamedParameterContext namedParameterContext, - AliasResolutionContext aliasResolutionContext, boolean returnProxies, boolean readOnly, ResultTransformer forcedResultTransformer, List 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 List extractResults( returnProxies, queryParameters, namedParameterContext, - aliasResolutionContext, hadSubselectFetches ); @@ -221,323 +177,125 @@ private void handlePotentiallyEmptyCollectionRootReturns( } - 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 entityReferenceReaders = getEntityReferenceReaders(); - final List 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 getEntityReferenceReaders(); - protected abstract List getCollectionReferenceReaders(); - - protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) - throws SQLException; - - } - - private class MixedReturnRowReader extends AbstractRowReader implements RowReader { - private final List returnReaders; - private List entityReferenceReaders = new ArrayList(); - private List collectionReferenceReaders = new ArrayList(); - - 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 readers = new ArrayList(); - - 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 getEntityReferenceReaders() { - return entityReferenceReaders; - } - - @Override - protected List 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 entityReferenceReaders = new ArrayList(); - private List 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(); -// } -// 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(); - } - 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 getEntityReferenceReaders() { - return entityReferenceReaders; - } - - @Override - protected List 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 entityReferenceReaders = null; - private final List collectionReferenceReaders = new ArrayList(); - - 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(); - } - entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) ); - } - - @Override - public void startingCollectionFetch(CollectionFetch collectionFetch) { - collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) ); - } - } - ); - - collectionReferenceReaders.add( returnReader ); - } - - @Override - protected List getEntityReferenceReaders() { - return entityReferenceReaders; - } - - @Override - protected List 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 returnReaders; +// private List entityReferenceReaders = new ArrayList(); +// private List collectionReferenceReaders = new ArrayList(); +// +// 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 readers = new ArrayList(); +// +// 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 getEntityReferenceReaders() { +// return entityReferenceReaders; +// } +// +// @Override +// protected List 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 entityReferenceReaders = null; +// private final List collectionReferenceReaders = new ArrayList(); +// +// 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(); +// } +// entityReferenceReaders.add( new EntityReferenceReader( entityFetch ) ); +// } +// +// @Override +// public void startingCollectionFetch(CollectionFetch collectionFetch) { +// collectionReferenceReaders.add( new CollectionReferenceReader( collectionFetch ) ); +// } +// } +// ); +// +// collectionReferenceReaders.add( returnReader ); +// } +// +// @Override +// protected List getEntityReferenceReaders() { +// return entityReferenceReaders; +// } +// +// @Override +// protected List getCollectionReferenceReaders() { +// return collectionReferenceReaders; +// } +// +// @Override +// protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException { +// return returnReader.read( resultSet, context ); +// } +// } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ScalarReturnReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ScalarReturnReader.java index e36347f0b0..0ddc12ffe6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ScalarReturnReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ScalarReturnReader.java @@ -35,16 +35,18 @@ */ 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 ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/AbstractRowReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/AbstractRowReader.java new file mode 100644 index 0000000000..0bf98b8c89 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/AbstractRowReader.java @@ -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 entityReferenceReaders = getEntityReferenceReaders(); + final List 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 getEntityReferenceReaders(); + protected abstract List getCollectionReferenceReaders(); + + protected abstract Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) + throws SQLException; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessingContext.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessingContext.java index 43249d2ed8..fb55263317 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessingContext.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessingContext.java @@ -129,8 +129,6 @@ public static interface EntityReferenceProcessingState { 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 static interface EntityKeyResolutionContext { 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; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessor.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessor.java index 211d48d83a..bd965aedc2 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ResultSetProcessor.java @@ -30,7 +30,6 @@ 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, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ReturnReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ReturnReader.java index a674d8ebd6..bbf487f339 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ReturnReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/spi/ReturnReader.java @@ -27,7 +27,7 @@ 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 */ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/EntityLoadQueryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/EntityLoadQueryBuilderImpl.java index 5db0e113e6..b874b870f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/EntityLoadQueryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/internal/EntityLoadQueryBuilderImpl.java @@ -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 String generateSql( 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 String generateSql( ); } - 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 String generateSql( 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 @@ protected String generateSql( 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(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/spi/EntityLoadQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/spi/EntityLoadQueryBuilder.java index 16a9a06a26..a777454a4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/spi/EntityLoadQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/query/spi/EntityLoadQueryBuilder.java @@ -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; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/EntityLoadQueryDetails.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/EntityLoadQueryDetails.java new file mode 100644 index 0000000000..234d56006a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/EntityLoadQueryDetails.java @@ -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:
    + *
  • generating the SQL query to perform
  • + *
  • creating the readers needed to read the results from the SQL's ResultSet
  • + *
+ * + * @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.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 entityReferenceReaders; + private List collectionReferenceReaders; + + @Override + public void addReader(CollectionReferenceReader collectionReferenceReader) { + if ( collectionReferenceReaders == null ) { + collectionReferenceReaders = new ArrayList(); + } + 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(); + } + 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 entityReferenceReaders; + private final List collectionReferenceReaders; + + public EntityLoaderRowReader( + EntityReturnReader rootReturnReader, + List entityReferenceReaders, + List collectionReferenceReaders) { + this.rootReturnReader = rootReturnReader; + this.entityReferenceReaders = entityReferenceReaders != null + ? entityReferenceReaders + : Collections.emptyList(); + this.collectionReferenceReaders = collectionReferenceReaders != null + ? collectionReferenceReaders + : Collections.emptyList(); + } + + @Override + protected List getEntityReferenceReaders() { + return entityReferenceReaders; + } + + @Override + protected List getCollectionReferenceReaders() { + return collectionReferenceReaders; + } + + @Override + protected Object readLogicalRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException { + return rootReturnReader.read( resultSet, context ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/LoadQueryDetails.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/LoadQueryDetails.java index a9447578c1..70f702c47e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/LoadQueryDetails.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/LoadQueryDetails.java @@ -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; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/ReaderCollector.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/ReaderCollector.java new file mode 100644 index 0000000000..1981f27250 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/ReaderCollector.java @@ -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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/RowReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/RowReader.java new file mode 100644 index 0000000000..4a56afc277 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/spi/RowReader.java @@ -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; +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityReturnImpl.java new file mode 100644 index 0000000000..ed8c731871 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/EntityReturnImpl.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java index f2d190abad..2fc265837e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanImpl.java @@ -47,14 +47,35 @@ protected LoadPlanImpl(List returns, 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) { 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 returns, boolean areLazyAttributesForceFetched) { this( returns, Disposition.MIXED, areLazyAttributesForceFetched ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/ScalarReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/ScalarReturnImpl.java new file mode 100644 index 0000000000..9c455f0c2a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/ScalarReturnImpl.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AnyFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AnyFetch.java index a26cf822da..13c9bfa110 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AnyFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AnyFetch.java @@ -115,20 +115,20 @@ public PropertyPath getPropertyPath() { 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) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/BidirectionalEntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/BidirectionalEntityFetch.java index 8e00dbda3e..359c8af7a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/BidirectionalEntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/BidirectionalEntityFetch.java @@ -27,9 +27,22 @@ * 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. + *

+ * 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(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java index 9c9b0f2a52..88315dd79e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java @@ -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 FetchStrategy getFetchStrategy() { 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) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index 18f16accc0..e802041b40 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -92,29 +92,29 @@ public EntityPersister retrieveFetchSourcePersister() { 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) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 770f93ff9a..63a99892bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -146,143 +146,143 @@ public void injectIdentifierDescription(IdentifierDescription identifierDescript 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() { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java index 6c2c0f46e8..8c4f68cbd9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference.java @@ -25,6 +25,7 @@ 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; /** diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference2.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference2.java new file mode 100644 index 0000000000..fd2084c6aa --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReference2.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn2.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn2.java new file mode 100644 index 0000000000..abafc44d85 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn2.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java index 240c3c505c..5571da6be4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java @@ -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); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java index 2b676bcaeb..26e7ed5e4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescription.java @@ -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); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/KeyManyToOneBidirectionalEntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/KeyManyToOneBidirectionalEntityFetch.java index 73bbf9e361..71b59770a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/KeyManyToOneBidirectionalEntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/KeyManyToOneBidirectionalEntityFetch.java @@ -29,8 +29,11 @@ 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. + *

+ * 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 */ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java index fe5f3c89fe..4a7dc88d02 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/LoadPlan.java @@ -30,26 +30,58 @@ * * Generally speaking there are 3 forms of load plans:

    *
  • - * 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. *
  • *
  • - * 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 *
  • *
  • - * 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. *
  • *
+ *

+ * todo : would also like to see "call back" style access for handling "subsequent actions" such as...

    + *
  • follow-on locking
  • + *
  • join fetch conversions to subselect fetches
  • + *
* * @author Steve Ebersole */ public interface LoadPlan { - public List 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.
    + *
  • + * A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link EntityReturn}. + *
  • + *
  • + * A {@link Disposition#COLLECTION_INITIALIZER} LoadPlan would have just a single Return of type + * {@link CollectionReturn}. + *
  • + *
  • + * A {@link Disposition#MIXED} LoadPlan would contain a mix of {@link EntityReturn} and + * {@link ScalarReturn} elements, but no {@link CollectionReturn}. + *
  • + *
+ * + * @return The Returns for this LoadPlan. + * + * @see Disposition + */ + public List getReturns(); + /** * Does this load plan indicate that lazy attributes are to be force fetched? *

@@ -93,8 +125,4 @@ public static enum Disposition { */ 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 } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java index 110da5e802..37e3854302 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java @@ -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.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.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 void finishingAttribute(AttributeDefinition attributeDefinition) { ); } + private Map fetchedAssociationKeyOwnerMap = new HashMap(); + + @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 static interface FetchStackAware { 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 @@ private IdentifierDescriptionImpl( 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 pathStack = new ArrayDeque(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/IdentifierDescriptionInjectable.java similarity index 91% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/IdentifierDescriptionInjectable.java index 4528bfa605..6b629cc632 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/IdentifierDescriptionInjectable.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/IdentifierDescriptionInjectable.java @@ -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 */ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/LoadPlanBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/MetadataDrivenLoadPlanBuilder.java similarity index 91% rename from hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/LoadPlanBuilder.java rename to hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/MetadataDrivenLoadPlanBuilder.java index 4c3d025489..e1f82b89ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/LoadPlanBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/MetadataDrivenLoadPlanBuilder.java @@ -29,13 +29,14 @@ 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). *

diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CascadeLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CascadeLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..29bb0e6275 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CascadeLoadPlanBuilderStrategy.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CollectionQuerySpaceImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CollectionQuerySpaceImpl.java new file mode 100644 index 0000000000..33fdeb9794 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CollectionQuerySpaceImpl.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CompositeQuerySpaceImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CompositeQuerySpaceImpl.java new file mode 100644 index 0000000000..507a8b9995 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/CompositeQuerySpaceImpl.java @@ -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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityIdentifierDescriptionImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityIdentifierDescriptionImpl.java new file mode 100644 index 0000000000..0feab79117 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityIdentifierDescriptionImpl.java @@ -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 fetches; + + public EntityIdentifierDescriptionImpl(EntityReference entityReference) { + this.entityReference = entityReference; + this.propertyPath = entityReference.getPropertyPath().append( "" ); + } + + // 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(); + } + 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" ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityQuerySpaceImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityQuerySpaceImpl.java new file mode 100644 index 0000000000..0369751495 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/EntityQuerySpaceImpl.java @@ -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; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/JoinImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/JoinImpl.java new file mode 100644 index 0000000000..95c03f3aef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/JoinImpl.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/LoadPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/LoadPlanImpl.java new file mode 100644 index 0000000000..c2de9784c2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/LoadPlanImpl.java @@ -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 returns; + private final QuerySpaces querySpaces; + private final Disposition disposition; + private final boolean areLazyAttributesForceFetched; + + protected LoadPlanImpl( + List 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 returns, QuerySpaces querySpaces, boolean areLazyAttributesForceFetched) { + this( returns, querySpaces, Disposition.MIXED, areLazyAttributesForceFetched ); + } + + @Override + public List 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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/QuerySpacesImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/QuerySpacesImpl.java new file mode 100644 index 0000000000..2cbc8e25db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/QuerySpacesImpl.java @@ -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 roots = new ArrayList(); + private final Map querySpaceByUid = new ConcurrentHashMap(); + + public QuerySpacesImpl(SessionFactoryImplementor sessionFactory) { + this.sessionFactory = sessionFactory; + } + + + // QuerySpaces impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public Iterable getRootQuerySpaces() { + return roots; + } + + @Override + public QuerySpace findQuerySpaceByUid(String uid) { + return querySpaceByUid.get( uid ); + } + + + + // ExpandingQuerySpaces impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + private int implicitUidBase = 0; + + @Override + public String generateImplicitUid() { + return ""; + } + + @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() ); + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/StandardFetchBasedLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/StandardFetchBasedLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..9694454cc5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/internal/StandardFetchBasedLoadPlanBuilderStrategy.java @@ -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 ) +// ); +// } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractMetamodelWalkingLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractMetamodelWalkingLoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..c09327b4f7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractMetamodelWalkingLoadPlanBuilderStrategy.java @@ -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. + *

+ * 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 fetchSourceStack = new ArrayDeque(); + + 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 collectionReferenceStack = new ArrayDeque(); + + 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 fetchedAssociationKeyOwnerMap = new HashMap(); + + @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 pathStack = new ArrayDeque(); + + public void push(PropertyPath path) { + pathStack.addFirst( path ); + } + + public void pop() { + pathStack.removeFirst(); + } + + public String toString() { + final PropertyPath path = pathStack.peekFirst(); + return path == null ? "" : path.getFullPath(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractQuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractQuerySpace.java new file mode 100644 index 0000000000..b0110e7cb1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/AbstractQuerySpace.java @@ -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 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 getJoins() { + return joins == null + ? Collections.emptyList() + : joins; + } + + protected List internalGetJoins() { + if ( joins == null ) { + joins = new ArrayList(); + } + + return joins; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingEntityIdentifierDescription.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingEntityIdentifierDescription.java new file mode 100644 index 0000000000..d727b4545b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingEntityIdentifierDescription.java @@ -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 { + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingFetchSource.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingFetchSource.java new file mode 100644 index 0000000000..5dbdadd3c6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingFetchSource.java @@ -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); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpace.java new file mode 100644 index 0000000000..3f1b3be45d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpace.java @@ -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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpaces.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpaces.java new file mode 100644 index 0000000000..0a1d74a368 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/ExpandingQuerySpaces.java @@ -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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuilderStrategy.java new file mode 100644 index 0000000000..ec0463342b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuilderStrategy.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuildingContext.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuildingContext.java new file mode 100644 index 0000000000..c4a1fa6a2e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/LoadPlanBuildingContext.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/MetadataDrivenLoadPlanBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/MetadataDrivenLoadPlanBuilder.java new file mode 100644 index 0000000000..f3b6474fbe --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/MetadataDrivenLoadPlanBuilder.java @@ -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). + *

+ * 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(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/package-info.java new file mode 100644 index 0000000000..dfb031e0a6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/build/spi/package-info.java @@ -0,0 +1,4 @@ +/** + * Defines the SPI for building a metamodel-driven LoadPlan + */ +package org.hibernate.loader.plan2.build.spi; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCollectionReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCollectionReference.java new file mode 100644 index 0000000000..95c64ea4bb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCollectionReference.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCompositeFetch.java new file mode 100644 index 0000000000..b29e67cace --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractCompositeFetch.java @@ -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 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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractEntityReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractEntityReference.java new file mode 100644 index 0000000000..7022fc2373 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/AbstractEntityReference.java @@ -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 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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementCompositeGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementCompositeGraph.java new file mode 100644 index 0000000000..f8824b2654 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementCompositeGraph.java @@ -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( "" ) + ); + 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" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementEntityGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementEntityGraph.java new file mode 100644 index 0000000000..ffa1876bf1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableElementEntityGraph.java @@ -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( "" ) ); + + 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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexCompositeGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexCompositeGraph.java new file mode 100644 index 0000000000..9fd44f8f86 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexCompositeGraph.java @@ -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( "" ) + ); + 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" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexEntityGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexEntityGraph.java new file mode 100644 index 0000000000..9fc04df7ee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionFetchableIndexEntityGraph.java @@ -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( "" ) ); + + 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) { + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionReturnImpl.java new file mode 100644 index 0000000000..8931d42899 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/CollectionReturnImpl.java @@ -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 + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityFetchImpl.java new file mode 100644 index 0000000000..83a4ed0762 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityFetchImpl.java @@ -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. + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityReturnImpl.java new file mode 100644 index 0000000000..a2a7177e8f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/EntityReturnImpl.java @@ -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 + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/ScalarReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/ScalarReturnImpl.java new file mode 100644 index 0000000000..6eb6cbe67b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/internal/ScalarReturnImpl.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetch.java new file mode 100644 index 0000000000..d90a0e6f14 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetch.java @@ -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(); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableElement.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableElement.java new file mode 100644 index 0000000000..de16f5ebb3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableElement.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableIndex.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableIndex.java new file mode 100644 index 0000000000..8c2e6d0c22 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionFetchableIndex.java @@ -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(); +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionQuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionQuerySpace.java new file mode 100644 index 0000000000..9aa768f09d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionQuerySpace.java @@ -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. + *

+ * 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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReference.java new file mode 100644 index 0000000000..1b404ac004 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReference.java @@ -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:

    + *
  • the collection is not indexed
  • + *
  • the index is not a composite or entity (cannot act as a FetchSource)
  • + *
+ *

+ * Works only for map keys, since a List index (int type) cannot act as a FetchSource. + *

+ * + * @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. + *

+ * + * @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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReturn.java new file mode 100644 index 0000000000..d49e56d13b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CollectionReturn.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeFetch.java new file mode 100644 index 0000000000..3bc26b9dc3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeFetch.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeQuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeQuerySpace.java new file mode 100644 index 0000000000..af4778501a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/CompositeQuerySpace.java @@ -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). + *

+ * It's {@link #getDisposition()} result will be {@link Disposition#COMPOSITE} + * + * @author Steve Ebersole + */ +public interface CompositeQuerySpace extends QuerySpace { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityFetch.java new file mode 100644 index 0000000000..e7b0405259 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityFetch.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityIdentifierDescription.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityIdentifierDescription.java new file mode 100644 index 0000000000..be88599539 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityIdentifierDescription.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityQuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityQuerySpace.java new file mode 100644 index 0000000000..e955bd66dc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityQuerySpace.java @@ -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). + *

+ * 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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReference.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReference.java new file mode 100644 index 0000000000..f43f7b982f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReference.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReturn.java new file mode 100644 index 0000000000..170d63ca44 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/EntityReturn.java @@ -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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Fetch.java new file mode 100644 index 0000000000..b2477bbb16 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Fetch.java @@ -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. + *

+ * 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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/FetchSource.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/FetchSource.java new file mode 100644 index 0000000000..08d2364a32 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/FetchSource.java @@ -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}. + *

+ * 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(); +// +// +// + +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Join.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Join.java new file mode 100644 index 0000000000..d3dcf7b0f1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Join.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/JoinDefinedByMetadata.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/JoinDefinedByMetadata.java new file mode 100644 index 0000000000..b870229f79 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/JoinDefinedByMetadata.java @@ -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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/LoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/LoadPlan.java new file mode 100644 index 0000000000..a41430a91f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/LoadPlan.java @@ -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:

    + *
  • + * {@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. + *
  • + *
  • + * {@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 + *
  • + *
  • + * {@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. + *
  • + *
+ *

+ * todo : would also like to see "call back" style access for handling "subsequent actions" such as...

    + *
  • follow-on locking
  • + *
  • join fetch conversions to subselect fetches
  • + *
+ * + * @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.
    + *
  • + * A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link org.hibernate.loader.plan.spi.EntityReturn}. + *
  • + *
  • + * A {@link Disposition#COLLECTION_INITIALIZER} LoadPlan would have just a single Return of type + * {@link org.hibernate.loader.plan.spi.CollectionReturn}. + *
  • + *
  • + * 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}. + *
  • + *
+ * + * @return The Returns for this LoadPlan. + * + * @see Disposition + */ + public List getReturns(); + + /** + * todo : document this... + *

+ * 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? + *

+ * 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) + *

+ * 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. + *

+ * 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 + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpace.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpace.java new file mode 100644 index 0000000000..716a1443d2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpace.java @@ -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. + *

+ * For all the joins returned here, {@link Join#getRightHandSide()} should point back to this QuerySpace such that + * + * space.getJoins().forEach{ join -> join.getRightHandSide() == space } + * + * is true for all. + * + * @return The joins which originate from this query space. + */ + public Iterable getJoins(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpaces.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpaces.java new file mode 100644 index 0000000000..82401b6318 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/QuerySpaces.java @@ -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 + *

+ * 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 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); +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Return.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Return.java new file mode 100644 index 0000000000..b9a00f7a45 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/Return.java @@ -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! + *

+ * 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 { +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/ScalarReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/ScalarReturn.java new file mode 100644 index 0000000000..0176784e01 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan2/spi/ScalarReturn.java @@ -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; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 732058a398..eeb63ff359 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -2052,8 +2052,8 @@ public String getName() { } @Override - public Type getType() { - return getElementType(); + public CompositeType getType() { + return (CompositeType) getElementType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java index f3b80c14f6..c253950772 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java @@ -225,8 +225,8 @@ public String getName() { } @Override - public Type getType() { - return type; + public CompositeType getType() { + return (CompositeType) type; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java index 3a3aba11b4..cb9e5c91eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java @@ -31,6 +31,7 @@ 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 static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDef 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 String toString() { return ""; } + @Override + public CompositeType getType() { + return (CompositeType) super.getType(); + } + @Override public Iterable getAttributes() { return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( getEntityPersister() ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java index 352c4a548f..5cfd6c843f 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationKey.java @@ -25,29 +25,63 @@ 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. *

- * 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; } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java index 26a8a5c098..16a2226af1 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java @@ -24,6 +24,14 @@ package org.hibernate.persister.walking.spi; /** + * Strategy for walking associations as defined by the Hibernate metamodel. + *

+ * {@link #start()} and {@link #finish()} are called at the start and at the finish of the process. + *

+ * 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); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java index e9d7c251cf..4160df526a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionDefinition.java @@ -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(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index 89453781b8..4e9c25208a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -118,15 +118,17 @@ private void visitAttributeDefinition(AttributeDefinition attributeDefinition) { 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 @@ else if ( attributeType.isComponentType() ) { 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 @@ protected void addAssociationKey(AssociationKey associationKey) { String.format( "Association has already been visited: %s", associationKey ) ); } + strategy.associationKeyRegistered( associationKey ); } /** diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java index a8285a674c..863b3a0b4b 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java @@ -54,16 +54,14 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; import org.hibernate.loader.entity.EntityJoinWalker; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; 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.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.EntityLoadQueryDetails; import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; 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.spi.NoOpLoadPlanAdvisor; +import org.hibernate.loader.plan.spi.build.MetadataDrivenLoadPlanBuilder; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.OuterJoinLoadable; @@ -160,11 +158,9 @@ private void doCompare(SessionFactoryImplementor sf, OuterJoinLoadable persister SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( sf, influencers ); - LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister ); - LoadQueryDetails details = LoadQueryDetails.makeForBatching( - persister.getKeyColumnNames(), - plan, - sf, + LoadPlan plan = MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister ); + EntityLoadQueryDetails details = EntityLoadQueryDetails.makeForBatching( + plan, persister.getKeyColumnNames(), new QueryBuildingParameters() { @Override public LoadQueryInfluencers getQueryInfluencers() { @@ -185,13 +181,13 @@ public LockMode getLockMode() { public LockOptions getLockOptions() { return null; } - } + }, sf ); compare( walker, details ); } - private void compare(JoinWalker walker, LoadQueryDetails details) { + private void compare(JoinWalker walker, EntityLoadQueryDetails details) { System.out.println( "WALKER : " + walker.getSQLString() ); System.out.println( "LOAD-PLAN : " + details.getSqlStatement() ); System.out.println(); @@ -255,19 +251,10 @@ public void testNestedCompositeElementCollectionProcessing() throws Exception { private List getResults(EntityPersister entityPersister ) { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - // ultimately, using a LoadPlan requires that it be interpreted into 2 pieces of information: - // 1) The query to execute - // 2) The ResultSetProcessor to use. - // - // Those 2 pieces of information share some common context: - // 1) alias resolution context - // + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory() ); - - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); - - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -281,7 +268,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -291,7 +277,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java index 69026877f5..f0c21098d1 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java @@ -41,12 +41,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.Type; @@ -187,11 +185,10 @@ public QueryParameters getQueryParameters() { private List getResults(final EntityPersister entityPersister, final Callback callback) { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); - - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); final List results = new ArrayList(); @@ -207,7 +204,6 @@ public void execute(Connection connection) throws SQLException { //callback.beforeExtractResults( workSession ); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, callback.getQueryParameters(), @@ -217,7 +213,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityAssociationResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityAssociationResultSetProcessorTest.java index bccdcf5584..cbdb24cf6e 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityAssociationResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityAssociationResultSetProcessorTest.java @@ -41,12 +41,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.junit.Test; @@ -87,11 +85,11 @@ public void testManyToOneEntityProcessing() throws Exception { { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -105,7 +103,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -115,7 +112,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, @@ -173,11 +169,11 @@ public void testNestedManyToOneEntityProcessing() throws Exception { { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -191,7 +187,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -201,7 +196,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java index 5fc9a90b82..ceb968eee7 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java @@ -44,12 +44,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.junit.Test; @@ -89,12 +87,13 @@ public void testEntityWithSet() throws Exception { session.close(); { + final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -108,7 +107,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -118,7 +116,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java index a061775a74..681f6658ce 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java @@ -42,12 +42,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.junit.Test; @@ -109,12 +107,13 @@ public void testEntityWithList() throws Exception { session.close(); { + final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -128,7 +127,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -138,7 +136,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java index 5a84e8a3a4..d54b3e3a6b 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java @@ -44,12 +44,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.junit.Test; @@ -121,11 +119,11 @@ else if ( message.mid == 2 ) { { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -139,7 +137,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -149,7 +146,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/Helper.java b/hibernate-core/src/test/java/org/hibernate/loader/Helper.java index 257b899b15..7508071e53 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/Helper.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/Helper.java @@ -30,9 +30,10 @@ import org.hibernate.loader.plan.exec.query.internal.EntityLoadQueryBuilderImpl; import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters; import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +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.persister.entity.EntityPersister; /** @@ -52,7 +53,16 @@ public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, EntityPersister enti sf, LoadQueryInfluencers.NONE ); - return LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + return MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + } + + public EntityLoadQueryDetails buildLoadQueryDetails(LoadPlan loadPlan, SessionFactoryImplementor sf) { + return EntityLoadQueryDetails.makeForBatching( + loadPlan, + null, + this, + sf + ); } public String generateSql(SessionFactoryImplementor sf, LoadPlan plan, AliasResolutionContext aliasResolutionContext) { diff --git a/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java index 81c2833de9..9889974b5b 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/NonEncapsulatedCompositeIdResultSetProcessorTest.java @@ -36,12 +36,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.Type; @@ -159,11 +157,11 @@ public QueryParameters getQueryParameters() { private List getResults(final EntityPersister entityPersister, final Callback callback) { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -179,7 +177,6 @@ public void execute(Connection connection) throws SQLException { //callback.beforeExtractResults( workSession ); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, callback.getQueryParameters(), @@ -189,7 +186,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java index e0cdd76818..c80c587067 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/SimpleResultSetProcessorTest.java @@ -36,12 +36,10 @@ import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; -import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; -import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl; +import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor; import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext; -import org.hibernate.loader.plan.exec.spi.AliasResolutionContext; +import org.hibernate.loader.plan.exec.spi.LoadQueryDetails; import org.hibernate.loader.plan.spi.LoadPlan; -import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; import org.junit.Test; @@ -75,11 +73,11 @@ public void testSimpleEntityProcessing() throws Exception { { final LoadPlan plan = Helper.INSTANCE.buildLoadPlan( sessionFactory(), entityPersister ); - final AliasResolutionContext aliasResolutionContext = new AliasResolutionContextImpl( sessionFactory(), 0 ); - final String sql = Helper.INSTANCE.generateSql( sessionFactory(), plan, aliasResolutionContext ); + final LoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( plan, sessionFactory() ); + final String sql = queryDetails.getSqlStatement(); + final ResultSetProcessor resultSetProcessor = queryDetails.getResultSetProcessor(); - final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan, true ); final List results = new ArrayList(); final Session workSession = openSession(); @@ -94,7 +92,6 @@ public void execute(Connection connection) throws SQLException { ResultSet resultSet = ps.executeQuery(); results.addAll( resultSetProcessor.extractResults( - NoOpLoadPlanAdvisor.INSTANCE, resultSet, (SessionImplementor) workSession, new QueryParameters(), @@ -104,7 +101,6 @@ public int[] getNamedParameterLocations(String name) { return new int[0]; } }, - aliasResolutionContext, true, false, null, diff --git a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java index 28bb284be9..3c5e71d2ce 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanBuilderTest.java @@ -35,7 +35,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.loader.plan.internal.CascadeLoadPlanBuilderStrategy; import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; -import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.plan.spi.build.MetadataDrivenLoadPlanBuilder; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; @@ -64,7 +64,7 @@ public void testSimpleBuild() { sessionFactory(), LoadQueryInfluencers.NONE ); - LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); + LoadPlan plan = MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); assertFalse( plan.hasAnyScalarReturns() ); assertEquals( 1, plan.getReturns().size() ); Return rtn = plan.getReturns().get( 0 ); @@ -85,7 +85,7 @@ public void testCascadeBasedBuild() { sessionFactory(), LoadQueryInfluencers.NONE ); - LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); + LoadPlan plan = MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep ); assertFalse( plan.hasAnyScalarReturns() ); assertEquals( 1, plan.getReturns().size() ); Return rtn = plan.getReturns().get( 0 ); @@ -105,7 +105,7 @@ public void testCollectionInitializerCase() { sessionFactory(), LoadQueryInfluencers.NONE ); - LoadPlan plan = LoadPlanBuilder.buildRootCollectionLoadPlan( strategy, cp ); + LoadPlan plan = MetadataDrivenLoadPlanBuilder.buildRootCollectionLoadPlan( strategy, cp ); assertFalse( plan.hasAnyScalarReturns() ); assertEquals( 1, plan.getReturns().size() ); Return rtn = plan.getReturns().get( 0 ); diff --git a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionHelper.java b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionHelper.java index a828a7a784..261a9cd76a 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionHelper.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionHelper.java @@ -30,11 +30,10 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.JoinWalker; import org.hibernate.loader.entity.EntityJoinWalker; -import org.hibernate.loader.entity.EntityLoader; 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.build.LoadPlanBuilder; +import org.hibernate.loader.plan.spi.build.MetadataDrivenLoadPlanBuilder; import org.hibernate.persister.entity.OuterJoinLoadable; /** @@ -78,12 +77,9 @@ public void performBasicComparison(SessionFactoryImplementor sf, OuterJoinLoadab ); // final EntityLoader loader = new EntityLoader( persister, lockMode, sf, influencers ); - SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( sf, influencers ); - LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister ); - LoadQueryDetails details = LoadQueryDetails.makeForBatching( - persister.getKeyColumnNames(), - plan, - sf, + LoadPlan plan = buildLoadPlan( sf, persister, influencers ); + EntityLoadQueryDetails details = EntityLoadQueryDetails.makeForBatching( + plan, persister.getKeyColumnNames(), new QueryBuildingParameters() { @Override public LoadQueryInfluencers getQueryInfluencers() { @@ -104,13 +100,25 @@ public LockMode getLockMode() { public LockOptions getLockOptions() { return null; } - } + }, sf ); compare( walker, details ); } - private void compare(JoinWalker walker, LoadQueryDetails details) { + public LoadPlan buildLoadPlan( + SessionFactoryImplementor sf, + OuterJoinLoadable persister, + LoadQueryInfluencers influencers) { + SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( sf, influencers ); + return MetadataDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, persister ); + } + + public LoadPlan buildLoadPlan(SessionFactoryImplementor sf, OuterJoinLoadable persister) { + return buildLoadPlan( sf, persister, LoadQueryInfluencers.NONE ); + } + + private void compare(JoinWalker walker, EntityLoadQueryDetails details) { System.out.println( "------ SQL -----------------------------------------------------------------" ); System.out.println( "WALKER : " + walker.getSQLString() ); System.out.println( "LOAD-PLAN : " + details.getSqlStatement() ); diff --git a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionTest.java b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionTest.java index 83d71adc35..2cd61e4ea3 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/plan/spi/LoadPlanStructureAssertionTest.java @@ -26,11 +26,26 @@ import org.hibernate.cfg.Configuration; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.EncapsulatedCompositeIdResultSetProcessorTest; +import org.hibernate.loader.Helper; +import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.exec.spi.EntityLoadQueryDetails; +import org.hibernate.loader.plan.exec.spi.RowReader; import org.hibernate.persister.entity.OuterJoinLoadable; import org.junit.Test; +import junit.framework.Assert; + import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.test.annotations.cid.keymanytoone.Card; +import org.hibernate.test.annotations.cid.keymanytoone.CardField; +import org.hibernate.test.annotations.cid.keymanytoone.Key; +import org.hibernate.test.annotations.cid.keymanytoone.PrimaryKey; + +import static junit.framework.Assert.assertNotNull; +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; /** * Used to assert that "fetch graphs" between JoinWalker and LoadPlan are same. @@ -62,13 +77,118 @@ public void testSpecialOneToOne() { } @Test - public void testEncapsulatedCompositeId() { + public void testEncapsulatedCompositeIdNoFetches() { + // CardField is an entity with a composite identifier mapped via a @EmbeddedId class (CardFieldPK) defining + // a @ManyToOne + // + // Parent is an entity with a composite identifier mapped via a @EmbeddedId class (ParentPK) which is defined + // using just basic types (strings, ints, etc) Configuration cfg = new Configuration(); cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.Parent.class ); cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.CardField.class ); cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.Card.class ); SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory(); doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.CardField.class ) ); + doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.Card.class ) ); + doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.Parent.class ) ); + } + + @Test + public void testEncapsulatedCompositeIdWithFetches1() { + Configuration cfg = new Configuration(); + cfg.addAnnotatedClass( Card.class ); + cfg.addAnnotatedClass( CardField.class ); + cfg.addAnnotatedClass( Key.class ); + cfg.addAnnotatedClass( PrimaryKey.class ); + + SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory(); + + final OuterJoinLoadable cardFieldPersister = (OuterJoinLoadable) sf.getClassMetadata( CardField.class ); + doCompare( sf, cardFieldPersister ); + final LoadPlan loadPlan = LoadPlanStructureAssertionHelper.INSTANCE.buildLoadPlan( sf, cardFieldPersister ); + assertEquals( LoadPlan.Disposition.ENTITY_LOADER, loadPlan.getDisposition() ); + assertEquals( 1, loadPlan.getReturns().size() ); + final EntityReturn cardFieldReturn = assertTyping( EntityReturn.class, loadPlan.getReturns().get( 0 ) ); + assertEquals( 0, cardFieldReturn.getFetches().length ); + assertEquals( 1, cardFieldReturn.getIdentifierDescription().getFetches().length ); + final CompositeFetch cardFieldCompositePkFetch = assertTyping( + CompositeFetch.class, + cardFieldReturn.getIdentifierDescription().getFetches()[0] + ); + assertEquals( 2, cardFieldCompositePkFetch.getFetches().length ); + final EntityFetch cardFetch = assertTyping( EntityFetch.class, cardFieldCompositePkFetch.getFetches()[0] ); + // i think this one might be a mistake; i think the collection reader still needs to be registered. Its zero + // because the inverse of the key-many-to-one already had a registered AssociationKey and so saw the + // CollectionFetch as a circularity (I think) + assertEquals( 0, cardFetch.getFetches().length ); + assertEquals( 0, cardFetch.getIdentifierDescription().getFetches().length ); + + final EntityFetch keyFetch = assertTyping( EntityFetch.class, cardFieldCompositePkFetch.getFetches()[1] ); + assertEquals( 0, keyFetch.getFetches().length ); + assertEquals( 0, keyFetch.getIdentifierDescription().getFetches().length ); + + // we need the readers ordered in a certain manner. Here specifically: Fetch(Card), Fetch(Key), Return(CardField) + // + // additionally, we need Fetch(Card) and Fetch(Key) to be hydrated/semi-resolved before attempting to + // resolve the EntityKey for Return(CardField) + // + // together those sound like argument enough to continue keeping readers for "identifier fetches" as part of + // a special "identifier reader". generated aliases could help here too to remove cyclic-ness from the graph. + // but at any rate, we need to know still when this becomes circularity + } + + @Test + public void testEncapsulatedCompositeIdWithFetches2() { + Configuration cfg = new Configuration(); + cfg.addAnnotatedClass( Card.class ); + cfg.addAnnotatedClass( CardField.class ); + cfg.addAnnotatedClass( Key.class ); + cfg.addAnnotatedClass( PrimaryKey.class ); + + SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory(); + + final OuterJoinLoadable cardPersister = (OuterJoinLoadable) sf.getClassMetadata( Card.class ); + doCompare( sf, cardPersister ); + final LoadPlan cardLoadPlan = LoadPlanStructureAssertionHelper.INSTANCE.buildLoadPlan( sf, cardPersister ); + assertEquals( LoadPlan.Disposition.ENTITY_LOADER, cardLoadPlan.getDisposition() ); + assertEquals( 1, cardLoadPlan.getReturns().size() ); + final EntityReturn cardReturn = assertTyping( EntityReturn.class, cardLoadPlan.getReturns().get( 0 ) ); + assertEquals( 0, cardReturn.getIdentifierDescription().getFetches().length ); + assertEquals( 1, cardReturn.getFetches().length ); + final CollectionFetch fieldsFetch = assertTyping( CollectionFetch.class, cardReturn.getFetches()[0] ); + assertNotNull( fieldsFetch.getElementGraph() ); + final EntityElementGraph fieldsElementElementGraph = assertTyping( EntityElementGraph.class, fieldsFetch.getElementGraph() ); + assertEquals( 0, fieldsElementElementGraph.getFetches().length ); + assertEquals( 1, fieldsElementElementGraph.getIdentifierDescription().getFetches().length ); + final CompositeFetch fieldsElementCompositeIdFetch = assertTyping( + CompositeFetch.class, + fieldsElementElementGraph.getIdentifierDescription().getFetches()[0] + ); + assertEquals( 2, fieldsElementCompositeIdFetch.getFetches().length ); + + BidirectionalEntityFetch circularCardFetch = assertTyping( + BidirectionalEntityFetch.class, + fieldsElementCompositeIdFetch.getFetches()[0] + ); + assertSame( circularCardFetch.getTargetEntityReference(), cardReturn ); + + // the fetch above is to the other key-many-to-one for CardField.primaryKey composite: key + EntityFetch keyFetch = assertTyping( + EntityFetch.class, + fieldsElementCompositeIdFetch.getFetches()[1] + ); + assertEquals( Key.class.getName(), keyFetch.getEntityPersister().getEntityName() ); + + + final EntityLoadQueryDetails queryDetails = Helper.INSTANCE.buildLoadQueryDetails( cardLoadPlan, sf ); + final ResultSetProcessorImpl resultSetProcessor = assertTyping( + ResultSetProcessorImpl.class, + queryDetails.getResultSetProcessor() + ); + final EntityLoadQueryDetails.EntityLoaderRowReader rowReader = assertTyping( + EntityLoadQueryDetails.EntityLoaderRowReader.class, + resultSetProcessor.getRowReader() + ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java index 5a7ef43eeb..3b1c9fbc20 100644 --- a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java @@ -34,6 +34,7 @@ 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.AssociationKey; import org.hibernate.persister.walking.spi.AssociationVisitationStrategy; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.CollectionDefinition; @@ -213,6 +214,29 @@ public void finishingAttribute(AttributeDefinition attributeDefinition) { @Override public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition) { } + + @Override + public void associationKeyRegistered(AssociationKey associationKey) { + System.out.println( + String.format( + "%s AssociationKey registered : %s", + StringHelper.repeat( ">>", depth + 1 ), + associationKey.toString() + ) + ); + } + + @Override + public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) { + System.out.println( + String.format( + "%s Handling circular association attribute (%s) : %s", + StringHelper.repeat( ">>", depth + 1 ), + attributeDefinition.toString(), + associationKey.toString() + ) + ); + } }, ep );