Package org.hibernate.hql.internal.ast.tree

Source Code of org.hibernate.hql.internal.ast.tree.FromElementFactory

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.hql.internal.ast.tree;

import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.internal.ast.util.AliasGenerator;
import org.hibernate.hql.internal.ast.util.PathHelper;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

import antlr.ASTFactory;
import antlr.SemanticException;
import antlr.collections.AST;

/**
* Encapsulates the creation of FromElements and JoinSequences.
*
* @author josh
*/
public class FromElementFactory implements SqlTokenTypes {
  private static final CoreMessageLogger LOG = CoreLogging.messageLogger( FromElementFactory.class );

  private FromClause fromClause;
  private FromElement origin;
  private String path;

  private String classAlias;
  private String[] columns;
  private boolean implied;
  private boolean inElementsFunction;
  private boolean collection;
  private QueryableCollection queryableCollection;
  private CollectionType collectionType;

  /**
   * Creates entity from elements.
   */
  public FromElementFactory(FromClause fromClause, FromElement origin, String path) {
    this.fromClause = fromClause;
    this.origin = origin;
    this.path = path;
    collection = false;
  }

  /**
   * Creates collection from elements.
   */
  public FromElementFactory(
      FromClause fromClause,
      FromElement origin,
      String path,
      String classAlias,
      String[] columns,
      boolean implied) {
    this( fromClause, origin, path );
    this.classAlias = classAlias;
    this.columns = columns;
    this.implied = implied;
    collection = true;
  }

  FromElement addFromElement() throws SemanticException {
    final FromClause parentFromClause = fromClause.getParentFromClause();
    if ( parentFromClause != null ) {
      // Look up class name using the first identifier in the path.
      final String pathAlias = PathHelper.getAlias( path );
      final FromElement parentFromElement = parentFromClause.getFromElement( pathAlias );
      if ( parentFromElement != null ) {
        return createFromElementInSubselect( path, pathAlias, parentFromElement, classAlias );
      }
    }

    final EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( path );

    final FromElement elem = createAndAddFromElement(
        path,
        classAlias,
        entityPersister,
        (EntityType) ( (Queryable) entityPersister ).getType(),
        null
    );

    // Add to the query spaces.
    fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );

