KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > query > SelectQuery


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.query;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.apache.cayenne.exp.Expression;
30 import org.apache.cayenne.map.DbAttribute;
31 import org.apache.cayenne.map.DbEntity;
32 import org.apache.cayenne.map.EntityResolver;
33 import org.apache.cayenne.map.ObjEntity;
34 import org.apache.cayenne.map.Procedure;
35 import org.apache.cayenne.map.QueryBuilder;
36 import org.apache.cayenne.util.Util;
37 import org.apache.cayenne.util.XMLEncoder;
38 import org.apache.cayenne.util.XMLSerializable;
39
40 /**
41  * A query that selects persistent objects of a certain type or "raw data" (aka DataRows).
42  * Supports expression qualifier, multiple orderings and a number of other parameters that
43  * serve as runtime hints to Cayenne on how to optimize the fetch and result processing.
44  *
45  * @author Andrus Adamchik
46  */

47 public class SelectQuery extends QualifiedQuery implements ParameterizedQuery,
48         XMLSerializable {
49
50     public static final String JavaDoc DISTINCT_PROPERTY = "cayenne.SelectQuery.distinct";
51     public static final boolean DISTINCT_DEFAULT = false;
52
53     protected List JavaDoc customDbAttributes;
54     protected List JavaDoc orderings;
55     protected boolean distinct;
56
57     protected Expression parentQualifier;
58     protected String JavaDoc parentObjEntityName;
59
60     SelectQueryMetadata selectInfo = new SelectQueryMetadata();
61
62     /** Creates an empty SelectQuery. */
63     public SelectQuery() {
64     }
65
66     /**
67      * Creates a SelectQuery with null qualifier, for the specifed ObjEntity
68      *
69      * @param root the ObjEntity this SelectQuery is for.
70      */

71     public SelectQuery(ObjEntity root) {
72         this(root, null);
73     }
74
75     /**
76      * Creates a SelectQuery for the specifed ObjEntity with the given qualifier
77      *
78      * @param root the ObjEntity this SelectQuery is for.
79      * @param qualifier an Expression indicating which objects should be fetched
80      */

81     public SelectQuery(ObjEntity root, Expression qualifier) {
82         this();
83         this.init(root, qualifier);
84     }
85
86     /**
87      * Creates a SelectQuery that selects all objects of a given persistent class.
88      *
89      * @param rootClass the Class of objects fetched by this query.
90      */

91     public SelectQuery(Class JavaDoc rootClass) {
92         this(rootClass, null);
93     }
94
95     /**
96      * Creates a SelectQuery that selects objects of a given persistent class that match
97      * supplied qualifier.
98      *
99      * @param rootClass the Class of objects fetched by this query.
100      */

101     public SelectQuery(Class JavaDoc rootClass, Expression qualifier) {
102         init(rootClass, qualifier);
103     }
104
105     /**
106      * Creates a SelectQuery for the specifed DbEntity.
107      *
108      * @param root the DbEntity this SelectQuery is for.
109      * @since 1.1
110      */

111     public SelectQuery(DbEntity root) {
112         this(root, null);
113     }
114
115     /**
116      * Creates a SelectQuery for the specifed DbEntity with the given qualifier.
117      *
118      * @param root the DbEntity this SelectQuery is for.
119      * @param qualifier an Expression indicating which objects should be fetched
120      * @since 1.1
121      */

122     public SelectQuery(DbEntity root, Expression qualifier) {
123         this();
124         this.init(root, qualifier);
125     }
126
127     /**
128      * Creates SelectQuery with <code>objEntityName</code> parameter.
129      */

130     public SelectQuery(String JavaDoc objEntityName) {
131         this(objEntityName, null);
132     }
133
134     /**
135      * Creates SelectQuery with <code>objEntityName</code> and <code>qualifier</code>
136      * parameters.
137      */

138     public SelectQuery(String JavaDoc objEntityName, Expression qualifier) {
139         init(objEntityName, qualifier);
140     }
141
142     private void init(Object JavaDoc root, Expression qualifier) {
143         this.setRoot(root);
144         this.setQualifier(qualifier);
145     }
146
147     /**
148      * @since 1.2
149      */

150     public QueryMetadata getMetaData(EntityResolver resolver) {
151         selectInfo.resolve(root, resolver, this);
152
153         // must force DataRows if custom attributes are fetched
154
if (isFetchingCustomAttributes()) {
155             QueryMetadataWrapper wrapper = new QueryMetadataWrapper(selectInfo);
156             wrapper.override(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, Boolean.TRUE);
157             return wrapper;
158         }
159         else {
160             return selectInfo;
161         }
162     }
163
164     /**
165      * Routes itself and if there are any prefetches configured, creates prefetch queries
166      * and routes them as well.
167      *
168      * @since 1.2
169      */

170     public void route(QueryRouter router, EntityResolver resolver, Query substitutedQuery) {
171         super.route(router, resolver, substitutedQuery);
172         routePrefetches(router, resolver);
173     }
174
175     /**
176      * Creates and routes extra disjoint prefetch queries.
177      *
178      * @since 1.2
179      */

180     void routePrefetches(QueryRouter router, EntityResolver resolver) {
181         new SelectQueryPrefetchRouterAction().route(this, router, resolver);
182     }
183
184     /**
185      * Calls "makeSelect" on the visitor.
186      *
187      * @since 1.2
188      */

189     public SQLAction createSQLAction(SQLActionVisitor visitor) {
190         return visitor.objectSelectAction(this);
191     }
192
193     /**
194      * Initializes query parameters using a set of properties.
195      *
196      * @since 1.1
197      */

198     public void initWithProperties(Map JavaDoc properties) {
199
200         // must init defaults even if properties are empty
201
if (properties == null) {
202             properties = Collections.EMPTY_MAP;
203         }
204
205         Object JavaDoc distinct = properties.get(DISTINCT_PROPERTY);
206
207         // init ivars from properties
208
this.distinct = (distinct != null)
209                 ? "true".equalsIgnoreCase(distinct.toString())
210                 : DISTINCT_DEFAULT;
211
212         selectInfo.initWithProperties(properties);
213     }
214
215     /**
216      * Prints itself as XML to the provided PrintWriter.
217      *
218      * @since 1.1
219      */

220     public void encodeAsXML(XMLEncoder encoder) {
221         encoder.print("<query name=\"");
222         encoder.print(getName());
223         encoder.print("\" factory=\"");
224         encoder.print("org.apache.cayenne.map.SelectQueryBuilder");
225
226         String JavaDoc rootString = null;
227         String JavaDoc rootType = null;
228
229         if (root instanceof String JavaDoc) {
230             rootType = QueryBuilder.OBJ_ENTITY_ROOT;
231             rootString = root.toString();
232         }
233         else if (root instanceof ObjEntity) {
234             rootType = QueryBuilder.OBJ_ENTITY_ROOT;
235             rootString = ((ObjEntity) root).getName();
236         }
237         else if (root instanceof DbEntity) {
238             rootType = QueryBuilder.DB_ENTITY_ROOT;
239             rootString = ((DbEntity) root).getName();
240         }
241         else if (root instanceof Procedure) {
242             rootType = QueryBuilder.PROCEDURE_ROOT;
243             rootString = ((Procedure) root).getName();
244         }
245         else if (root instanceof Class JavaDoc) {
246             rootType = QueryBuilder.JAVA_CLASS_ROOT;
247             rootString = ((Class JavaDoc) root).getName();
248         }
249
250         if (rootType != null) {
251             encoder.print("\" root=\"");
252             encoder.print(rootType);
253             encoder.print("\" root-name=\"");
254             encoder.print(rootString);
255         }
256
257         encoder.println("\">");
258
259         encoder.indent(1);
260
261         // print properties
262
if (distinct != DISTINCT_DEFAULT) {
263             encoder.printProperty(DISTINCT_PROPERTY, distinct);
264         }
265
266         selectInfo.encodeAsXML(encoder);
267
268         // encode qualifier
269
if (qualifier != null) {
270             encoder.print("<qualifier>");
271             qualifier.encodeAsXML(encoder);
272             encoder.println("</qualifier>");
273         }
274
275         // encode orderings
276
if (orderings != null && !orderings.isEmpty()) {
277             Iterator JavaDoc it = orderings.iterator();
278             while (it.hasNext()) {
279                 Ordering ordering = (Ordering) it.next();
280                 ordering.encodeAsXML(encoder);
281             }
282         }
283
284         encoder.indent(-1);
285         encoder.println("</query>");
286     }
287
288     /**
289      * A shortcut for {@link #queryWithParameters(Map, boolean)}that prunes parts of
290      * qualifier that have no parameter value set.
291      */

292     public SelectQuery queryWithParameters(Map JavaDoc parameters) {
293         return queryWithParameters(parameters, true);
294     }
295
296     /**
297      * Returns a query built using this query as a prototype, using a set of parameters to
298      * build the qualifier.
299      *
300      * @see org.apache.cayenne.exp.Expression#expWithParameters(java.util.Map, boolean)
301      * parameter substitution.
302      */

303     public SelectQuery queryWithParameters(Map JavaDoc parameters, boolean pruneMissing) {
304         // create a query replica
305
SelectQuery query = new SelectQuery();
306         query.setDistinct(distinct);
307
308         query.selectInfo.copyFromInfo(this.selectInfo);
309         query.setParentObjEntityName(parentObjEntityName);
310         query.setParentQualifier(parentQualifier);
311         query.setRoot(root);
312
313         // The following algorithm is for building the new query name based
314
// on the original query name and a hashcode of the map of parameters.
315
// This way the query clone can take advantage of caching. Fixes
316
// problem reported in CAY-360.
317

318         if (!Util.isEmptyString(name)) {
319             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(name);
320
321             if (parameters != null && !parameters.isEmpty()) {
322                 buffer.append(parameters.hashCode());
323             }
324
325             query.setName(buffer.toString());
326         }
327
328         if (orderings != null) {
329             query.addOrderings(orderings);
330         }
331
332         if (customDbAttributes != null) {
333             query.addCustomDbAttributes(customDbAttributes);
334         }
335
336         // substitute qualifier parameters
337
if (qualifier != null) {
338             query.setQualifier(qualifier.expWithParameters(parameters, pruneMissing));
339         }
340
341         return query;
342     }
343
344     /**
345      * Creates and returns a new SelectQuery built using this query as a prototype and
346      * substituting qualifier parameters with the values from the map.
347      *
348      * @since 1.1
349      */

350     public Query createQuery(Map JavaDoc parameters) {
351         return queryWithParameters(parameters);
352     }
353
354     /**
355      * Adds ordering specification to this query orderings.
356      */

357     public void addOrdering(Ordering ordering) {
358         nonNullOrderings().add(ordering);
359     }
360
361     /**
362      * Adds a list of orderings.
363      */

364     public void addOrderings(List JavaDoc orderings) {
365         nonNullOrderings().addAll(orderings);
366     }
367
368     /** Adds ordering specification to this query orderings. */
369     public void addOrdering(String JavaDoc sortPathSpec, boolean isAscending) {
370         this.addOrdering(new Ordering(sortPathSpec, isAscending));
371     }
372
373     /** Adds ordering specification to this query orderings. */
374     public void addOrdering(String JavaDoc sortPathSpec, boolean isAscending, boolean ignoreCase) {
375         this.addOrdering(new Ordering(sortPathSpec, isAscending, ignoreCase));
376     }
377
378     /**
379      * Removes ordering.
380      *
381      * @since 1.1
382      */

383     public void removeOrdering(Ordering ordering) {
384         if (orderings != null) {
385             orderings.remove(ordering);
386         }
387     }
388
389     /**
390      * Returns a list of orderings used by this query.
391      */

392     public List JavaDoc getOrderings() {
393         return (orderings != null) ? orderings : Collections.EMPTY_LIST;
394     }
395
396     /**
397      * Clears all configured orderings.
398      */

399     public void clearOrderings() {
400         orderings = null;
401     }
402
403     /**
404      * Returns true if this query returns distinct rows.
405      */

406     public boolean isDistinct() {
407         return distinct;
408     }
409
410     /**
411      * Sets <code>distinct</code> property that determines whether this query returns
412      * distinct row.
413      */

414     public void setDistinct(boolean distinct) {
415         this.distinct = distinct;
416     }
417
418     /**
419      * Returns a list of attributes that will be included in the results of this query.
420      */

421     public List JavaDoc getCustomDbAttributes() {
422         // if query root is DbEntity, and no custom attributes
423
// are defined, return DbEntity attributes.
424
if ((customDbAttributes == null || customDbAttributes.isEmpty())
425                 && (root instanceof DbEntity)) {
426             Collection JavaDoc attributes = ((DbEntity) root).getAttributes();
427             List JavaDoc attributeNames = new ArrayList JavaDoc(attributes.size());
428             Iterator JavaDoc it = attributes.iterator();
429             while (it.hasNext()) {
430                 DbAttribute attribute = (DbAttribute) it.next();
431                 attributeNames.add(attribute.getName());
432             }
433
434             return attributeNames;
435         }
436         else {
437             return (customDbAttributes != null)
438                     ? customDbAttributes
439                     : Collections.EMPTY_LIST;
440         }
441     }
442
443     /**
444      * Adds a path to the DbAttribute that should be included in the results of this
445      * query. Valid paths would look like <code>ARTIST_NAME</code>,
446      * <code>PAINTING_ARRAY.PAINTING_ID</code>, etc.
447      */

448     public void addCustomDbAttribute(String JavaDoc attributePath) {
449         nonNullCustomDbAttributes().add(attributePath);
450     }
451
452     public void addCustomDbAttributes(List JavaDoc attrPaths) {
453         nonNullCustomDbAttributes().addAll(attrPaths);
454     }
455
456     /**
457      * Returns <code>true</code> if there is at least one custom query attribute
458      * specified, otherwise returns <code>false</code> for the case when the query
459      * results will contain only the root entity attributes.
460      * <p>
461      * Note that queries that are fetching custom attributes always return data rows
462      * instead of DataObjects.
463      * </p>
464      */

465     public boolean isFetchingCustomAttributes() {
466         return (root instanceof DbEntity)
467                 || (customDbAttributes != null && !customDbAttributes.isEmpty());
468     }
469
470     /**
471      * @since 1.2
472      */

473     public PrefetchTreeNode getPrefetchTree() {
474         return selectInfo.getPrefetchTree();
475     }
476
477     /**
478      * @since 1.2
479      */

480     public void setPrefetchTree(PrefetchTreeNode prefetchTree) {
481         selectInfo.setPrefetchTree(prefetchTree);
482     }
483
484     /**
485      * Adds a prefetch with specified relationship path to the query.
486      *
487      * @since 1.2 signature changed to return created PrefetchTreeNode.
488      */

489     public PrefetchTreeNode addPrefetch(String JavaDoc prefetchPath) {
490         return selectInfo.addPrefetch(prefetchPath, PrefetchTreeNode.UNDEFINED_SEMANTICS);
491     }
492
493     /**
494      * Clears all stored prefetch paths.
495      */

496     public void clearPrefetches() {
497         selectInfo.clearPrefetches();
498     }
499
500     /**
501      * Removes prefetch.
502      *
503      * @since 1.1
504      */

505     public void removePrefetch(String JavaDoc prefetchPath) {
506         selectInfo.removePrefetch(prefetchPath);
507     }
508
509     /**
510      * Returns <code>true</code> if this query should produce a list of data rows as
511      * opposed to DataObjects, <code>false</code> for DataObjects. This is a hint to
512      * QueryEngine executing this query.
513      */

514     public boolean isFetchingDataRows() {
515         return this.isFetchingCustomAttributes() || selectInfo.isFetchingDataRows();
516     }
517
518     /**
519      * Sets query result type. If <code>flag</code> parameter is <code>true</code>,
520      * then results will be in the form of data rows.
521      * <p>
522      * <i>Note that if <code>isFetchingCustAttributes()</code> returns <code>true</code>,
523      * this setting has no effect, and data rows are always fetched. </i>
524      * </p>
525      */

526     public void setFetchingDataRows(boolean flag) {
527         selectInfo.setFetchingDataRows(flag);
528     }
529
530     /**
531      * Returns refresh policy of this query. Default is <code>true</code>.
532      *
533      * @since 1.1
534      */

535     public boolean isRefreshingObjects() {
536         return selectInfo.isRefreshingObjects();
537     }
538
539     /**
540      * @since 1.1
541      */

542     public void setRefreshingObjects(boolean flag) {
543         selectInfo.setRefreshingObjects(flag);
544     }
545
546     /**
547      * @since 1.1
548      */

549     public String JavaDoc getCachePolicy() {
550         return selectInfo.getCachePolicy();
551     }
552
553     /**
554      * @since 1.1
555      */

556     public void setCachePolicy(String JavaDoc policy) {
557         this.selectInfo.setCachePolicy(policy);
558     }
559
560     /**
561      * @since 3.0
562      */

563     public String JavaDoc[] getCacheGroups() {
564         return selectInfo.getCacheGroups();
565     }
566
567     /**
568      * @since 3.0
569      */

570     public void setCacheGroups(String JavaDoc[] cachGroups) {
571         this.selectInfo.setCacheGroups(cachGroups);
572     }
573
574     /**
575      * Returns the fetchLimit.
576      *
577      * @return int
578      */

579     public int getFetchLimit() {
580         return selectInfo.getFetchLimit();
581     }
582
583     /**
584      * Sets the fetchLimit.
585      *
586      * @param fetchLimit The fetchLimit to set
587      */

588     public void setFetchLimit(int fetchLimit) {
589         this.selectInfo.setFetchLimit(fetchLimit);
590     }
591
592     /** Setter for query's parent entity qualifier. */
593     public void setParentQualifier(Expression parentQualifier) {
594         this.parentQualifier = parentQualifier;
595     }
596
597     /** Getter for query parent entity qualifier. */
598     public Expression getParentQualifier() {
599         return parentQualifier;
600     }
601
602     /**
603      * Adds specified parent entity qualifier to the existing parent entity qualifier
604      * joining it using "AND".
605      */

606     public void andParentQualifier(Expression e) {
607         parentQualifier = (parentQualifier != null) ? parentQualifier.andExp(e) : e;
608     }
609
610     /**
611      * Adds specified parent entity qualifier to the existing qualifier joining it using
612      * "OR".
613      */

614     public void orParentQualifier(Expression e) {
615         parentQualifier = (parentQualifier != null) ? parentQualifier.orExp(e) : e;
616     }
617
618     /**
619      * Returns the name of parent ObjEntity.
620      *
621      * @return String
622      */

623     public String JavaDoc getParentObjEntityName() {
624         return parentObjEntityName;
625     }
626
627     /**
628      * Sets the name of parent ObjEntity. If query's root ObjEntity maps to a derived
629      * entity in the DataMap, this query qualifier will resolve to a HAVING clause of an
630      * SQL statement. To allow fine tuning the query before applying GROUP BY and HAVING,
631      * callers can setup the name of parent ObjEntity and parent qualifier that will be
632      * used to create WHERE clause preceeding GROUP BY.
633      * <p>
634      * For instance this is helpful to qualify the fetch on a related entity attributes,
635      * since HAVING does not allow joins.
636      * </p>
637      *
638      * @param parentObjEntityName The parentObjEntityName to set
639      */

640     public void setParentObjEntityName(String JavaDoc parentObjEntityName) {
641         this.parentObjEntityName = parentObjEntityName;
642     }
643
644     /**
645      * Returns <code>true</code> if this query has an extra qualifier that uses a parent
646      * entity of the query root entity for additional result filtering.
647      */

648     public boolean isQualifiedOnParent() {
649         return getParentObjEntityName() != null && parentQualifier != null;
650     }
651
652     /**
653      * Returns <code>pageSize</code> property. Page size is a hint telling Cayenne
654      * QueryEngine that query result should use paging instead of reading the whole result
655      * in the memory.
656      *
657      * @return int
658      */

659     public int getPageSize() {
660         return selectInfo.getPageSize();
661     }
662
663     /**
664      * Sets <code>pageSize</code> property.
665      *
666      * @param pageSize The pageSize to set
667      */

668     public void setPageSize(int pageSize) {
669         selectInfo.setPageSize(pageSize);
670     }
671
672     /**
673      * Returns true if objects fetched via this query should be fully resolved according
674      * to the inheritance hierarchy.
675      *
676      * @since 1.1
677      */

678     public boolean isResolvingInherited() {
679         return selectInfo.isResolvingInherited();
680     }
681
682     /**
683      * Sets whether the objects fetched via this query should be fully resolved according
684      * to the inheritance hierarchy.
685      *
686      * @since 1.1
687      */

688     public void setResolvingInherited(boolean b) {
689         selectInfo.setResolvingInherited(b);
690     }
691
692     /**
693      * Returns a list that internally stores custom db attributes, creating it on demand.
694      *
695      * @since 1.2
696      */

697     List JavaDoc nonNullCustomDbAttributes() {
698         if (customDbAttributes == null) {
699             customDbAttributes = new ArrayList JavaDoc();
700         }
701
702         return customDbAttributes;
703     }
704
705     /**
706      * Returns a list that internally stores orderings, creating it on demand.
707      *
708      * @since 1.2
709      */

710     List JavaDoc nonNullOrderings() {
711         if (orderings == null) {
712             orderings = new ArrayList JavaDoc();
713         }
714
715         return orderings;
716     }
717 }
718
Popular Tags