Package org.hibernate.loader.hql

Source Code of org.hibernate.loader.hql.QueryLoader

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, 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.hql;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.QueryException;
import org.hibernate.ScrollableResults;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.hql.internal.HolderInstantiator;
import org.hibernate.hql.internal.ast.QueryTranslatorImpl;
import org.hibernate.hql.internal.ast.tree.AggregatedSelectExpression;
import org.hibernate.hql.internal.ast.tree.FromElement;
import org.hibernate.hql.internal.ast.tree.QueryNode;
import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.internal.IteratorImpl;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;

/**
* A delegate that implements the Loader part of QueryTranslator.
*
* @author josh
*/
public class QueryLoader extends BasicLoader {

  /**
   * The query translator that is delegating to this object.
   */
  private QueryTranslatorImpl queryTranslator;

  private Queryable[] entityPersisters;
  private String[] entityAliases;
  private String[] sqlAliases;
  private String[] sqlAliasSuffixes;
  private boolean[] includeInSelect;

  private String[] collectionSuffixes;

  private boolean hasScalars;
  private String[][] scalarColumnNames;
  //private Type[] sqlResultTypes;
  private Type[] queryReturnTypes;

  private final Map sqlAliasByEntityAlias = new HashMap(8);

  private EntityType[] ownerAssociationTypes;
  private int[] owners;
  private boolean[] entityEagerPropertyFetches;

  private int[] collectionOwners;
  private QueryableCollection[] collectionPersisters;

  private int selectLength;

  private AggregatedSelectExpression aggregatedSelectExpression;
  private String[] queryReturnAliases;

  private LockMode[] defaultLockModes;


  /**
   * Creates a new Loader implementation.
   *
   * @param queryTranslator The query translator that is the delegator.
   * @param factory The factory from which this loader is being created.
   * @param selectClause The AST representing the select clause for loading.
   */
  public QueryLoader(
      final QueryTranslatorImpl queryTranslator,
          final SessionFactoryImplementor factory,
          final SelectClause selectClause) {
    super( factory );
    this.queryTranslator = queryTranslator;
    initialize( selectClause );
    postInstantiate();
  }

  private void initialize(SelectClause selectClause) {

    List fromElementList = selectClause.getFromElementsForLoad();

    hasScalars = selectClause.isScalarSelect();
    scalarColumnNames = selectClause.getColumnNames();
    //sqlResultTypes = selectClause.getSqlResultTypes();
    queryReturnTypes = selectClause.getQueryReturnTypes();

    aggregatedSelectExpression = selectClause.getAggregatedSelectExpression();
    queryReturnAliases = selectClause.getQueryReturnAliases();

    List collectionFromElements = selectClause.getCollectionFromElements();
    if ( collectionFromElements != null && collectionFromElements.size()!=0 ) {
      int length = collectionFromElements.size();
      collectionPersisters = new QueryableCollection[length];
      collectionOwners = new int[length];
      collectionSuffixes = new String[length];
      for ( int i=0; i<length; i++ ) {
        FromElement collectionFromElement = (FromElement) collectionFromElements.get(i);
        collectionPersisters[i] = collectionFromElement.getQueryableCollection();
        collectionOwners[i] = fromElementList.indexOf( collectionFromElement.getOrigin() );
//        collectionSuffixes[i] = collectionFromElement.getColumnAliasSuffix();
//        collectionSuffixes[i] = Integer.toString( i ) + "_";
        collectionSuffixes[i] = collectionFromElement.getCollectionSuffix();
      }
    }

    int size = fromElementList.size();
    entityPersisters = new Queryable[size];
    entityEagerPropertyFetches = new boolean[size];
    entityAliases = new String[size];
    sqlAliases = new String[size];
    sqlAliasSuffixes = new String[size];
    includeInSelect = new boolean[size];
    owners = new int[size];
    ownerAssociationTypes = new EntityType[size];

    for ( int i = 0; i < size; i++ ) {
      final FromElement element = ( FromElement ) fromElementList.get( i );
      entityPersisters[i] = ( Queryable ) element.getEntityPersister();

      if ( entityPersisters[i] == null ) {
        throw new IllegalStateException( "No entity persister for " + element.toString() );
      }

      entityEagerPropertyFetches[i] = element.isAllPropertyFetch();
      sqlAliases[i] = element.getTableAlias();
      entityAliases[i] = element.getClassAlias();
      sqlAliasByEntityAlias.put( entityAliases[i], sqlAliases[i] );
      // TODO should we just collect these like with the collections above?
      sqlAliasSuffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + "_";
//      sqlAliasSuffixes[i] = element.getColumnAliasSuffix();
      includeInSelect[i] = !element.isFetch();
      if ( includeInSelect[i] ) {
        selectLength++;
      }

      owners[i] = -1; //by default
      if ( element.isFetch() ) {
        if ( element.isCollectionJoin() || element.getQueryableCollection() != null ) {
          // This is now handled earlier in this method.
        }
        else if ( element.getDataType().isEntityType() ) {
          EntityType entityType = ( EntityType ) element.getDataType();
          if ( entityType.isOneToOne() ) {
            owners[i] = fromElementList.indexOf( element.getOrigin() );
          }
          ownerAssociationTypes[i] = entityType;
        }
      }
    }

    //NONE, because its the requested lock mode, not the actual!
    defaultLockModes = ArrayHelper.fillArray( LockMode.NONE, size );
  }

