Package org.hibernate.loader.plan.exec.internal

Source Code of org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor

/*
* 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.jboss.logging.Logger;

import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl;
import org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl;
import org.hibernate.loader.plan.exec.process.spi.ReaderCollector;
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.spi.CollectionAttributeFetch;
import org.hibernate.loader.plan.spi.CollectionQuerySpace;
import org.hibernate.loader.plan.spi.CompositeQuerySpace;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityQuerySpace;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchSource;
import org.hibernate.loader.plan.spi.Join;
import org.hibernate.loader.plan.spi.JoinDefinedByMetadata;
import org.hibernate.loader.plan.spi.QuerySpace;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.CollectionPropertyNames;
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.walking.internal.FetchStrategyHelper;
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
* functionality, especially in regards to handling QuerySpace {@link Join}s and {@link Fetch}es.
* <p/>
* Exposes 2 main methods:<ol>
*     <li>{@link #processQuerySpaceJoins(QuerySpace, SelectStatementBuilder)}</li>
*     <li>{@link #processFetches(FetchSource, SelectStatementBuilder, org.hibernate.loader.plan.exec.process.spi.ReaderCollector)}li>
* </ol>
*
* @author Steve Ebersole
*/
public class LoadQueryJoinAndFetchProcessor {
  private static final Logger LOG = CoreLogging.logger( LoadQueryJoinAndFetchProcessor.class );

  private final AliasResolutionContextImpl aliasResolutionContext;
  private final QueryBuildingParameters buildingParameters;
  private final SessionFactoryImplementor factory;

  /**
   * Instantiates a LoadQueryBuilderHelper with the given information
   *
   * @param aliasResolutionContext
   * @param buildingParameters
   * @param factory
   */
  public LoadQueryJoinAndFetchProcessor(
      AliasResolutionContextImpl aliasResolutionContext,
      QueryBuildingParameters buildingParameters,
      SessionFactoryImplementor factory) {
    this.aliasResolutionContext = aliasResolutionContext;
    this.buildingParameters = buildingParameters;
    this.factory = factory;
  }

  public AliasResolutionContext getAliasResolutionContext() {
    return aliasResolutionContext;
  }

  public QueryBuildingParameters getQueryBuildingParameters() {
    return buildingParameters;
  }

  public SessionFactoryImplementor getSessionFactory() {
    return factory;
  }

  public void processQuerySpaceJoins(QuerySpace querySpace, SelectStatementBuilder selectStatementBuilder) {
    LOG.debug( "processing queryspace " + querySpace.getUid() );
    final JoinFragment joinFragment = factory.getDialect().createOuterJoinFragment();
    processQuerySpaceJoins( querySpace, joinFragment );

    selectStatementBuilder.setOuterJoins(
        joinFragment.toFromFragmentString(),
        joinFragment.toWhereFragmentString()
    );
  }

  private void processQuerySpaceJoins(QuerySpace querySpace, JoinFragment joinFragment) {
    // IMPL NOTES:
    //
    // 1) The querySpace and the left-hand-side of each of the querySpace's joins should really be the same.
    // validate that?  any cases where they wont be the same?
    //
    // 2) Assume that the table fragments for the left-hand-side have already been rendered.  We just need to
    // figure out the proper lhs table alias to use and the column/formula from the lhs to define the join
    // condition, which can be different per Join

    for ( Join join : querySpace.getJoins() ) {
      processQuerySpaceJoin( join, joinFragment );
    }
  }

  private void processQuerySpaceJoin(Join join, JoinFragment joinFragment) {
    renderJoin( join, joinFragment );
    processQuerySpaceJoins( join.getRightHandSide(), joinFragment );
  }

  private void renderJoin(Join join, JoinFragment joinFragment) {
    if ( CompositeQuerySpace.class.isInstance( join.getRightHandSide() ) ) {
      handleCompositeJoin( join, joinFragment );
    }
    else if ( EntityQuerySpace.class.isInstance( join.getRightHandSide() ) ) {
      // do not render the entity join for a one-to-many association, since the collection join
      // already joins to the associated entity table (see doc in renderCollectionJoin()).
      if ( join.getLeftHandSide().getDisposition() == QuerySpace.Disposition.COLLECTION ) {
        if ( CollectionQuerySpace.class.cast( join.getLeftHandSide() ).getCollectionPersister().isManyToMany() ) {
          renderManyToManyJoin( join, joinFragment );
        }
        else if ( JoinDefinedByMetadata.class.isInstance( join ) &&
            CollectionPropertyNames.COLLECTION_INDICES.equals( JoinDefinedByMetadata.class.cast( join ).getJoinedPropertyName() ) ) {
          renderManyToManyJoin( join, joinFragment );
        }
      }
      else {
        renderEntityJoin( join, joinFragment );
      }
    }
    else if ( CollectionQuerySpace.class.isInstance( join.getRightHandSide() ) ) {
      renderCollectionJoin( join, joinFragment );
    }
  }

