Source code: org/hibernate/impl/AbstractQueryImpl.java
1 //$Id: AbstractQueryImpl.java,v 1.32 2005/04/13 07:37:49 oneovthafew Exp $
2 package org.hibernate.impl;
3
4 import java.io.Serializable;
5 import java.math.BigDecimal;
6 import java.math.BigInteger;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Calendar;
10 import java.util.Collection;
11 import java.util.Date;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.StringTokenizer;
20
21 import org.hibernate.CacheMode;
22 import org.hibernate.FlushMode;
23 import org.hibernate.Hibernate;
24 import org.hibernate.HibernateException;
25 import org.hibernate.MappingException;
26 import org.hibernate.NonUniqueResultException;
27 import org.hibernate.PropertyNotFoundException;
28 import org.hibernate.Query;
29 import org.hibernate.QueryException;
30 import org.hibernate.engine.QueryParameters;
31 import org.hibernate.engine.RowSelection;
32 import org.hibernate.engine.SessionImplementor;
33 import org.hibernate.engine.TypedValue;
34 import org.hibernate.hql.classic.ParserHelper;
35 import org.hibernate.property.Getter;
36 import org.hibernate.proxy.HibernateProxyHelper;
37 import org.hibernate.type.SerializableType;
38 import org.hibernate.type.Type;
39 import org.hibernate.type.TypeFactory;
40 import org.hibernate.util.ArrayHelper;
41 import org.hibernate.util.MarkerObject;
42 import org.hibernate.util.ReflectHelper;
43 import org.hibernate.util.StringHelper;
44
45 /**
46 * Abstract implementation of the Query interface
47 * @author Gavin King, Max Andersen
48 */
49 public abstract class AbstractQueryImpl implements Query {
50
51 private static final Object UNSET_PARAMETER = new MarkerObject("<unset parameter>");
52 private static final Object UNSET_TYPE = new MarkerObject("<unset type>");
53
54 private String queryString;
55 private final SessionImplementor session;
56
57 private RowSelection selection;
58 private List values = new ArrayList(4);
59 private List types = new ArrayList(4);
60 private int positionalParameterCount = 0;
61 private Set actualNamedParameters = new HashSet(4);
62 private Map namedParameters = new HashMap(4);
63 private Map namedParameterLists = new HashMap(4);
64 private boolean cacheable;
65 private String cacheRegion;
66 private String comment;
67 private FlushMode flushMode;
68 private CacheMode cacheMode;
69 private FlushMode sessionFlushMode;
70 private CacheMode sessionCacheMode;
71 private Serializable collectionKey;
72 private boolean readOnly;
73
74 public AbstractQueryImpl(String queryString, FlushMode flushMode, SessionImplementor session) {
75 this.session = session;
76 this.queryString = queryString;
77 this.selection = new RowSelection();
78 this.flushMode = flushMode;
79 this.cacheMode = null;
80 initParameterBookKeeping();
81 }
82
83 public final String getQueryString() {
84 return queryString;
85 }
86
87 public Query setFlushMode(FlushMode flushMode) {
88 this.flushMode = flushMode;
89 return this;
90 }
91
92 public Query setCacheMode(CacheMode cacheMode) {
93 this.cacheMode = cacheMode;
94 return this;
95 }
96
97 protected Map getNamedParams() {
98 return new HashMap(namedParameters);
99 }
100
101 protected void verifyParameters() throws QueryException {
102 verifyParameters(false);
103 }
104
105 /**
106 * @param reserveFirstParameter if true, the first ? will not be verified since its needed for e.g. callable statements returning a out parameter
107 * @throws HibernateException
108 */
109 protected void verifyParameters(boolean reserveFirstParameter) throws HibernateException {
110
111 if ( actualNamedParameters.size() != namedParameters.size() + namedParameterLists.size() ) {
112 Set missingParams = new HashSet(actualNamedParameters);
113 missingParams.removeAll( namedParameterLists.keySet() );
114 missingParams.removeAll( namedParameters.keySet() );
115 throw new QueryException( "Not all named parameters have been set: " + missingParams, getQueryString() );
116 }
117
118 int positionalValueSpan = 0;
119 for ( int i=0; i<values.size(); i++ ) {
120 Object object = types.get(i);
121 if( values.get(i)==UNSET_PARAMETER || object==UNSET_TYPE ) {
122 if(reserveFirstParameter && i==0) {
123 continue;
124 } else {
125 throw new QueryException( "Unset positional parameter at position: " + i, getQueryString() );
126 }
127 }
128 positionalValueSpan += ( (Type) object ).getColumnSpan( session.getFactory() );
129 }
130
131 if ( positionalParameterCount!=positionalValueSpan ) {
132 if(reserveFirstParameter && positionalParameterCount-1!=positionalValueSpan) {
133 throw new QueryException(
134 "Expected positional parameter count: " +
135 (positionalParameterCount-1) +
136 ", actual parameters: " +
137 values,
138 getQueryString()
139 );
140 } else if (!reserveFirstParameter) {
141 throw new QueryException(
142 "Expected positional parameter count: " +
143 positionalParameterCount +
144 ", actual parameters: " +
145 values,
146 getQueryString()
147 );
148 }
149 }
150 }
151
152 protected Map getNamedParameterLists() {
153 return namedParameterLists;
154 }
155
156 protected List getValues() {
157 return values;
158 }
159
160 protected List getTypes() {
161 return types;
162 }
163
164 //TODO: maybe call it getRowSelection() ?
165 public RowSelection getSelection() {
166 return selection;
167 }
168
169 public Query setMaxResults(int maxResults) {
170 selection.setMaxRows( new Integer(maxResults) );
171 return this;
172 }
173
174 public Query setTimeout(int timeout) {
175 selection.setTimeout( new Integer(timeout) );
176 return this;
177 }
178 public Query setFetchSize(int fetchSize) {
179 selection.setFetchSize( new Integer(fetchSize) );
180 return this;
181 }
182
183
184 public Query setFirstResult(int firstResult) {
185 selection.setFirstRow( new Integer(firstResult) );
186 return this;
187 }
188
189 public Query setParameter(int position, Object val, Type type) {
190 if ( positionalParameterCount==0 ) {
191 throw new IllegalArgumentException("No positional parameters in query: " + getQueryString() );
192 }
193 if ( position<0 || position>positionalParameterCount-1 ) {
194 throw new IllegalArgumentException("Positional parameter does not exist: " + position + " in query: " + getQueryString() );
195 }
196 int size = values.size();
197 if ( position<size ) {
198 values.set(position, val);
199 types.set(position, type);
200 }
201 else {
202 // prepend value and type list with null for any positions before the wanted position.
203 for ( int i=0; i<position-size; i++ ) {
204 values.add(UNSET_PARAMETER);
205 types.add(UNSET_TYPE);
206 }
207 values.add(val);
208 types.add(type);
209 }
210 return this;
211 }
212
213 public Query setString(int position, String val) {
214 setParameter(position, val, Hibernate.STRING);
215 return this;
216 }
217
218 public Query setCharacter(int position, char val) {
219 setParameter(position, new Character(val), Hibernate.CHARACTER);
220 return this;
221 }
222
223 public Query setBoolean(int position, boolean val) {
224 setParameter(position, val ? Boolean.TRUE : Boolean.FALSE, Hibernate.BOOLEAN);
225 return this;
226 }
227
228 public Query setByte(int position, byte val) {
229 setParameter(position, new Byte(val), Hibernate.BYTE);
230 return this;
231 }
232
233 public Query setShort(int position, short val) {
234 setParameter(position, new Short(val), Hibernate.SHORT);
235 return this;
236 }
237
238 public Query setInteger(int position, int val) {
239 setParameter(position, new Integer(val), Hibernate.INTEGER);
240 return this;
241 }
242
243 public Query setLong(int position, long val) {
244 setParameter(position, new Long(val), Hibernate.LONG);
245 return this;
246 }
247
248 public Query setFloat(int position, float val) {
249 setParameter(position, new Float(val), Hibernate.FLOAT);
250 return this;
251 }
252
253 public Query setDouble(int position, double val) {
254 setParameter(position, new Double(val), Hibernate.DOUBLE);
255 return this;
256 }
257
258 public Query setBinary(int position, byte[] val) {
259 setParameter(position, val, Hibernate.BINARY);
260 return this;
261 }
262
263 public Query setText(int position, String val) {
264 setParameter(position, val, Hibernate.TEXT);
265 return this;
266 }
267
268 public Query setSerializable(int position, Serializable val) {
269 setParameter(position, val, Hibernate.SERIALIZABLE);
270 return this;
271 }
272
273 public Query setDate(int position, Date date) {
274 setParameter(position, date, Hibernate.DATE);
275 return this;
276 }
277
278 public Query setTime(int position, Date date) {
279 setParameter(position, date, Hibernate.TIME);
280 return this;
281 }
282
283 public Query setTimestamp(int position, Date date) {
284 setParameter(position, date, Hibernate.TIMESTAMP);
285 return this;
286 }
287
288 public Query setEntity(int position, Object val) {
289 setParameter( position, val, Hibernate.entity( session.bestGuessEntityName(val) ) );
290 return this;
291 }
292
293 public Query setLocale(int position, Locale locale) {
294 setParameter(position, locale, Hibernate.LOCALE);
295 return this;
296 }
297
298 public Query setCalendar(int position, Calendar calendar) {
299 setParameter(position, calendar, Hibernate.CALENDAR);
300 return this;
301 }
302
303 public Query setCalendarDate(int position, Calendar calendar) {
304 setParameter(position, calendar, Hibernate.CALENDAR_DATE);
305 return this;
306 }
307
308 public Query setBinary(String name, byte[] val) {
309 setParameter(name, val, Hibernate.BINARY);
310 return this;
311 }
312
313 public Query setText(String name, String val) {
314 setParameter(name, val, Hibernate.TEXT);
315 return this;
316 }
317
318 public Query setBoolean(String name, boolean val) {
319 setParameter(name, val ? Boolean.TRUE : Boolean.FALSE, Hibernate.BOOLEAN);
320 return this;
321 }
322
323 public Query setByte(String name, byte val) {
324 setParameter(name, new Byte(val), Hibernate.BYTE);
325 return this;
326 }
327
328 public Query setCharacter(String name, char val) {
329 setParameter(name, new Character(val), Hibernate.CHARACTER);
330 return this;
331 }
332
333 public Query setDate(String name, Date date) {
334 setParameter(name, date, Hibernate.DATE);
335 return this;
336 }
337
338 public Query setDouble(String name, double val) {
339 setParameter(name, new Double(val), Hibernate.DOUBLE);
340 return this;
341 }
342
343 public Query setEntity(String name, Object val) {
344 setParameter( name, val, Hibernate.entity( session.bestGuessEntityName(val) ) );
345 return this;
346 }
347
348 public Query setFloat(String name, float val) {
349 setParameter(name, new Float(val), Hibernate.FLOAT);
350 return this;
351 }
352
353 public Query setInteger(String name, int val) {
354 setParameter(name, new Integer(val), Hibernate.INTEGER);
355 return this;
356 }
357
358 public Query setLocale(String name, Locale locale) {
359 setParameter(name, locale, Hibernate.LOCALE);
360 return this;
361 }
362
363 public Query setCalendar(String name, Calendar calendar) {
364 setParameter(name, calendar, Hibernate.CALENDAR);
365 return this;
366 }
367
368 public Query setCalendarDate(String name, Calendar calendar) {
369 setParameter(name, calendar, Hibernate.CALENDAR_DATE);
370 return this;
371 }
372
373 public Query setLong(String name, long val) {
374 setParameter(name, new Long(val), Hibernate.LONG);
375 return this;
376 }
377
378 public Query setParameter(String name, Object val, Type type) {
379 if( !actualNamedParameters.contains(name) ) {
380 throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() + "]");
381 }
382 else {
383 namedParameters.put( name, new TypedValue( type, val, session.getEntityMode() ) );
384 return this;
385 }
386 }
387
388 public Query setSerializable(String name, Serializable val) {
389 setParameter(name, val, Hibernate.SERIALIZABLE);
390 return this;
391 }
392
393 public Query setShort(String name, short val) {
394 setParameter(name, new Short(val), Hibernate.SHORT);
395 return this;
396 }
397
398 public Query setString(String name, String val) {
399 setParameter(name, val, Hibernate.STRING);
400 return this;
401 }
402
403 public Query setTime(String name, Date date) {
404 setParameter(name, date, Hibernate.TIME);
405 return this;
406 }
407
408 public Query setTimestamp(String name, Date date) {
409 setParameter(name, date, Hibernate.TIMESTAMP);
410 return this;
411 }
412
413 public Query setBigDecimal(int position, BigDecimal number) {
414 setParameter(position, number, Hibernate.BIG_DECIMAL);
415 return this;
416 }
417
418 public Query setBigDecimal(String name, BigDecimal number) {
419 setParameter(name, number, Hibernate.BIG_DECIMAL);
420 return this;
421 }
422
423 public Query setBigInteger(int position, BigInteger number) {
424 setParameter(position, number, Hibernate.BIG_DECIMAL);
425 return this;
426 }
427
428 public Query setBigInteger(String name, BigInteger number) {
429 setParameter(name, number, Hibernate.BIG_DECIMAL);
430 return this;
431 }
432
433 public Query setParameter(int position, Object val) throws HibernateException {
434 if (val == null) {
435 setParameter( position, val, Hibernate.SERIALIZABLE );
436 }
437 else {
438 setParameter( position, val, guessType(val) );
439 }
440 return this;
441 }
442
443 public Query setParameter(String name, Object val) throws HibernateException {
444 if (val == null) {
445 setParameter( name, val, Hibernate.SERIALIZABLE );
446 }
447 else {
448 setParameter( name, val, guessType(val) );
449 }
450 return this;
451 }
452
453 private Type guessType(Object param) throws HibernateException {
454 Class clazz = HibernateProxyHelper.getClassWithoutInitializingProxy(param);
455 return guessType(clazz);
456 }
457
458 private Type guessType(Class clazz) throws HibernateException {
459 String typename = clazz.getName();
460 Type type = TypeFactory.heuristicType(typename);
461 boolean serializable = type!=null && type instanceof SerializableType;
462 if (type==null || serializable) {
463 try {
464 session.getFactory().getEntityPersister( clazz.getName() );
465 }
466 catch (MappingException me) {
467 if (serializable) {
468 return type;
469 }
470 else {
471 throw new HibernateException("Could not determine a type for class: " + typename);
472 }
473 }
474 return Hibernate.entity(clazz);
475 }
476 else {
477 return type;
478 }
479 }
480
481
482 public Type[] getReturnTypes() throws HibernateException {
483 return session.getFactory().getReturnTypes( queryString );
484 }
485
486 public String[] getReturnAliases() throws HibernateException {
487 return session.getFactory().getReturnAliases( queryString );
488 }
489
490 public Query setParameterList(String name, Collection vals, Type type) throws HibernateException {
491 if( !actualNamedParameters.contains(name) ) {
492 throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() + "]");
493 }
494 namedParameterLists.put( name, new TypedValue( type, vals, session.getEntityMode() ) );
495 return this;
496 }
497
498 /**
499 * Warning: adds new parameters to the argument by side-effect!
500 */
501 protected String bindParameterLists(Map namedParamsCopy) {
502 String query = this.queryString;
503 Iterator iter = namedParameterLists.entrySet().iterator();
504 while ( iter.hasNext() ) {
505 Map.Entry me = (Map.Entry) iter.next();
506 query = bindParameterList( query, (String) me.getKey(), (TypedValue) me.getValue(), namedParamsCopy );
507 }
508 return query;
509 }
510
511 /**
512 * Warning: adds new parameters to the argument by side-effect!
513 */
514 private String bindParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
515 Collection vals = (Collection) typedList.getValue();
516 Type type = typedList.getType();
517 StringBuffer list = new StringBuffer(16);
518 Iterator iter = vals.iterator();
519 int i=0;
520 while ( iter.hasNext() ) {
521 String alias = name + i++ + '_';
522 namedParamsCopy.put(alias, new TypedValue( type, iter.next(), session.getEntityMode() ) );
523 list.append( ParserHelper.HQL_VARIABLE_PREFIX + alias );
524 if ( iter.hasNext() ) list.append(", ");
525 }
526 return StringHelper.replace( query, ParserHelper.HQL_VARIABLE_PREFIX + name, list.toString(), true );
527 }
528
529 public Query setParameterList(String name, Collection vals) throws HibernateException {
530 if (vals==null) {
531 throw new QueryException("Collection must be not null!");
532 }
533
534 if(vals.size()==0) {
535 setParameterList(name, vals, null);
536 }
537 else {
538 setParameterList(name, vals, guessType( vals.iterator().next() ) );
539 }
540
541 return this;
542 }
543
544 public String[] getNamedParameters() throws HibernateException {
545 return (String[]) actualNamedParameters.toArray(new String[actualNamedParameters.size()]);
546 }
547
548 private void initParameterBookKeeping() {
549 StringTokenizer st = new StringTokenizer(queryString, ParserHelper.HQL_SEPARATORS);
550 Set result = new HashSet();
551
552 while ( st.hasMoreTokens() ) {
553 String string = st.nextToken();
554 if( string.startsWith(ParserHelper.HQL_VARIABLE_PREFIX) ) {
555 result.add( string.substring(1) );
556 }
557 }
558
559 actualNamedParameters = result;
560 positionalParameterCount = StringHelper.countUnquoted(queryString, '?');
561 }
562
563 public Query setProperties(Object bean) throws HibernateException {
564 Class clazz = bean.getClass();
565 String[] params = getNamedParameters();
566 for (int i = 0; i < params.length; i++) {
567 String namedParam = params[i];
568 try {
569 Getter getter = ReflectHelper.getGetter(clazz, namedParam);
570 Class retType = getter.getReturnType();
571 final Object object = getter.get( bean );
572 if ( Collection.class.isAssignableFrom(retType) ) {
573 setParameterList( namedParam, (Collection) object );
574 }
575 else if ( retType.isArray() ) {
576 setParameterList( namedParam, (Object[]) object );
577 }
578 else {
579 setParameter( namedParam, object, guessType( getter.getReturnType() ) );
580 }
581 }
582 catch (PropertyNotFoundException pnfe) {}
583 }
584 return this;
585 }
586
587
588 public Query setParameterList(String name, Object[] vals, Type type)
589 throws HibernateException {
590 return setParameterList(name, Arrays.asList(vals), type);
591 }
592
593 public Query setParameterList(String name, Object[] vals)
594 throws HibernateException {
595 return setParameterList( name, Arrays.asList(vals) );
596 }
597
598 SessionImplementor getSession() {
599 return session;
600 }
601
602 public Object uniqueResult() throws HibernateException {
603 return uniqueElement( list() );
604 }
605
606 public int executeUpdate() throws HibernateException {
607 throw new UnsupportedOperationException( "Update queries only supported through HQL" );
608 }
609
610 static Object uniqueElement(List list) throws NonUniqueResultException {
611 int size = list.size();
612 if (size==0) return null;
613 Object first = list.get(0);
614 for ( int i=1; i<size; i++ ) {
615 if ( list.get(i)!=first ) {
616 throw new NonUniqueResultException( list.size() );
617 }
618 }
619 return first;
620 }
621
622 protected RowSelection getRowSelection() {
623 return selection;
624 }
625
626 public Type[] typeArray() {
627 return ArrayHelper.toTypeArray( getTypes() );
628 }
629
630 public Object[] valueArray() {
631 return getValues().toArray();
632 }
633
634 public QueryParameters getQueryParameters(Map namedParams) {
635 return new QueryParameters(
636 typeArray(),
637 valueArray(),
638 namedParams,
639 getLockModes(),
640 getSelection(),
641 readOnly,
642 cacheable,
643 cacheRegion,
644 //forceCacheRefresh,
645 comment,
646 collectionKey==null ?
647 null :
648 new Serializable[] { collectionKey }
649 );
650 }
651
652 protected abstract Map getLockModes();
653
654 public Query setCacheable(boolean cacheable) {
655 this.cacheable = cacheable;
656 return this;
657 }
658
659 public Query setCacheRegion(String cacheRegion) {
660 if (cacheRegion != null)
661 this.cacheRegion = cacheRegion.trim();
662 return this;
663 }
664
665 public Query setComment(String comment) {
666 this.comment = comment;
667 return this;
668 }
669
670 public String toString() {
671 return StringHelper.unqualify( getClass().getName() ) + '(' + queryString + ')';
672 }
673
674 protected void before() {
675 if ( flushMode!=null ) {
676 sessionFlushMode = getSession().getFlushMode();
677 getSession().setFlushMode(flushMode);
678 }
679 if ( cacheMode!=null ) {
680 sessionCacheMode = getSession().getCacheMode();
681 getSession().setCacheMode(cacheMode);
682 }
683 }
684
685 protected void after() {
686 if (sessionFlushMode!=null) {
687 getSession().setFlushMode(sessionFlushMode);
688 sessionFlushMode = null;
689 }
690 if (sessionCacheMode!=null) {
691 getSession().setCacheMode(sessionCacheMode);
692 sessionCacheMode = null;
693 }
694 }
695
696 public Query setCollectionKey(Serializable collectionKey) {
697 this.collectionKey = collectionKey;
698 return this;
699 }
700
701 public Query setParameters(Object[] values, Type[] types) {
702 this.values = Arrays.asList(values);
703 this.types = Arrays.asList(types);
704 return this;
705 }
706
707 public boolean isReadOnly() {
708 return readOnly;
709 }
710
711 public Query setReadOnly(boolean readOnly) {
712 this.readOnly = readOnly;
713 return this;
714 }
715 }