  public AggregatedSelectExpression getAggregatedSelectExpression() {
    return aggregatedSelectExpression;
  }


  // -- Loader implementation --

  public final void validateScrollability() throws HibernateException {
    queryTranslator.validateScrollability();
  }

  protected boolean needsFetchingScroll() {
    return queryTranslator.containsCollectionFetches();
  }

  public Loadable[] getEntityPersisters() {
    return entityPersisters;
  }

  public String[] getAliases() {
    return sqlAliases;
  }

  public String[] getSqlAliasSuffixes() {
    return sqlAliasSuffixes;
  }

  public String[] getSuffixes() {
    return getSqlAliasSuffixes();
  }

  public String[] getCollectionSuffixes() {
    return collectionSuffixes;
  }

  protected String getQueryIdentifier() {
    return queryTranslator.getQueryIdentifier();
  }

  /**
   * The SQL query string to be called.
   */
  protected String getSQLString() {
    return queryTranslator.getSQLString();
  }

  /**
   * An (optional) persister for a collection to be initialized; only collection loaders
   * return a non-null value
   */
  protected CollectionPersister[] getCollectionPersisters() {
    return collectionPersisters;
  }

  protected int[] getCollectionOwners() {
    return collectionOwners;
  }

  protected boolean[] getEntityEagerPropertyFetches() {
    return entityEagerPropertyFetches;
  }

  /**
   * An array of indexes of the entity that owns a one-to-one association
   * to the entity at the given index (-1 if there is no "owner")
   */
  protected int[] getOwners() {
    return owners;
  }

  protected EntityType[] getOwnerAssociationTypes() {
    return ownerAssociationTypes;
  }

  // -- Loader overrides --

  protected boolean isSubselectLoadingEnabled() {
    return hasSubselectLoadableCollections();
  }

  /**
   * @param lockOptions a collection of lock modes specified dynamically via the Query interface
   */
  protected LockMode[] getLockModes(LockOptions lockOptions) {
    if ( lockOptions == null ) {
      return defaultLockModes;
    }

    if ( lockOptions.getAliasLockCount() == 0
        && ( lockOptions.getLockMode() == null || LockMode.NONE.equals( lockOptions.getLockMode() ) ) ) {
      return defaultLockModes;
    }

    // unfortunately this stuff can't be cached because
    // it is per-invocation, not constant for the
    // QueryTranslator instance

    LockMode[] lockModesArray = new LockMode[entityAliases.length];
    for ( int i = 0; i < entityAliases.length; i++ ) {
      LockMode lockMode = lockOptions.getEffectiveLockMode( entityAliases[i] );
      if ( lockMode == null ) {
        //NONE, because its the requested lock mode, not the actual!
        lockMode = LockMode.NONE;
      }
      lockModesArray[i] = lockMode;
    }

    return lockModesArray;
  }

  protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
    // can't cache this stuff either (per-invocation)
    // we are given a map of user-alias -> lock mode
    // create a new map of sql-alias -> lock mode

