001: /**********************************************************************
002: Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.store.query;
018:
019: import java.util.Collection;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import org.jpox.ObjectManager;
024: import org.jpox.exceptions.JPOXUserException;
025: import org.jpox.metadata.AbstractClassMetaData;
026:
027: /**
028: * Abstract representation of a JPQL query used by JPOX.
029: * The query can be specified via method calls, or via a single-string form.
030: *
031: * @see Query
032: * @version $Revision: 1.4 $
033: */
034: public abstract class AbstractJPQLQuery extends AbstractJavaQuery {
035: /** Keywords used in single-string JPQL. Uppercase variants specified here, but JPQL allows case-insensitive. */
036: public static final String[] SINGLE_STRING_KEYWORDS = { "SELECT",
037: "UPDATE", "DELETE", "FROM", "WHERE", "GROUP BY", "HAVING",
038: "ORDER BY" };
039:
040: /** List of identifier names not allowed by JPQL. */
041: public static final String[] RESERVED_IDENTIFIERS = { "SELECT",
042: "FROM", "WHERE", "UPDATE", "DELETE", "JOIN", "OUTER",
043: "INNER", "LEFT", "GROUP", "BY", "HAVING", "FETCH",
044: "DISTINCT", "OBJECT", "NULL", "TRUE", "FALSE", "NOT",
045: "AND", "OR", "BETWEEN", "LIKE", "IN", "AS", "UNKNOWN",
046: "EMPTY", "MEMBER", "OF", "IS", "AVG", "MAX", "MIN", "SUM",
047: "COUNT", "ORDER", "ASC", "DESC", "MOD", "UPPER", "LOWER",
048: "TRIM", "POSITION", "CHARACTER_LENGTH", "CHAR_LENGTH",
049: "BIT_LENGTH", "CURRENT_TIME", "CURRENT_DATE",
050: "CURRENT_TIMESTAMP", "NEW", "EXISTS", "ALL", "ANY", "SOME" };
051:
052: /** UPDATE clause of a JPQL query. */
053: protected transient String update = null;
054:
055: /**
056: * Constructor.
057: * @param om ObjectManager
058: */
059: public AbstractJPQLQuery(ObjectManager om) {
060: super (om);
061: }
062:
063: /**
064: * Set the UPDATE clause of the query.
065: * @param update the update clause
066: */
067: public void setUpdate(String update) {
068: discardCompiled();
069: assertIsModifiable();
070:
071: this .update = update;
072: }
073:
074: /**
075: * Accessor for the UPDATE clause of the JPQL query.
076: * @return Update clause
077: */
078: public String getUpdate() {
079: return update;
080: }
081:
082: /**
083: * Method to take the defined parameters for the query and form a single string.
084: * This is used to print out the query for logging.
085: * @return The single string
086: */
087: public String getSingleStringQuery() {
088: if (singleString != null) {
089: return singleString;
090: }
091:
092: StringBuffer str = new StringBuffer();
093: if (type == BULK_UPDATE) {
094: str.append("UPDATE " + update + " ");
095: } else if (type == BULK_DELETE) {
096: str.append("DELETE ");
097: } else {
098: str.append("SELECT ");
099: }
100:
101: if (result != null) {
102: str.append(result + " ");
103: }
104: if (from != null) {
105: str.append("FROM " + from + " ");
106: }
107: if (filter != null) {
108: str.append("WHERE " + filter + " ");
109: }
110: if (grouping != null) {
111: str.append("GROUP BY " + grouping + " ");
112: }
113: if (having != null) {
114: str.append("HAVING " + having + " ");
115: }
116: if (ordering != null) {
117: str.append("ORDER BY " + ordering + " ");
118: }
119:
120: singleString = str.toString().trim();
121: return singleString;
122: }
123:
124: /**
125: * Execute the query to delete persistent objects.
126: * @param parameters the Map containing all of the parameters.
127: * @return the number of deleted objects.
128: */
129: protected long performDeletePersistentAll(Map parameters) {
130: // Save the required value for unique (find the instances without it)
131: boolean requiresUnique = unique;
132: if (unique) {
133: unique = false;
134: }
135:
136: Collection results = (Collection) performExecute(parameters);
137: int number = 0;
138: if (results == null) {
139: number = 0;
140: } else {
141: // TODO : To make this most efficient we shouldn't instantiate things into memory
142: // but instead check if the object to be deleted implements DeleteCallback, or there are
143: // any lifecycle listeners for this object type, or if there are any dependent fields
144: // and only then do we instantiate it.
145: number = results.size();
146: if (requiresUnique && number > 1) {
147: throw new JPOXUserException(LOCALISER.msg("021032"));
148: }
149:
150: // Instances to be deleted are flushed first (JDO2 [14.8-4])
151: Iterator resultsIter = results.iterator();
152: while (resultsIter.hasNext()) {
153: om.findStateManager(resultsIter.next()).flush();
154: }
155:
156: // Perform the deletion
157: om.deleteObjects(results);
158:
159: // TODO Do we need to remove these from the PM.queryRun store and this.queryResults ?
160: ((QueryResult) results).close();
161: }
162: return number;
163: }
164:
165: /**
166: * Utility to resolve the declaration to a particular class.
167: * Takes the passed in name, together with the defined import declarations and returns the
168: * class represented by the declaration.
169: * @param classDecl The declaration
170: * @return The class it resolves to (if any)
171: * @throws JPOXUserException Thrown if the class cannot be resolved.
172: */
173: public Class resolveClassDeclaration(String classDecl) {
174: // Try to find an entity name before relaying to the superclass method
175: AbstractClassMetaData acmd = this .getStoreManager()
176: .getMetaDataManager().getMetaDataForEntityName(
177: classDecl);
178: if (acmd != null) {
179: classDecl = acmd.getFullClassName();
180: }
181:
182: return super .resolveClassDeclaration(classDecl);
183: }
184:
185: /**
186: * Convenience method returning if the supplied name is a keyword for this query language.
187: * @param name Name to check
188: * @return Whether it is a keyword
189: */
190: public static boolean isKeyword(String name) {
191: for (int i = 0; i < SINGLE_STRING_KEYWORDS.length; i++) {
192: // JPQL is case-insensitive
193: if (name.equalsIgnoreCase(SINGLE_STRING_KEYWORDS[i])) {
194: return true;
195: }
196: }
197: return false;
198: }
199:
200: /**
201: * Convenience method returning if the supplied name is a reserved identifier for this query language.
202: * @param name Name to check
203: * @return Whether it is a reserved identifier
204: */
205: public static boolean isReservedIdentifier(String name) {
206: for (int i = 0; i < RESERVED_IDENTIFIERS.length; i++) {
207: // JPQL is case-insensitive
208: if (name.equalsIgnoreCase(RESERVED_IDENTIFIERS[i])) {
209: return true;
210: }
211: }
212: return false;
213: }
214: }
|