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

Source Code of org.hibernate.loader.plan.exec.internal.LoadQueryBuilderHelper$FetchStatsImpl

/*
* 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 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;
import org.hibernate.loader.plan.spi.CompositeIndexGraph;
import org.hibernate.loader.plan.spi.EntityElementGraph;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
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
* functionality
*
* @author Steve Ebersole
*/
public class LoadQueryBuilderHelper {
  private static final Logger log = CoreLogging.logger( LoadQueryBuilderHelper.class );

  private LoadQueryBuilderHelper() {
  }

  // 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,
      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,
        factory,
        joinFragment,
        fetchOwner,
        buildingParameters,
        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,
      ReaderCollector readerCollector,
      FetchStatsImpl stats) {

    for ( Fetch fetch : fetchOwner.getFetches() ) {
      processFetch(
          selectStatementBuilder,
          factory,
          joinFragment,
          fetchOwner,
          fetch,
          buildingParameters,
          aliasResolutionContext,
          readerCollector,
          stats
      );
    }
  }

  private static void processFetch(
      SelectStatementBuilder selectStatementBuilder,
      SessionFactoryImplementor factory,
      JoinFragment joinFragment,
      FetchOwner fetchOwner,
      Fetch fetch,
      QueryBuildingParameters buildingParameters,
      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,
          buildingParameters,
          aliasResolutionContext,
          readerCollector,
          stats
      );
    }
    else if ( CollectionFetch.class.isInstance( fetch ) ) {
      final CollectionFetch collectionFetch = (CollectionFetch) fetch;
      processCollectionFetch(
          selectStatementBuilder,
          factory,
          joinFragment,
          fetchOwner,
          collectionFetch,
          buildingParameters,
          aliasResolutionContext,
          readerCollector,
          stats
      );
      if ( collectionFetch.getIndexGraph() != null ) {
        processJoinFetches(
            selectStatementBuilder,
            factory,
            joinFragment,
            collectionFetch.getIndexGraph(),
            buildingParameters,
            aliasResolutionContext,
            readerCollector,
            stats
        );
      }
      if ( collectionFetch.getElementGraph() != null ) {
        processJoinFetches(
            selectStatementBuilder,
            factory,
            joinFragment,
            collectionFetch.getElementGraph(),
            buildingParameters,
            aliasResolutionContext,
            readerCollector,
            stats
        );
      }
    }
    else {
      // could also be a CompositeFetch, we ignore those here
      // but do still need to visit their fetches...
      if ( FetchOwner.class.isInstance( fetch ) ) {
        processJoinFetches(
            selectStatementBuilder,
            factory,
            joinFragment,
            (FetchOwner) fetch,
            buildingParameters,
            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,
      FetchOwner fetchOwner,
      EntityFetch fetch,
      QueryBuildingParameters buildingParameters,
      AliasResolutionContext aliasResolutionContext) {
    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 );
    // todo : this is not exactly correct.  it assumes the join refers to the LHS PK
    final String[] aliasedLhsColumnNames = fetch.toSqlSelectFragments( lhsTableAlias );

    final String additionalJoinConditions = resolveAdditionalJoinCondition(
        factory,
        rhsAlias,
        fetchOwner,
        fetch,
        buildingParameters.getQueryInfluencers(),
        aliasResolutionContext
    );

    final Joinable joinable = (Joinable) fetch.getEntityPersister();

    addJoins(
        joinFragment,
        joinable,
        fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
        rhsAlias,
        rhsColumnNames,
        aliasedLhsColumnNames,
        additionalJoinConditions
    );

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

    return aliases;
  }

  private static EntityIdentifierReader buildIdentifierReader(
      SelectStatementBuilder selectStatementBuilder,
      SessionFactoryImplementor factory,
      JoinFragment joinFragment,
      EntityReference entityReference,
      QueryBuildingParameters buildingParameters,
      AliasResolutionContext aliasResolutionContext,
      ReaderCollector readerCollector,
      EntityReferenceAliases aliases,
      FetchStatsImpl stats) {
    final List<EntityReferenceReader> identifierFetchReaders = new ArrayList<EntityReferenceReader>();
    final ReaderCollector identifierFetchReaderCollector = new ReaderCollector() {
      @Override
      public void addReader(CollectionReferenceReader collectionReferenceReader) {
        throw new IllegalStateException( "Identifier cannot contain collection fetches" );
      }

      @Override
      public void addReader(EntityReferenceReader entityReferenceReader) {
        identifierFetchReaders.add( entityReferenceReader );
      }
    };
    for ( Fetch fetch : entityReference.getIdentifierDescription().getFetches() ) {
      processFetch(
          selectStatementBuilder,
          factory,
          joinFragment,
          (FetchOwner) entityReference,
          fetch,
          buildingParameters,
          aliasResolutionContext,
          identifierFetchReaderCollector,
          stats
      );
    }
    return new EntityIdentifierReaderImpl(
        entityReference,
        aliases,
        identifierFetchReaders
    );
  }

  private static List<EntityReferenceReader> collectIdentifierFetchReaders(
      EntityReference entityReference,
      AliasResolutionContext aliasResolutionContext,
      ReaderCollector readerCollector) {
    final Type identifierType = entityReference.getEntityPersister().getIdentifierType();
    if ( ! identifierType.isComponentType() ) {
      return Collections.emptyList();
    }

    final Fetch[] fetches = entityReference.getIdentifierDescription().getFetches();
    if ( fetches == null || fetches.length == 0 ) {
      return Collections.emptyList();
    }

    final List<EntityReferenceReader> readers = new ArrayList<EntityReferenceReader>();
    for ( Fetch fetch : fetches ) {
      collectIdentifierFetchReaders( aliasResolutionContext, readers, entityReference, fetch, readerCollector );
    }
    return readers;
  }


  private static void collectIdentifierFetchReaders(
      AliasResolutionContext aliasResolutionContext,
      List<EntityReferenceReader> readers,
      EntityReference entityReference,
      Fetch fetch,
      ReaderCollector readerCollector) {
    if ( CompositeFetch.class.isInstance( fetch ) ) {
      for ( Fetch subFetch : ( (CompositeFetch) fetch).getFetches() ) {
        collectIdentifierFetchReaders( aliasResolutionContext, readers, entityReference, subFetch, readerCollector );
      }
    }
    else if ( ! EntityReference.class.isInstance( fetch ) ) {
      throw new IllegalStateException(
          String.format(
              "Non-entity (and non-composite) fetch [%s] was found as part of entity identifier : %s",
              fetch,
              entityReference.getEntityPersister().getEntityName()
          )
      );
    }
    else {
      // todo : add a mapping here from EntityReference -> EntityReferenceReader
      //
      // need to be careful here about bi-directionality, just not sure how to best check for bi-directionality here.
      //
      final EntityReference fetchedEntityReference = (EntityReference) fetch;
      final EntityReferenceAliases fetchedAliases = aliasResolutionContext.resolveAliases( fetchedEntityReference );

      if ( BidirectionalEntityFetch.class.isInstance( fetchedEntityReference ) ) {
        return;
      }


      final EntityReferenceReader reader = new EntityReferenceReader(
          fetchedEntityReference,
          aliasResolutionContext.resolveAliases( fetchedEntityReference ),
          new EntityIdentifierReaderImpl(
              fetchedEntityReference,
              fetchedAliases,
              Collections.<EntityReferenceReader>emptyList()
          )
      );

      readerCollector.addReader( reader );
//      readers.add( reader );
    }
  }

  private static String[] resolveAliasedLhsJoinColumns(
      FetchOwner fetchOwner,
      Fetch fetch,
      AliasResolutionContext aliasResolutionContext) {
    // IMPL NOTE : the fetch-owner is the LHS; the fetch is the RHS
    final String lhsTableAlias = resolveLhsTableAlias( fetchOwner, fetch, aliasResolutionContext );
    return fetch.toSqlSelectFragments( lhsTableAlias );
  }

  private static String resolveLhsTableAlias(
      FetchOwner fetchOwner,
      Fetch fetch,
      AliasResolutionContext aliasResolutionContext) {
    // IMPL NOTE : the fetch-owner is the LHS; the fetch is the RHS

    if ( AnyFetch.class.isInstance( fetchOwner ) ) {
      throw new WalkingException( "Any type should never be joined!" );
    }
    else if ( EntityReference.class.isInstance( fetchOwner ) ) {
      return aliasResolutionContext.resolveAliases( (EntityReference) fetchOwner ).getTableAlias();
    }
    else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
      return aliasResolutionContext.resolveAliases(
          locateCompositeFetchEntityReferenceSource( (CompositeFetch) fetchOwner )
      ).getTableAlias();
    }
    else if ( CompositeElementGraph.class.isInstance( fetchOwner ) ) {
      final CompositeElementGraph compositeElementGraph = (CompositeElementGraph) fetchOwner;
      return aliasResolutionContext.resolveAliases( compositeElementGraph.getCollectionReference() ).getCollectionTableAlias();
    }
    else if ( CompositeIndexGraph.class.isInstance( fetchOwner ) ) {
      final CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) fetchOwner;
      return aliasResolutionContext.resolveAliases( compositeIndexGraph.getCollectionReference() ).getCollectionTableAlias();
    }
    else {
      throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." );
    }
  }

  private static EntityReference locateCompositeFetchEntityReferenceSource(CompositeFetch composite) {
    final FetchOwner owner = composite.getOwner();
    if ( EntityReference.class.isInstance( owner ) ) {
      return (EntityReference) owner;
    }
    if ( CompositeFetch.class.isInstance( owner ) ) {
      return locateCompositeFetchEntityReferenceSource( (CompositeFetch) owner );
    }

    throw new WalkingException( "Cannot resolve entity source for a CompositeFetch" );
  }

  private static String resolveAdditionalJoinCondition(
      SessionFactoryImplementor factory,
      String rhsTableAlias,
      FetchOwner fetchOwner,
      Fetch fetch,
      LoadQueryInfluencers influencers,
      AliasResolutionContext aliasResolutionContext) {
    final String withClause = StringHelper.isEmpty( fetch.getAdditionalJoinConditions() )
        ? ""
        : " and ( " + fetch.getAdditionalJoinConditions() + " )";
    return ( (AssociationType) fetch.getFetchedType() ).getOnCondition(
        rhsTableAlias,
        factory,
        influencers.getEnabledFilters()
    ) + withClause;
  }

  private static void addJoins(
      JoinFragment joinFragment,
      Joinable joinable,
      JoinType joinType,
      String rhsAlias,
      String[] rhsColumnNames,
      String[] aliasedLhsColumnNames,
      String additionalJoinConditions) {
    joinFragment.addJoin(
        joinable.getTableName(),
        rhsAlias,
        aliasedLhsColumnNames,
        rhsColumnNames,
        joinType,
        additionalJoinConditions
    );
    joinFragment.addJoins(
        joinable.fromJoinFragment( rhsAlias, false, true ),
        joinable.whereJoinFragment( rhsAlias, false, true )
    );
  }

  private static void processCollectionFetch(
      SelectStatementBuilder selectStatementBuilder,
      SessionFactoryImplementor factory,
      JoinFragment joinFragment,
      FetchOwner fetchOwner,
      CollectionFetch fetch,
      QueryBuildingParameters buildingParameters,
      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 = aliases.getCollectionTableAlias();
      //    3) the element table : role
      final String elementTableAlias = aliases.getElementTableAlias();

      {
        // add join fragments from the owner table -> collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        final String filterFragment = ( (Joinable) fetch.getCollectionPersister() ).filterFragment(
            collectionTableAlias,
            buildingParameters.getQueryInfluencers().getEnabledFilters()
        );

        joinFragment.addJoin(
            joinableCollection.getTableName(),
            collectionTableAlias,
            StringHelper.qualify( ownerTableAlias, extractJoinable( fetchOwner ).getKeyColumnNames() ),
            queryableCollection.getKeyColumnNames(),
            fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
            filterFragment
        );
        joinFragment.addJoins(
            joinableCollection.fromJoinFragment( collectionTableAlias, false, true ),
            joinableCollection.whereJoinFragment( collectionTableAlias, false, true )
        );

        // add select fragments from the collection table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        selectStatementBuilder.appendSelectClauseFragment(
            joinableCollection.selectFragment(
                (Joinable) queryableCollection.getElementPersister(),
                ownerTableAlias,
                collectionTableAlias,
                aliases.getEntityElementColumnAliases().getSuffix(),
                aliases.getCollectionColumnAliases().getSuffix(),
                true
            )
        );
      }

      {
        // add join fragments from the collection table -> element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        final String additionalJoinConditions = resolveAdditionalJoinCondition(
            factory,
            elementTableAlias,
            fetchOwner,
            fetch,
            buildingParameters.getQueryInfluencers(),
            aliasResolutionContext
        );

        final String manyToManyFilter = fetch.getCollectionPersister().getManyToManyFilterFragment(
            collectionTableAlias,
            buildingParameters.getQueryInfluencers().getEnabledFilters()
        );

        final String condition;
        if ( "".equals( manyToManyFilter ) ) {
          condition = additionalJoinConditions;
        }
        else if ( "".equals( additionalJoinConditions ) ) {
          condition = manyToManyFilter;
        }
        else {
          condition = additionalJoinConditions + " and " + manyToManyFilter;
        }

        final OuterJoinLoadable elementPersister = (OuterJoinLoadable) queryableCollection.getElementPersister();

        addJoins(
            joinFragment,
            elementPersister,
//            JoinType.INNER_JOIN,
            JoinType.LEFT_OUTER_JOIN,
            elementTableAlias,
            elementPersister.getIdentifierColumnNames(),
            StringHelper.qualify( collectionTableAlias, queryableCollection.getElementColumnNames() ),
            condition
        );

        // add select fragments from the element entity table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        selectStatementBuilder.appendSelectClauseFragment(
            elementPersister.selectFragment(
                aliases.getElementTableAlias(),
                aliases.getEntityElementColumnAliases().getSuffix()
            )
        );
      }

      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 );
      }


      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 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 );
      // todo : this is not exactly correct.  it assumes the join refers to the LHS PK
      final String[] aliasedLhsColumnNames = fetch.toSqlSelectFragments( lhsTableAlias );

      final String on = resolveAdditionalJoinCondition(
          factory,
          rhsTableAlias,
          fetchOwner,
          fetch,
          buildingParameters.getQueryInfluencers(),
          aliasResolutionContext
      );

      addJoins(
          joinFragment,
          joinableCollection,
          fetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN,
          rhsTableAlias,
          rhsColumnNames,
          aliasedLhsColumnNames,
          on
      );

      // select the "collection columns"
      selectStatementBuilder.appendSelectClauseFragment(
          queryableCollection.selectFragment(
              rhsTableAlias,
              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 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 );
      if ( StringHelper.isNotEmpty( ordering ) ) {
        selectStatementBuilder.appendOrderByFragment( ordering );
      }
    }

    readerCollector.addReader( new CollectionReferenceReader( fetch, aliases ) );
  }

  private static Joinable extractJoinable(FetchOwner fetchOwner) {
    // this is used for collection fetches.  At the end of the day, a fetched collection must be owned by
    // an entity.  Find that entity's persister and return it
    if ( EntityReference.class.isInstance( fetchOwner ) ) {
      return (Joinable) ( (EntityReference) fetchOwner ).getEntityPersister();
    }
    else if ( CompositeFetch.class.isInstance( fetchOwner ) ) {
      return (Joinable) locateCompositeFetchEntityReferenceSource( (CompositeFetch) fetchOwner ).getEntityPersister();
    }
    else if ( EntityElementGraph.class.isInstance( fetchOwner ) ) {
      return (Joinable) ( (EntityElementGraph) fetchOwner ).getEntityPersister();
    }

    throw new IllegalStateException( "Uncertain how to extract Joinable from given FetchOwner : " + fetchOwner );
  }

}
TOP

Related Classes of org.hibernate.loader.plan.exec.internal.LoadQueryBuilderHelper$FetchStatsImpl

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.