  private void handleCompositeJoin(Join join, JoinFragment joinFragment) {
    final String leftHandSideUid = join.getLeftHandSide().getUid();
    final String rightHandSideUid = join.getRightHandSide().getUid();

    final String leftHandSideTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( leftHandSideUid );
    if ( leftHandSideTableAlias == null ) {
      throw new IllegalStateException(
          "QuerySpace with that UID was not yet registered in the AliasResolutionContext"
      );
    }

    aliasResolutionContext.registerCompositeQuerySpaceUidResolution( rightHandSideUid, leftHandSideTableAlias );
  }

  private void renderEntityJoin(Join join, JoinFragment joinFragment) {
    final EntityQuerySpace rightHandSide = (EntityQuerySpace) join.getRightHandSide();

    // see if there is already aliases registered for this entity query space (collection joins)
    EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases( rightHandSide.getUid() );
    if ( aliases == null ) {
      aliasResolutionContext.generateEntityReferenceAliases(
          rightHandSide.getUid(),
          rightHandSide.getEntityPersister()
      );
    }

    final Joinable joinable = (Joinable) rightHandSide.getEntityPersister();
    addJoins(
        join,
        joinFragment,
        joinable
    );
  }

  private AssociationType getJoinedAssociationTypeOrNull(Join join) {

    if ( !JoinDefinedByMetadata.class.isInstance( join ) ) {
      return null;
    }
    final Type joinedType = ( (JoinDefinedByMetadata) join ).getJoinedPropertyType();
    return joinedType.isAssociationType()
        ? (AssociationType) joinedType
        : null;
  }

  private String resolveAdditionalJoinCondition(String rhsTableAlias, String withClause, Joinable joinable, AssociationType associationType) {
    // turns out that the call to AssociationType#getOnCondition in the initial code really just translates to
    // calls to the Joinable.filterFragment() method where the Joinable is either the entity or
    // collection persister
    final String filter = associationType!=null?
        associationType.getOnCondition( rhsTableAlias, factory, buildingParameters.getQueryInfluencers().getEnabledFilters() ):
        joinable.filterFragment(
          rhsTableAlias,
          buildingParameters.getQueryInfluencers().getEnabledFilters()
    );

    if ( StringHelper.isEmpty( withClause ) && StringHelper.isEmpty( filter ) ) {
      return "";
    }
    else if ( StringHelper.isNotEmpty( withClause ) && StringHelper.isNotEmpty( filter ) ) {
      return filter + " and " + withClause;
    }
    else {
      // only one is non-empty...
      return StringHelper.isNotEmpty( filter ) ? filter : withClause;
    }
  }

  private void addJoins(
      Join join,
      JoinFragment joinFragment,
      Joinable joinable) {

    final String rhsTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
        join.getRightHandSide().getUid()
    );
    if ( StringHelper.isEmpty( rhsTableAlias ) ) {
      throw new IllegalStateException( "Join's RHS table alias cannot be empty" );
    }

