KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > spi > persistence > support > sqlstore > query > QueryImpl


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /*
25  * QueryImpl.java
26  *
27  * Created on March 8, 2000
28  */

29
30 package com.sun.jdo.spi.persistence.support.sqlstore.query;
31
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collection JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.ResourceBundle JavaDoc;
36
37 import com.sun.jdo.api.persistence.support.Query;
38 import com.sun.jdo.api.persistence.support.Transaction;
39 import com.sun.jdo.api.persistence.support.JDOException;
40 import com.sun.jdo.api.persistence.support.JDOQueryException;
41 import com.sun.jdo.api.persistence.support.JDOUnsupportedOptionException;
42 import com.sun.jdo.spi.persistence.support.sqlstore.PersistenceManager;
43 import com.sun.jdo.spi.persistence.support.sqlstore.RetrieveDesc;
44 import com.sun.jdo.spi.persistence.utility.I18NHelper;
45 import com.sun.jdo.spi.persistence.utility.logging.Logger;
46 import com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc.JQLC;
47 import com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc.ParameterTable;
48 import com.sun.jdo.spi.persistence.support.sqlstore.ValueFetcher;
49
50 /**
51  *
52  * @author Michael Bouschen
53  * @version 0.1
54  */

55 public class QueryImpl
56     implements Query
57 {
58     /**
59      *
60      */

61     private Class JavaDoc candidateClass;
62
63     /**
64      *
65      */

66     private String JavaDoc filterExpression;
67
68     /**
69      *
70      */

71     private String JavaDoc importDeclarations;
72
73     /**
74      *
75      */

76     private String JavaDoc parameterDeclarations;
77
78     /**
79      *
80      */

81     private String JavaDoc variableDeclarations;
82
83     /**
84      *
85      */

86     private String JavaDoc orderingSpecification;
87
88     /**
89      *
90      */

91     private String JavaDoc resultExpression;
92
93     /**
94      *
95      */

96     private boolean compiled = false;
97
98     /**
99      *
100      */

101     private transient PersistenceManager pm;
102
103     /**
104      *
105      */

106     private transient Collection JavaDoc candidateCollection;
107
108     /**
109      *
110      */

111     private transient boolean ignoreCache;
112
113     /**
114      * Enable relationship fields prefetch for this query.
115      */

116     private transient boolean prefetchEnabled = true;
117
118     /**
119      *
120      */

121     private transient JQLC jqlc;
122
123     /**
124      *
125      */

126     private transient ParameterTable paramtab;
127
128     /**
129      * Flag indicating whtehr this instance was created by serialization.
130      */

131     private transient boolean createdBySerialization = false;
132
133     /**
134      * I18N support
135      */

136     protected final static ResourceBundle JavaDoc messages =
137         I18NHelper.loadBundle(QueryImpl.class);
138
139     /** The logger */
140     private static Logger logger = LogHelperQueryExecute.getLogger();
141
142     /**
143      * Create an empty query instance with no elements.
144      */

145     public QueryImpl(PersistenceManager pm)
146     {
147         if (logger.isLoggable(Logger.FINER))
148             logger.finer("LOG_CreateNewQuery", identity()); //NOI18N
149
this.pm = pm;
150         this.paramtab = new ParameterTable();
151         this.ignoreCache = pm.getPersistenceManagerFactory().getIgnoreCache();
152     }
153
154     /**
155      * Create a new Query using elements from another Query. The other Query
156      * must have been created by the same JDO implementation. It might be active
157      * in a different PersistenceManager or might have been serialized and restored.
158      * @param compiled another Query from the same JDO implementation
159      */

160     public QueryImpl (PersistenceManager pm, Object JavaDoc compiled)
161     {
162         if (logger.isLoggable(Logger.FINER))
163             logger.finer("LOG_CreateNewQueryFromCompiled", identity(), compiled); //NOI18N
164
this.pm = pm;
165         if (compiled == null)
166         {
167             JDOException ex = new JDOQueryException(I18NHelper.getMessage(
168                 messages, "query.queryimpl.init.compiledquery.isnull")); //NOI18N
169
logger.throwing("query.QueryImpl", "<init>", ex); //NOI18N
170
throw ex;
171         }
172
173         if (!(compiled instanceof QueryImpl))
174         {
175             JDOException ex = new JDOQueryException(I18NHelper.getMessage(
176                 messages, "query.queryimpl.init.compiledquery.invalidtype", //NOI18N
177
compiled.getClass().getName()));
178             logger.throwing("query.QueryImpl", "<init>", ex); //NOI18N
179
throw ex;
180         }
181
182         QueryImpl other = (QueryImpl)compiled;
183         this.candidateClass = other.candidateClass;
184         this.filterExpression = other.filterExpression;
185         this.importDeclarations = other.importDeclarations;
186         this.parameterDeclarations = other.parameterDeclarations;
187         this.variableDeclarations = other.variableDeclarations;
188         this.orderingSpecification = other.orderingSpecification;
189         this.resultExpression = other.resultExpression;
190         this.ignoreCache = other.ignoreCache;
191         this.prefetchEnabled = other.prefetchEnabled;
192         this.candidateCollection = null;
193
194         // initialize paramtab, jqlc and compiled
195
if (other.paramtab != null)
196         {
197             this.jqlc = other.jqlc;
198             this.paramtab = new ParameterTable(other.paramtab);
199             this.compiled = other.compiled;
200         }
201         else
202         {
203             // other.paramtab == null means deserialized query =>
204
// - parameter table
205
// - set compiled = false
206
this.jqlc = null;
207             this.paramtab = new ParameterTable();
208             this.compiled = false;
209         }
210     }
211
212     /**
213      * Create a query instance with the candidate class specified.
214      * @param candidateClass the Class of the candidate instances.
215      */

216     public QueryImpl(PersistenceManager pm, Class JavaDoc candidateClass)
217     {
218         this(pm);
219         setClass(candidateClass);
220     }
221
222     /**
223      * Create a query instance with the candidate class and
224      * candidate collection specified.
225      * @param candidateClass the Class of the candidate instances.
226      * @param candidateCollection the Collection of candidate instances.
227      */

228     public QueryImpl(PersistenceManager pm, Class JavaDoc candidateClass, Collection JavaDoc candidateCollection)
229     {
230         this(pm);
231         setClass(candidateClass);
232         setCandidates(candidateCollection);
233     }
234
235     /**
236      * Create a query instance with the candidate class and
237      * filter specified.
238      * @param candidateClass the Class of the candidate instances.
239      * @param filter the Filter for candidate instances.
240      */

241     public QueryImpl(PersistenceManager pm, Class JavaDoc candidateClass, String JavaDoc filter)
242     {
243         this(pm);
244         setClass(candidateClass);
245         setFilter(filter);
246     }
247
248     /**
249      * Create a query instance with the candidate class,
250      * the candidate collection, and filter specified.
251      * @param candidateClass the Class of the candidate instances.
252      * @param candidateCollection the Collection of candidate instances.
253      * @param filter the Filter for candidate instances
254      */

255     public QueryImpl(PersistenceManager pm, Class JavaDoc candidateClass, Collection JavaDoc candidateCollection, String JavaDoc filter)
256     {
257         this(pm);
258         setClass(candidateClass);
259         setCandidates(candidateCollection);
260         setFilter(filter);
261     }
262
263     /**
264      * Bind the candidate class to the query instance.
265      *
266      * The class is used to scope the names in the query filter.
267      * All of the candidate instances will be of this class or subclass.
268      *
269      * @param candidateClass the Class of the candidate instances.
270      */

271     public void setClass(Class JavaDoc candidateClass)
272     {
273         synchronized (this.paramtab)
274         {
275             this.candidateClass = candidateClass;
276             this.compiled = false;
277         }
278     }
279
280     /**
281      * Bind the candidate Collection to the query instance.
282      *
283      * @param pcs the Candidate collection.
284      */

285     public void setCandidates(Collection JavaDoc candidateCollection)
286     {
287         synchronized (this.paramtab)
288         {
289             this.candidateCollection = candidateCollection;
290             // candidateCollection is not part of query compilation =>
291
// do not change compiled flag
292
}
293     }
294
295     /**
296      * Bind the query filter to the query instance.
297      *
298      * The query filter is a Java boolean expression, which tells whether
299      * instances in the candidate collection are to be returned in the result.
300      *
301      * @param filter the query filter.
302      */

303     public void setFilter(String JavaDoc filter)
304     {
305         synchronized (this.paramtab)
306         {
307             this.filterExpression = filter;
308             this.compiled = false;
309         }
310     }
311
312     /**
313      * Bind the import statements to the query instance.
314      * All imports must be declared in the same method call,
315      * and the imports must be separated by semicolons.
316      * The syntax is the same as in the Java language import statement.
317      *
318      * Parameters and unbound variables might come from a different class
319      * from the candidate class, and the names might need to be declared in an
320      * import statement to eliminate ambiguity.
321      *
322      * @param imports import statements separated by semicolons.
323      */

324     public void declareImports(String JavaDoc imports)
325     {
326         synchronized (this.paramtab)
327         {
328             this.importDeclarations = imports;
329             this.compiled = false;
330         }
331     }
332
333     /**
334      * Bind the parameter statements to the query instance.
335      * This method defines the parameter types and names
336      * which will be used by a subsequent execute method.
337      *
338      * The parameter declaration is a String containing one or
339      * more query parameter declarations separated with commas.
340      * It follows the syntax for formal parameters in the Java language.
341      * Each parameter named in the parameter declaration must be bound
342      * to a value when the query is executed.
343      *
344      * @param parameters the list of parameters separated by commas.
345      */

346     public void declareParameters(String JavaDoc parameters)
347     {
348         synchronized (this.paramtab)
349         {
350             this.parameterDeclarations = parameters;
351             this.compiled = false;
352         }
353     }
354
355     /**
356      * Bind the unbound variable statements to the query instance.
357      * This method defines the types and names of variables that will be used
358      * in the filter but not provided as values by the execute method.
359      *
360      * Variables might be used in the filter, and these variables must be
361      * declared with their type. The unbound variable declaration is a
362      * String containing one or more unbound variable declarations separated with
363      * semicolons. It follows the syntax for local variables in the Java language.
364      *
365      * @param variables the variables separated by semicolons.
366      */

367     public void declareVariables(String JavaDoc variables)
368     {
369         synchronized (this.paramtab)
370         {
371             this.variableDeclarations = variables;
372             this.compiled = false;
373         }
374     }
375
376     /**
377      * Bind the ordering statements to the query instance.
378      *
379      * The ordering specification includes a list of expressions
380      * with the ascending/descending indicator.
381      */

382     public void setOrdering(String JavaDoc ordering)
383     {
384         synchronized (this.paramtab)
385         {
386             this.orderingSpecification = ordering;
387             this.compiled = false;
388         }
389     }
390
391     /**
392      * Set the result of the query.
393      * <p>
394      * The query result is an optional keyword distinct followed by a Java
395      * expression, which tells what values are to be returned by the JDO query.
396      * If the result is not specified, then it defaults to "distinct this",
397      * which has the effect of returning the elements of the candidates
398      * that match the filter.
399      */

400     public void setResult(String JavaDoc result)
401     {
402         synchronized (this.paramtab)
403         {
404             this.resultExpression = result;
405             this.compiled = false;
406         }
407     }
408
409     /**
410      * Set the ignoreCache option.
411      *
412      * The ignoreCache option setting specifies whether the query should execute
413      * entirely in the back end, instead of in the cache.
414      * @param ignoreCache the setting of the ignoreCache option.
415      */

416     public void setIgnoreCache(boolean ignoreCache)
417     {
418         synchronized (this.paramtab)
419         {
420             this.ignoreCache = ignoreCache;
421         }
422     }
423
424     /**
425      * Get the ignoreCache option setting.
426      * @return the ignoreCache option setting.
427      * @see #setIgnoreCache
428      */

429     public boolean getIgnoreCache()
430     {
431         return ignoreCache;
432     }
433
434     /** Sets the prefetchEnabled option.
435      *
436      * The prefetchEnabled option specifies whether prefetch of relationship
437      * fields should be enabled for this query. The prefetch is enabled by
438      * default if such fields are part of DFG. A user needs to explicitely
439      * disable prefetch for any particular query if the related instances
440      * will not be used in this transaction.
441      *
442      * @param prefetchEnabled the setting of the prefetchEnabled option.
443      */

444     public void setPrefetchEnabled(boolean prefetchEnabled)
445     {
446         synchronized (this.paramtab)
447         {
448             this.prefetchEnabled = prefetchEnabled;
449             this.compiled = false;
450         }
451     }
452
453     /**
454      * Verify the elements of the query and provide a hint to the query to
455      * prepare and optimize an execution plan.
456      */

457     public void compile()
458     {
459         synchronized (this.paramtab)
460         {
461             if (!this.compiled)
462             {
463                 if (logger.isLoggable(Logger.FINER))
464                     logger.finer("LOG_CompileQuery", this); //NOI18N
465
// create new query compiler instance
466
jqlc = new JQLC();
467                 // define the query parts including syntax checks
468
jqlc.setClass(candidateClass);
469                 jqlc.declareImports(importDeclarations);
470                 jqlc.declareParameters(parameterDeclarations);
471                 jqlc.declareVariables(variableDeclarations);
472                 jqlc.setOrdering(orderingSpecification);
473                 jqlc.setResult(resultExpression);
474                 jqlc.setFilter(filterExpression);
475                 jqlc.setPrefetchEnabled(prefetchEnabled);
476
477                 // semantic analysis
478
jqlc.semanticCheck(paramtab);
479                 this.compiled = true;
480             }
481         }
482     }
483
484     /**
485      * Execute the query and return the filtered Collection.
486      * @return the filtered Collection.
487      * @see #executeWithArray (Object[] parameters)
488      */

489     public Object JavaDoc execute()
490     {
491         synchronized (this.paramtab)
492         {
493             compile();
494             ParameterTable params = new ParameterTable(paramtab);
495             params.initValueHandling();
496             params.checkUnboundParams();
497             return doExecute(params);
498         }
499     }
500
501     /**
502      * Execute the query and return the filtered Collection.
503      * @return the filtered Collection.
504      * @see #executeWithArray (Object[] parameters)
505      * @param p1 the value of the first parameter declared.
506      */

507     public Object JavaDoc execute(Object JavaDoc p1)
508     {
509         Object JavaDoc [] params = new Object JavaDoc[1];
510         params[0] = p1;
511         return executeWithArray(params);
512     }
513
514     /**
515      * Execute the query and return the filtered Collection.
516      * @return the filtered Collection.
517      * @see #executeWithArray (Object[] parameters)
518      * @param p1 the value of the first parameter declared.
519      * @param p2 the value of the second parameter declared.
520      */

521     public Object JavaDoc execute(Object JavaDoc p1, Object JavaDoc p2)
522     {
523         Object JavaDoc [] params = new Object JavaDoc[2];
524         params[0] = p1;
525         params[1] = p2;
526         return executeWithArray(params);
527     }
528
529     /**
530      * Execute the query and return the filtered Collection.
531      * @return the filtered Collection.
532      * @see #executeWithArray (Object[] parameters)
533      * @param p1 the value of the first parameter declared.
534      * @param p2 the value of the second parameter declared.
535      * @param p3 the value of the third parameter declared.
536      */

537     public Object JavaDoc execute(Object JavaDoc p1, Object JavaDoc p2, Object JavaDoc p3)
538     {
539         Object JavaDoc [] params = new Object JavaDoc[3];
540         params[0] = p1;
541         params[1] = p2;
542         params[2] = p3;
543         return executeWithArray(params);
544     }
545
546     /**
547      * Execute the query and return the filtered Collection.
548      * @return the filtered Collection.
549      * @see #executeWithArray (Object[] parameters)
550      * @param parameters the Map containing all of the parameters.
551      */

552     public Object JavaDoc executeWithMap (Map JavaDoc parameters)
553     {
554         synchronized (this.paramtab)
555         {
556             compile();
557             ParameterTable params = new ParameterTable(paramtab);
558             params.initValueHandling();
559             params.setValues(parameters);
560             params.checkUnboundParams();
561             return doExecute(params);
562         }
563     }
564
565     /**
566      * Execute the query and return the filtered Collection.
567      *
568      * <P>The execution of the query obtains the values of the parameters and
569      * matches them against the declared parameters in order. The type of
570      * the declared parameters must match the type of the passed parameters,
571      * except that the passed parameters might need to be unwrapped to get
572      * their primitive values.
573      *
574      * <P>The filter, import, declared parameters, declared variables, and
575      * ordering statements are verified for consistency.
576      *
577      * <P>Each element in the candidate Collection is examined to see that it
578      * is assignment compatible to the Class of the query. It is then evaluated
579      * by the boolean expression of the filter. The element passes the filter
580      * if there exist unique values for all variables for which the filter
581      * expression evaluates to true.
582      * @return the filtered Collection.
583      * @param parameters the Object array with all of the parameters.
584      */

585     public Object JavaDoc executeWithArray (Object JavaDoc[] parameters)
586     {
587         synchronized (this.paramtab)
588         {
589             compile();
590             ParameterTable params = new ParameterTable(paramtab);
591             params.initValueHandling();
592             params.setValues(parameters);
593             params.checkUnboundParams();
594             return doExecute(params);
595         }
596
597     }
598
599     /**
600      * Get the PersistenceManager associated with this Query.
601      *
602      * <P>If this Query has no PersistenceManager return null.
603      * @return the PersistenceManager associated with this Query.
604      */

605     public com.sun.jdo.api.persistence.support.PersistenceManager getPersistenceManager()
606     {
607         return (pm == null)? null : pm.getCurrentWrapper();
608     }
609     
610     /**
611      * This method clears the PersistenceManager and the candidateCollection fields.
612      * Then this query instance cannot be executed anymore, but it might be used to
613      * create a new equivalent query instance by passing this query instance to
614      * PersistenceManager newQuery method taking a compiled query.
615      * <p>
616      * This method effectively disconnects the PersistenceManager allowing it to be
617      * garbage collected.
618      */

619     public void clearPersistenceManager()
620     {
621         this.pm = null;
622         this.candidateCollection = null;
623     }
624     
625     /**
626      * Internal method called by execute, executeWithArray, executeWithMap.
627      * - calls the code generation of the query compiler
628      * - flushes updates
629      * - executes the RetrieveDesc returned by the code generation
630      * - resets the compiler
631      */

632     private Object JavaDoc doExecute(ParameterTable params)
633     {
634         Object JavaDoc result = null;
635         RetrieveDesc rd = null;
636
637         try
638         {
639             // We need to make sure that no parallel thread closes the pm =>
640
// try to get a shared lock for the pm. Today, the pm impl does
641
// not allow to promote a shared lock into a exclusive lock. Thus
642
// we need to get an exclusive lock here. Otherwise pm.internalFlush
643
// runs into a deadlock, because it tries to get a exclusive lock.
644
// This code need to be changed to get a ahared lock as soon as
645

646             // The next line might result in a NPE, if pm is closed or if the
647
// query instance was deserialized. Please note, I cannot check the
648
// pm and then get the lock, because the pm might be closed in
649
// parallel. Then subsequent uses of pm in doexecute would fail.
650
pm.acquireExclusiveLock();
651         }
652         catch (NullPointerException JavaDoc npe)
653         {
654             // NPE means pm is closed or query instance was serialized.
655
String JavaDoc key = (createdBySerialization ?
656                           "query.queryimpl.doexecute.notboundtopm" : //NOI18N
657
"query.queryimpl.doexecute.pmclosed"); //NOI18N
658
JDOException ex = new JDOQueryException(
659                 I18NHelper.getMessage(messages, key));
660             logger.throwing("query.QueryImpl", "compile", ex); //NOI18N
661
throw ex;
662         }
663
664         try
665         {
666             checkCandidates();
667             // call the code generation
668
rd = jqlc.codeGen(pm, params);
669             // flush changes (inserts, updates, deletes) to the datastore
670
flush();
671             if (logger.isLoggable(Logger.FINER))
672                 logger.finer("LOG_ExecuteQuery", this, params.getValues()); //NOI18N
673
// Note, the RetrieveDesc returned by the code generation is null
674
// in the case of a query having a false filter =>
675
// do not go to the datastore, but return an emtpy collection
676
result = (rd != null) ? pm.retrieve(rd, params.getValueFetcher()) : new ArrayList JavaDoc();
677         }
678         finally
679         {
680             // Note, the following stmt needs to be replaced by
681
// pm.releaseSharedLock, as soon as the pm supports promoting a
682
// shared lock into an exclusive lock.
683
pm.releaseExclusiveLock();
684         }
685
686         return result;
687     }
688
689     /**
690      * This method checks a valid candidates setting for this query.
691      */

692     private void checkCandidates()
693     {
694         if ((candidateCollection == null) && (candidateClass != null))
695         {
696             // Set candidateCollection to the extent of the candidate class, if
697
// candidateCollection is not specified. Note, the JDO spec defines
698
// subclasses=true as the default, but since this is not supported
699
// right now, I set it to subclasses=false.
700
candidateCollection = pm.getExtent(candidateClass, false);
701         }
702         else {
703             jqlc.checkCandidates(candidateClass, candidateCollection);
704         }
705     }
706
707     /**
708      *
709      */

710     private void flush()
711     {
712         Transaction tx = pm.currentTransaction();
713         // flush updates to the database,
714
// - if the is a transaction active and
715
// - if transaction is not optimistic
716
// - if ignoreCache is false
717
if ((tx != null) && tx.isActive() &&
718             !tx.getOptimistic() && !this.ignoreCache)
719         {
720             pm.internalFlush();
721         }
722     }
723
724     /** Returns a string representation of the object. */
725     public String JavaDoc toString()
726     {
727         StringBuffer JavaDoc repr = new StringBuffer JavaDoc();
728         repr.append("QueryImpl("); //NOI18N
729
repr.append("candidateClass: "); //NOI18N
730
repr.append(candidateClass);
731         if (importDeclarations != null) {
732             repr.append(", imports: "); //NOI18N
733
repr.append(importDeclarations);
734         }
735         if (parameterDeclarations != null) {
736             repr.append(", parameters: "); //NOI18N
737
repr.append(parameterDeclarations);
738         }
739         if (variableDeclarations != null) {
740             repr.append(", variables: "); //NOI18N
741
repr.append(variableDeclarations);
742         }
743         if (filterExpression != null) {
744             repr.append(", filter: "); //NOI18N
745
repr.append(filterExpression);
746         }
747         if (orderingSpecification != null) {
748             repr.append(", ordering: "); //NOI18N
749
repr.append(orderingSpecification);
750         }
751         if (resultExpression != null) {
752             repr.append(", result: "); //NOI18N
753
repr.append(resultExpression);
754         }
755         repr.append(", prefetchEnabled: "); //NOI18N
756
repr.append(prefetchEnabled);
757         repr.append(", identity: "); //NOI18N
758
repr.append(identity());
759         repr.append(")"); //NOI18N
760
return repr.toString();
761     }
762
763     /** */
764     private String JavaDoc identity()
765     {
766         return "QueryImpl@" + System.identityHashCode(this); //NOI18N
767
}
768     
769
770     /**
771      * Define readObject to initialize the transient field paramtab after deserialization.
772      * This object is used for synchronization, thus it cannot be null.
773      */

774     private void readObject(java.io.ObjectInputStream JavaDoc in)
775         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc
776     {
777         in.defaultReadObject();
778         this.paramtab = new ParameterTable();
779         this.createdBySerialization = true;
780     }
781
782 }
783
Popular Tags