    return elem;
  }

  private FromElement createFromElementInSubselect(
      String path,
      String pathAlias,
      FromElement parentFromElement,
      String classAlias) throws SemanticException {
    LOG.debugf( "createFromElementInSubselect() : path = %s", path );

    // Create an DotNode AST for the path and resolve it.
    FromElement fromElement = evaluateFromElementPath( path, classAlias );
    EntityPersister entityPersister = fromElement.getEntityPersister();

    // If the first identifier in the path refers to the class alias (not the class name), then this
    // is a correlated subselect.  If it's a correlated sub-select, use the existing table alias.  Otherwise
    // generate a new one.
    String tableAlias = null;
    boolean correlatedSubselect = pathAlias.equals( parentFromElement.getClassAlias() );
    if ( correlatedSubselect ) {
      tableAlias = fromElement.getTableAlias();
    }
    else {
      tableAlias = null;
    }

    // If the from element isn't in the same clause, create a new from element.
    if ( fromElement.getFromClause() != fromClause ) {
      LOG.debug( "createFromElementInSubselect() : creating a new FROM element..." );
      fromElement = createFromElement( entityPersister );
      initializeAndAddFromElement(
          fromElement,
          path,
          classAlias,
          entityPersister,
          (EntityType) ( (Queryable) entityPersister ).getType(),
          tableAlias
      );
    }
    LOG.debugf( "createFromElementInSubselect() : %s -> %s", path, fromElement );
    return fromElement;
  }

  private FromElement evaluateFromElementPath(String path, String classAlias) throws SemanticException {
    ASTFactory factory = fromClause.getASTFactory();
    FromReferenceNode pathNode = (FromReferenceNode) PathHelper.parsePath( path, factory );
    pathNode.recursiveResolve(
        // This is the root level node.
        FromReferenceNode.ROOT_LEVEL,
        // Generate an explicit from clause at the root.
        false,
        classAlias,
        null
    );
    if ( pathNode.getImpliedJoin() != null ) {
      return pathNode.getImpliedJoin();
    }
    return pathNode.getFromElement();
  }

  FromElement createCollectionElementsJoin(
      QueryableCollection queryableCollection,
      String collectionName) throws SemanticException {
    JoinSequence collectionJoinSequence = fromClause.getSessionFactoryHelper()
        .createCollectionJoinSequence( queryableCollection, collectionName );
    this.queryableCollection = queryableCollection;
    return createCollectionJoin( collectionJoinSequence, null );
  }

  public FromElement createCollection(
      QueryableCollection queryableCollection,
      String role,
      JoinType joinType,
      boolean fetchFlag,
      boolean indexed)
      throws SemanticException {
    if ( !collection ) {
      throw new IllegalStateException( "FromElementFactory not initialized for collections!" );
    }
    this.inElementsFunction = indexed;
    FromElement elem;
    this.queryableCollection = queryableCollection;
    collectionType = queryableCollection.getCollectionType();
    String roleAlias = fromClause.getAliasGenerator().createName( role );

    // Correlated subqueries create 'special' implied from nodes
    // because correlated subselects can't use an ANSI-style join
    boolean explicitSubqueryFromElement = fromClause.isSubQuery() && !implied;
    if ( explicitSubqueryFromElement ) {
      String pathRoot = StringHelper.root( path );
      FromElement origin = fromClause.getFromElement( pathRoot );
      if ( origin == null || origin.getFromClause() != fromClause ) {
        implied = true;
      }
    }

    // super-duper-classic-parser-regression-testing-mojo-magic...
    if ( explicitSubqueryFromElement && DotNode.useThetaStyleImplicitJoins ) {
      implied = true;
    }

    Type elementType = queryableCollection.getElementType();
    if ( elementType.isEntityType() ) {
      // A collection of entities...
      elem = createEntityAssociation( role, roleAlias, joinType );
    }
    else if ( elementType.isComponentType() ) {
      // A collection of components...
      JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
      elem = createCollectionJoin( joinSequence, roleAlias );
    }
    else {
      // A collection of scalar elements...
      JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
      elem = createCollectionJoin( joinSequence, roleAlias );
    }

    elem.setRole( role );
    elem.setQueryableCollection( queryableCollection );
    // Don't include sub-classes for implied collection joins or subquery joins.
    if ( implied ) {
      elem.setIncludeSubclasses( false );
    }

    if ( explicitSubqueryFromElement ) {
      // Treat explict from elements in sub-queries properly.
      elem.setInProjectionList( true );
    }

    if ( fetchFlag ) {
      elem.setFetch( true );
    }
    return elem;
  }

  public FromElement createEntityJoin(
      String entityClass,
      String tableAlias,
      JoinSequence joinSequence,
      boolean fetchFlag,
      boolean inFrom,
      EntityType type,
      String role,
      String joinPath) throws SemanticException {
    FromElement elem = createJoin( entityClass, tableAlias, joinSequence, type, false );
    elem.setFetch( fetchFlag );

    if ( joinPath != null ) {
      elem.applyTreatAsDeclarations( fromClause.getWalker().getTreatAsDeclarationsByPath( joinPath ) );
    }

    EntityPersister entityPersister = elem.getEntityPersister();
    int numberOfTables = entityPersister.getQuerySpaces().length;
    if ( numberOfTables > 1 && implied && !elem.useFromFragment() ) {
      LOG.debug( "createEntityJoin() : Implied multi-table entity join" );
      elem.setUseFromFragment( true );
    }

    // If this is an implied join in a FROM clause, then use ANSI-style joining, and set the
    // flag on the FromElement that indicates that it was implied in the FROM clause itself.
    if ( implied && inFrom ) {
      joinSequence.setUseThetaStyle( false );
      elem.setUseFromFragment( true );
      elem.setImpliedInFromClause( true );
    }
    if ( elem.getWalker().isSubQuery() ) {
      // two conditions where we need to transform this to a theta-join syntax:
      //      1) 'elem' is the "root from-element" in correlated subqueries
      //      2) The DotNode.useThetaStyleImplicitJoins has been set to true
      //          and 'elem' represents an implicit join
      if ( elem.getFromClause() != elem.getOrigin().getFromClause() ||
//              ( implied && DotNode.useThetaStyleImplicitJoins ) ) {
          DotNode.useThetaStyleImplicitJoins ) {
        // the "root from-element" in correlated subqueries do need this piece
        elem.setType( FROM_FRAGMENT );
        joinSequence.setUseThetaStyle( true );
        elem.setUseFromFragment( false );
      }
    }

    elem.setRole( role );

    return elem;
  }

  public FromElement createComponentJoin(ComponentType type) {

    // need to create a "place holder" from-element that can store the component/alias for this
    //     component join
    return new ComponentJoin( fromClause, origin, classAlias, path, type );
  }

  FromElement createElementJoin(QueryableCollection queryableCollection) throws SemanticException {
    FromElement elem;

    implied = true; //TODO: always true for now, but not if we later decide to support elements() in the from clause
    inElementsFunction = true;
    Type elementType = queryableCollection.getElementType();
    if ( !elementType.isEntityType() ) {
      throw new IllegalArgumentException( "Cannot create element join for a collection of non-entities!" );
    }
    this.queryableCollection = queryableCollection;
    SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
    FromElement destination = null;
    String tableAlias = null;
    EntityPersister entityPersister = queryableCollection.getElementPersister();
    tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
    String associatedEntityName = entityPersister.getEntityName();
    EntityPersister targetEntityPersister = sfh.requireClassPersister( associatedEntityName );
    // Create the FROM element for the target (the elements of the collection).
    destination = createAndAddFromElement(
        associatedEntityName,
        classAlias,
        targetEntityPersister,
        (EntityType) queryableCollection.getElementType(),
        tableAlias
    );
    // If the join is implied, then don't include sub-classes on the element.
    if ( implied ) {
      destination.setIncludeSubclasses( false );
    }
    fromClause.addCollectionJoinFromElementByPath( path, destination );
//    origin.addDestination(destination);
    // Add the query spaces.
    fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );

    CollectionType type = queryableCollection.getCollectionType();
    String role = type.getRole();
    String roleAlias = origin.getTableAlias();

    String[] targetColumns = sfh.getCollectionElementColumns( role, roleAlias );
    AssociationType elementAssociationType = sfh.getElementAssociationType( type );

    // Create the join element under the from element.
    JoinType joinType = JoinType.INNER_JOIN;
    JoinSequence joinSequence = sfh.createJoinSequence(
        implied,
        elementAssociationType,
        tableAlias,
        joinType,
        targetColumns
    );
    elem = initializeJoin( path, destination, joinSequence, targetColumns, origin, false );
    elem.setUseFromFragment( true );    // The associated entity is implied, but it must be included in the FROM.
    elem.setCollectionTableAlias( roleAlias );    // The collection alias is the role.
    return elem;
  }

  private FromElement createCollectionJoin(JoinSequence collectionJoinSequence, String tableAlias)
      throws SemanticException {
    String text = queryableCollection.getTableName();
    AST ast = createFromElement( text );
    FromElement destination = (FromElement) ast;
    Type elementType = queryableCollection.getElementType();
    if ( elementType.isCollectionType() ) {
      throw new SemanticException( "Collections of collections are not supported!" );
    }
    destination.initializeCollection( fromClause, classAlias, tableAlias );
    destination.setType( JOIN_FRAGMENT );        // Tag this node as a JOIN.
    destination.setIncludeSubclasses( false );    // Don't include subclasses in the join.
    destination.setCollectionJoin( true );        // This is a clollection join.
    destination.setJoinSequence( collectionJoinSequence );
    destination.setOrigin( origin, false );
    destination.setCollectionTableAlias( tableAlias );
//    origin.addDestination( destination );
// This was the cause of HHH-242
//    origin.setType( FROM_FRAGMENT );      // Set the parent node type so that the AST is properly formed.
    origin.setText( "" );                        // The destination node will have all the FROM text.
    origin.setCollectionJoin( true );            // The parent node is a collection join too (voodoo - see JoinProcessor)
    fromClause.addCollectionJoinFromElementByPath( path, destination );
    fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
    return destination;
  }

  private FromElement createEntityAssociation(
      String role,
      String roleAlias,
      JoinType joinType) throws SemanticException {
    FromElement elem;
    Queryable entityPersister = (Queryable) queryableCollection.getElementPersister();
    String associatedEntityName = entityPersister.getEntityName();
    // Get the class name of the associated entity.
    if ( queryableCollection.isOneToMany() ) {
      LOG.debugf(
          "createEntityAssociation() : One to many - path = %s role = %s associatedEntityName = %s",
          path,
          role,
          associatedEntityName
      );
      JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );

      elem = createJoin(
          associatedEntityName,
          roleAlias,
          joinSequence,
          (EntityType) queryableCollection.getElementType(),
          false
      );
    }
    else {
      LOG.debugf(
          "createManyToMany() : path = %s role = %s associatedEntityName = %s",
          path,
          role,
          associatedEntityName
      );
      elem = createManyToMany(
          role, associatedEntityName,
          roleAlias, entityPersister, (EntityType) queryableCollection.getElementType(), joinType
      );
      fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
    }
    elem.setCollectionTableAlias( roleAlias );
    return elem;
  }

  private FromElement createJoin(
      String entityClass,
      String tableAlias,
      JoinSequence joinSequence,
      EntityType type,
      boolean manyToMany) throws SemanticException {
    //  origin, path, implied, columns, classAlias,
    EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( entityClass );
    FromElement destination = createAndAddFromElement(
        entityClass,
        classAlias,
        entityPersister,
        type,
        tableAlias
    );
    return initializeJoin( path, destination, joinSequence, getColumns(), origin, manyToMany );
  }

  private FromElement createManyToMany(
      String role,
      String associatedEntityName,
      String roleAlias,
      Queryable entityPersister,
      EntityType type,
      JoinType joinType) throws SemanticException {
    FromElement elem;
    SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
    if ( inElementsFunction /*implied*/ ) {
      // For implied many-to-many, just add the end join.
      JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
      elem = createJoin( associatedEntityName, roleAlias, joinSequence, type, true );
    }
    else {
      // For an explicit many-to-many relationship, add a second join from the intermediate
      // (many-to-many) table to the destination table.  Also, make sure that the from element's
      // idea of the destination is the destination table.
      String tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
      String[] secondJoinColumns = sfh.getCollectionElementColumns( role, roleAlias );
      // Add the second join, the one that ends in the destination table.
      JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
      joinSequence.addJoin(
          sfh.getElementAssociationType( collectionType ),
          tableAlias,
          joinType,
          secondJoinColumns
      );
      elem = createJoin( associatedEntityName, tableAlias, joinSequence, type, false );
      elem.setUseFromFragment( true );
    }
    return elem;
  }

  private JoinSequence createJoinSequence(String roleAlias, JoinType joinType) {
    SessionFactoryHelper sessionFactoryHelper = fromClause.getSessionFactoryHelper();
    String[] joinColumns = getColumns();
    if ( collectionType == null ) {
      throw new IllegalStateException( "collectionType is null!" );
    }
    return sessionFactoryHelper.createJoinSequence( implied, collectionType, roleAlias, joinType, joinColumns );
  }

  private FromElement createAndAddFromElement(
      String className,
      String classAlias,
      EntityPersister entityPersister,
      EntityType type,
      String tableAlias) {
    if ( !( entityPersister instanceof Joinable ) ) {
      throw new IllegalArgumentException( "EntityPersister " + entityPersister + " does not implement Joinable!" );
    }
    FromElement element = createFromElement( entityPersister );
    initializeAndAddFromElement( element, className, classAlias, entityPersister, type, tableAlias );
    return element;
  }

  private void initializeAndAddFromElement(
      FromElement element,
      String className,
      String classAlias,
      EntityPersister entityPersister,
      EntityType type,
      String tableAlias) {
    if ( tableAlias == null ) {
      AliasGenerator aliasGenerator = fromClause.getAliasGenerator();
      tableAlias = aliasGenerator.createName( entityPersister.getEntityName() );
    }
    element.initializeEntity( fromClause, className, entityPersister, type, classAlias, tableAlias );
  }

  private FromElement createFromElement(EntityPersister entityPersister) {
    Joinable joinable = (Joinable) entityPersister;
    String text = joinable.getTableName();
    AST ast = createFromElement( text );
    FromElement element = (FromElement) ast;
    return element;
  }

  private AST createFromElement(String text) {
    AST ast = ASTUtil.create(
        fromClause.getASTFactory(),
        implied ? IMPLIED_FROM : FROM_FRAGMENT, // This causes the factory to instantiate the desired class.
        text
    );
    // Reset the node type, because the rest of the system is expecting FROM_FRAGMENT, all we wanted was
    // for the factory to create the right sub-class.  This might get reset again later on anyway to make the
    // SQL generation simpler.
    ast.setType( FROM_FRAGMENT );
    return ast;
  }

  private FromElement initializeJoin(
      String path,
      FromElement destination,
      JoinSequence joinSequence,
      String[] columns,
      FromElement origin,
      boolean manyToMany) {
    destination.setType( JOIN_FRAGMENT );
    destination.setJoinSequence( joinSequence );
    destination.setColumns( columns );
    destination.setOrigin( origin, manyToMany );
    fromClause.addJoinByPathMap( path, destination );
    return destination;
  }

  private String[] getColumns() {
    if ( columns == null ) {
      throw new IllegalStateException( "No foriegn key columns were supplied!" );
    }
    return columns;
  }
}
TOP

Related Classes of org.hibernate.hql.internal.ast.tree.FromElementFactory

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.