KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > query > SQLTemplate


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.query;
58
59 import java.util.Collection JavaDoc;
60 import java.util.Collections JavaDoc;
61 import java.util.HashMap JavaDoc;
62 import java.util.Iterator JavaDoc;
63 import java.util.Map JavaDoc;
64
65 import org.apache.commons.collections.IteratorUtils;
66 import org.apache.commons.collections.Transformer;
67 import org.objectstyle.cayenne.map.DataMap;
68 import org.objectstyle.cayenne.map.DbEntity;
69 import org.objectstyle.cayenne.map.ObjEntity;
70 import org.objectstyle.cayenne.map.Procedure;
71 import org.objectstyle.cayenne.map.QueryBuilder;
72 import org.objectstyle.cayenne.util.XMLEncoder;
73 import org.objectstyle.cayenne.util.XMLSerializable;
74
75 /**
76  * A generic raw SQL query that can be either a DML/DDL or a select.
77  * <p>
78  * <strong>Template Script </strong>
79  * </p>
80  * <p>
81  * SQLTemplate stores a dynamic template for the SQL query that supports parameters and
82  * customization using Velocity scripting language. The most straightforward use of
83  * scripting abilities is to build parameterized queries. For example:
84  * </p>
85  *
86  * <pre>
87  * SELECT ID, NAME FROM SOME_TABLE WHERE NAME LIKE $a
88  * </pre>
89  *
90  * <p>
91  * Another area where scripting is needed is "dynamic SQL" - SQL that changes its
92  * structure depending on parameter values. E.g. if a value is null, a string
93  * <code>"COLUMN_X = ?"</code> must be replaced with <code>"COLUMN_X IS NULL"</code>.
94  * </p>
95  * <p>
96  * <strong>Customizing Template by DB.</strong>
97  * </p>
98  * <p>
99  * SQLTemplate has a {@link #getDefaultTemplate() default template script}, but also it
100  * allows to configure multiple templates and switch them dynamically. This way a single
101  * query can have multiple "dialects" specific to a given database.
102  * </p>
103  * <p>
104  * <strong>Parameter Sets </strong>
105  * </p>
106  * <p>
107  * SQLTemplate supports multiple sets of parameters, so a single query can be executed
108  * multiple times with different parameters. "Scrolling" through parameter list is done by
109  * calling {@link #parametersIterator()}. This iterator goes over parameter sets,
110  * returning a Map on each call to "next()"
111  * </p>
112  *
113  * @since 1.1
114  * @author Andrei Adamchik
115  */