    if ( lockOptions == null ||
      ( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
      return sql;
    }

    // we need both the set of locks and the columns to reference in locks
    // as the ultimate output of this section...
    final LockOptions locks = new LockOptions( lockOptions.getLockMode() );
    final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;

    locks.setScope( lockOptions.getScope() );
    locks.setTimeOut( lockOptions.getTimeOut() );

    final Iterator itr = sqlAliasByEntityAlias.entrySet().iterator();
    while ( itr.hasNext() ) {
      final Map.Entry entry = (Map.Entry) itr.next();
      final String userAlias = (String) entry.getKey();
      final String drivingSqlAlias = (String) entry.getValue();
      if ( drivingSqlAlias == null ) {
        throw new IllegalArgumentException( "could not locate alias to apply lock mode : " + userAlias );
      }
      // at this point we have (drivingSqlAlias) the SQL alias of the driving table
      // corresponding to the given user alias.  However, the driving table is not
      // (necessarily) the table against which we want to apply locks.  Mainly,
      // the exception case here is joined-subclass hierarchies where we instead
      // want to apply the lock against the root table (for all other strategies,
      // it just happens that driving and root are the same).
      final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
      final Lockable drivingPersister = ( Lockable ) select.getFromClause()
          .findFromElementByUserOrSqlAlias( userAlias, drivingSqlAlias )
          .getQueryable();
      final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );

      final LockMode effectiveLockMode = lockOptions.getEffectiveLockMode( userAlias );
      locks.setAliasSpecificLockMode( sqlAlias, effectiveLockMode );

      if ( keyColumnNames != null ) {
        keyColumnNames.put( sqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
      }
    }

    // apply the collected locks and columns
    return dialect.applyLocksToSql( sql, locks, keyColumnNames );
  }

  protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
    // todo : scalars???
//    if ( row.length != lockModesArray.length ) {
//      return;
//    }
//
//    for ( int i = 0; i < lockModesArray.length; i++ ) {
//      if ( LockMode.OPTIMISTIC_FORCE_INCREMENT.equals( lockModesArray[i] ) ) {
//        final EntityEntry pcEntry =
//      }
//      else if ( LockMode.PESSIMISTIC_FORCE_INCREMENT.equals( lockModesArray[i] ) ) {
//
//      }
//    }
  }

  protected boolean upgradeLocks() {
    return true;
  }

  private boolean hasSelectNew() {
    return aggregatedSelectExpression != null &&  aggregatedSelectExpression.getResultTransformer() != null;
  }

  protected String[] getResultRowAliases() {
    return queryReturnAliases;
  }
 
  protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
    final ResultTransformer implicitResultTransformer = aggregatedSelectExpression == null
        ? null
        : aggregatedSelectExpression.getResultTransformer();
    return HolderInstantiator.resolveResultTransformer( implicitResultTransformer, resultTransformer );
  }

  protected boolean[] includeInResultRow() {
    boolean[] includeInResultTuple = includeInSelect;
    if ( hasScalars ) {
      includeInResultTuple = new boolean[ queryReturnTypes.length ];
      Arrays.fill( includeInResultTuple, true );
    }
    return includeInResultTuple;
  }

  protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
      throws SQLException, HibernateException {

    Object[] resultRow = getResultRow( row, rs, session );
    boolean hasTransform = hasSelectNew() || transformer!=null;
    return ( ! hasTransform && resultRow.length == 1 ?
        resultRow[ 0 ] :
        resultRow
    );
  }

  protected Object[] getResultRow(Object[] row, ResultSet rs, SessionImplementor session)
      throws SQLException, HibernateException {
    Object[] resultRow;
    if ( hasScalars ) {
      String[][] scalarColumns = scalarColumnNames;
      int queryCols = queryReturnTypes.length;
      resultRow = new Object[queryCols];
      for ( int i = 0; i < queryCols; i++ ) {
        resultRow[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
      }
    }
    else {
      resultRow = toResultRow( row );
    }
    return resultRow;
  }

  protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
    // meant to handle dynamic instantiation queries...
    HolderInstantiator holderInstantiator = buildHolderInstantiator( resultTransformer );
    if ( holderInstantiator.isRequired() ) {
      for ( int i = 0; i < results.size(); i++ ) {
        Object[] row = ( Object[] ) results.get( i );
        Object result = holderInstantiator.instantiate(row);
        results.set( i, result );
      }

      if ( !hasSelectNew() && resultTransformer != null ) {
        return resultTransformer.transformList(results);
      }
      else {
        return results;
      }
    }
    else {
      return results;
    }
  }

  private HolderInstantiator buildHolderInstantiator(ResultTransformer queryLocalResultTransformer) {
    final ResultTransformer implicitResultTransformer = aggregatedSelectExpression == null
        ? null
        : aggregatedSelectExpression.getResultTransformer();
    return HolderInstantiator.getHolderInstantiator(
        implicitResultTransformer,
        queryLocalResultTransformer,
        queryReturnAliases
    );
  }
  // --- Query translator methods ---

  public List list(
      SessionImplementor session,
      QueryParameters queryParameters) throws HibernateException {
    checkQuery( queryParameters );
    return list( session, queryParameters, queryTranslator.getQuerySpaces(), queryReturnTypes );
  }

  private void checkQuery(QueryParameters queryParameters) {
    if ( hasSelectNew() && queryParameters.getResultTransformer() != null ) {
      throw new QueryException( "ResultTransformer is not allowed for 'select new' queries." );
    }
  }

  public Iterator iterate(
      QueryParameters queryParameters,
      EventSource session) throws HibernateException {
    checkQuery( queryParameters );
    final boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
    long startTime = 0;
    if ( stats ) {
      startTime = System.currentTimeMillis();
    }

    try {
      final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
      if ( queryParameters.isCallable() ) {
        throw new QueryException("iterate() not supported for callable statements");
      }
      final ResultSet rs = getResultSet(st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session);
      final Iterator result = new IteratorImpl(
          rs,
              st,
              session,
              queryParameters.isReadOnly( session ),
              queryReturnTypes,
              queryTranslator.getColumnNames(),
              buildHolderInstantiator( queryParameters.getResultTransformer() )
      );

      if ( stats ) {
        session.getFactory().getStatisticsImplementor().queryExecuted(
//            "HQL: " + queryTranslator.getQueryString(),
            getQueryIdentifier(),
            0,
            System.currentTimeMillis() - startTime
        );
      }

      return result;

    }
    catch ( SQLException sqle ) {
      throw getFactory().getSQLExceptionHelper().convert(
              sqle,
              "could not execute query using iterate",
              getSQLString()
        );
    }

  }

  public ScrollableResults scroll(
      final QueryParameters queryParameters,
          final SessionImplementor session) throws HibernateException {
    checkQuery( queryParameters );
    return scroll(
        queryParameters,
        queryReturnTypes,
        buildHolderInstantiator( queryParameters.getResultTransformer() ),
        session
    );
  }

  // -- Implementation private methods --

  private Object[] toResultRow(Object[] row) {
    if ( selectLength == row.length ) {
      return row;
    }
    else {
      Object[] result = new Object[selectLength];
      int j = 0;
      for ( int i = 0; i < row.length; i++ ) {
        if ( includeInSelect[i] ) {
          result[j++] = row[i];
        }
      }
      return result;
    }
  }

  /**
   * Returns the locations of all occurrences of the named parameter.
   */
  public int[] getNamedParameterLocs(String name) throws QueryException {
    return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
  }

  /**
   * We specifically override this method here, because in general we know much more
   * about the parameters and their appropriate bind positions here then we do in
   * our super because we track them explciitly here through the ParameterSpecification
   * interface.
   *
   * @param queryParameters The encapsulation of the parameter values to be bound.
   * @param startIndex The position from which to start binding parameter values.
   * @param session The originating session.
   * @return The number of JDBC bind positions actually bound during this method execution.
   * @throws SQLException Indicates problems performing the binding.
   */
  protected int bindParameterValues(
      final PreparedStatement statement,
      final QueryParameters queryParameters,
      final int startIndex,
      final SessionImplementor session) throws SQLException {
//    int position = bindFilterParameterValues( statement, queryParameters, startIndex, session );
    int position = startIndex;
//    List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
    List parameterSpecs = queryTranslator.getCollectedParameterSpecifications();
    Iterator itr = parameterSpecs.iterator();
    while ( itr.hasNext() ) {
      ParameterSpecification spec = ( ParameterSpecification ) itr.next();
      position += spec.bind( statement, queryParameters, session, position );
    }
    return position - startIndex;
  }

  private int bindFilterParameterValues(
      PreparedStatement st,
      QueryParameters queryParameters,
      int position,
      SessionImplementor session) throws SQLException {
    // todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
    // see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
    // it is currently not done that way.
    int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
        ? 0
        : queryParameters.getFilteredPositionalParameterTypes().length;
    int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
        ? 0
        : queryParameters.getPositionalParameterTypes().length;
    int filterParamCount = filteredParamCount - nonfilteredParamCount;
    for ( int i = 0; i < filterParamCount; i++ ) {
      Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
      Object value = queryParameters.getFilteredPositionalParameterValues()[i];
      type.nullSafeSet( st, value, position, session );
      position += type.getColumnSpan( getFactory() );
    }

    return position;
  }
}
TOP

Related Classes of org.hibernate.loader.hql.QueryLoader

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.