0001: /**********************************************************************
0002: Copyright (c) 2002 Kelly Grizzle (TJDO) and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015: Contributors:
0016: 2003 Andy Jefferson - commented and localised
0017: 2003 Andy Jefferson - coding standards
0018: 2004 Andy Jefferson - addition of setUnique() and setRange()
0019: 2005 Andy Jefferson - added timeout support.
0020: ...
0021: **********************************************************************/package org.jpox.store.query;
0022:
0023: import java.io.Serializable;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.Map;
0030:
0031: import org.jpox.FetchPlan;
0032: import org.jpox.ObjectManager;
0033: import org.jpox.exceptions.ClassNotResolvedException;
0034: import org.jpox.exceptions.JPOXException;
0035: import org.jpox.exceptions.JPOXUserException;
0036: import org.jpox.jdo.exceptions.TransactionNotActiveException;
0037: import org.jpox.management.runtime.QueryRuntime;
0038: import org.jpox.metadata.QueryResultMetaData;
0039: import org.jpox.store.Extent;
0040: import org.jpox.store.StoreManager;
0041: import org.jpox.store.exceptions.QueryNotUniqueException;
0042: import org.jpox.util.Imports;
0043: import org.jpox.util.Localiser;
0044: import org.jpox.util.StringUtils;
0045:
0046: /**
0047: * Abstract implementation for all queries in JPOX.
0048: * Implementations of JDOQL, SQL, JPQL, etc should extend this.
0049: *
0050: * @version $Revision: 1.82 $
0051: */
0052: public abstract class Query implements Serializable {
0053: /** Localiser for messages. */
0054: protected static final Localiser LOCALISER = Localiser
0055: .getInstance("org.jpox.store.Localisation");
0056:
0057: /** Object Manager managing this query. */
0058: protected final transient ObjectManager om;
0059:
0060: public static final short SELECT = 0;
0061: public static final short BULK_UPDATE = 1;
0062: public static final short BULK_DELETE = 2;
0063:
0064: /** Type of query. */
0065: protected short type = SELECT;
0066:
0067: /** The candidate class for this query. */
0068: protected Class candidateClass;
0069:
0070: /** Name of the candidate class (used when specified via Single-String). */
0071: protected String candidateClassName;
0072:
0073: /** Whether to allow subclasses of the candidate class be returned. */
0074: protected boolean subclasses = true;
0075:
0076: /** Whether to return single value, or collection from the query. */
0077: protected boolean unique = false;
0078:
0079: /** From clause of the query (optional). */
0080: protected transient String from = null;
0081:
0082: /** Specification of the result of the query e.g aggregates etc. */
0083: protected String result = null;
0084:
0085: /** User-defined class that best represents the results of a query. Populated if specified via setResultClass(). */
0086: protected Class resultClass = null;
0087:
0088: /** Name of user-defined class to use as the result class. */
0089: protected String resultClassName = null;
0090:
0091: /** The filter for the query. */
0092: protected String filter;
0093:
0094: /** Any import declarations for the types used in the query. */
0095: protected String imports;
0096:
0097: /** Any explicit variables defined for this query. */
0098: protected String explicitVariables;
0099:
0100: /** Any explicit parameters defined for this query. */
0101: protected String explicitParameters;
0102:
0103: /** Ordering clause for the query, governing the order objects are returned. */
0104: protected String ordering;
0105:
0106: /** Grouping clause for the query, for use with aggregate expressions. */
0107: protected String grouping;
0108:
0109: /** Having clause for the query */
0110: protected String having;
0111:
0112: /** String form of the query result range. Only populated if specified via String. */
0113: protected String range;
0114:
0115: /** Query result range start position (included). Either specified, or compiled from "range". */
0116: protected long fromInclNo = 0;
0117:
0118: /** Query result range end position (excluded). Either specified, or compiled from "range". */
0119: protected long toExclNo = Long.MAX_VALUE;
0120:
0121: /** Whether the query can be modified */
0122: protected boolean unmodifiable = false;
0123:
0124: /** Whether to ignore dirty instances in the query. */
0125: protected boolean ignoreCache = false;
0126:
0127: /** Fetch Plan to use for the query. */
0128: private FetchPlan fetchPlan;
0129:
0130: /** Any JPOX extensions */
0131: protected Map extensions = null;
0132:
0133: /** Any subqueries, keyed by the variable name that they represent. */
0134: protected Map subqueries = null;
0135:
0136: /** State variable for the compilation state */
0137: protected transient boolean isCompiled = false;
0138:
0139: /** Map of implicit parameters, keyed by the name/number(as String). */
0140: protected transient HashMap implicitParameters = null;
0141:
0142: /** The imports definition. */
0143: protected transient Imports parsedImports = null;
0144:
0145: /** Array of (explicit) parameter names. */
0146: protected transient String[] parameterNames = null;
0147:
0148: /**
0149: * All query results obtained from this query.
0150: * This is required because the query can be executed multiple times changing
0151: * the input slightly each time for example.
0152: */
0153: protected transient HashSet queryResults = new HashSet();
0154:
0155: /**
0156: * Constructs a new query instance that uses the given persistence manager.
0157: * @param om The ObjectManager for this query.
0158: */
0159: public Query(ObjectManager om) {
0160: this .om = om;
0161: if (om == null) {
0162: // OM should always be provided so throw exception if null
0163: throw new JPOXUserException(LOCALISER.msg("021012"));
0164: }
0165:
0166: // Inherit IgnoreCache from PersistenceManager (JDO 1.0 $12.6.3)
0167: this .ignoreCache = om.getIgnoreCache();
0168: }
0169:
0170: /**
0171: * Utility to remove any previous compilation of this Query.
0172: **/
0173: protected void discardCompiled() {
0174: isCompiled = false;
0175: parsedImports = null;
0176: parameterNames = null;
0177: }
0178:
0179: /**
0180: * Equality operator.
0181: * @param obj Object to compare against
0182: * @return Whether this and the other object are equal.
0183: **/
0184: public boolean equals(Object obj) {
0185: if (obj == this ) {
0186: return true;
0187: }
0188:
0189: if (!(obj instanceof Query)) {
0190: return false;
0191: }
0192:
0193: Query q = (Query) obj;
0194:
0195: if (candidateClass == null) {
0196: if (q.candidateClass != null) {
0197: return false;
0198: }
0199: } else if (!candidateClass.equals(q.candidateClass)) {
0200: return false;
0201: }
0202:
0203: if (filter == null) {
0204: if (q.filter != null) {
0205: return false;
0206: }
0207: } else if (!filter.equals(q.filter)) {
0208: return false;
0209: }
0210:
0211: if (imports == null) {
0212: if (q.imports != null) {
0213: return false;
0214: }
0215: } else if (!imports.equals(q.imports)) {
0216: return false;
0217: }
0218:
0219: if (explicitParameters == null) {
0220: if (q.explicitParameters != null) {
0221: return false;
0222: }
0223: } else if (!explicitParameters.equals(q.explicitParameters)) {
0224: return false;
0225: }
0226:
0227: if (explicitVariables == null) {
0228: if (q.explicitVariables != null) {
0229: return false;
0230: }
0231: } else if (!explicitVariables.equals(q.explicitVariables)) {
0232: return false;
0233: }
0234:
0235: if (unique != q.unique) {
0236: return false;
0237: }
0238:
0239: if (unmodifiable != q.unmodifiable) {
0240: return false;
0241: }
0242:
0243: if (resultClass != q.resultClass) {
0244: return false;
0245: }
0246:
0247: if (grouping == null) {
0248: if (q.grouping != null) {
0249: return false;
0250: }
0251: } else if (!grouping.equals(q.grouping)) {
0252: return false;
0253: }
0254:
0255: if (ordering == null) {
0256: if (q.ordering != null) {
0257: return false;
0258: }
0259: } else if (!ordering.equals(q.ordering)) {
0260: return false;
0261: }
0262:
0263: return true;
0264: }
0265:
0266: /**
0267: * Hashcode generator.
0268: * @return The Hashcode for this object.
0269: */
0270: public int hashCode() {
0271: return (candidateClass == null ? 0 : candidateClass.hashCode())
0272: ^ (result == null ? 0 : result.hashCode())
0273: ^ (filter == null ? 0 : filter.hashCode())
0274: ^ (imports == null ? 0 : imports.hashCode())
0275: ^ (explicitParameters == null ? 0 : explicitParameters
0276: .hashCode())
0277: ^ (explicitVariables == null ? 0 : explicitVariables
0278: .hashCode())
0279: ^ (resultClass == null ? 0 : resultClass.hashCode())
0280: ^ (grouping == null ? 0 : grouping.hashCode())
0281: ^ (having == null ? 0 : having.hashCode())
0282: ^ (ordering == null ? 0 : ordering.hashCode())
0283: ^ (range == null ? 0 : range.hashCode());
0284: }
0285:
0286: /**
0287: * Accessor for the query type.
0288: * @return The query type
0289: */
0290: public short getType() {
0291: return type;
0292: }
0293:
0294: /**
0295: * Mutator to set the query type.
0296: * @param type The query type
0297: */
0298: public void setType(short type) {
0299: if (type == SELECT || type == BULK_UPDATE
0300: || type == BULK_DELETE) {
0301: this .type = type;
0302: } else {
0303: throw new JPOXUserException(
0304: "JPOX Query only supports types of SELECT, BULK_UPDATE, BULK_DELETE : unknown value "
0305: + type);
0306: }
0307: }
0308:
0309: /**
0310: * Accessor for the StoreManager associated with this Query.
0311: * @return the StoreManager associated with this Query.
0312: */
0313: public StoreManager getStoreManager() {
0314: return om.getStoreManager();
0315: }
0316:
0317: /**
0318: * Accessor for the PersistenceManager associated with this Query.
0319: * @return the PersistenceManager associated with this Query.
0320: * @see javax.jdo.Query#getPersistenceManager
0321: */
0322: public ObjectManager getObjectManager() {
0323: return om;
0324: }
0325:
0326: /**
0327: * Add a vendor-specific extension this query. The key and value
0328: * are not standard.
0329: * An implementation must ignore keys that are not recognized.
0330: * @param key the extension key
0331: * @param value the extension value
0332: * @since JDO 2.0
0333: */
0334: public void addExtension(String key, Object value) {
0335: if (extensions == null) {
0336: extensions = new HashMap();
0337: }
0338: extensions.put(key, value);
0339: }
0340:
0341: /**
0342: * Set multiple extensions, or use null to clear extensions.
0343: * Map keys and values are not standard.
0344: * An implementation must ignore entries that are not recognized.
0345: * @param extensions
0346: * @see #addExtension
0347: * @since 1.1
0348: */
0349: public void setExtensions(Map extensions) {
0350: this .extensions = new HashMap(extensions);
0351: }
0352:
0353: /**
0354: * Accessor for the value of an extension for this query.
0355: * @param key The key
0356: * @return The value (if this extension is specified)
0357: */
0358: public Object getExtension(String key) {
0359: return (extensions != null ? extensions.get(key) : null);
0360: }
0361:
0362: /**
0363: * This method retrieves the fetch plan associated with the Query. It always
0364: * returns the identical instance for the same Query instance. Any change
0365: * made to the fetch plan affects subsequent query execution. Fetch plan is
0366: * described in Section 12.7
0367: * @return the FetchPlan
0368: */
0369: public FetchPlan getFetchPlan() {
0370: if (fetchPlan == null) {
0371: // Copy the FetchPlan of the ObjectManager
0372: this .fetchPlan = om.getFetchPlan().getCopy();
0373: }
0374:
0375: return fetchPlan;
0376: }
0377:
0378: /**
0379: * Mutator for the FetchPlan of the query.
0380: * This is called when applying a named FetchPlan.
0381: * @param fp The FetchPlan
0382: */
0383: public void setFetchPlan(FetchPlan fp) {
0384: // Update the FetchPlan
0385: this .fetchPlan = fp;
0386: }
0387:
0388: /**
0389: * Accessor for the class of the candidate instances of the query.
0390: * @return the Class of the candidate instances.
0391: * @see javax.jdo.Query#setClass
0392: */
0393: public Class getCandidateClass() {
0394: return candidateClass;
0395: }
0396:
0397: /**
0398: * Mutator for the class of the candidate instances of the query.
0399: * @param candidateClass the Class of the candidate instances.
0400: * @see javax.jdo.Query#setClass
0401: */
0402: public void setClass(Class candidateClass) {
0403: discardCompiled();
0404: assertIsModifiable();
0405:
0406: this .candidateClassName = (candidateClass != null ? candidateClass
0407: .getName()
0408: : null);
0409: this .candidateClass = candidateClass;
0410: }
0411:
0412: /**
0413: * Convenience method to set the name of the candidate class.
0414: * @param candidateClassName Name of the candidate class
0415: */
0416: public void setCandidateClassName(String candidateClassName) {
0417: this .candidateClassName = (candidateClassName != null ? candidateClassName
0418: .trim()
0419: : null);
0420: }
0421:
0422: /**
0423: * Accessor for the candidate class name.
0424: * @return Name of the candidate class (if any)
0425: */
0426: public String getCandidateClassName() {
0427: return candidateClassName;
0428: }
0429:
0430: /**
0431: * Set the candidates to the query.
0432: * @param from the candidates
0433: */
0434: public void setFrom(String from) {
0435: discardCompiled();
0436: assertIsModifiable();
0437:
0438: this .from = from;
0439: }
0440:
0441: /**
0442: * Accessor for the FROM clause of the query.
0443: * @return From clause
0444: */
0445: public String getFrom() {
0446: return from;
0447: }
0448:
0449: /**
0450: * Set the candidate Extent to query. To be implemented by extensions.
0451: * @param pcs the Candidate Extent.
0452: * @see javax.jdo.Query#setCandidates(javax.jdo.Extent)
0453: */
0454: public abstract void setCandidates(Extent pcs);
0455:
0456: /**
0457: * Set the candidate Collection to query. To be implemented by extensions.
0458: * @param pcs the Candidate collection.
0459: * @see javax.jdo.Query#setCandidates(java.util.Collection)
0460: */
0461: public abstract void setCandidates(Collection pcs);
0462:
0463: /**
0464: * Set the filter for the query.
0465: * @param filter the query filter.
0466: * @see javax.jdo.Query#setFilter
0467: */
0468: public void setFilter(String filter) {
0469: discardCompiled();
0470: assertIsModifiable();
0471:
0472: this .filter = (filter != null ? filter.trim() : null);
0473: }
0474:
0475: /**
0476: * Accessor for the filter specification.
0477: * @return Filter specification
0478: */
0479: public String getFilter() {
0480: return filter;
0481: }
0482:
0483: /**
0484: * Set the import statements to be used to identify the fully qualified name
0485: * of variables or parameters.
0486: * @param imports import statements separated by semicolons.
0487: * @see javax.jdo.Query#declareImports
0488: */
0489: public void declareImports(String imports) {
0490: discardCompiled();
0491: assertIsModifiable();
0492:
0493: this .imports = (imports != null ? imports.trim() : null);
0494: }
0495:
0496: /**
0497: * Accessor for the imports specification.
0498: * @return Imports specification
0499: */
0500: public String getImports() {
0501: return imports;
0502: }
0503:
0504: /**
0505: * Method to define the explicit parameters.
0506: * @param parameters the list of parameters separated by commas
0507: */
0508: public void declareExplicitParameters(String parameters) {
0509: discardCompiled();
0510: assertIsModifiable();
0511:
0512: this .explicitParameters = (StringUtils.isWhitespace(parameters) ? null
0513: : parameters.trim());
0514: }
0515:
0516: /**
0517: * Accessor for the explicit parameters specification.
0518: * @return Explicit parameters specification
0519: */
0520: public String getExplicitParameters() {
0521: return explicitParameters;
0522: }
0523:
0524: /**
0525: * Method to set the value of a named implicit parameter where known before execution.
0526: * @param name Name of the parameter
0527: * @param value Value of the parameter
0528: * @throws JPOXQueryInvalidParametersException if the parameter is invalid
0529: */
0530: public void setImplicitParameter(String name, Object value) {
0531: if (implicitParameters == null) {
0532: implicitParameters = new HashMap();
0533: }
0534: implicitParameters.put(name, value);
0535: discardCompiled();
0536: compileInternal(false, implicitParameters);
0537: }
0538:
0539: /**
0540: * Method to set the value of a numbered implicit parameter where known before execution.
0541: * @param position Position of the parameter
0542: * @param value Value of the parameter
0543: * @throws JPOXQueryInvalidParametersException if the parameter is invalid
0544: */
0545: public void setImplicitParameter(int position, Object value) {
0546: if (implicitParameters == null) {
0547: implicitParameters = new HashMap();
0548: }
0549: implicitParameters.put(new Integer(position), value);
0550: discardCompiled();
0551: compileInternal(false, implicitParameters);
0552: }
0553:
0554: /**
0555: * Method to define the explicit variables for the query.
0556: * @param variables the variables separated by semicolons.
0557: */
0558: public void declareExplicitVariables(String variables) {
0559: discardCompiled();
0560: assertIsModifiable();
0561:
0562: this .explicitVariables = (StringUtils.isWhitespace(variables) ? null
0563: : variables.trim());
0564: }
0565:
0566: /**
0567: * Accessor for the explicit variables specification.
0568: * @return Explicit variables specification
0569: */
0570: public String getExplicitVariables() {
0571: return explicitVariables;
0572: }
0573:
0574: /**
0575: * Set the ordering specification for the result Collection.
0576: * @param ordering the ordering specification.
0577: * @see javax.jdo.Query#setOrdering
0578: */
0579: public void setOrdering(String ordering) {
0580: discardCompiled();
0581: assertIsModifiable();
0582:
0583: this .ordering = (ordering != null ? ordering.trim() : null);
0584: }
0585:
0586: /**
0587: * Accessor for the ordering string for the query.
0588: * @return Ordering specification
0589: */
0590: public String getOrdering() {
0591: return ordering;
0592: }
0593:
0594: /**
0595: * Set the grouping specification for the result Collection.
0596: * @param grouping the grouping specification.
0597: * @see javax.jdo.Query#setGrouping
0598: */
0599: public void setGrouping(String grouping) {
0600: discardCompiled();
0601: assertIsModifiable();
0602:
0603: this .grouping = (grouping != null ? grouping.trim() : null);
0604: }
0605:
0606: /**
0607: * Accessor for the grouping string for the query.
0608: * @return Grouping specification
0609: */
0610: public String getGrouping() {
0611: return grouping;
0612: }
0613:
0614: /**
0615: * Set the having specification for the result Collection.
0616: * @param having the having specification.
0617: */
0618: public void setHaving(String having) {
0619: discardCompiled();
0620: assertIsModifiable();
0621:
0622: this .having = (having != null ? having.trim() : null);
0623: }
0624:
0625: /**
0626: * Accessor for the having string for the query.
0627: * @return Having specification
0628: */
0629: public String getHaving() {
0630: return having;
0631: }
0632:
0633: /**
0634: * Set the uniqueness of the results. A value of true will return a single
0635: * value (or null) where the application knows that there are 0 or 1
0636: * objects to be returned. See JDO 2.0 specification section 14.6
0637: * @param unique whether the result is unique
0638: * @see javax.jdo.Query#setUnique
0639: * @since 1.1
0640: */
0641: public void setUnique(boolean unique) {
0642: discardCompiled();
0643: assertIsModifiable();
0644:
0645: this .unique = unique;
0646: }
0647:
0648: /**
0649: * Accessor for whether the query results are unique.
0650: * @return Whether it is unique
0651: */
0652: public boolean isUnique() {
0653: return unique;
0654: }
0655:
0656: /**
0657: * Set the range of the results. By default all results are returned but
0658: * this allows specification of a range of elements required. See JDO 2.0
0659: * specification section 14.6.8
0660: * @param fromIncl From element no (inclusive) to return
0661: * @param toExcl To element no (exclusive) to return
0662: * @see javax.jdo.Query#setRange(long, long)
0663: * @since 1.1
0664: */
0665: public void setRange(long fromIncl, long toExcl) {
0666: discardCompiled();
0667:
0668: // JDO2 spec 14.6 setRange is valid when unmodifiable so dont check it
0669: this .range = null;
0670: this .fromInclNo = fromIncl;
0671: this .toExclNo = toExcl;
0672: }
0673:
0674: /**
0675: * Set the range of the results. By default all results are returned but
0676: * this allows specification of a range of elements required. See JDO 2.0
0677: * specification section 14.6.8
0678: * @param range Range string
0679: * @see javax.jdo.Query#setRange(java.lang.String)
0680: * @since 1.1
0681: */
0682: public void setRange(String range) {
0683: discardCompiled();
0684:
0685: // JDO2 spec 14.6 setRange is valid when unmodifiable so dont check it
0686: // fromInclNo, toExclNo will be extracted when compiling
0687: this .range = (range != null ? range.trim() : null);
0688: fromInclNo = 0;
0689: toExclNo = Long.MAX_VALUE;
0690: }
0691:
0692: /**
0693: * Accessor for the range specification.
0694: * @return Range specification
0695: */
0696: public String getRange() {
0697: return range;
0698: }
0699:
0700: /**
0701: * Accessor for the range lower limit (inclusive).
0702: * @return Range lower limit
0703: */
0704: public long getRangeFromIncl() {
0705: return fromInclNo;
0706: }
0707:
0708: /**
0709: * Accessor for the range upper limit (exclusive).
0710: * @return Range upper limit
0711: */
0712: public long getRangeToExcl() {
0713: return toExclNo;
0714: }
0715:
0716: /**
0717: * Set the result for the results. The application might want to get results
0718: * from a query that are not instances of the candidate class. The results
0719: * might be fields of persistent instances, instances of classes other than
0720: * the candidate class, or aggregates of fields.
0721: * @param result The result parameter consists of the optional keyword
0722: * distinct followed by a commaseparated list of named result expressions or
0723: * a result class specification.
0724: * @see javax.jdo.Query#setResult
0725: * @since 1.1
0726: */
0727: public void setResult(String result) {
0728: discardCompiled();
0729: assertIsModifiable();
0730:
0731: this .result = (result != null ? result.trim() : null);
0732: }
0733:
0734: /**
0735: * Accessor for the result specification string.
0736: * @return Result specification
0737: */
0738: public String getResult() {
0739: return result;
0740: }
0741:
0742: /**
0743: * Set the result class for the results.
0744: * The result class must obey various things as per the JDO 2.0 spec 14.6.12.
0745: * @param result_cls The result class
0746: * @see javax.jdo.Query#setResultClass
0747: * @since 1.1
0748: */
0749: public void setResultClass(Class result_cls) {
0750: discardCompiled();
0751:
0752: // JDO2 spec 14.6 setResultClass is valid when unmodifiable so dont check it
0753: this .resultClassName = (result_cls != null ? result_cls
0754: .getName() : null);
0755: this .resultClass = result_cls;
0756: }
0757:
0758: /**
0759: * Accessor for the result class.
0760: * @return Result class
0761: */
0762: public Class getResultClass() {
0763: return resultClass;
0764: }
0765:
0766: /**
0767: * Convenience method to set the name of the result class.
0768: * @param resultClassName Name of the result class
0769: */
0770: public void setResultClassName(String resultClassName) {
0771: this .resultClassName = resultClassName;
0772: }
0773:
0774: /**
0775: * Accessor for the name of the result class.
0776: * @return Result class name
0777: */
0778: public String getResultClassName() {
0779: return resultClassName;
0780: }
0781:
0782: /**
0783: * Method to set the MetaData defining the result.
0784: * If the query doesn't support such a setting will throw a JPOXException.
0785: * @param qrmd QueryResultMetaData
0786: * @since 1.2
0787: */
0788: public void setResultMetaData(QueryResultMetaData qrmd) {
0789: throw new JPOXException(
0790: "This query doesn't support the use of setResultMetaData()");
0791: }
0792:
0793: /**
0794: * Set the ignoreCache option. Currently this simply stores the ignoreCache value, and doesn't
0795: * necessarily use it. The parameter is a "hint" to the query engine. TODO : Implement this fully.
0796: * @param ignoreCache the setting of the ignoreCache option.
0797: * @see javax.jdo.Query#setIgnoreCache
0798: * @see javax.jdo.PersistenceManager#setIgnoreCache
0799: */
0800: public void setIgnoreCache(boolean ignoreCache) {
0801: discardCompiled();
0802:
0803: // JDO2 spec 14.6 setIgnoreCache is valid when unmodifiable so dont check it
0804: this .ignoreCache = ignoreCache;
0805: }
0806:
0807: /**
0808: * Accessor for the ignoreCache option setting.
0809: * @return the ignoreCache option setting
0810: * @see #setIgnoreCache
0811: * @see javax.jdo.Query#getIgnoreCache
0812: * @see javax.jdo.PersistenceManager#getIgnoreCache
0813: */
0814: public boolean getIgnoreCache() {
0815: return ignoreCache;
0816: }
0817:
0818: /**
0819: * Accessor for whether this query includes subclasses
0820: * @return Returns whether the query includes subclasses.
0821: */
0822: public boolean isSubclasses() {
0823: return subclasses;
0824: }
0825:
0826: /**
0827: * Mutator for whether this query includes subclasses
0828: * @param subclasses Where subclasses of the candidate class are to be included.
0829: */
0830: public void setSubclasses(boolean subclasses) {
0831: discardCompiled();
0832: assertIsModifiable();
0833:
0834: this .subclasses = subclasses;
0835:
0836: // Set the candidates also if the candidate has been set
0837: /*if (candidateClass != null)
0838: {
0839: setCandidates(om.getExtent(candidateClass, subclasses));
0840: }*/
0841: }
0842:
0843: /**
0844: * Accessor for unmodifiable.
0845: * @return Returns the unmodifiable.
0846: */
0847: public boolean isUnmodifiable() {
0848: return unmodifiable;
0849: }
0850:
0851: /**
0852: * Method to throw a JPOXUserException if the query is currently not modifiable.
0853: * @throws JPOXUserException Thrown when it is unmodifiable
0854: */
0855: protected void assertIsModifiable() {
0856: if (unmodifiable) {
0857: throw new JPOXUserException(LOCALISER.msg("021014"));
0858: }
0859: }
0860:
0861: /**
0862: * Mutator for unmodifiable.
0863: */
0864: public void setUnmodifiable() {
0865: this .unmodifiable = true;
0866: }
0867:
0868: /**
0869: * Method to add a subquery to this query.
0870: * @param sub The subquery
0871: * @param variableDecl Declaration of variables
0872: * @param candidateExpr Candidate expression
0873: * @param paramMap Map of parameters for this subquery
0874: */
0875: public void addSubquery(Query sub, String variableDecl,
0876: String candidateExpr, Map paramMap) {
0877: if (StringUtils.isWhitespace(variableDecl)) {
0878: throw new JPOXUserException(LOCALISER.msg("021078"));
0879: }
0880:
0881: if (sub == null) {
0882: // No subquery so the variable is unset effectively meaning that it is an explicit variable
0883: if (explicitVariables == null) {
0884: explicitVariables = variableDecl;
0885: } else {
0886: explicitVariables += ";" + variableDecl;
0887: }
0888: } else {
0889: // Register the subquery against its variable name for later use
0890: if (subqueries == null) {
0891: subqueries = new HashMap();
0892: }
0893: String subqueryVariableName = variableDecl.trim();
0894: int sepPos = subqueryVariableName.indexOf(' ');
0895: subqueryVariableName = subqueryVariableName
0896: .substring(sepPos + 1);
0897: subqueries.put(subqueryVariableName,
0898: new SubqueryDefinition(sub, StringUtils
0899: .isWhitespace(candidateExpr) ? null
0900: : candidateExpr, variableDecl, paramMap));
0901: }
0902: }
0903:
0904: /**
0905: * Simple representation of a subquery, its candidate, params and variables.
0906: */
0907: public class SubqueryDefinition {
0908: Query query;
0909: String candidateExpression;
0910: String variableDecl;
0911: Map parameterMap;
0912:
0913: public SubqueryDefinition(Query q, String candidates,
0914: String variables, Map params) {
0915: this .query = q;
0916: this .candidateExpression = candidates;
0917: this .variableDecl = variables;
0918: this .parameterMap = params;
0919: }
0920:
0921: public Query getQuery() {
0922: return this .query;
0923: }
0924:
0925: public String getCandidateExpression() {
0926: return this .candidateExpression;
0927: }
0928:
0929: public String getVariableDeclaration() {
0930: return this .variableDecl;
0931: }
0932:
0933: public Map getParameterMap() {
0934: return this .parameterMap;
0935: }
0936: }
0937:
0938: /**
0939: * Accessor for the subquery for the supplied variable.
0940: * @param variableName Name of the variable
0941: * @return Subquery for the variable (if a subquery exists for this variable)
0942: */
0943: public SubqueryDefinition getSubqueryForVariable(String variableName) {
0944: if (subqueries == null) {
0945: return null;
0946: }
0947: return (SubqueryDefinition) subqueries.get(variableName);
0948: }
0949:
0950: /**
0951: * Accessor for whether there is a subquery for the specified variable name.
0952: * @param variableName Name of the variable
0953: * @return Whether there is a subquery defined
0954: */
0955: public boolean hasSubqueryForVariable(String variableName) {
0956: return (subqueries == null ? false : subqueries
0957: .containsKey(variableName));
0958: }
0959:
0960: /**
0961: * Convenience method that will flush any outstanding updates to the datastore.
0962: * This is intended to be used before execution so that the datastore has all relevant data present
0963: * for what the query needs.
0964: */
0965: protected void prepareDatastore() {
0966: boolean flush = false;
0967: if (!ignoreCache && !om.isDelayDatastoreOperationsEnabled()) {
0968: flush = true;
0969: } else if (extensions != null
0970: && extensions
0971: .containsKey("org.jpox.query.flushBeforeExecution")) {
0972: flush = Boolean
0973: .valueOf(
0974: (String) extensions
0975: .get("org.jpox.query.flushBeforeExecution"))
0976: .booleanValue();
0977: } else if (om.getOMFContext().getPersistenceConfiguration()
0978: .getQueryFlushBeforeExecution()) {
0979: flush = true;
0980: }
0981: if (flush) {
0982: // Make sure all changes are in the datastore before proceeding
0983: om.flushInternal(false);
0984: }
0985: }
0986:
0987: /**
0988: * Accessor for whether the query is compiled.
0989: * @return Whether the query is compiled.
0990: */
0991: public boolean isCompiled() {
0992: return isCompiled;
0993: }
0994:
0995: /**
0996: * Verify the elements of the query and provide a hint to the query to prepare and optimize an execution plan.
0997: */
0998: public void compile() {
0999: if (isCompiled) {
1000: return;
1001: }
1002:
1003: compileInternal(false, null);
1004: }
1005:
1006: /**
1007: * Method to compile the query. To be implemented by the query implementation.
1008: * @param forExecute Whether to compile the query ready for execution (using any param values)
1009: * @param parameterValues Param values keyed by name (when compiling for execution)
1010: */
1011: protected abstract void compileInternal(boolean forExecute,
1012: Map parameterValues);
1013:
1014: /**
1015: * Accessor for the parsed imports.
1016: * If no imports are set then adds candidate class and user imports.
1017: * @return Parsed imports
1018: */
1019: protected Imports getParsedImports() {
1020: if (parsedImports == null) {
1021: parsedImports = new Imports();
1022: if (candidateClassName != null) {
1023: parsedImports.importPackage(candidateClassName);
1024: }
1025: if (imports != null) {
1026: parsedImports.parseImports(imports);
1027: }
1028: }
1029: return parsedImports;
1030: }
1031:
1032: /**
1033: * Utility to resolve the declaration to a particular class.
1034: * Takes the passed in name, together with the defined import declarations and returns the
1035: * class represented by the declaration.
1036: * @param classDecl The declaration
1037: * @return The class it resolves to (if any)
1038: * @throws JPOXUserException Thrown if the class cannot be resolved.
1039: */
1040: public Class resolveClassDeclaration(String classDecl) {
1041: try {
1042: return getParsedImports().resolveClassDeclaration(
1043: classDecl,
1044: om.getClassLoaderResolver(),
1045: (candidateClass == null ? null : candidateClass
1046: .getClassLoader()));
1047: } catch (ClassNotResolvedException e) {
1048: throw new JPOXUserException(LOCALISER.msg("021015",
1049: classDecl));
1050: }
1051: }
1052:
1053: // ----------------------------- Execute -----------------------------------
1054:
1055: /**
1056: * Execute the query and return the filtered List.
1057: * @return the filtered List.
1058: * @see javax.jdo.Query#execute()
1059: * @see #executeWithArray(Object[] parameters)
1060: */
1061: public Object execute() {
1062: return executeWithArray(new Object[0]);
1063: }
1064:
1065: /**
1066: * Execute the query and return the filtered List.
1067: * @param p1 the value of the first parameter declared.
1068: * @return the filtered List.
1069: * @see javax.jdo.Query#execute(Object)
1070: * @see #executeWithArray(Object[] parameters)
1071: */
1072: public Object execute(Object p1) {
1073: return executeWithArray(new Object[] { p1 });
1074: }
1075:
1076: /**
1077: * Execute the query and return the filtered List.
1078: * @param p1 the value of the first parameter declared.
1079: * @param p2 the value of the second parameter declared.
1080: * @return the filtered List.
1081: * @see javax.jdo.Query#execute(Object,Object)
1082: * @see #executeWithArray(Object[] parameters)
1083: */
1084: public Object execute(Object p1, Object p2) {
1085: return executeWithArray(new Object[] { p1, p2 });
1086: }
1087:
1088: /**
1089: * Execute the query and return the filtered List.
1090: * @param p1 the value of the first parameter declared.
1091: * @param p2 the value of the second parameter declared.
1092: * @param p3 the value of the third parameter declared.
1093: * @return the filtered List.
1094: * @see javax.jdo.Query#execute(Object,Object,Object)
1095: * @see #executeWithArray(Object[] parameters)
1096: */
1097: public Object execute(Object p1, Object p2, Object p3) {
1098: return executeWithArray(new Object[] { p1, p2, p3 });
1099: }
1100:
1101: /**
1102: * Execute the query and return the filtered List.
1103: * @param parameterValues the Object array with all of the parameters.
1104: * @return the filtered List.
1105: * @see javax.jdo.Query#executeWithArray(Object[])
1106: */
1107: public Object executeWithArray(Object[] parameterValues) {
1108: if (om == null) {
1109: // Is checked at construction, but could have been deserialised (and serialised have no manager)
1110: throw new JPOXUserException(LOCALISER.msg("021017"));
1111: }
1112:
1113: // Compile the explicit parameters
1114: QueryCompiler c = new QueryCompiler(this , getParsedImports(),
1115: null);
1116: c.compile(QueryCompiler.COMPILE_EXPLICIT_PARAMETERS);
1117: parameterNames = c.getParameterNames();
1118:
1119: // Build the parameter values into a Map so we have a common interface
1120: HashMap parameterMap = new HashMap();
1121: if (parameterNames != null && parameterNames.length > 0) {
1122: // Explicit parameters defined
1123: for (int i = 0; i < parameterValues.length; ++i) {
1124: parameterMap.put(parameterNames[i], parameterValues[i]);
1125: }
1126: } else {
1127: // Must be implicit parameters, so give them dummy names for now
1128: for (int i = 0; i < parameterValues.length; ++i) {
1129: parameterMap.put("JPOX_" + i, parameterValues[i]);
1130: }
1131: }
1132:
1133: return executeWithMap(parameterMap);
1134: }
1135:
1136: /**
1137: * Execute the query and return the filtered result(s).
1138: * @param parameters the Map containing all of the parameters.
1139: * @return the filtered results (List, or Object)
1140: * @see javax.jdo.Query#executeWithMap(Map)
1141: * @see #executeWithArray(Object[] parameters)
1142: * @throws NoQueryResultsException Thrown if no results were returned from the query.
1143: */
1144: public Object executeWithMap(Map parameters) {
1145: if (om == null) {
1146: // Is checked at construction, but could have been deserialised (and serialised have no manager)
1147: throw new JPOXUserException(LOCALISER.msg("021017"));
1148: }
1149: if (om.isClosed()) {
1150: // Throw exception if query is closed (e.g JDO2 [14.6.1])
1151: throw new JPOXUserException(LOCALISER.msg("021013"))
1152: .setFatal();
1153: }
1154: if (!om.getTransaction().isActive()
1155: && !om.getTransaction().getNontransactionalRead()) {
1156: throw new TransactionNotActiveException();
1157: }
1158:
1159: boolean failed = true; // flag to use for checking the state of the execution results
1160: long start = System.currentTimeMillis();
1161:
1162: QueryRuntime queryRuntime = om.getOMFContext()
1163: .getQueryManager().getQueryRuntime();
1164: if (queryRuntime != null) {
1165: queryRuntime.queryBegin();
1166: }
1167:
1168: try {
1169: // Execute the query
1170: Collection qr = (Collection) performExecute(parameters);
1171:
1172: failed = false;
1173: if (shouldReturnSingleRow()) {
1174: try {
1175: // Single row only needed (unique specified, or using aggregates etc), so just take the first row of the results
1176: if (qr == null || qr.size() == 0) {
1177: throw new NoQueryResultsException(
1178: "No query results were returned");
1179: } else {
1180: if (applyRangeChecks()
1181: && (toExclNo - fromInclNo <= 0)) {
1182: // JDO2 spec 14.6.8 (range excludes instances, so return null)
1183: throw new NoQueryResultsException(
1184: "No query results were returned in the required range");
1185: }
1186:
1187: Iterator qrIter = qr.iterator();
1188: Object firstRow = qrIter.next();
1189: if (qrIter.hasNext()) {
1190: failed = true;
1191: throw new QueryNotUniqueException();
1192: }
1193: return firstRow;
1194: }
1195: } finally {
1196: // can close qr right now because we don't return it,
1197: // also user cannot close it otherwise except for calling closeAll()
1198: if (qr != null) {
1199: close(qr);
1200: }
1201: }
1202: }
1203:
1204: // Process any specified range
1205: if (applyRangeChecks()) {
1206: // Range not applied in the compiled query so throw away objects outside the required range
1207: int i = 0;
1208: Iterator qr_iter = qr.iterator();
1209: Collection res = new ArrayList();
1210: while (qr_iter.hasNext()) {
1211: Object obj = qr_iter.next();
1212: if (i >= fromInclNo && i < toExclNo) {
1213: // Accept the item if within range
1214: res.add(obj);
1215: }
1216: i++;
1217: }
1218: return res;
1219: } else {
1220: // Range applied in the compiled query (or no range) so just return it
1221: return qr;
1222: }
1223: } finally {
1224: if (queryRuntime != null) {
1225: if (failed) {
1226: queryRuntime.queryExecutedWithError();
1227: } else {
1228: queryRuntime.queryExecuted(System
1229: .currentTimeMillis()
1230: - start);
1231: }
1232: }
1233: }
1234: }
1235:
1236: /**
1237: * Convenience method to return whether the query should return a single row.
1238: * @return Whether a single row should result
1239: */
1240: protected abstract boolean shouldReturnSingleRow();
1241:
1242: /**
1243: * Method to return if the query results should have the range checked and unnecessary rows
1244: * discarded. This is for where the query language has specified a range but the datastore doesnt
1245: * allow removal of unnecessary results in the query itself (so has to be done in post-processing).
1246: * This implementation returns false and so should be overridden by query languages to match their
1247: * capabilities.
1248: * @return Whether to apply range checks in post-processing of results.
1249: */
1250: protected boolean applyRangeChecks() {
1251: return false;
1252: }
1253:
1254: /**
1255: * Method to actually execute the query. To be implemented by extending
1256: * classes for the particular query language.
1257: * @param parameters Map containing the parameters.
1258: * @return Query result - QueryResult if SELECT, or Long if BULK_UPDATE, BULK_DELETE
1259: */
1260: protected abstract Object performExecute(Map parameters);
1261:
1262: // ------------------------- Delete Persistent -----------------------------
1263:
1264: /**
1265: * Method to delete all objects found by this query, catering for cascade changes and updates
1266: * to in-memory objects.
1267: * @return The number of deleted objects.
1268: * @see javax.jdo.Query#deletePersistentAll()
1269: */
1270: public long deletePersistentAll() {
1271: return deletePersistentAll(new Object[0]);
1272: }
1273:
1274: /**
1275: * Method to delete all objects found by this query, catering for cascade changes and updates
1276: * to in-memory objects.
1277: * @param parameters the Object array with all of the parameters.
1278: * @return the filtered Collection.
1279: * @see javax.jdo.Query#deletePersistentAll(Object[])
1280: */
1281: public long deletePersistentAll(Object[] parameters) {
1282: // Compile the explicit parameters
1283: QueryCompiler c = new QueryCompiler(this , getParsedImports(),
1284: null);
1285: c.compile(QueryCompiler.COMPILE_EXPLICIT_PARAMETERS);
1286: parameterNames = c.getParameterNames();
1287:
1288: // Build the parameter values into a Map so we have a common interface
1289: HashMap parameterMap = new HashMap();
1290: if (parameterNames != null && parameterNames.length > 0) {
1291: // Explicit parameters defined
1292: for (int i = 0; i < parameters.length; ++i) {
1293: parameterMap.put(parameterNames[i], parameters[i]);
1294: }
1295: } else {
1296: // Must be implicit parameters, so give them dummy names for now
1297: for (int i = 0; i < parameters.length; ++i) {
1298: parameterMap.put("JPOX_" + i, parameters[i]);
1299: }
1300: }
1301:
1302: return deletePersistentAll(parameterMap);
1303: }
1304:
1305: /**
1306: * Method to delete all objects found by this query, catering for cascade changes and updates
1307: * to in-memory objects.
1308: * @param parameters Map of parameters for the query
1309: * @return the number of deleted objects
1310: * @see javax.jdo.Query#deletePersistentAll(Map)
1311: */
1312: public long deletePersistentAll(Map parameters) {
1313: if (om.isClosed()) {
1314: // Throw exception if query is closed (e.g JDO2 [14.6.1])
1315: throw new JPOXUserException(LOCALISER.msg("021013"))
1316: .setFatal();
1317: }
1318: if (!om.getTransaction().isActive()
1319: && !om.getTransaction().getNontransactionalWrite()) {
1320: // tx not active and not allowing non-tx write
1321: throw new TransactionNotActiveException();
1322: }
1323:
1324: // Compile the "query" for execution
1325: compileInternal(true, parameters);
1326:
1327: // Check for specification of any illegal attributes
1328: if (result != null) {
1329: throw new JPOXUserException(LOCALISER.msg("021029"));
1330: }
1331: if (resultClass != null) {
1332: throw new JPOXUserException(LOCALISER.msg("021030"));
1333: }
1334: if (resultClassName != null) {
1335: throw new JPOXUserException(LOCALISER.msg("021030"));
1336: }
1337: if (ordering != null) {
1338: throw new JPOXUserException(LOCALISER.msg("021027"));
1339: }
1340: if (grouping != null) {
1341: throw new JPOXUserException(LOCALISER.msg("021028"));
1342: }
1343: if (range != null) {
1344: throw new JPOXUserException(LOCALISER.msg("021031"));
1345: }
1346: if (fromInclNo >= 0 && toExclNo >= 0
1347: && (fromInclNo != 0 || toExclNo != Long.MAX_VALUE)) {
1348: throw new JPOXUserException(LOCALISER.msg("021031"));
1349: }
1350:
1351: long qr = performDeletePersistentAll(parameters);
1352:
1353: return qr;
1354: }
1355:
1356: /**
1357: * Method to actually execute the deletion of objects.
1358: * To be implemented by extending classes.
1359: * @param parameters Map containing the parameters.
1360: * @return The filtered QueryResult.
1361: */
1362: protected abstract long performDeletePersistentAll(Map parameters);
1363:
1364: // -------------------------------------------------------------------------
1365:
1366: /**
1367: * Close a query result and release any resources associated with it.
1368: * @param queryResult the result of execute(...) on this Query instance.
1369: * @see javax.jdo.Query#close
1370: */
1371: public void close(Object queryResult) {
1372: if (queryResult != null && queryResult instanceof QueryResult) {
1373: ((QueryResult) queryResult).close();
1374: queryResults.remove(queryResult);
1375: }
1376: }
1377:
1378: /**
1379: * Close all query results associated with this Query instance,
1380: * and release all resources associated with them.
1381: * @see javax.jdo.Query#closeAll
1382: */
1383: public void closeAll() {
1384: QueryResult[] qrs = (QueryResult[]) queryResults
1385: .toArray(new QueryResult[queryResults.size()]);
1386: for (int i = 0; i < qrs.length; ++i) {
1387: close(qrs[i]);
1388: }
1389: }
1390: }
|