Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/hibernate/loader/hql/QueryLoader.java


1   // $Id: QueryLoader.java,v 1.9 2005/04/22 18:05:58 oneovthafew Exp $
2   package org.hibernate.loader.hql;
3   
4   import java.sql.PreparedStatement;
5   import java.sql.ResultSet;
6   import java.sql.SQLException;
7   import java.util.HashMap;
8   import java.util.Iterator;
9   import java.util.List;
10  import java.util.Map;
11  
12  import org.apache.commons.logging.Log;
13  import org.apache.commons.logging.LogFactory;
14  import org.hibernate.HibernateException;
15  import org.hibernate.LockMode;
16  import org.hibernate.QueryException;
17  import org.hibernate.ScrollableResults;
18  import org.hibernate.dialect.Dialect;
19  import org.hibernate.engine.QueryParameters;
20  import org.hibernate.engine.SessionFactoryImplementor;
21  import org.hibernate.engine.SessionImplementor;
22  import org.hibernate.engine.TypedValue;
23  import org.hibernate.exception.JDBCExceptionHelper;
24  import org.hibernate.hql.HolderInstantiator;
25  import org.hibernate.hql.QueryTranslator;
26  import org.hibernate.hql.ast.FromElement;
27  import org.hibernate.hql.ast.SelectClause;
28  import org.hibernate.impl.IteratorImpl;
29  import org.hibernate.loader.BasicLoader;
30  import org.hibernate.persister.collection.CollectionPersister;
31  import org.hibernate.persister.collection.QueryableCollection;
32  import org.hibernate.persister.entity.Loadable;
33  import org.hibernate.persister.entity.Queryable;
34  import org.hibernate.sql.ForUpdateFragment;
35  import org.hibernate.type.EntityType;
36  import org.hibernate.type.Type;
37  import org.hibernate.util.ArrayHelper;
38  
39  /**
40   * A delegate that implements the Loader part of QueryTranslator.
41   * <p/>
42   * User: josh
43   * Date: Jan 3, 2004
44   * Time: 8:29:58 PM
45   */
46  public class QueryLoader extends BasicLoader {
47  
48    private static final Log log = LogFactory.getLog( QueryLoader.class );
49    /**
50     * The query translator that is delegating to this object.
51     */
52    private QueryTranslator queryTranslator;
53  
54    private Queryable[] entityPersisters;
55    private String[] entityAliases;
56    private String[] sqlAliases;
57    private String[] sqlAliasSuffixes;
58    private boolean[] includeInSelect;
59  
60    private boolean hasScalars;
61    private String[][] scalarColumnNames;
62    //private Type[] sqlResultTypes;
63    private Type[] queryReturnTypes;
64    
65    private final Map sqlAliasByEntityAlias = new HashMap(8);
66  
67    private EntityType[] ownerAssociationTypes;
68    private int[] owners;
69    private boolean[] entityEagerPropertyFetches;
70  
71    private int collectionOwner = -1;
72    private QueryableCollection collectionPersister;
73  
74    private int selectLength;
75    private HolderInstantiator holderInstantiator;
76    
77    private LockMode[] defaultLockModes;
78  
79    /**
80     * Creates a new Loader implementation.
81     *
82     * @param queryTranslator The query translator that is the delegator.
83     * @param factory         The factory from which this loader is being created.
84     */
85    public QueryLoader(final QueryTranslator queryTranslator,
86               final SessionFactoryImplementor factory,
87               final SelectClause selectClause) {
88      super( factory );
89      this.queryTranslator = queryTranslator;
90      initialize( selectClause );
91      postInstantiate();
92    }
93  
94    private void initialize(SelectClause selectClause) {
95  
96      List fromElementList = selectClause.getFromElementsForLoad();
97  
98      hasScalars = selectClause.isScalarSelect();
99      scalarColumnNames = selectClause.getColumnNames();
100     //sqlResultTypes = selectClause.getSqlResultTypes();
101     queryReturnTypes = selectClause.getQueryReturnTypes();
102 
103     holderInstantiator = new HolderInstantiator(
104         selectClause.getConstructor(),
105         selectClause.isMap(),
106         selectClause.isList(),
107         selectClause.getQueryReturnAliases()
108       );
109 
110     // make sure we grab the first collection persister to fully mimic old parser
111     FromElement collectionFromElement = selectClause.getCollectionFromElement();
112     if (collectionFromElement != null) {
113       collectionPersister = collectionFromElement.getQueryableCollection();
114       collectionOwner = fromElementList.indexOf( collectionFromElement.getOrigin() );
115     }
116 
117     int size = fromElementList.size();
118     entityPersisters = new Queryable[size];
119     entityEagerPropertyFetches = new boolean[size];
120     entityAliases = new String[size];
121     sqlAliases = new String[size];
122     sqlAliasSuffixes = new String[size];
123     includeInSelect = new boolean[size];
124     owners = new int[size];
125     ownerAssociationTypes = new EntityType[size];
126 
127     for ( int i = 0; i < size; i++ ) {
128       final FromElement element = ( FromElement ) fromElementList.get( i );
129       entityPersisters[i] = ( Queryable ) element.getEntityPersister();
130 
131       if ( entityPersisters[i] == null ) {
132         throw new IllegalStateException( "No entity persister for " + element.toString() );
133       }
134       
135       entityEagerPropertyFetches[i] = element.isAllPropertyFetch();
136       sqlAliases[i] = element.getTableAlias();
137       entityAliases[i] = element.getClassAlias();
138       sqlAliasByEntityAlias.put( entityAliases[i], sqlAliases[i] );
139       sqlAliasSuffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + "_";
140       includeInSelect[i] = !element.isFetch();
141       if ( includeInSelect[i] ) selectLength++;
142       
143       owners[i] = -1; //by default
144       if ( element.isFetch() ) {
145         if ( element.isCollectionJoin() || element.getQueryableCollection() != null ) {
146           // This is now handled earlier in this method.
147         }
148         else if ( element.getDataType().isEntityType() ) {
149           EntityType entityType = ( EntityType ) element.getDataType();
150           if ( entityType.isOneToOne() ) {
151             owners[i] = fromElementList.indexOf( element.getOrigin() );
152           }
153           ownerAssociationTypes[i] = entityType;
154         }
155       }
156     }
157     
158     //NONE, because its the requested lock mode, not the actual! 
159     defaultLockModes = ArrayHelper.fillArray(LockMode.NONE, size);
160     
161   }
162 
163   // -- Loader implementation --
164 
165   public Loadable[] getEntityPersisters() {
166     return entityPersisters;
167   }
168 
169   public String[] getAliases() {
170     return sqlAliases;
171   }
172 
173   public String[] getSqlAliasSuffixes() {
174     return sqlAliasSuffixes;
175   }
176 
177   public String[] getSuffixes() {
178     return getSqlAliasSuffixes();
179   }
180 
181   protected String getQueryIdentifier() {
182     return queryTranslator.getQueryString();
183   }
184 
185   /**
186    * The SQL query string to be called.
187    */
188   protected String getSQLString() {
189     return queryTranslator.getSQLString();
190   }
191 
192   /**
193    * An (optional) persister for a collection to be initialized; only collection loaders
194    * return a non-null value
195    */
196   protected CollectionPersister getCollectionPersister() {
197     return collectionPersister;
198   }
199 
200   protected int getCollectionOwner() {
201     return collectionOwner;
202   }
203 
204   protected boolean[] getEntityEagerPropertyFetches() {
205     return entityEagerPropertyFetches;
206   }
207 
208   /**
209    * An array of indexes of the entity that owns a one-to-one association
210    * to the entity at the given index (-1 if there is no "owner")
211    */
212   protected int[] getOwners() {
213     return owners;
214   }
215 
216   protected EntityType[] getOwnerAssociationTypes() {
217     return ownerAssociationTypes;
218   }
219 
220   // -- Loader overrides --
221 
222   protected boolean isSubselectLoadingEnabled() {
223     return hasSubselectLoadableCollections();
224   }
225   
226   protected int bindNamedParameters(final PreparedStatement ps,
227                     final Map namedParams,
228                     final int start,
229                     final SessionImplementor session)
230       throws SQLException, HibernateException {
231 
232     if ( namedParams != null ) {
233       // assumes that types are all of span 1
234       Iterator iter = namedParams.entrySet().iterator();
235       int result = 0;
236       while ( iter.hasNext() ) {
237         Map.Entry e = ( Map.Entry ) iter.next();
238         String name = ( String ) e.getKey();
239         TypedValue typedval = ( TypedValue ) e.getValue();
240         int[] locs = getNamedParameterLocs( name );
241         for ( int i = 0; i < locs.length; i++ ) {
242           if ( log.isDebugEnabled() ) {
243             log.debug( "bindNamedParameters() " +
244                 typedval.getValue() + " -> " + name +
245                 " [" + ( locs[i] + start ) + "]" );
246           }
247           typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
248         }
249         result += locs.length;
250       }
251       return result;
252     }
253     else {
254       return 0;
255     }
256   }
257 
258   /**
259    * @param lockModes a collection of lock modes specified dynamically via the Query interface
260    */
261   protected LockMode[] getLockModes(Map lockModes) {
262     
263     if ( lockModes==null || lockModes.size()==0 ) {
264       return defaultLockModes;
265     }
266     else {
267       // unfortunately this stuff can't be cached because
268       // it is per-invocation, not constant for the
269       // QueryTranslator instance
270   
271       LockMode[] lockModeArray = new LockMode[entityAliases.length];
272       for ( int i = 0; i < entityAliases.length; i++ ) {
273         LockMode lockMode = (LockMode) lockModes.get( entityAliases[i] );
274         if ( lockMode == null ) {
275           //NONE, because its the requested lock mode, not the actual! 
276           lockMode = LockMode.NONE;
277         }
278         lockModeArray[i] = lockMode;
279       }
280       return lockModeArray;
281     }
282   }
283 
284   protected String applyLocks(String sql, Map lockModes, Dialect dialect)
285       throws QueryException {
286 
287     if ( lockModes == null || lockModes.size() == 0 ) {
288       return sql;
289     }
290     else {
291       // can't cache this stuff either (per-invocation)
292       
293       //we are given a map of user alias -> lock mode
294       //create a new map of sql alias -> lock mode
295       final Map aliasedLockModes = new HashMap();
296       final Iterator iter = lockModes.entrySet().iterator();
297       while ( iter.hasNext() ) {
298         Map.Entry me = ( Map.Entry ) iter.next();
299         final String userAlias = ( String ) me.getKey();
300         final String sqlAlias = (String) sqlAliasByEntityAlias.get( userAlias );
301         aliasedLockModes.put( sqlAlias, me.getValue() );
302       }
303       
304       //if necessary, create a map of sql alias -> key columns
305       Map keyColumnNames = null;
306       if ( dialect.forUpdateOfColumns() ) {
307         final Loadable[] persisters = getEntityPersisters();
308         keyColumnNames = new HashMap();
309         for ( int i = 0; i < sqlAliases.length; i++ ) {
310           keyColumnNames.put( sqlAliases[i], persisters[i].getIdentifierColumnNames() );
311         }
312       }
313       
314       return sql + new ForUpdateFragment( dialect, aliasedLockModes, keyColumnNames ).toFragmentString();
315 
316     }
317   }
318 
319   protected boolean upgradeLocks() {
320     return true;
321   }
322 
323   protected Object getResultColumnOrRow(Object[] row, ResultSet rs, SessionImplementor session)
324       throws SQLException, HibernateException {
325 
326     row = toResultRow( row );
327     boolean isHolder = holderInstantiator.isRequired();
328     if ( hasScalars ) {
329       String[][] scalarColumns = scalarColumnNames;
330       int queryCols = queryReturnTypes.length;
331       if ( !isHolder && queryCols == 1 ) {
332         return queryReturnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
333       }
334       else {
335         row = new Object[queryCols];
336         for ( int i = 0; i < queryCols; i++ ) {
337           row[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
338         }
339         return row;
340       }
341     }
342     else if ( !isHolder ) {
343       return row.length == 1 ? row[0] : row;
344     }
345     else {
346       return row;
347     }
348 
349   }
350 
351   protected List getResultList(List results) throws QueryException {
352     // meant to handle dynamic instantiation queries...
353     if ( holderInstantiator.isRequired() ) {
354       for ( int i = 0; i < results.size(); i++ ) {
355         Object[] row = ( Object[] ) results.get( i );
356         Object result = holderInstantiator.instantiate(row);
357         results.set( i, result );
358       }
359     }
360     return results;
361   }
362 
363   // --- Query translator methods ---
364 
365   /**
366    * Delegats
367    *
368    * @param session
369    * @param queryParameters
370    * @return
371    * @throws HibernateException
372    */
373   public List list(SessionImplementor session, QueryParameters queryParameters)
374       throws HibernateException {
375     return list( session, queryParameters, queryTranslator.getQuerySpaces(), queryReturnTypes );
376   }
377 
378   /**
379    * Return the query results as an iterator
380    */
381   public Iterator iterate(QueryParameters queryParameters, SessionImplementor session)
382       throws HibernateException {
383 
384     final boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
385     long startTime = 0;
386     if ( stats ) startTime = System.currentTimeMillis();
387 
388     try {
389 
390       final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
391       final ResultSet rs = getResultSet( st, queryParameters.getRowSelection(), session );
392       final Iterator result = new IteratorImpl( 
393           rs,
394           st,
395           session,
396           queryReturnTypes,
397           queryTranslator.getColumnNames(),
398           holderInstantiator
399         );
400 
401       if ( stats ) {
402         session.getFactory().getStatisticsImplementor().queryExecuted( "HQL: " + queryTranslator.getQueryString(),
403             0,
404             System.currentTimeMillis() - startTime );
405       }
406 
407       return result;
408 
409     }
410     catch ( SQLException sqle ) {
411       throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(),
412           sqle,
413           "could not execute query using iterate",
414           getSQLString() );
415     }
416 
417   }
418 
419   public ScrollableResults scroll(final QueryParameters queryParameters,
420                   final SessionImplementor session)
421       throws HibernateException {
422     return scroll( queryParameters, queryReturnTypes, null, session );
423   }
424 
425   // -- Implementation private methods --
426 
427   private Object[] toResultRow(Object[] row) {
428     if ( selectLength == row.length ) {
429       return row;
430     }
431     else {
432       Object[] result = new Object[selectLength];
433       int j = 0;
434       for ( int i = 0; i < row.length; i++ ) {
435         if ( includeInSelect[i] ) result[j++] = row[i];
436       }
437       return result;
438     }
439   }
440 
441   /**
442    * Returns the locations of all occurrences of the named parameter.
443    */
444   private int[] getNamedParameterLocs(String name) throws QueryException {
445     return queryTranslator.getNamedParameterLocs( name );
446   }
447 }