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.hql.classic;
26
27 import java.io.Serializable;
28 import java.lang.reflect.Constructor;
29 import java.sql.PreparedStatement;
30 import java.sql.ResultSet;
31 import java.sql.SQLException;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.LinkedHashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import org.hibernate.HibernateException;
45 import org.hibernate.LockMode;
46 import org.hibernate.MappingException;
47 import org.hibernate.QueryException;
48 import org.hibernate.ScrollableResults;
49 import org.hibernate.dialect.Dialect;
50 import org.hibernate.engine.JoinSequence;
51 import org.hibernate.engine.QueryParameters;
52 import org.hibernate.engine.SessionFactoryImplementor;
53 import org.hibernate.engine.SessionImplementor;
54 import org.hibernate.event.EventSource;
55 import org.hibernate.exception.JDBCExceptionHelper;
56 import org.hibernate.hql.FilterTranslator;
57 import org.hibernate.hql.HolderInstantiator;
58 import org.hibernate.hql.NameGenerator;
59 import org.hibernate.hql.ParameterTranslations;
60 import org.hibernate.impl.IteratorImpl;
61 import org.hibernate.loader.BasicLoader;
62 import org.hibernate.persister.collection.CollectionPersister;
63 import org.hibernate.persister.collection.QueryableCollection;
64 import org.hibernate.persister.entity.Loadable;
65 import org.hibernate.persister.entity.PropertyMapping;
66 import org.hibernate.persister.entity.Queryable;
67 import org.hibernate.sql.JoinFragment;
68 import org.hibernate.sql.QuerySelect;
69 import org.hibernate.transform.ResultTransformer;
70 import org.hibernate.type.AssociationType;
71 import org.hibernate.type.EntityType;
72 import org.hibernate.type.Type;
73 import org.hibernate.type.TypeFactory;
74 import org.hibernate.util.ArrayHelper;
75 import org.hibernate.util.ReflectHelper;
76 import org.hibernate.util.StringHelper;
77
78 /**
79 * An instance of <tt>QueryTranslator</tt> translates a Hibernate
80 * query string to SQL.
81 */
82 public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator {
83
84 private static final String[] NO_RETURN_ALIASES = new String[] {};
85
86 private final String queryIdentifier;
87 private final String queryString;
88
89 private final Map typeMap = new LinkedHashMap();
90 private final Map collections = new LinkedHashMap();
91 private List returnedTypes = new ArrayList();
92 private final List fromTypes = new ArrayList();
93 private final List scalarTypes = new ArrayList();
94 private final Map namedParameters = new HashMap();
95 private final Map aliasNames = new HashMap();
96 private final Map oneToOneOwnerNames = new HashMap();
97 private final Map uniqueKeyOwnerReferences = new HashMap();
98 private final Map decoratedPropertyMappings = new HashMap();
99
100 private final List scalarSelectTokens = new ArrayList();
101 private final List whereTokens = new ArrayList();
102 private final List havingTokens = new ArrayList();
103 private final Map joins = new LinkedHashMap();
104 private final List orderByTokens = new ArrayList();
105 private final List groupByTokens = new ArrayList();
106 private final Set querySpaces = new HashSet();
107 private final Set entitiesToFetch = new HashSet();
108
109 private final Map pathAliases = new HashMap();
110 private final Map pathJoins = new HashMap();
111
112 private Queryable[] persisters;
113 private int[] owners;
114 private EntityType[] ownerAssociationTypes;
115 private String[] names;
116 private boolean[] includeInSelect;
117 private int selectLength;
118 private Type[] returnTypes;
119 private Type[] actualReturnTypes;
120 private String[][] scalarColumnNames;
121 private Map tokenReplacements;
122 private int nameCount = 0;
123 private int parameterCount = 0;
124 private boolean distinct = false;
125 private boolean compiled;
126 private String sqlString;
127 private Class holderClass;
128 private Constructor holderConstructor;
129 private boolean hasScalars;
130 private boolean shallowQuery;
131 private QueryTranslatorImpl superQuery;
132
133 private QueryableCollection collectionPersister;
134 private int collectionOwnerColumn = -1;
135 private String collectionOwnerName;
136 private String fetchName;
137
138 private String[] suffixes;
139
140 private Map enabledFilters;
141
142 private static final Logger log = LoggerFactory.getLogger( QueryTranslatorImpl.class );
143
144 /**
145 * Construct a query translator
146 *
147 * @param queryIdentifier A unique identifier for the query of which this
148 * translation is part; typically this is the original, user-supplied query string.
149 * @param queryString The "preprocessed" query string; at the very least
150 * already processed by {@link org.hibernate.hql.QuerySplitter}.
151 * @param enabledFilters Any enabled filters.
152 * @param factory The session factory.
153 */
154 public QueryTranslatorImpl(
155 String queryIdentifier,
156 String queryString,
157 Map enabledFilters,
158 SessionFactoryImplementor factory) {
159 super( factory );
160 this.queryIdentifier = queryIdentifier;
161 this.queryString = queryString;
162 this.enabledFilters = enabledFilters;
163 }
164
165 /**
166 * Construct a query translator; this form used internally.
167 *
168 * @param queryString The query string to process.
169 * @param enabledFilters Any enabled filters.
170 * @param factory The session factory.
171 */
172 public QueryTranslatorImpl(
173 String queryString,
174 Map enabledFilters,
175 SessionFactoryImplementor factory) {
176 this( queryString, queryString, enabledFilters, factory );
177 }
178
179 /**
180 * Compile a subquery.
181 *
182 * @param superquery The containing query of the query to be compiled.
183 *
184 * @throws org.hibernate.MappingException Indicates problems resolving
185 * things referenced in the query.
186 * @throws org.hibernate.QueryException Generally some form of syntatic
187 * failure.
188 */
189 void compile(QueryTranslatorImpl superquery) throws QueryException, MappingException {
190 this.tokenReplacements = superquery.tokenReplacements;
191 this.superQuery = superquery;
192 this.shallowQuery = true;
193 this.enabledFilters = superquery.getEnabledFilters();
194 compile();
195 }
196
197
198 /**
199 * Compile a "normal" query. This method may be called multiple
200 * times. Subsequent invocations are no-ops.
201 */
202 public synchronized void compile(
203 Map replacements,
204 boolean scalar) throws QueryException, MappingException {
205 if ( !compiled ) {
206 this.tokenReplacements = replacements;
207 this.shallowQuery = scalar;
208 compile();
209 }
210 }
211
212 /**
213 * Compile a filter. This method may be called multiple
214 * times. Subsequent invocations are no-ops.
215 */
216 public synchronized void compile(
217 String collectionRole,
218 Map replacements,
219 boolean scalar) throws QueryException, MappingException {
220
221 if ( !isCompiled() ) {
222 addFromAssociation( "this", collectionRole );
223 compile( replacements, scalar );
224 }
225 }
226
227 /**
228 * Compile the query (generate the SQL).
229 *
230 * @throws org.hibernate.MappingException Indicates problems resolving
231 * things referenced in the query.
232 * @throws org.hibernate.QueryException Generally some form of syntatic
233 * failure.
234 */
235 private void compile() throws QueryException, MappingException {
236
237 log.trace( "compiling query" );
238 try {
239 ParserHelper.parse( new PreprocessingParser( tokenReplacements ),
240 queryString,
241 ParserHelper.HQL_SEPARATORS,
242 this );
243 renderSQL();
244 }
245 catch ( QueryException qe ) {
246 qe.setQueryString( queryString );
247 throw qe;
248 }
249 catch ( MappingException me ) {
250 throw me;
251 }
252 catch ( Exception e ) {
253 log.debug( "unexpected query compilation problem", e );
254 e.printStackTrace();
255 QueryException qe = new QueryException( "Incorrect query syntax", e );
256 qe.setQueryString( queryString );
257 throw qe;
258 }
259
260 postInstantiate();
261
262 compiled = true;
263
264 }
265
266 public String getSQLString() {
267 return sqlString;
268 }
269
270 public List collectSqlStrings() {
271 return ArrayHelper.toList( new String[] { sqlString } );
272 }
273
274 public String getQueryString() {
275 return queryString;
276 }
277
278 /**
279 * Persisters for the return values of a <tt>find()</tt> style query.
280 *
281 * @return an array of <tt>EntityPersister</tt>s.
282 */
283 protected Loadable[] getEntityPersisters() {
284 return persisters;
285 }
286
287 /**
288 * Types of the return values of an <tt>iterate()</tt> style query.
289 *
290 * @return an array of <tt>Type</tt>s.
291 */
292 public Type[] getReturnTypes() {
293 return actualReturnTypes;
294 }
295
296 public String[] getReturnAliases() {
297 // return aliases not supported in classic translator!
298 return NO_RETURN_ALIASES;
299 }
300
301 public String[][] getColumnNames() {
302 return scalarColumnNames;
303 }
304
305 private static void logQuery(String hql, String sql) {
306 if ( log.isDebugEnabled() ) {
307 log.debug( "HQL: " + hql );
308 log.debug( "SQL: " + sql );
309 }
310 }
311
312 void setAliasName(String alias, String name) {
313 aliasNames.put( alias, name );
314 }
315
316 public String getAliasName(String alias) {
317 String name = ( String ) aliasNames.get( alias );
318 if ( name == null ) {
319 if ( superQuery != null ) {
320 name = superQuery.getAliasName( alias );
321 }
322 else {
323 name = alias;
324 }
325 }
326 return name;
327 }
328
329 String unalias(String path) {
330 String alias = StringHelper.root( path );
331 String name = getAliasName( alias );
332 if ( name != null ) {
333 return name + path.substring( alias.length() );
334 }
335 else {
336 return path;
337 }
338 }
339
340 void addEntityToFetch(String name, String oneToOneOwnerName, AssociationType ownerAssociationType) {
341 addEntityToFetch( name );
342 if ( oneToOneOwnerName != null ) oneToOneOwnerNames.put( name, oneToOneOwnerName );
343 if ( ownerAssociationType != null ) uniqueKeyOwnerReferences.put( name, ownerAssociationType );
344 }
345
346 private void addEntityToFetch(String name) {
347 entitiesToFetch.add( name );
348 }
349
350 private int nextCount() {
351 return ( superQuery == null ) ? nameCount++ : superQuery.nameCount++;
352 }
353
354 String createNameFor(String type) {
355 return StringHelper.generateAlias( type, nextCount() );
356 }
357
358 String createNameForCollection(String role) {
359 return StringHelper.generateAlias( role, nextCount() );
360 }
361
362 private String getType(String name) {
363 String type = ( String ) typeMap.get( name );
364 if ( type == null && superQuery != null ) {
365 type = superQuery.getType( name );
366 }
367 return type;
368 }
369
370 private String getRole(String name) {
371 String role = ( String ) collections.get( name );
372 if ( role == null && superQuery != null ) {
373 role = superQuery.getRole( name );
374 }
375 return role;
376 }
377
378 boolean isName(String name) {
379 return aliasNames.containsKey( name ) ||
380 typeMap.containsKey( name ) ||
381 collections.containsKey( name ) || (
382 superQuery != null && superQuery.isName( name )
383 );
384 }
385
386 PropertyMapping getPropertyMapping(String name) throws QueryException {
387 PropertyMapping decorator = getDecoratedPropertyMapping( name );
388 if ( decorator != null ) return decorator;
389
390 String type = getType( name );
391 if ( type == null ) {
392 String role = getRole( name );
393 if ( role == null ) {
394 throw new QueryException( "alias not found: " + name );
395 }
396 return getCollectionPersister( role ); //.getElementPropertyMapping();
397 }
398 else {
399 Queryable persister = getEntityPersister( type );
400 if ( persister == null ) throw new QueryException( "persistent class not found: " + type );
401 return persister;
402 }
403 }
404
405 private PropertyMapping getDecoratedPropertyMapping(String name) {
406 return ( PropertyMapping ) decoratedPropertyMappings.get( name );
407 }
408
409 void decoratePropertyMapping(String name, PropertyMapping mapping) {
410 decoratedPropertyMappings.put( name, mapping );
411 }
412
413 private Queryable getEntityPersisterForName(String name) throws QueryException {
414 String type = getType( name );
415 Queryable persister = getEntityPersister( type );
416 if ( persister == null ) throw new QueryException( "persistent class not found: " + type );
417 return persister;
418 }
419
420 Queryable getEntityPersisterUsingImports(String className) {
421 final String importedClassName = getFactory().getImportedClassName( className );
422 if ( importedClassName == null ) {
423 return null;
424 }
425 try {
426 return ( Queryable ) getFactory().getEntityPersister( importedClassName );
427 }
428 catch ( MappingException me ) {
429 return null;
430 }
431 }
432
433 Queryable getEntityPersister(String entityName) throws QueryException {
434 try {
435 return ( Queryable ) getFactory().getEntityPersister( entityName );
436 }
437 catch ( Exception e ) {
438 throw new QueryException( "persistent class not found: " + entityName );
439 }
440 }
441
442 QueryableCollection getCollectionPersister(String role) throws QueryException {
443 try {
444 return ( QueryableCollection ) getFactory().getCollectionPersister( role );
445 }
446 catch ( ClassCastException cce ) {
447 throw new QueryException( "collection role is not queryable: " + role );
448 }
449 catch ( Exception e ) {
450 throw new QueryException( "collection role not found: " + role );
451 }
452 }
453
454 void addType(String name, String type) {
455 typeMap.put( name, type );
456 }
457
458 void addCollection(String name, String role) {
459 collections.put( name, role );
460 }
461
462 void addFrom(String name, String type, JoinSequence joinSequence)
463 throws QueryException {
464 addType( name, type );
465 addFrom( name, joinSequence );
466 }
467
468 void addFromCollection(String name, String collectionRole, JoinSequence joinSequence)
469 throws QueryException {
470 //register collection role
471 addCollection( name, collectionRole );
472 addJoin( name, joinSequence );
473 }
474
475 void addFrom(String name, JoinSequence joinSequence)
476 throws QueryException {
477 fromTypes.add( name );
478 addJoin( name, joinSequence );
479 }
480
481 void addFromClass(String name, Queryable classPersister)
482 throws QueryException {
483 JoinSequence joinSequence = new JoinSequence( getFactory() )
484 .setRoot( classPersister, name );
485 //crossJoins.add(name);
486 addFrom( name, classPersister.getEntityName(), joinSequence );
487 }
488
489 void addSelectClass(String name) {
490 returnedTypes.add( name );
491 }
492
493 void addSelectScalar(Type type) {
494 scalarTypes.add( type );
495 }
496
497 void appendWhereToken(String token) {
498 whereTokens.add( token );
499 }
500
501 void appendHavingToken(String token) {
502 havingTokens.add( token );
503 }
504
505 void appendOrderByToken(String token) {
506 orderByTokens.add( token );
507 }
508
509 void appendGroupByToken(String token) {
510 groupByTokens.add( token );
511 }
512
513 void appendScalarSelectToken(String token) {
514 scalarSelectTokens.add( token );
515 }
516
517 void appendScalarSelectTokens(String[] tokens) {
518 scalarSelectTokens.add( tokens );
519 }
520
521 void addFromJoinOnly(String name, JoinSequence joinSequence) throws QueryException {
522 addJoin( name, joinSequence.getFromPart() );
523 }
524
525 void addJoin(String name, JoinSequence joinSequence) throws QueryException {
526 if ( !joins.containsKey( name ) ) joins.put( name, joinSequence );
527 }
528
529 void addNamedParameter(String name) {
530 if ( superQuery != null ) superQuery.addNamedParameter( name );
531 Integer loc = new Integer( parameterCount++ );
532 Object o = namedParameters.get( name );
533 if ( o == null ) {
534 namedParameters.put( name, loc );
535 }
536 else if ( o instanceof Integer ) {
537 ArrayList list = new ArrayList( 4 );
538 list.add( o );
539 list.add( loc );
540 namedParameters.put( name, list );
541 }
542 else {
543 ( ( ArrayList ) o ).add( loc );
544 }
545 }
546
547 public int[] getNamedParameterLocs(String name) throws QueryException {
548 Object o = namedParameters.get( name );
549 if ( o == null ) {
550 QueryException qe = new QueryException( ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
551 qe.setQueryString( queryString );
552 throw qe;
553 }
554 if ( o instanceof Integer ) {
555 return new int[]{ ( ( Integer ) o ).intValue() };
556 }
557 else {
558 return ArrayHelper.toIntArray( ( ArrayList ) o );
559 }
560 }
561
562 private void renderSQL() throws QueryException, MappingException {
563
564 final int rtsize;
565 if ( returnedTypes.size() == 0 && scalarTypes.size() == 0 ) {
566 //ie no select clause in HQL
567 returnedTypes = fromTypes;
568 rtsize = returnedTypes.size();
569 }
570 else {
571 rtsize = returnedTypes.size();
572 Iterator iter = entitiesToFetch.iterator();
573 while ( iter.hasNext() ) {
574 returnedTypes.add( iter.next() );
575 }
576 }
577 int size = returnedTypes.size();
578 persisters = new Queryable[size];
579 names = new String[size];
580 owners = new int[size];
581 ownerAssociationTypes = new EntityType[size];
582 suffixes = new String[size];
583 includeInSelect = new boolean[size];
584 for ( int i = 0; i < size; i++ ) {
585 String name = ( String ) returnedTypes.get( i );
586 //if ( !isName(name) ) throw new QueryException("unknown type: " + name);
587 persisters[i] = getEntityPersisterForName( name );
588 // TODO: cannot use generateSuffixes() - it handles the initial suffix differently.
589 suffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + '_';
590 names[i] = name;
591 includeInSelect[i] = !entitiesToFetch.contains( name );
592 if ( includeInSelect[i] ) selectLength++;
593 if ( name.equals( collectionOwnerName ) ) collectionOwnerColumn = i;
594 String oneToOneOwner = ( String ) oneToOneOwnerNames.get( name );
595 owners[i] = ( oneToOneOwner == null ) ? -1 : returnedTypes.indexOf( oneToOneOwner );
596 ownerAssociationTypes[i] = (EntityType) uniqueKeyOwnerReferences.get( name );
597 }
598
599 if ( ArrayHelper.isAllNegative( owners ) ) owners = null;
600
601 String scalarSelect = renderScalarSelect(); //Must be done here because of side-effect! yuck...
602
603 int scalarSize = scalarTypes.size();
604 hasScalars = scalarTypes.size() != rtsize;
605
606 returnTypes = new Type[scalarSize];
607 for ( int i = 0; i < scalarSize; i++ ) {
608 returnTypes[i] = ( Type ) scalarTypes.get( i );
609 }
610
611 QuerySelect sql = new QuerySelect( getFactory().getDialect() );
612 sql.setDistinct( distinct );
613
614 if ( !shallowQuery ) {
615 renderIdentifierSelect( sql );
616 renderPropertiesSelect( sql );
617 }
618
619 if ( collectionPersister != null ) {
620 sql.addSelectFragmentString( collectionPersister.selectFragment( fetchName, "__" ) );
621 }
622
623 if ( hasScalars || shallowQuery ) sql.addSelectFragmentString( scalarSelect );
624
625 //TODO: for some dialects it would be appropriate to add the renderOrderByPropertiesSelect() to other select strings
626 mergeJoins( sql.getJoinFragment() );
627
628 sql.setWhereTokens( whereTokens.iterator() );
629
630 sql.setGroupByTokens( groupByTokens.iterator() );
631 sql.setHavingTokens( havingTokens.iterator() );
632 sql.setOrderByTokens( orderByTokens.iterator() );
633
634 if ( collectionPersister != null && collectionPersister.hasOrdering() ) {
635 sql.addOrderBy( collectionPersister.getSQLOrderByString( fetchName ) );
636 }
637
638 scalarColumnNames = NameGenerator.generateColumnNames( returnTypes, getFactory() );
639
640 // initialize the Set of queried identifier spaces (ie. tables)
641 Iterator iter = collections.values().iterator();
642 while ( iter.hasNext() ) {
643 CollectionPersister p = getCollectionPersister( ( String ) iter.next() );
644 addQuerySpaces( p.getCollectionSpaces() );
645 }
646 iter = typeMap.keySet().iterator();
647 while ( iter.hasNext() ) {
648 Queryable p = getEntityPersisterForName( ( String ) iter.next() );
649 addQuerySpaces( p.getQuerySpaces() );
650 }
651
652 sqlString = sql.toQueryString();
653
654 if ( holderClass != null ) holderConstructor = ReflectHelper.getConstructor( holderClass, returnTypes );
655
656 if ( hasScalars ) {
657 actualReturnTypes = returnTypes;
658 }
659 else {
660 actualReturnTypes = new Type[selectLength];
661 int j = 0;
662 for ( int i = 0; i < persisters.length; i++ ) {
663 if ( includeInSelect[i] ) {
664 actualReturnTypes[j++] = TypeFactory.manyToOne( persisters[i].getEntityName(), shallowQuery );
665 }
666 }
667 }
668
669 }
670
671 private void renderIdentifierSelect(QuerySelect sql) {
672 int size = returnedTypes.size();
673
674 for ( int k = 0; k < size; k++ ) {
675 String name = ( String ) returnedTypes.get( k );
676 String suffix = size == 1 ? "" : Integer.toString( k ) + '_';
677 sql.addSelectFragmentString( persisters[k].identifierSelectFragment( name, suffix ) );
678 }
679
680 }
681
682 /*private String renderOrderByPropertiesSelect() {
683 StringBuffer buf = new StringBuffer(10);
684
685 //add the columns we are ordering by to the select ID select clause
686 Iterator iter = orderByTokens.iterator();
687 while ( iter.hasNext() ) {
688 String token = (String) iter.next();
689 if ( token.lastIndexOf(".") > 0 ) {
690 //ie. it is of form "foo.bar", not of form "asc" or "desc"
691 buf.append(StringHelper.COMMA_SPACE).append(token);
692 }
693 }
694
695 return buf.toString();
696 }*/
697
698 private void renderPropertiesSelect(QuerySelect sql) {
699 int size = returnedTypes.size();
700 for ( int k = 0; k < size; k++ ) {
701 String suffix = size == 1 ? "" : Integer.toString( k ) + '_';
702 String name = ( String ) returnedTypes.get( k );
703 sql.addSelectFragmentString( persisters[k].propertySelectFragment( name, suffix, false ) );
704 }
705 }
706
707 /**
708 * WARNING: side-effecty
709 */
710 private String renderScalarSelect() {
711
712 boolean isSubselect = superQuery != null;
713
714 StringBuffer buf = new StringBuffer( 20 );
715
716 if ( scalarTypes.size() == 0 ) {
717 //ie. no select clause
718 int size = returnedTypes.size();
719 for ( int k = 0; k < size; k++ ) {
720
721 scalarTypes.add( TypeFactory.manyToOne( persisters[k].getEntityName(), shallowQuery ) );
722
723 String[] idColumnNames = persisters[k].getIdentifierColumnNames();
724 for ( int i = 0; i < idColumnNames.length; i++ ) {
725 buf.append( returnedTypes.get( k ) ).append( '.' ).append( idColumnNames[i] );
726 if ( !isSubselect ) buf.append( " as " ).append( NameGenerator.scalarName( k, i ) );
727 if ( i != idColumnNames.length - 1 || k != size - 1 ) buf.append( ", " );
728 }
729
730 }
731
732 }
733 else {
734 //there _was_ a select clause
735 Iterator iter = scalarSelectTokens.iterator();
736 int c = 0;
737 boolean nolast = false; //real hacky...
738 int parenCount = 0; // used to count the nesting of parentheses
739 while ( iter.hasNext() ) {
740 Object next = iter.next();
741 if ( next instanceof String ) {
742 String token = ( String ) next;
743
744 if ( "(".equals( token ) ) {
745 parenCount++;
746 }
747 else if ( ")".equals( token ) ) {
748 parenCount--;
749 }
750
751 String lc = token.toLowerCase();
752 if ( lc.equals( ", " ) ) {
753 if ( nolast ) {
754 nolast = false;
755 }
756 else {
757 if ( !isSubselect && parenCount == 0 ) {
758 int x = c++;
759 buf.append( " as " )
760 .append( NameGenerator.scalarName( x, 0 ) );
761 }
762 }
763 }
764 buf.append( token );
765 if ( lc.equals( "distinct" ) || lc.equals( "all" ) ) {
766 buf.append( ' ' );
767 }
768 }
769 else {
770 nolast = true;
771 String[] tokens = ( String[] ) next;
772 for ( int i = 0; i < tokens.length; i++ ) {
773 buf.append( tokens[i] );
774 if ( !isSubselect ) {
775 buf.append( " as " )
776 .append( NameGenerator.scalarName( c, i ) );
777 }
778 if ( i != tokens.length - 1 ) buf.append( ", " );
779 }
780 c++;
781 }
782 }
783 if ( !isSubselect && !nolast ) {
784 int x = c++;
785 buf.append( " as " )
786 .append( NameGenerator.scalarName( x, 0 ) );
787 }
788
789 }
790
791 return buf.toString();
792 }
793
794 private void mergeJoins(JoinFragment ojf) throws MappingException, QueryException {
795
796 Iterator iter = joins.entrySet().iterator();
797 while ( iter.hasNext() ) {
798 Map.Entry me = ( Map.Entry ) iter.next();
799 String name = ( String ) me.getKey();
800 JoinSequence join = ( JoinSequence ) me.getValue();
801 join.setSelector( new JoinSequence.Selector() {
802 public boolean includeSubclasses(String alias) {
803 boolean include = returnedTypes.contains( alias ) && !isShallowQuery();
804 return include;
805 }
806 } );
807
808 if ( typeMap.containsKey( name ) ) {
809 ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
810 }
811 else if ( collections.containsKey( name ) ) {
812 ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
813 }
814 else {
815 //name from a super query (a bit inelegant that it shows up here)
816 }
817
818 }
819
820 }
821
822 public final Set getQuerySpaces() {
823 return querySpaces;
824 }
825
826 /**
827 * Is this query called by scroll() or iterate()?
828 *
829 * @return true if it is, false if it is called by find() or list()
830 */
831 boolean isShallowQuery() {
832 return shallowQuery;
833 }
834
835 void addQuerySpaces(Serializable[] spaces) {
836 for ( int i = 0; i < spaces.length; i++ ) {
837 querySpaces.add( spaces[i] );
838 }
839 if ( superQuery != null ) superQuery.addQuerySpaces( spaces );
840 }
841
842 void setDistinct(boolean distinct) {
843 this.distinct = distinct;
844 }
845
846 boolean isSubquery() {
847 return superQuery != null;
848 }
849
850 /**
851 * Overrides method from Loader
852 */
853 public CollectionPersister[] getCollectionPersisters() {
854 return collectionPersister == null ? null : new CollectionPersister[] { collectionPersister };
855 }
856
857 protected String[] getCollectionSuffixes() {
858 return collectionPersister == null ? null : new String[] { "__" };
859 }
860
861 void setCollectionToFetch(String role, String name, String ownerName, String entityName)
862 throws QueryException {
863 fetchName = name;
864 collectionPersister = getCollectionPersister( role );
865 collectionOwnerName = ownerName;
866 if ( collectionPersister.getElementType().isEntityType() ) {
867 addEntityToFetch( entityName );
868 }
869 }
870
871 protected String[] getSuffixes() {
872 return suffixes;
873 }
874
875 protected String[] getAliases() {
876 return names;
877 }
878
879 /**
880 * Used for collection filters
881 */
882 private void addFromAssociation(final String elementName, final String collectionRole)
883 throws QueryException {
884 //q.addCollection(collectionName, collectionRole);
885 QueryableCollection persister = getCollectionPersister( collectionRole );
886 Type collectionElementType = persister.getElementType();
887 if ( !collectionElementType.isEntityType() ) {
888 throw new QueryException( "collection of values in filter: " + elementName );
889 }
890
891 String[] keyColumnNames = persister.getKeyColumnNames();
892 //if (keyColumnNames.length!=1) throw new QueryException("composite-key collection in filter: " + collectionRole);
893
894 String collectionName;
895 JoinSequence join = new JoinSequence( getFactory() );
896 collectionName = persister.isOneToMany() ?
897 elementName :
898 createNameForCollection( collectionRole );
899 join.setRoot( persister, collectionName );
900 if ( !persister.isOneToMany() ) {
901 //many-to-many
902 addCollection( collectionName, collectionRole );
903 try {
904 join.addJoin( ( AssociationType ) persister.getElementType(),
905 elementName,
906 JoinFragment.INNER_JOIN,
907 persister.getElementColumnNames(collectionName) );
908 }
909 catch ( MappingException me ) {
910 throw new QueryException( me );
911 }
912 }
913 join.addCondition( collectionName, keyColumnNames, " = ?" );
914 //if ( persister.hasWhere() ) join.addCondition( persister.getSQLWhereString(collectionName) );
915 EntityType elemType = ( EntityType ) collectionElementType;
916 addFrom( elementName, elemType.getAssociatedEntityName(), join );
917
918 }
919
920 String getPathAlias(String path) {
921 return ( String ) pathAliases.get( path );
922 }
923
924 JoinSequence getPathJoin(String path) {
925 return ( JoinSequence ) pathJoins.get( path );
926 }
927
928 void addPathAliasAndJoin(String path, String alias, JoinSequence joinSequence) {
929 pathAliases.put( path, alias );
930 pathJoins.put( path, joinSequence );
931 }
932
933 public List list(SessionImplementor session, QueryParameters queryParameters)
934 throws HibernateException {
935 return list( session, queryParameters, getQuerySpaces(), actualReturnTypes );
936 }
937
938 /**
939 * Return the query results as an iterator
940 */
941 public Iterator iterate(QueryParameters queryParameters, EventSource session)
942 throws HibernateException {
943
944 boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
945 long startTime = 0;
946 if ( stats ) startTime = System.currentTimeMillis();
947
948 try {
949
950 PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
951 ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session );
952 HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
953 Iterator result = new IteratorImpl( rs, st, session, returnTypes, getColumnNames(), hi );
954
955 if ( stats ) {
956 session.getFactory().getStatisticsImplementor().queryExecuted(
957 "HQL: " + queryString,
958 0,
959 System.currentTimeMillis() - startTime
960 );
961 }
962
963 return result;
964
965 }
966 catch ( SQLException sqle ) {
967 throw JDBCExceptionHelper.convert(
968 getFactory().getSQLExceptionConverter(),
969 sqle,
970 "could not execute query using iterate",
971 getSQLString()
972 );
973 }
974
975 }
976
977 public int executeUpdate(QueryParameters queryParameters, SessionImplementor session) throws HibernateException {
978 throw new UnsupportedOperationException( "Not supported! Use the AST translator...");
979 }
980
981 protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
982 throws SQLException, HibernateException {
983 row = toResultRow( row );
984 if ( hasScalars ) {
985 String[][] scalarColumns = getColumnNames();
986 int queryCols = returnTypes.length;
987 if ( holderClass == null && queryCols == 1 ) {
988 return returnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
989 }
990 else {
991 row = new Object[queryCols];
992 for ( int i = 0; i < queryCols; i++ )
993 row[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
994 return row;
995 }
996 }
997 else if ( holderClass == null ) {
998 return row.length == 1 ? row[0] : row;
999 }
1000 else {
1001 return row;
1002 }
1003
1004 }
1005
1006 protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
1007 if ( holderClass != null ) {
1008 for ( int i = 0; i < results.size(); i++ ) {
1009 Object[] row = ( Object[] ) results.get( i );
1010 try {
1011 results.set( i, holderConstructor.newInstance( row ) );
1012 }
1013 catch ( Exception e ) {
1014 throw new QueryException( "could not instantiate: " + holderClass, e );
1015 }
1016 }
1017 }
1018 return results;
1019 }
1020
1021 private Object[] toResultRow(Object[] row) {
1022 if ( selectLength == row.length ) {
1023 return row;
1024 }
1025 else {
1026 Object[] result = new Object[selectLength];
1027 int j = 0;
1028 for ( int i = 0; i < row.length; i++ ) {
1029 if ( includeInSelect[i] ) result[j++] = row[i];
1030 }
1031 return result;
1032 }
1033 }
1034
1035 void setHolderClass(Class clazz) {
1036 holderClass = clazz;
1037 }
1038
1039 protected LockMode[] getLockModes(Map lockModes) {
1040 // unfortunately this stuff can't be cached because
1041 // it is per-invocation, not constant for the
1042 // QueryTranslator instance
1043 HashMap nameLockModes = new HashMap();
1044 if ( lockModes != null ) {
1045 Iterator iter = lockModes.entrySet().iterator();
1046 while ( iter.hasNext() ) {
1047 Map.Entry me = ( Map.Entry ) iter.next();
1048 nameLockModes.put( getAliasName( ( String ) me.getKey() ),
1049 me.getValue() );
1050 }
1051 }
1052 LockMode[] lockModeArray = new LockMode[names.length];
1053 for ( int i = 0; i < names.length; i++ ) {
1054 LockMode lm = ( LockMode ) nameLockModes.get( names[i] );
1055 if ( lm == null ) lm = LockMode.NONE;
1056 lockModeArray[i] = lm;
1057 }
1058 return lockModeArray;
1059 }
1060
1061 protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws QueryException {
1062 // can't cache this stuff either (per-invocation)
1063 final String result;
1064 if ( lockModes == null || lockModes.size() == 0 ) {
1065 result = sql;
1066 }
1067 else {
1068 Map aliasedLockModes = new HashMap();
1069 Iterator iter = lockModes.entrySet().iterator();
1070 while ( iter.hasNext() ) {
1071 Map.Entry me = ( Map.Entry ) iter.next();
1072 aliasedLockModes.put( getAliasName( ( String ) me.getKey() ), me.getValue() );
1073 }
1074 Map keyColumnNames = null;
1075 if ( dialect.forUpdateOfColumns() ) {
1076 keyColumnNames = new HashMap();
1077 for ( int i = 0; i < names.length; i++ ) {
1078 keyColumnNames.put( names[i], persisters[i].getIdentifierColumnNames() );
1079 }
1080 }
1081 result = dialect.applyLocksToSql( sql, aliasedLockModes, keyColumnNames );
1082 }
1083 logQuery( queryString, result );
1084 return result;
1085 }
1086
1087 protected boolean upgradeLocks() {
1088 return true;
1089 }
1090
1091 protected int[] getCollectionOwners() {
1092 return new int[] { collectionOwnerColumn };
1093 }
1094
1095 protected boolean isCompiled() {
1096 return compiled;
1097 }
1098
1099 public String toString() {
1100 return queryString;
1101 }
1102
1103 protected int[] getOwners() {
1104 return owners;
1105 }
1106
1107 protected EntityType[] getOwnerAssociationTypes() {
1108 return ownerAssociationTypes;
1109 }
1110
1111 public Class getHolderClass() {
1112 return holderClass;
1113 }
1114
1115 public Map getEnabledFilters() {
1116 return enabledFilters;
1117 }
1118
1119 public ScrollableResults scroll(final QueryParameters queryParameters,
1120 final SessionImplementor session)
1121 throws HibernateException {
1122 HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
1123 return scroll( queryParameters, returnTypes, hi, session );
1124 }
1125
1126 public String getQueryIdentifier() {
1127 return queryIdentifier;
1128 }
1129
1130 protected boolean isSubselectLoadingEnabled() {
1131 return hasSubselectLoadableCollections();
1132 }
1133
1134 public void validateScrollability() throws HibernateException {
1135 // This is the legacy behaviour for HQL queries...
1136 if ( getCollectionPersisters() != null ) {
1137 throw new HibernateException( "Cannot scroll queries which initialize collections" );
1138 }
1139 }
1140
1141 public boolean containsCollectionFetches() {
1142 return false;
1143 }
1144
1145 public boolean isManipulationStatement() {
1146 // classic parser does not support bulk manipulation statements
1147 return false;
1148 }
1149
1150 public ParameterTranslations getParameterTranslations() {
1151 return new ParameterTranslations() {
1152
1153 public boolean supportsOrdinalParameterMetadata() {
1154 // classic translator does not support collection of ordinal
1155 // param metadata
1156 return false;
1157 }
1158
1159 public int getOrdinalParameterCount() {
1160 return 0; // not known!
1161 }
1162
1163 public int getOrdinalParameterSqlLocation(int ordinalPosition) {
1164 return 0; // not known!
1165 }
1166
1167 public Type getOrdinalParameterExpectedType(int ordinalPosition) {
1168 return null; // not known!
1169 }
1170
1171 public Set getNamedParameterNames() {
1172 return namedParameters.keySet();
1173 }
1174
1175 public int[] getNamedParameterSqlLocations(String name) {
1176 return getNamedParameterLocs( name );
1177 }
1178
1179 public Type getNamedParameterExpectedType(String name) {
1180 return null; // not known!
1181 }
1182 };
1183 }
1184 }