Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » loader » hql » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.loader.hql;
   26   
   27   import java.sql.PreparedStatement;
   28   import java.sql.ResultSet;
   29   import java.sql.SQLException;
   30   import java.util.HashMap;
   31   import java.util.Iterator;
   32   import java.util.List;
   33   import java.util.Map;
   34   
   35   import org.hibernate.HibernateException;
   36   import org.hibernate.LockMode;
   37   import org.hibernate.QueryException;
   38   import org.hibernate.ScrollableResults;
   39   import org.hibernate.dialect.Dialect;
   40   import org.hibernate.engine.QueryParameters;
   41   import org.hibernate.engine.SessionFactoryImplementor;
   42   import org.hibernate.engine.SessionImplementor;
   43   import org.hibernate.event.EventSource;
   44   import org.hibernate.exception.JDBCExceptionHelper;
   45   import org.hibernate.hql.HolderInstantiator;
   46   import org.hibernate.hql.ast.QueryTranslatorImpl;
   47   import org.hibernate.hql.ast.tree.FromElement;
   48   import org.hibernate.hql.ast.tree.SelectClause;
   49   import org.hibernate.hql.ast.tree.QueryNode;
   50   import org.hibernate.impl.IteratorImpl;
   51   import org.hibernate.loader.BasicLoader;
   52   import org.hibernate.param.ParameterSpecification;
   53   import org.hibernate.persister.collection.CollectionPersister;
   54   import org.hibernate.persister.collection.QueryableCollection;
   55   import org.hibernate.persister.entity.Loadable;
   56   import org.hibernate.persister.entity.Queryable;
   57   import org.hibernate.persister.entity.Lockable;
   58   import org.hibernate.transform.ResultTransformer;
   59   import org.hibernate.type.EntityType;
   60   import org.hibernate.type.Type;
   61   import org.hibernate.util.ArrayHelper;
   62   
   63   /**
   64    * A delegate that implements the Loader part of QueryTranslator.
   65    *
   66    * @author josh
   67    */
   68   public class QueryLoader extends BasicLoader {
   69   
   70   	/**
   71   	 * The query translator that is delegating to this object.
   72   	 */
   73   	private QueryTranslatorImpl queryTranslator;
   74   
   75   	private Queryable[] entityPersisters;
   76   	private String[] entityAliases;
   77   	private String[] sqlAliases;
   78   	private String[] sqlAliasSuffixes;
   79   	private boolean[] includeInSelect;
   80   
   81   	private String[] collectionSuffixes;
   82   
   83   	private boolean hasScalars;
   84   	private String[][] scalarColumnNames;
   85   	//private Type[] sqlResultTypes;
   86   	private Type[] queryReturnTypes;
   87   
   88   	private final Map sqlAliasByEntityAlias = new HashMap(8);
   89   
   90   	private EntityType[] ownerAssociationTypes;
   91   	private int[] owners;
   92   	private boolean[] entityEagerPropertyFetches;
   93   
   94   	private int[] collectionOwners;
   95   	private QueryableCollection[] collectionPersisters;
   96   
   97   	private int selectLength;
   98   
   99   	private ResultTransformer selectNewTransformer;
  100   	private String[] queryReturnAliases;
  101   
  102   	private LockMode[] defaultLockModes;
  103   
  104   
  105   	/**
  106   	 * Creates a new Loader implementation.
  107   	 *
  108   	 * @param queryTranslator The query translator that is the delegator.
  109   	 * @param factory The factory from which this loader is being created.
  110   	 * @param selectClause The AST representing the select clause for loading.
  111   	 */
  112   	public QueryLoader(
  113   			final QueryTranslatorImpl queryTranslator,
  114   	        final SessionFactoryImplementor factory,
  115   	        final SelectClause selectClause) {
  116   		super( factory );
  117   		this.queryTranslator = queryTranslator;
  118   		initialize( selectClause );
  119   		postInstantiate();
  120   	}
  121   
  122   	private void initialize(SelectClause selectClause) {
  123   
  124   		List fromElementList = selectClause.getFromElementsForLoad();
  125   
  126   		hasScalars = selectClause.isScalarSelect();
  127   		scalarColumnNames = selectClause.getColumnNames();
  128   		//sqlResultTypes = selectClause.getSqlResultTypes();
  129   		queryReturnTypes = selectClause.getQueryReturnTypes();
  130   
  131   		selectNewTransformer = HolderInstantiator.createSelectNewTransformer(
  132   				selectClause.getConstructor(),
  133   				selectClause.isMap(),
  134   				selectClause.isList());
  135   		queryReturnAliases = selectClause.getQueryReturnAliases();
  136   
  137   		List collectionFromElements = selectClause.getCollectionFromElements();
  138   		if ( collectionFromElements != null && collectionFromElements.size()!=0 ) {
  139   			int length = collectionFromElements.size();
  140   			collectionPersisters = new QueryableCollection[length];
  141   			collectionOwners = new int[length];
  142   			collectionSuffixes = new String[length];
  143   			for ( int i=0; i<length; i++ ) {
  144   				FromElement collectionFromElement = (FromElement) collectionFromElements.get(i);
  145   				collectionPersisters[i] = collectionFromElement.getQueryableCollection();
  146   				collectionOwners[i] = fromElementList.indexOf( collectionFromElement.getOrigin() );
  147   //				collectionSuffixes[i] = collectionFromElement.getColumnAliasSuffix();
  148   //				collectionSuffixes[i] = Integer.toString( i ) + "_";
  149   				collectionSuffixes[i] = collectionFromElement.getCollectionSuffix();
  150   			}
  151   		}
  152   
  153   		int size = fromElementList.size();
  154   		entityPersisters = new Queryable[size];
  155   		entityEagerPropertyFetches = new boolean[size];
  156   		entityAliases = new String[size];
  157   		sqlAliases = new String[size];
  158   		sqlAliasSuffixes = new String[size];
  159   		includeInSelect = new boolean[size];
  160   		owners = new int[size];
  161   		ownerAssociationTypes = new EntityType[size];
  162   
  163   		for ( int i = 0; i < size; i++ ) {
  164   			final FromElement element = ( FromElement ) fromElementList.get( i );
  165   			entityPersisters[i] = ( Queryable ) element.getEntityPersister();
  166   
  167   			if ( entityPersisters[i] == null ) {
  168   				throw new IllegalStateException( "No entity persister for " + element.toString() );
  169   			}
  170   
  171   			entityEagerPropertyFetches[i] = element.isAllPropertyFetch();
  172   			sqlAliases[i] = element.getTableAlias();
  173   			entityAliases[i] = element.getClassAlias();
  174   			sqlAliasByEntityAlias.put( entityAliases[i], sqlAliases[i] );
  175   			// TODO should we just collect these like with the collections above?
  176   			sqlAliasSuffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + "_";
  177   //			sqlAliasSuffixes[i] = element.getColumnAliasSuffix();
  178   			includeInSelect[i] = !element.isFetch();
  179   			if ( includeInSelect[i] ) {
  180   				selectLength++;
  181   			}
  182   
  183   			owners[i] = -1; //by default
  184   			if ( element.isFetch() ) {
  185   				if ( element.isCollectionJoin() || element.getQueryableCollection() != null ) {
  186   					// This is now handled earlier in this method.
  187   				}
  188   				else if ( element.getDataType().isEntityType() ) {
  189   					EntityType entityType = ( EntityType ) element.getDataType();
  190   					if ( entityType.isOneToOne() ) {
  191   						owners[i] = fromElementList.indexOf( element.getOrigin() );
  192   					}
  193   					ownerAssociationTypes[i] = entityType;
  194   				}
  195   			}
  196   		}
  197   
  198   		//NONE, because its the requested lock mode, not the actual! 
  199   		defaultLockModes = ArrayHelper.fillArray(LockMode.NONE, size);
  200   
  201   	}
  202   
  203   	// -- Loader implementation --
  204   
  205   	public final void validateScrollability() throws HibernateException {
  206   		queryTranslator.validateScrollability();
  207   	}
  208   
  209   	protected boolean needsFetchingScroll() {
  210   		return queryTranslator.containsCollectionFetches();
  211   	}
  212   
  213   	public Loadable[] getEntityPersisters() {
  214   		return entityPersisters;
  215   	}
  216   
  217   	public String[] getAliases() {
  218   		return sqlAliases;
  219   	}
  220   
  221   	public String[] getSqlAliasSuffixes() {
  222   		return sqlAliasSuffixes;
  223   	}
  224   
  225   	public String[] getSuffixes() {
  226   		return getSqlAliasSuffixes();
  227   	}
  228   
  229   	public String[] getCollectionSuffixes() {
  230   		return collectionSuffixes;
  231   	}
  232   
  233   	protected String getQueryIdentifier() {
  234   		return queryTranslator.getQueryIdentifier();
  235   	}
  236   
  237   	/**
  238   	 * The SQL query string to be called.
  239   	 */
  240   	protected String getSQLString() {
  241   		return queryTranslator.getSQLString();
  242   	}
  243   
  244   	/**
  245   	 * An (optional) persister for a collection to be initialized; only collection loaders
  246   	 * return a non-null value
  247   	 */
  248   	protected CollectionPersister[] getCollectionPersisters() {
  249   		return collectionPersisters;
  250   	}
  251   
  252   	protected int[] getCollectionOwners() {
  253   		return collectionOwners;
  254   	}
  255   
  256   	protected boolean[] getEntityEagerPropertyFetches() {
  257   		return entityEagerPropertyFetches;
  258   	}
  259   
  260   	/**
  261   	 * An array of indexes of the entity that owns a one-to-one association
  262   	 * to the entity at the given index (-1 if there is no "owner")
  263   	 */
  264   	protected int[] getOwners() {
  265   		return owners;
  266   	}
  267   
  268   	protected EntityType[] getOwnerAssociationTypes() {
  269   		return ownerAssociationTypes;
  270   	}
  271   
  272   	// -- Loader overrides --
  273   
  274   	protected boolean isSubselectLoadingEnabled() {
  275   		return hasSubselectLoadableCollections();
  276   	}
  277   
  278   	/**
  279   	 * @param lockModes a collection of lock modes specified dynamically via the Query interface
  280   	 */
  281   	protected LockMode[] getLockModes(Map lockModes) {
  282   
  283   		if ( lockModes==null || lockModes.size()==0 ) {
  284   			return defaultLockModes;
  285   		}
  286   		else {
  287   			// unfortunately this stuff can't be cached because
  288   			// it is per-invocation, not constant for the
  289   			// QueryTranslator instance
  290   
  291   			LockMode[] lockModeArray = new LockMode[entityAliases.length];
  292   			for ( int i = 0; i < entityAliases.length; i++ ) {
  293   				LockMode lockMode = (LockMode) lockModes.get( entityAliases[i] );
  294   				if ( lockMode == null ) {
  295   					//NONE, because its the requested lock mode, not the actual! 
  296   					lockMode = LockMode.NONE;
  297   				}
  298   				lockModeArray[i] = lockMode;
  299   			}
  300   			return lockModeArray;
  301   		}
  302   	}
  303   
  304   	protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws QueryException {
  305   		if ( lockModes == null || lockModes.size() == 0 ) {
  306   			return sql;
  307   		}
  308   
  309   		// can't cache this stuff either (per-invocation)
  310   		// we are given a map of user-alias -> lock mode
  311   		// create a new map of sql-alias -> lock mode
  312   		final Map aliasedLockModes = new HashMap();
  313   		final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
  314   		final Iterator iter = lockModes.entrySet().iterator();
  315   		while ( iter.hasNext() ) {
  316   			Map.Entry me = ( Map.Entry ) iter.next();
  317   			final String userAlias = ( String ) me.getKey();
  318   			final String drivingSqlAlias = ( String ) sqlAliasByEntityAlias.get( userAlias );
  319   			if ( drivingSqlAlias == null ) {
  320   				throw new IllegalArgumentException( "could not locate alias to apply lock mode : " + userAlias );
  321   			}
  322   			// at this point we have (drivingSqlAlias) the SQL alias of the driving table
  323   			// corresponding to the given user alias.  However, the driving table is not
  324   			// (necessarily) the table against which we want to apply locks.  Mainly,
  325   			// the exception case here is joined-subclass hierarchies where we instead
  326   			// want to apply the lock against the root table (for all other strategies,
  327   			// it just happens that driving and root are the same).
  328   			final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
  329   			final Lockable drivingPersister = ( Lockable ) select.getFromClause().getFromElement( userAlias ).getQueryable();
  330   			final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );
  331   			aliasedLockModes.put( sqlAlias, me.getValue() );
  332   			if ( keyColumnNames != null ) {
  333   				keyColumnNames.put( sqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
  334   			}
  335   		}
  336   		return dialect.applyLocksToSql( sql, aliasedLockModes, keyColumnNames );
  337   	}
  338   
  339   	protected boolean upgradeLocks() {
  340   		return true;
  341   	}
  342   
  343   	private boolean hasSelectNew() {
  344   		return selectNewTransformer!=null;
  345   	}
  346   
  347   	protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
  348   			throws SQLException, HibernateException {
  349   
  350   		row = toResultRow( row );
  351   		boolean hasTransform = hasSelectNew() || transformer!=null;
  352   		if ( hasScalars ) {
  353   			String[][] scalarColumns = scalarColumnNames;
  354   			int queryCols = queryReturnTypes.length;
  355   			if ( !hasTransform && queryCols == 1 ) {
  356   				return queryReturnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
  357   			}
  358   			else {
  359   				row = new Object[queryCols];
  360   				for ( int i = 0; i < queryCols; i++ ) {
  361   					row[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
  362   				}
  363   				return row;
  364   			}
  365   		}
  366   		else if ( !hasTransform ) {
  367   			return row.length == 1 ? row[0] : row;
  368   		}
  369   		else {
  370   			return row;
  371   		}
  372   
  373   	}
  374   
  375   	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
  376   		// meant to handle dynamic instantiation queries...
  377   		HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(selectNewTransformer, resultTransformer, queryReturnAliases);
  378   		if ( holderInstantiator.isRequired() ) {
  379   			for ( int i = 0; i < results.size(); i++ ) {
  380   				Object[] row = ( Object[] ) results.get( i );
  381   				Object result = holderInstantiator.instantiate(row);
  382   				results.set( i, result );
  383   			}
  384   
  385   			if(!hasSelectNew() && resultTransformer!=null) {
  386   				return resultTransformer.transformList(results);
  387   			} else {
  388   				return results;
  389   			}
  390   		} else {
  391   			return results;
  392   		}
  393   	}
  394   
  395   	// --- Query translator methods ---
  396   
  397   	public List list(
  398   			SessionImplementor session,
  399   			QueryParameters queryParameters) throws HibernateException {
  400   		checkQuery( queryParameters );
  401   		return list( session, queryParameters, queryTranslator.getQuerySpaces(), queryReturnTypes );
  402   	}
  403   
  404   	private void checkQuery(QueryParameters queryParameters) {
  405   		if ( hasSelectNew() && queryParameters.getResultTransformer() != null ) {
  406   			throw new QueryException( "ResultTransformer is not allowed for 'select new' queries." );
  407   		}
  408   	}
  409   
  410   	public Iterator iterate(
  411   			QueryParameters queryParameters,
  412   			EventSource session) throws HibernateException {
  413   		checkQuery( queryParameters );
  414   		final boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
  415   		long startTime = 0;
  416   		if ( stats ) {
  417   			startTime = System.currentTimeMillis();
  418   		}
  419   
  420   		try {
  421   
  422   			final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
  423   
  424   			if(queryParameters.isCallable()) {
  425   				throw new QueryException("iterate() not supported for callable statements");
  426   			}
  427   			final ResultSet rs = getResultSet(st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session);
  428   			final Iterator result = new IteratorImpl(
  429   					rs,
  430   			        st,
  431   			        session,
  432   			        queryReturnTypes,
  433   			        queryTranslator.getColumnNames(),
  434   			        HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases)
  435   				);
  436   
  437   			if ( stats ) {
  438   				session.getFactory().getStatisticsImplementor().queryExecuted(
  439   //						"HQL: " + queryTranslator.getQueryString(),
  440   						getQueryIdentifier(),
  441   						0,
  442   						System.currentTimeMillis() - startTime
  443   				);
  444   			}
  445   
  446   			return result;
  447   
  448   		}
  449   		catch ( SQLException sqle ) {
  450   			throw JDBCExceptionHelper.convert(
  451   					getFactory().getSQLExceptionConverter(),
  452   			        sqle,
  453   			        "could not execute query using iterate",
  454   			        getSQLString()
  455   				);
  456   		}
  457   
  458   	}
  459   
  460   	public ScrollableResults scroll(
  461   			final QueryParameters queryParameters,
  462   	        final SessionImplementor session) throws HibernateException {
  463   		checkQuery( queryParameters );
  464   		return scroll( queryParameters, queryReturnTypes, HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases), session );
  465   	}
  466   
  467   	// -- Implementation private methods --
  468   
  469   	private Object[] toResultRow(Object[] row) {
  470   		if ( selectLength == row.length ) {
  471   			return row;
  472   		}
  473   		else {
  474   			Object[] result = new Object[selectLength];
  475   			int j = 0;
  476   			for ( int i = 0; i < row.length; i++ ) {
  477   				if ( includeInSelect[i] ) {
  478   					result[j++] = row[i];
  479   				}
  480   			}
  481   			return result;
  482   		}
  483   	}
  484   
  485   	/**
  486   	 * Returns the locations of all occurrences of the named parameter.
  487   	 */
  488   	public int[] getNamedParameterLocs(String name) throws QueryException {
  489   		return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
  490   	}
  491   
  492   	/**
  493   	 * We specifically override this method here, because in general we know much more
  494   	 * about the parameters and their appropriate bind positions here then we do in
  495   	 * our super because we track them explciitly here through the ParameterSpecification
  496   	 * interface.
  497   	 *
  498   	 * @param queryParameters The encapsulation of the parameter values to be bound.
  499   	 * @param startIndex The position from which to start binding parameter values.
  500   	 * @param session The originating session.
  501   	 * @return The number of JDBC bind positions actually bound during this method execution.
  502   	 * @throws SQLException Indicates problems performing the binding.
  503   	 */
  504   	protected int bindParameterValues(
  505   			final PreparedStatement statement,
  506   			final QueryParameters queryParameters,
  507   			final int startIndex,
  508   			final SessionImplementor session) throws SQLException {
  509   		int position = bindFilterParameterValues( statement, queryParameters, startIndex, session );
  510   		List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
  511   		Iterator itr = parameterSpecs.iterator();
  512   		while ( itr.hasNext() ) {
  513   			ParameterSpecification spec = ( ParameterSpecification ) itr.next();
  514   			position += spec.bind( statement, queryParameters, session, position );
  515   		}
  516   		return position - startIndex;
  517   	}
  518   
  519   	private int bindFilterParameterValues(
  520   			PreparedStatement st,
  521   			QueryParameters queryParameters,
  522   			int position,
  523   			SessionImplementor session) throws SQLException {
  524   		// todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
  525   		// see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
  526   		// it is currently not done that way.
  527   		int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
  528   				? 0
  529   				: queryParameters.getFilteredPositionalParameterTypes().length;
  530   		int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
  531   				? 0
  532   				: queryParameters.getPositionalParameterTypes().length;
  533   		int filterParamCount = filteredParamCount - nonfilteredParamCount;
  534   		for ( int i = 0; i < filterParamCount; i++ ) {
  535   			Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
  536   			Object value = queryParameters.getFilteredPositionalParameterValues()[i];
  537   			type.nullSafeSet( st, value, position, session );
  538   			position += type.getColumnSpan( getFactory() );
  539   		}
  540   
  541   		return position;
  542   	}
  543   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » loader » hql » [javadoc | source]