Package org.hibernate.loader.criteria

Source Code of org.hibernate.loader.criteria.CriteriaQueryTranslator

/*
* 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.loader.criteria;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.EnhancedProjection;
import org.hibernate.criterion.Projection;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.PropertyMapping;
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.StringRepresentableType;
import org.hibernate.type.Type;
import org.hibernate.internal.util.StringHelper;

/**
* @author Gavin King
*/
public class CriteriaQueryTranslator implements CriteriaQuery {

  public static final String ROOT_SQL_ALIAS = Criteria.ROOT_ALIAS + '_';

  private CriteriaQuery outerQueryTranslator;

  private final CriteriaImpl rootCriteria;
  private final String rootEntityName;
  private final String rootSQLAlias;
  private int aliasCount = 0;

  private final Map /* <Criteria, CriteriaInfoProvider> */ criteriaInfoMap = new LinkedHashMap();
  private final Map /* <String, CriteriaInfoProvider> */ nameCriteriaInfoMap = new LinkedHashMap();
  private final Map criteriaSQLAliasMap = new HashMap();
  private final Map aliasCriteriaMap = new HashMap();
  private final Map associationPathCriteriaMap = new LinkedHashMap();
  private final Map<String,JoinType> associationPathJoinTypesMap = new LinkedHashMap<String,JoinType>();
  private final Map withClauseMap = new HashMap();
 
  private final SessionFactoryImplementor sessionFactory;
  private final SessionFactoryHelper helper;

  public CriteriaQueryTranslator(
      final SessionFactoryImplementor factory,
          final CriteriaImpl criteria,
          final String rootEntityName,
          final String rootSQLAlias,
          CriteriaQuery outerQuery) throws HibernateException {
    this( factory, criteria, rootEntityName, rootSQLAlias );
    outerQueryTranslator = outerQuery;
  }

  public CriteriaQueryTranslator(
      final SessionFactoryImplementor factory,
          final CriteriaImpl criteria,
          final String rootEntityName,
          final String rootSQLAlias) throws HibernateException {
    this.rootCriteria = criteria;
    this.rootEntityName = rootEntityName;
    this.sessionFactory = factory;
    this.rootSQLAlias = rootSQLAlias;
    this.helper = new SessionFactoryHelper(factory);
    createAliasCriteriaMap();
    createAssociationPathCriteriaMap();
    createCriteriaEntityNameMap();
    createCriteriaSQLAliasMap();
  }

  public String generateSQLAlias() {
    return StringHelper.generateAlias( Criteria.ROOT_ALIAS, aliasCount ) + '_';
  }

  public String getRootSQLALias() {
    return rootSQLAlias;
  }

  private Criteria getAliasedCriteria(String alias) {
    return ( Criteria ) aliasCriteriaMap.get( alias );
  }

  public boolean isJoin(String path) {
    return associationPathCriteriaMap.containsKey( path );
  }

  public JoinType getJoinType(String path) {
    JoinType result = associationPathJoinTypesMap.get( path );
    return ( result == null ? JoinType.INNER_JOIN : result );
  }

  public Criteria getCriteria(String path) {
    return ( Criteria ) associationPathCriteriaMap.get( path );
  }

  public Set getQuerySpaces() {
    Set result = new HashSet();
    Iterator iter = criteriaInfoMap.values().iterator();
    while ( iter.hasNext() ) {
      CriteriaInfoProvider info = ( CriteriaInfoProvider )iter.next();
      result.addAll( Arrays.asList( info.getSpaces() ) );
    }
    return result;
  }

  private void createAliasCriteriaMap() {
    aliasCriteriaMap.put( rootCriteria.getAlias(), rootCriteria );
    Iterator iter = rootCriteria.iterateSubcriteria();
    while ( iter.hasNext() ) {
      Criteria subcriteria = ( Criteria ) iter.next();
      if ( subcriteria.getAlias() != null ) {
        Object old = aliasCriteriaMap.put( subcriteria.getAlias(), subcriteria );
        if ( old != null ) {
          throw new QueryException( "duplicate alias: " + subcriteria.getAlias() );
        }
      }
    }
  }