    final String lhsTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
        join.getLeftHandSide().getUid()
    );
    if ( lhsTableAlias == null ) {
      throw new IllegalStateException( "QuerySpace with that UID was not yet registered in the AliasResolutionContext" );
    }

    // add join fragments from the collection table -> element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    final String additionalJoinConditions = resolveAdditionalJoinCondition(
        rhsTableAlias,
        join.getAnyAdditionalJoinConditions( rhsTableAlias ),
        joinable,
        getJoinedAssociationTypeOrNull( join )
    );

    joinFragment.addJoin(
        joinable.getTableName(),
        rhsTableAlias,
        join.resolveAliasedLeftHandSideJoinConditionColumns( lhsTableAlias ),
        join.resolveNonAliasedRightHandSideJoinConditionColumns(),
        join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
        additionalJoinConditions
    );
    joinFragment.addJoins(
        joinable.fromJoinFragment( rhsTableAlias, false, true ),
        joinable.whereJoinFragment( rhsTableAlias, false, true )
    );
  }

  private void renderCollectionJoin(Join join, JoinFragment joinFragment) {
    final CollectionQuerySpace rightHandSide = (CollectionQuerySpace) join.getRightHandSide();
    final CollectionReferenceAliases aliases = aliasResolutionContext.generateCollectionReferenceAliases(
        rightHandSide.getUid(),
        rightHandSide.getCollectionPersister()
    );

    // The SQL join to the "collection table" needs to be rendered.
    //
    // In the case of a basic collection, that's the only join needed.
    //
    // For one-to-many/many-to-many, we need to render the "collection table join"
    // here (as already stated). There will be a follow-on join (rhs will have a join) for the associated entity.
    // For many-to-many, the follow-on join will join to the associated entity element table. For one-to-many,
    // the collection table is the associated entity table, so the follow-on join will not be rendered..

    if ( rightHandSide.getCollectionPersister().isOneToMany()
        || rightHandSide.getCollectionPersister().isManyToMany() ) {
      // relatedly, for collections with entity elements (one-to-many, many-to-many) we need to register the
      // sql aliases to use for the entity.
      //
      // currently we do not explicitly track the joins under the CollectionQuerySpace to know which is
      // the element join and which is the index join (maybe we should?).  Another option here is to have the
      // "collection join" act as the entity element join in this case (much like I do with entity identifiers).
      // The difficulty there is that collections can theoretically could be multiple joins in that case (one
      // for element, one for index).  However, that's a bit of future-planning as today Hibernate does not
      // properly deal with the index anyway in terms of allowing dynamic fetching across a collection index...
      //
      // long story short, for now we'll use an assumption that the last join in the CollectionQuerySpace is the
      // element join (that's how the joins are built as of now..)
      //
      // todo : remove this assumption ^^; maybe we make CollectionQuerySpace "special" and rather than have it
      // hold a list of joins, we have it expose the 2 (index, element) separately.

      Join collectionElementJoin = null;
      for ( Join collectionJoin : rightHandSide.getJoins() ) {
        collectionElementJoin = collectionJoin;
      }
      if ( collectionElementJoin == null ) {
        throw new IllegalStateException(
            String.format(
                "Could not locate collection element join within collection join [%s : %s]",
                rightHandSide.getUid(),
                rightHandSide.getCollectionPersister()
            )
        );
      }
      aliasResolutionContext.registerQuerySpaceAliases(
          collectionElementJoin.getRightHandSide().getUid(),
          new EntityReferenceAliasesImpl(
              aliases.getElementTableAlias(),
              aliases.getEntityElementColumnAliases()
          )
      );
    }

    addJoins(
        join,
        joinFragment,
        (Joinable) rightHandSide.getCollectionPersister()
    );
  }

  private void renderManyToManyJoin(
      Join join,
      JoinFragment joinFragment) {

    // 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 - in terms of rendering the joins (not the fetch select fragments), the
    //       lhs table alias is only needed to qualify the lhs join columns, but we already have the qualified
    //       columns here (aliasedLhsColumnNames)
    //final String ownerTableAlias = ...;
    //    2) the m-n table : user_role
    //    3) the element table : role
    final EntityPersister entityPersister = ( (EntityQuerySpace) join.getRightHandSide() ).getEntityPersister();
    final String entityTableAlias = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid(
      join.getRightHandSide().getUid()
    );

    if ( StringHelper.isEmpty( entityTableAlias ) ) {
      throw new IllegalStateException( "Collection element (many-to-many) table alias cannot be empty" );
    }
    if ( JoinDefinedByMetadata.class.isInstance( join ) &&
        CollectionPropertyNames.COLLECTION_ELEMENTS.equals( ( (JoinDefinedByMetadata) join ).getJoinedPropertyName() ) ) {
      final CollectionQuerySpace leftHandSide = (CollectionQuerySpace) join.getLeftHandSide();
      final CollectionPersister persister = leftHandSide.getCollectionPersister();
      final String manyToManyFilter = persister.getManyToManyFilterFragment(
          entityTableAlias,
          buildingParameters.getQueryInfluencers().getEnabledFilters()
      );
      joinFragment.addCondition( manyToManyFilter );
    }

    addJoins(
        join,
        joinFragment,
        (Joinable) entityPersister
    );
  }

  public FetchStats processFetches(
      FetchSource fetchSource,
      SelectStatementBuilder selectStatementBuilder,
      ReaderCollector readerCollector) {
    final FetchStatsImpl fetchStats = new FetchStatsImpl();

    // if the fetchSource is an entityReference, we should also walk its identifier fetches here...
    //
    // what if fetchSource is a composite fetch (as it would be in the case of a key-many-to-one)?
    if ( EntityReference.class.isInstance( fetchSource ) ) {
      final EntityReference fetchOwnerAsEntityReference = (EntityReference) fetchSource;
      if ( fetchOwnerAsEntityReference.getIdentifierDescription().hasFetches() ) {
        final FetchSource entityIdentifierAsFetchSource = (FetchSource) fetchOwnerAsEntityReference.getIdentifierDescription();
        for ( Fetch fetch : entityIdentifierAsFetchSource.getFetches() ) {
          processFetch(
              selectStatementBuilder,
              fetchSource,
              fetch,
              readerCollector,
              fetchStats
          );
        }
      }
    }

    processFetches( fetchSource, selectStatementBuilder, readerCollector, fetchStats );
    return fetchStats;
  }

  private void processFetches(
      FetchSource fetchSource,
      SelectStatementBuilder selectStatementBuilder,
      ReaderCollector readerCollector,
      FetchStatsImpl fetchStats) {
    for ( Fetch fetch : fetchSource.getFetches() ) {
      processFetch(
          selectStatementBuilder,
          fetchSource,
          fetch,
          readerCollector,
          fetchStats
      );
    }
  }


  private void processFetch(
      SelectStatementBuilder selectStatementBuilder,
      FetchSource fetchSource,
      Fetch fetch,
      ReaderCollector readerCollector,
      FetchStatsImpl fetchStats) {
    if ( ! FetchStrategyHelper.isJoinFetched( fetch.getFetchStrategy() ) ) {
      return;
    }

    if ( EntityFetch.class.isInstance( fetch ) ) {
      final EntityFetch entityFetch = (EntityFetch) fetch;
      processEntityFetch(
          selectStatementBuilder,
          fetchSource,
          entityFetch,
          readerCollector,
          fetchStats
      );
    }
    else if ( CollectionAttributeFetch.class.isInstance( fetch ) ) {
      final CollectionAttributeFetch collectionFetch = (CollectionAttributeFetch) fetch;
      processCollectionFetch(
          selectStatementBuilder,
          fetchSource,
          collectionFetch,
          readerCollector,
          fetchStats
      );
    }
    else {
      // could also be a CompositeFetch, we ignore those here
      // but do still need to visit their fetches...
      if ( FetchSource.class.isInstance( fetch ) ) {
        processFetches(
            (FetchSource) fetch,
            selectStatementBuilder,
            readerCollector,
            fetchStats
        );
      }
    }
  }

  private void processEntityFetch(
      SelectStatementBuilder selectStatementBuilder,
      FetchSource fetchSource,
      EntityFetch fetch,
      ReaderCollector readerCollector,
      FetchStatsImpl fetchStats) {
    // todo : still need to think through expressing bi-directionality in the new model...
//    if ( BidirectionalEntityFetch.class.isInstance( fetch ) ) {
//      log.tracef( "Skipping bi-directional entity fetch [%s]", fetch );
//      return;
//    }

    fetchStats.processingFetch( fetch );

    // First write out the SQL SELECT fragments
    final Joinable joinable = (Joinable) fetch.getEntityPersister();
    EntityReferenceAliases aliases = aliasResolutionContext.resolveEntityReferenceAliases(
        fetch.getQuerySpaceUid()
    );

    // the null arguments here relate to many-to-many fetches
    selectStatementBuilder.appendSelectClauseFragment(
        joinable.selectFragment(
            null,
            null,
            aliases.getTableAlias(),
            aliases.getColumnAliases().getSuffix(),
            null,
            true
        )
    );

    // process its identifier fetches first (building EntityReferenceInitializers for them if needed)
    if ( fetch.getIdentifierDescription().hasFetches() ) {
      final FetchSource entityIdentifierAsFetchSource = (FetchSource) fetch.getIdentifierDescription();
      for ( Fetch identifierFetch : entityIdentifierAsFetchSource.getFetches() ) {
        processFetch(
            selectStatementBuilder,
            fetch,
            identifierFetch,
            readerCollector,
            fetchStats
        );
      }
    }

    // build an EntityReferenceInitializers for the incoming fetch itself
    readerCollector.add( new EntityReferenceInitializerImpl( fetch, aliases ) );

    // then visit each of our (non-identifier) fetches
    processFetches( fetch, selectStatementBuilder, readerCollector, fetchStats );
  }

  private void processCollectionFetch(
      SelectStatementBuilder selectStatementBuilder,
      FetchSource fetchSource,
      CollectionAttributeFetch fetch,
      ReaderCollector readerCollector,
      FetchStatsImpl fetchStats) {
    fetchStats.processingFetch( fetch );

    final CollectionReferenceAliases aliases = aliasResolutionContext.resolveCollectionReferenceAliases(
        fetch.getQuerySpaceUid()
    );

    final QueryableCollection queryableCollection = (QueryableCollection) fetch.getCollectionPersister();
    final Joinable joinableCollection = (Joinable) fetch.getCollectionPersister();

    if ( fetch.getCollectionPersister().isManyToMany() ) {
      // todo : better way to access `ownerTableAlias` here.
      //     when processing the Join part of this we are able to look up the "lhs table alias" because we know
      //     the 'lhs' QuerySpace.
      //
      // Good idea to be able resolve a Join by lookup on the rhs and lhs uid?  If so, Fetch

      // 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 = aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( fetchSource.getQuerySpaceUid() );
      //    2) the m-n table : user_role
      final String collectionTableAlias = aliases.getCollectionTableAlias();
      //    3) the element table : role
      final String elementTableAlias = aliases.getElementTableAlias();

      // add select fragments from the collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      selectStatementBuilder.appendSelectClauseFragment(
          joinableCollection.selectFragment(
              (Joinable) queryableCollection.getElementPersister(),
              elementTableAlias,
              collectionTableAlias,

              aliases.getEntityElementColumnAliases().getSuffix(),
              aliases.getCollectionColumnAliases().getSuffix(),
              true
          )
      );

      // add select fragments from the element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
      selectStatementBuilder.appendSelectClauseFragment(
          elementPersister.selectFragment(
              elementTableAlias,
              aliases.getEntityElementColumnAliases().getSuffix()
          )
      );

      // add SQL ORDER-BY fragments
      final String manyToManyOrdering = queryableCollection.getManyToManyOrderByString( collectionTableAlias );
      if ( StringHelper.isNotEmpty( manyToManyOrdering ) ) {
        selectStatementBuilder.appendOrderByFragment( manyToManyOrdering );
      }

      final String ordering = queryableCollection.getSQLOrderByString( collectionTableAlias );
      if ( StringHelper.isNotEmpty( ordering ) ) {
        selectStatementBuilder.appendOrderByFragment( ordering );
      }

      // add an EntityReferenceInitializer for the collection elements (keys also?)
      final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliasesImpl(
          aliases.getCollectionTableAlias(),
          aliases.getEntityElementColumnAliases()
      );
      aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases );
      readerCollector.add(
          new EntityReferenceInitializerImpl(
              (EntityReference) fetch.getElementGraph(),
              entityReferenceAliases
          )
      );
    }
    else {
      // select the "collection columns"
      selectStatementBuilder.appendSelectClauseFragment(
          queryableCollection.selectFragment(
              aliases.getElementTableAlias(),
              aliases.getCollectionColumnAliases().getSuffix()
          )
      );

      if ( fetch.getCollectionPersister().isOneToMany() ) {
        // if the collection elements are entities, select the entity columns as well
        final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();
        selectStatementBuilder.appendSelectClauseFragment(
            elementPersister.selectFragment(
                aliases.getElementTableAlias(),
                aliases.getEntityElementColumnAliases().getSuffix()
            )
        );
        final EntityReferenceAliases entityReferenceAliases = new EntityReferenceAliasesImpl(
            aliases.getElementTableAlias(),
            aliases.getEntityElementColumnAliases()
        );
        aliasResolutionContext.registerQuerySpaceAliases( fetch.getQuerySpaceUid(), entityReferenceAliases );
        readerCollector.add(
            new EntityReferenceInitializerImpl(
                (EntityReference) fetch.getElementGraph(),
                entityReferenceAliases
            )
        );
      }

      final String ordering = queryableCollection.getSQLOrderByString( aliases.getElementTableAlias() );
      if ( StringHelper.isNotEmpty( ordering ) ) {
        selectStatementBuilder.appendOrderByFragment( ordering );
      }
    }

    if ( fetch.getElementGraph() != null ) {
      processFetches( fetch.getElementGraph(), selectStatementBuilder, readerCollector );
    }

    readerCollector.add( new CollectionReferenceInitializerImpl( fetch, aliases ) );
  }

  /**
   * Implementation of FetchStats
   */
  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;
    }
  }

}
TOP

Related Classes of org.hibernate.loader.plan.exec.internal.LoadQueryJoinAndFetchProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.