116 public class SQLTemplate extends AbstractQuery implements GenericSelectQuery,
117         ParameterizedQuery, XMLSerializable {
118
119     private static final Transformer nullMapTransformer = new Transformer() {
120
121         public Object JavaDoc transform(Object JavaDoc input) {
122             return (input != null) ? input : Collections.EMPTY_MAP;
123         }
124     };
125
126     protected SelectExecutionProperties selectProperties = new SelectExecutionProperties();
127     protected String JavaDoc defaultTemplate;
128     protected Map JavaDoc templates;
129     protected Map JavaDoc[] parameters;
130     protected boolean selecting;
131
132     /**
133      * Creates an empty SQLTemplate.
134      */

135     public SQLTemplate(boolean selecting) {
136         setSelecting(selecting);
137     }
138
139     public SQLTemplate(DataMap rootMap, String JavaDoc defaultTemplate, boolean selecting) {
140         setDefaultTemplate(defaultTemplate);
141         setSelecting(selecting);
142         setRoot(rootMap);
143     }
144
145     public SQLTemplate(ObjEntity rootEntity, String JavaDoc defaultTemplate, boolean selecting) {
146         setDefaultTemplate(defaultTemplate);
147         setSelecting(selecting);
148         setRoot(rootEntity);
149     }
150
151     public SQLTemplate(Class JavaDoc rootClass, String JavaDoc defaultTemplate, boolean selecting) {
152         setDefaultTemplate(defaultTemplate);
153         setSelecting(selecting);
154         setRoot(rootClass);
155     }
156
157     public SQLTemplate(DbEntity rootEntity, String JavaDoc defaultTemplate, boolean selecting) {
158         setDefaultTemplate(defaultTemplate);
159         setSelecting(selecting);
160         setRoot(rootEntity);
161     }
162
163     public SQLTemplate(String JavaDoc objEntityName, String JavaDoc defaultTemplate, boolean selecting) {
164         setSelecting(selecting);
165         setRoot(objEntityName);
166         setDefaultTemplate(defaultTemplate);
167     }
168
169     /**
170      * Calls "makeSQL" on the visitor.
171      *
172      * @since 1.2
173      */

174     public SQLAction createSQLAction(SQLActionVisitor visitor) {
175         return visitor.sqlAction(this);
176     }
177
178     /**
179      * Prints itself as XML to the provided PrintWriter.
180      *
181      * @since 1.1
182      */

183     public void encodeAsXML(XMLEncoder encoder) {
184         encoder.print("<query name=\"");
185         encoder.print(getName());
186         encoder.print("\" factory=\"");
187         encoder.print("org.objectstyle.cayenne.map.SQLTemplateBuilder");
188
189         String JavaDoc rootString = null;
190         String JavaDoc rootType = null;
191
192         if (root instanceof String JavaDoc) {
193             rootType = QueryBuilder.OBJ_ENTITY_ROOT;
194             rootString = root.toString();
195         }
196         else if (root instanceof ObjEntity) {
197             rootType = QueryBuilder.OBJ_ENTITY_ROOT;
198             rootString = ((ObjEntity) root).getName();
199         }
200         else if (root instanceof DbEntity) {
201             rootType = QueryBuilder.DB_ENTITY_ROOT;
202             rootString = ((DbEntity) root).getName();
203         }
204         else if (root instanceof Procedure) {
205             rootType = QueryBuilder.PROCEDURE_ROOT;
206             rootString = ((Procedure) root).getName();
207         }
208         else if (root instanceof Class JavaDoc) {
209             rootType = QueryBuilder.JAVA_CLASS_ROOT;
210             rootString = ((Class JavaDoc) root).getName();
211         }
212         else if (root instanceof DataMap) {
213             rootType = QueryBuilder.DATA_MAP_ROOT;
214             rootString = ((DataMap) root).getName();
215         }
216
217         if (rootType != null) {
218             encoder.print("\" root=\"");
219             encoder.print(rootType);
220             encoder.print("\" root-name=\"");
221             encoder.print(rootString);
222         }
223
224         if (!selecting) {
225             encoder.print("\" selecting=\"false");
226         }
227
228         encoder.println("\">");
229
230         encoder.indent(1);
231
232         selectProperties.encodeAsXML(encoder);
233
234         // encode default SQL
235
if (defaultTemplate != null) {
236             encoder.print("<sql><![CDATA[");
237             encoder.print(defaultTemplate);
238             encoder.println("]]></sql>");
239         }
240
241         // encode adapter SQL
242
if (templates != null && !templates.isEmpty()) {
243             Iterator JavaDoc it = templates.entrySet().iterator();
244             while (it.hasNext()) {
245                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
246                 Object JavaDoc key = entry.getKey();
247                 Object JavaDoc value = entry.getValue();
248
249                 if (key != null && value != null) {
250                     String JavaDoc sql = value.toString().trim();
251                     if (sql.length() > 0) {
252                         encoder.print("<sql adapter-class=\"");
253                         encoder.print(key.toString());
254                         encoder.print("\"><![CDATA[");
255                         encoder.print(sql);
256                         encoder.println("]]></sql>");
257                     }
258                 }
259             }
260         }
261
262         // TODO: support parameter encoding
263

264         encoder.indent(-1);
265         encoder.println("</query>");
266     }
267
268     /**
269      * Initializes query parameters using a set of properties.
270      *
271      * @since 1.1
272      */

273     public void initWithProperties(Map JavaDoc properties) {
274
275         // must init defaults even if properties are empty
276
if (properties == null) {
277             properties = Collections.EMPTY_MAP;
278         }
279
280         selectProperties.initWithProperties(properties);
281     }
282
283     /**
284      * Returns an iterator over parameter sets. Each element returned from the iterator is
285      * a java.util.Map.
286      */

287     public Iterator JavaDoc parametersIterator() {
288         return (parameters == null || parameters.length == 0) ? IteratorUtils
289                 .emptyIterator() : IteratorUtils.transformedIterator(IteratorUtils
290                 .arrayIterator(parameters), nullMapTransformer);
291     }
292
293     /**
294      * Returns the number of parameter sets.
295      */

296     public int parametersSize() {
297         return (parameters != null) ? parameters.length : 0;
298     }
299
300     /**
301      * Returns a new query built using this query as a prototype and a new set of
302      * parameters.
303      */

304     public SQLTemplate queryWithParameters(Map JavaDoc parameters) {
305         return queryWithParameters(new Map JavaDoc[] {
306             parameters
307         });
308     }
309
310     /**
311      * Returns a new query built using this query as a prototype and a new set of
312      * parameters.
313      */

314     public SQLTemplate queryWithParameters(Map JavaDoc[] parameters) {
315         // create a query replica
316
SQLTemplate query = new SQLTemplate(isSelecting());
317
318         query.setLoggingLevel(logLevel);
319         query.setRoot(root);
320         query.setDefaultTemplate(getDefaultTemplate());
321
322         if (templates != null) {
323             query.templates = new HashMap JavaDoc(templates);
324         }
325
326         selectProperties.copyToProperties(query.selectProperties);
327         query.setParameters(parameters);
328
329         // TODO: implement algorithm for building the name based on the original name and
330
// the hashcode of the map of parameters. This way query clone can take advantage
331
// of caching.
332

333         return query;
334     }
335
336     /**
337      * Creates and returns a new SQLTemplate built using this query as a prototype and
338      * substituting template parameters with the values from the map.
339      *
340      * @since 1.1
341      */

342     public Query createQuery(Map JavaDoc parameters) {
343         return queryWithParameters(parameters);
344     }
345
346     public String JavaDoc getCachePolicy() {
347         return selectProperties.getCachePolicy();
348     }
349
350     public void setCachePolicy(String JavaDoc policy) {
351         this.selectProperties.setCachePolicy(policy);
352     }
353
354     public int getFetchLimit() {
355         return selectProperties.getFetchLimit();
356     }
357
358     public void setFetchLimit(int fetchLimit) {
359         this.selectProperties.setFetchLimit(fetchLimit);
360     }
361
362     public int getPageSize() {
363         return selectProperties.getPageSize();
364     }
365
366     public void setPageSize(int pageSize) {
367         selectProperties.setPageSize(pageSize);
368     }
369
370     public void setFetchingDataRows(boolean flag) {
371         selectProperties.setFetchingDataRows(flag);
372     }
373
374     public boolean isFetchingDataRows() {
375         return selectProperties.isFetchingDataRows();
376     }
377
378     public boolean isRefreshingObjects() {
379         return selectProperties.isRefreshingObjects();
380     }
381
382     public void setRefreshingObjects(boolean flag) {
383         selectProperties.setRefreshingObjects(flag);
384     }
385
386     public boolean isResolvingInherited() {
387         return selectProperties.isResolvingInherited();
388     }
389
390     public void setResolvingInherited(boolean b) {
391         selectProperties.setResolvingInherited(b);
392     }
393
394     /**
395      * Returns default SQL template for this query.
396      */

397     public String JavaDoc getDefaultTemplate() {
398         return defaultTemplate;
399     }
400
401     /**
402      * Sets default SQL template for this query.
403      */

404     public void setDefaultTemplate(String JavaDoc string) {
405         defaultTemplate = string;
406     }
407
408     /**
409      * Returns a template for key, or a default template if a template for key is not
410      * found.
411      */

412     public synchronized String JavaDoc getTemplate(String JavaDoc key) {
413         if (templates == null) {
414             return defaultTemplate;
415         }
416
417         String JavaDoc template = (String JavaDoc) templates.get(key);
418         return (template != null) ? template : defaultTemplate;
419     }
420
421     /**
422      * Returns template for key, or null if there is no template configured for this key.
423      * Unlike {@link #getTemplate(String)}this method does not return a default template
424      * as a failover strategy, rather it returns null.
425      */

426     public synchronized String JavaDoc getCustomTemplate(String JavaDoc key) {
427         return (templates != null) ? (String JavaDoc) templates.get(key) : null;
428     }
429
430     /**
431      * Adds a SQL template string for a given key.
432      *
433      * @see #setDefaultTemplate(String)
434      */

435     public synchronized void setTemplate(String JavaDoc key, String JavaDoc template) {
436         if (templates == null) {
437             templates = new HashMap JavaDoc();
438         }
439
440         templates.put(key, template);
441     }
442
443     public synchronized void removeTemplate(String JavaDoc key) {
444         if (templates != null) {
445             templates.remove(key);
446         }
447     }
448
449     /**
450      * Returns a collection of configured template keys.
451      */

452     public synchronized Collection JavaDoc getTemplateKeys() {
453         return (templates != null) ? Collections.unmodifiableCollection(templates
454                 .keySet()) : Collections.EMPTY_LIST;
455     }
456
457     /**
458      * Utility method to get the first set of parameters, since most queries will only
459      * have one.
460      */

461     public Map JavaDoc getParameters() {
462         Map JavaDoc map = (parameters != null && parameters.length > 0) ? parameters[0] : null;
463         return (map != null) ? map : Collections.EMPTY_MAP;
464     }
465
466     /**
467      * Utility method to initialize query with only a single set of parameters. Useful,
468      * since most queries will only have one set. Internally calls
469      * {@link #setParameters(Map[])}.
470      */

471     public void setParameters(Map JavaDoc map) {
472         setParameters(map != null ? new Map JavaDoc[] {
473             map
474         } : null);
475     }
476
477     public void setParameters(Map JavaDoc[] parameters) {
478         this.parameters = parameters;
479     }
480
481     /**
482      * Returns true if SQLTemplate is expected to return a ResultSet.
483      */

484     public boolean isSelecting() {
485         return selecting;
486     }
487
488     /**
489      * Sets whether SQLTemplate is expected to return a ResultSet.
490      */

491     public void setSelecting(boolean b) {
492         selecting = b;
493     }
494
495     /**
496      * Returns a collection of joint prefetches.
497      *
498      * @since 1.2
499      */

500     public Collection JavaDoc getJointPrefetches() {
501         return selectProperties.getJointPrefetches();
502     }
503
504     /**
505      * Adds a joint prefetch.
506      *
507      * @since 1.2
508      */

509     public void addJointPrefetch(String JavaDoc relationshipPath) {
510         selectProperties.addJointPrefetch(relationshipPath);
511     }
512
513     /**
514      * Adds all joint prefetches from a provided collection.
515      *
516      * @since 1.2
517      */

518     public void addJointPrefetches(Collection JavaDoc relationshipPaths) {
519         selectProperties.addJointPrefetches(relationshipPaths);
520     }
521
522     /**
523      * Clears all joint prefetches.
524      *
525      * @since 1.2
526      */

527     public void clearJointPrefetches() {
528         selectProperties.clearJointPrefetches();
529     }
530
531     /**
532      * Removes joint prefetch.
533      *
534      * @since 1.2
535      */

536     public void removeJointPrefetch(String JavaDoc relationshipPath) {
537         selectProperties.removeJointPrefetch(relationshipPath);
538     }
539 }
Popular Tags