  private void createAssociationPathCriteriaMap() {
    Iterator iter = rootCriteria.iterateSubcriteria();
    while ( iter.hasNext() ) {
      CriteriaImpl.Subcriteria crit = ( CriteriaImpl.Subcriteria ) iter.next();
      String wholeAssociationPath = getWholeAssociationPath( crit );
      Object old = associationPathCriteriaMap.put( wholeAssociationPath, crit );
      if ( old != null ) {
        throw new QueryException( "duplicate association path: " + wholeAssociationPath );
      }
      JoinType joinType = crit.getJoinType();
      old = associationPathJoinTypesMap.put( wholeAssociationPath, joinType );
      if ( old != null ) {
        // TODO : not so sure this is needed...
        throw new QueryException( "duplicate association path: " + wholeAssociationPath );
      }
      if ( crit.getWithClause() != null )
      {
        this.withClauseMap.put(wholeAssociationPath, crit.getWithClause());
      }
    }
  }

  private String getWholeAssociationPath(CriteriaImpl.Subcriteria subcriteria) {
    String path = subcriteria.getPath();

    // some messy, complex stuff here, since createCriteria() can take an
    // aliased path, or a path rooted at the creating criteria instance
    Criteria parent = null;
    if ( path.indexOf( '.' ) > 0 ) {
      // if it is a compound path
      String testAlias = StringHelper.root( path );
      if ( !testAlias.equals( subcriteria.getAlias() ) ) {
        // and the qualifier is not the alias of this criteria
        //      -> check to see if we belong to some criteria other
        //          than the one that created us
        parent = ( Criteria ) aliasCriteriaMap.get( testAlias );
      }
    }
    if ( parent == null ) {
      // otherwise assume the parent is the the criteria that created us
      parent = subcriteria.getParent();
    }
    else {
      path = StringHelper.unroot( path );
    }

    if ( parent.equals( rootCriteria ) ) {
      // if its the root criteria, we are done
      return path;
    }
    else {
      // otherwise, recurse
      return getWholeAssociationPath( ( CriteriaImpl.Subcriteria ) parent ) + '.' + path;
    }
  }

  private void createCriteriaEntityNameMap() {
    // initialize the rootProvider first
    CriteriaInfoProvider rootProvider = new EntityCriteriaInfoProvider(( Queryable ) sessionFactory.getEntityPersister( rootEntityName ) );
    criteriaInfoMap.put( rootCriteria, rootProvider);
    nameCriteriaInfoMap.put ( rootProvider.getName(), rootProvider );

    Iterator iter = associationPathCriteriaMap.entrySet().iterator();
    while ( iter.hasNext() ) {
      Map.Entry me = ( Map.Entry ) iter.next();
      CriteriaInfoProvider info = getPathInfo((String)me.getKey());

      criteriaInfoMap.put(
          me.getValue(), //the criteria instance
          info
      );

      nameCriteriaInfoMap.put( info.getName(), info );
    }
  }


  private CriteriaInfoProvider getPathInfo(String path) {
    StringTokenizer tokens = new StringTokenizer( path, "." );
    String componentPath = "";

    // start with the 'rootProvider'
    CriteriaInfoProvider provider = ( CriteriaInfoProvider )nameCriteriaInfoMap.get( rootEntityName );

    while ( tokens.hasMoreTokens() ) {
      componentPath += tokens.nextToken();
      Type type = provider.getType( componentPath );
      if ( type.isAssociationType() ) {
        // CollectionTypes are always also AssociationTypes - but there's not always an associated entity...
        AssociationType atype = ( AssociationType ) type;
        CollectionType ctype = type.isCollectionType() ? (CollectionType)type : null;
        Type elementType = (ctype != null) ? ctype.getElementType( sessionFactory ) : null;
        // is the association a collection of components or value-types? (i.e a colloction of valued types?)
        if ( ctype != null  && elementType.isComponentType() ) {
          provider = new ComponentCollectionCriteriaInfoProvider( helper.getCollectionPersister(ctype.getRole()) );
        }
        else if ( ctype != null && !elementType.isEntityType() ) {
          provider = new ScalarCollectionCriteriaInfoProvider( helper, ctype.getRole() );
        }
        else {
          provider = new EntityCriteriaInfoProvider(( Queryable ) sessionFactory.getEntityPersister(
                        atype.getAssociatedEntityName( sessionFactory )
                        ));
        }
       
        componentPath = "";
      }
      else if ( type.isComponentType() ) {
        if (!tokens.hasMoreTokens()) {
          throw new QueryException("Criteria objects cannot be created directly on components.  Create a criteria on owning entity and use a dotted property to access component property: "+path);
        } else {
          componentPath += '.';
        }
      }
      else {
        throw new QueryException( "not an association: " + componentPath );
      }
    }
   
    return provider;
  }

  public int getSQLAliasCount() {
    return criteriaSQLAliasMap.size();
  }

  private void createCriteriaSQLAliasMap() {
    int i = 0;
    Iterator criteriaIterator = criteriaInfoMap.entrySet().iterator();
    while ( criteriaIterator.hasNext() ) {
      Map.Entry me = ( Map.Entry ) criteriaIterator.next();
      Criteria crit = ( Criteria ) me.getKey();
      String alias = crit.getAlias();
      if ( alias == null ) {
        alias = (( CriteriaInfoProvider ) me.getValue()).getName(); // the entity name
      }
      criteriaSQLAliasMap.put( crit, StringHelper.generateAlias( alias, i++ ) );
    }
    criteriaSQLAliasMap.put( rootCriteria, rootSQLAlias );
  }

  public CriteriaImpl getRootCriteria() {
    return rootCriteria;
  }

  public QueryParameters getQueryParameters() {
    LockOptions lockOptions = new LockOptions();
    RowSelection selection = new RowSelection();
    selection.setFirstRow( rootCriteria.getFirstResult() );
    selection.setMaxRows( rootCriteria.getMaxResults() );
    selection.setTimeout( rootCriteria.getTimeout() );
    selection.setFetchSize( rootCriteria.getFetchSize() );

    Iterator iter = rootCriteria.getLockModes().entrySet().iterator();
    while ( iter.hasNext() ) {
      Map.Entry me = ( Map.Entry ) iter.next();
      final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
      lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), (LockMode)me.getValue() );
    }
    List values = new ArrayList();
    List types = new ArrayList();
    iter = rootCriteria.iterateSubcriteria();
    while ( iter.hasNext() ) {
      CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
      LockMode lm = subcriteria.getLockMode();
      if ( lm != null ) {
        lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), lm );
      }
      if ( subcriteria.getWithClause() != null )
      {
        TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
        for ( int i = 0; i < tv.length; i++ ) {
          values.add( tv[i].getValue() );
          types.add( tv[i].getType() );
        }
      }
    }

    // Type and value gathering for the WHERE clause needs to come AFTER lock mode gathering,
    // because the lock mode gathering loop now contains join clauses which can contain
    // parameter bindings (as in the HQL WITH clause).
    iter = rootCriteria.iterateExpressionEntries();
    while ( iter.hasNext() ) {
      CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
      TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
      for ( int i = 0; i < tv.length; i++ ) {
        values.add( tv[i].getValue() );
        types.add( tv[i].getType() );
      }
    }

    Object[] valueArray = values.toArray();
    Type[] typeArray = ArrayHelper.toTypeArray( types );
    return new QueryParameters(
        typeArray,
            valueArray,
            lockOptions,
            selection,
            rootCriteria.isReadOnlyInitialized(),
            ( rootCriteria.isReadOnlyInitialized() ? rootCriteria.isReadOnly() : false ),
            rootCriteria.getCacheable(),
            rootCriteria.getCacheRegion(),
            rootCriteria.getComment(),
            rootCriteria.isLookupByNaturalKey(),
            rootCriteria.getResultTransformer()
    );
  }

  public boolean hasProjection() {
    return rootCriteria.getProjection() != null;
  }

  public String getGroupBy() {
    if ( rootCriteria.getProjection().isGrouped() ) {
      return rootCriteria.getProjection()
          .toGroupSqlString( rootCriteria.getProjectionCriteria(), this );
    }
    else {
      return "";
    }
  }

  public String getSelect() {
    return rootCriteria.getProjection().toSqlString(
        rootCriteria.getProjectionCriteria(),
            0,
            this
    );
  }

  /* package-protected */
  Type getResultType(Criteria criteria) {
    return getFactory().getTypeResolver().getTypeFactory().manyToOne( getEntityName( criteria ) );
  }

  public Type[] getProjectedTypes() {
    return rootCriteria.getProjection().getTypes( rootCriteria, this );
  }

  public String[] getProjectedColumnAliases() {
    return rootCriteria.getProjection() instanceof EnhancedProjection ?
        ( ( EnhancedProjection ) rootCriteria.getProjection() ).getColumnAliases( 0, rootCriteria, this ) :
        rootCriteria.getProjection().getColumnAliases( 0 );
  }

  public String[] getProjectedAliases() {
    return rootCriteria.getProjection().getAliases();
  }

  public String getWhereCondition() {
    StringBuffer condition = new StringBuffer( 30 );
    Iterator criterionIterator = rootCriteria.iterateExpressionEntries();
    while ( criterionIterator.hasNext() ) {
      CriteriaImpl.CriterionEntry entry = ( CriteriaImpl.CriterionEntry ) criterionIterator.next();
      String sqlString = entry.getCriterion().toSqlString( entry.getCriteria(), this );
      condition.append( sqlString );
      if ( criterionIterator.hasNext() ) {
        condition.append( " and " );
      }
    }
    return condition.toString();
  }

  public String getOrderBy() {
    StringBuffer orderBy = new StringBuffer( 30 );
    Iterator criterionIterator = rootCriteria.iterateOrderings();
    while ( criterionIterator.hasNext() ) {
      CriteriaImpl.OrderEntry oe = ( CriteriaImpl.OrderEntry ) criterionIterator.next();
      orderBy.append( oe.getOrder().toSqlString( oe.getCriteria(), this ) );
      if ( criterionIterator.hasNext() ) {
        orderBy.append( ", " );
      }
    }
    return orderBy.toString();
  }

  public SessionFactoryImplementor getFactory() {
    return sessionFactory;
  }

  public String getSQLAlias(Criteria criteria) {
    return ( String ) criteriaSQLAliasMap.get( criteria );
  }

  public String getEntityName(Criteria criteria) {
    return (( CriteriaInfoProvider ) criteriaInfoMap.get( criteria )).getName();
  }

  public String getColumn(Criteria criteria, String propertyName) {
    String[] cols = getColumns( propertyName, criteria );
    if ( cols.length != 1 ) {
      throw new QueryException( "property does not map to a single column: " + propertyName );
    }
    return cols[0];
  }

  /**
   * Get the names of the columns constrained
   * by this criterion.
   */
  public String[] getColumnsUsingProjection(
      Criteria subcriteria,
          String propertyName) throws HibernateException {

    //first look for a reference to a projection alias
    final Projection projection = rootCriteria.getProjection();
    String[] projectionColumns = null;
    if ( projection != null ) {
      projectionColumns = ( projection instanceof EnhancedProjection ?
          ( ( EnhancedProjection ) projection ).getColumnAliases( propertyName, 0, rootCriteria, this ) :
          projection.getColumnAliases( propertyName, 0 )
      );
    }
    if ( projectionColumns == null ) {
      //it does not refer to an alias of a projection,
      //look for a property
      try {
        return getColumns( propertyName, subcriteria );
      }
      catch ( HibernateException he ) {
        //not found in inner query , try the outer query
        if ( outerQueryTranslator != null ) {
          return outerQueryTranslator.getColumnsUsingProjection( subcriteria, propertyName );
        }
        else {
          throw he;
        }
      }
    }
    else {
      //it refers to an alias of a projection
      return projectionColumns;
    }
  }

  public String[] getIdentifierColumns(Criteria subcriteria) {
    String[] idcols =
        ( ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) ) ).getIdentifierColumnNames();
    return StringHelper.qualify( getSQLAlias( subcriteria ), idcols );
  }

  public Type getIdentifierType(Criteria subcriteria) {
    return ( ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) ) ).getIdentifierType();
  }

  public TypedValue getTypedIdentifierValue(Criteria subcriteria, Object value) {
    final Loadable loadable = ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) );
    return new TypedValue(
        loadable.getIdentifierType(),
            value,
            EntityMode.POJO
    );
  }

  public String[] getColumns(
      String propertyName,
          Criteria subcriteria) throws HibernateException {
    return getPropertyMapping( getEntityName( subcriteria, propertyName ) )
        .toColumns(
            getSQLAlias( subcriteria, propertyName ),
                getPropertyName( propertyName )
        );
  }

  /**
   * Get the names of the columns mapped by a property path; if the
   * property path is not found in subcriteria, try the "outer" query.
   * Projection aliases are ignored.
   */
  public String[] findColumns(String propertyName, Criteria subcriteria )
  throws HibernateException {
    try {
      return getColumns( propertyName, subcriteria );
    }
    catch ( HibernateException he ) {
      //not found in inner query, try the outer query
      if ( outerQueryTranslator != null ) {
        return outerQueryTranslator.findColumns( propertyName, subcriteria );
      }
      else {
        throw he;
      }
    }
  }

  public Type getTypeUsingProjection(Criteria subcriteria, String propertyName)
      throws HibernateException {

    //first look for a reference to a projection alias
    final Projection projection = rootCriteria.getProjection();
    Type[] projectionTypes = projection == null ?
                             null :
                             projection.getTypes( propertyName, subcriteria, this );

    if ( projectionTypes == null ) {
      try {
        //it does not refer to an alias of a projection,
        //look for a property
        return getType( subcriteria, propertyName );
      }
      catch ( HibernateException he ) {
        //not found in inner query , try the outer query
        if ( outerQueryTranslator != null ) {
          return outerQueryTranslator.getType( subcriteria, propertyName );
        }
        else {
          throw he;
        }
      }
    }
    else {
      if ( projectionTypes.length != 1 ) {
        //should never happen, i think
        throw new QueryException( "not a single-length projection: " + propertyName );
      }
      return projectionTypes[0];
    }
  }

  public Type getType(Criteria subcriteria, String propertyName)
      throws HibernateException {
    return getPropertyMapping( getEntityName( subcriteria, propertyName ) )
        .toType( getPropertyName( propertyName ) );
  }

  /**
   * Get the a typed value for the given property value.
   */
  public TypedValue getTypedValue(Criteria subcriteria, String propertyName, Object value)
      throws HibernateException {
    // Detect discriminator values...
    if ( value instanceof Class ) {
      Class entityClass = ( Class ) value;
      Queryable q = SessionFactoryHelper.findQueryableUsingImports( sessionFactory, entityClass.getName() );
      if ( q != null ) {
        Type type = q.getDiscriminatorType();
        String stringValue = q.getDiscriminatorSQLValue();
        if (stringValue != null && stringValue.length() > 2
            && stringValue.startsWith("'")
            && stringValue.endsWith("'")) { // remove the single
                            // quotes
          stringValue = stringValue.substring(1,
              stringValue.length() - 1);
        }
       
        // Convert the string value into the proper type.
        if ( type instanceof StringRepresentableType ) {
          StringRepresentableType nullableType = (StringRepresentableType) type;
          value = nullableType.fromStringValue( stringValue );
        }
        else {
          throw new QueryException( "Unsupported discriminator type " + type );
        }
        return new TypedValue(
            type,
                value,
                EntityMode.POJO
        );
      }
    }
    // Otherwise, this is an ordinary value.
    return new TypedValue(
        getTypeUsingProjection( subcriteria, propertyName ),
            value,
            EntityMode.POJO
    );
  }

  private PropertyMapping getPropertyMapping(String entityName)
      throws MappingException {
    CriteriaInfoProvider info = ( CriteriaInfoProvider )nameCriteriaInfoMap.get(entityName);
    return info.getPropertyMapping();
  }

  //TODO: use these in methods above

  public String getEntityName(Criteria subcriteria, String propertyName) {
    if ( propertyName.indexOf( '.' ) > 0 ) {
      String root = StringHelper.root( propertyName );
      Criteria crit = getAliasedCriteria( root );
      if ( crit != null ) {
        return getEntityName( crit );
      }
    }
    return getEntityName( subcriteria );
  }

  public String getSQLAlias(Criteria criteria, String propertyName) {
    if ( propertyName.indexOf( '.' ) > 0 ) {
      String root = StringHelper.root( propertyName );
      Criteria subcriteria = getAliasedCriteria( root );
      if ( subcriteria != null ) {
        return getSQLAlias( subcriteria );
      }
    }
    return getSQLAlias( criteria );
  }

  public String getPropertyName(String propertyName) {
    if ( propertyName.indexOf( '.' ) > 0 ) {
      String root = StringHelper.root( propertyName );
      Criteria crit = getAliasedCriteria( root );
      if ( crit != null ) {
        return propertyName.substring( root.length() + 1 );
      }
    }
    return propertyName;
  }

  public String getWithClause(String path)
  {
    final Criterion crit = (Criterion)this.withClauseMap.get(path);
    return crit == null ? null : crit.toSqlString(getCriteria(path), this);
  }

  public boolean hasRestriction(String path)
  {
    final CriteriaImpl.Subcriteria crit = ( CriteriaImpl.Subcriteria ) getCriteria( path );
    return crit == null ? false : crit.hasRestriction();
  }


}
TOP

Related Classes of org.hibernate.loader.criteria.CriteriaQueryTranslator

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.