001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence;
020:
021: import java.io.Serializable;
022: import java.lang.reflect.Method;
023: import java.util.ArrayList;
024: import java.util.Calendar;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.Date;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Set;
033: import javax.persistence.FlushModeType;
034: import javax.persistence.Query;
035: import javax.persistence.TemporalType;
036:
037: import org.apache.commons.collections.map.LinkedMap;
038: import org.apache.openjpa.enhance.Reflection;
039: import org.apache.openjpa.kernel.DelegatingQuery;
040: import org.apache.openjpa.kernel.DelegatingResultList;
041: import org.apache.openjpa.kernel.Filters;
042: import org.apache.openjpa.kernel.QueryOperations;
043: import org.apache.openjpa.kernel.exps.AggregateListener;
044: import org.apache.openjpa.kernel.exps.FilterListener;
045: import org.apache.openjpa.lib.rop.ResultList;
046: import org.apache.openjpa.lib.util.Localizer;
047: import org.apache.openjpa.util.ImplHelper;
048: import org.apache.openjpa.util.RuntimeExceptionTranslator;
049:
050: /**
051: * Implementation of {@link Query} interface.
052: *
053: * @author Marc Prud'hommeaux
054: * @author Abe White
055: * @nojavadoc
056: */
057: public class QueryImpl implements OpenJPAQuerySPI, Serializable {
058:
059: private static final Object[] EMPTY_ARRAY = new Object[0];
060:
061: private static final Localizer _loc = Localizer
062: .forPackage(QueryImpl.class);
063:
064: private final DelegatingQuery _query;
065: private transient EntityManagerImpl _em;
066: private transient FetchPlan _fetch;
067:
068: private Map _named;
069: private List _positional;
070:
071: /**
072: * Constructor; supply factory and delegate.
073: */
074: public QueryImpl(EntityManagerImpl em,
075: RuntimeExceptionTranslator ret,
076: org.apache.openjpa.kernel.Query query) {
077: _em = em;
078: _query = new DelegatingQuery(query, ret);
079: }
080:
081: /**
082: * Delegate.
083: */
084: public org.apache.openjpa.kernel.Query getDelegate() {
085: return _query.getDelegate();
086: }
087:
088: public OpenJPAEntityManager getEntityManager() {
089: return _em;
090: }
091:
092: public String getLanguage() {
093: return _query.getLanguage();
094: }
095:
096: public QueryOperationType getOperation() {
097: return QueryOperationType.fromKernelConstant(_query
098: .getOperation());
099: }
100:
101: public FetchPlan getFetchPlan() {
102: _em.assertNotCloseInvoked();
103: _query.assertNotSerialized();
104: _query.lock();
105: try {
106: if (_fetch == null)
107: _fetch = ((EntityManagerFactoryImpl) _em
108: .getEntityManagerFactory()).toFetchPlan(_query
109: .getBroker(), _query.getFetchConfiguration());
110: return _fetch;
111: } finally {
112: _query.unlock();
113: }
114: }
115:
116: public String getQueryString() {
117: return _query.getQueryString();
118: }
119:
120: public boolean getIgnoreChanges() {
121: return _query.getIgnoreChanges();
122: }
123:
124: public OpenJPAQuery setIgnoreChanges(boolean ignore) {
125: _em.assertNotCloseInvoked();
126: _query.setIgnoreChanges(ignore);
127: return this ;
128: }
129:
130: public OpenJPAQuery addFilterListener(FilterListener listener) {
131: _em.assertNotCloseInvoked();
132: _query.addFilterListener(listener);
133: return this ;
134: }
135:
136: public OpenJPAQuery removeFilterListener(FilterListener listener) {
137: _em.assertNotCloseInvoked();
138: _query.removeFilterListener(listener);
139: return this ;
140: }
141:
142: public OpenJPAQuery addAggregateListener(AggregateListener listener) {
143: _em.assertNotCloseInvoked();
144: _query.addAggregateListener(listener);
145: return this ;
146: }
147:
148: public OpenJPAQuery removeAggregateListener(
149: AggregateListener listener) {
150: _em.assertNotCloseInvoked();
151: _query.removeAggregateListener(listener);
152: return this ;
153: }
154:
155: public Collection getCandidateCollection() {
156: return _query.getCandidateCollection();
157: }
158:
159: public OpenJPAQuery setCandidateCollection(Collection coll) {
160: _em.assertNotCloseInvoked();
161: _query.setCandidateCollection(coll);
162: return this ;
163: }
164:
165: public Class getResultClass() {
166: Class res = _query.getResultType();
167: if (res != null)
168: return res;
169: return _query.getCandidateType();
170: }
171:
172: public OpenJPAQuery setResultClass(Class cls) {
173: _em.assertNotCloseInvoked();
174: if (ImplHelper.isManagedType(_em.getConfiguration(), cls))
175: _query.setCandidateType(cls, true);
176: else
177: _query.setResultType(cls);
178: return this ;
179: }
180:
181: public boolean hasSubclasses() {
182: return _query.hasSubclasses();
183: }
184:
185: public OpenJPAQuery setSubclasses(boolean subs) {
186: _em.assertNotCloseInvoked();
187: Class cls = _query.getCandidateType();
188: _query.setCandidateExtent(_query.getBroker().newExtent(cls,
189: subs));
190: return this ;
191: }
192:
193: public int getFirstResult() {
194: return asInt(_query.getStartRange());
195: }
196:
197: public OpenJPAQuery setFirstResult(int startPosition) {
198: _em.assertNotCloseInvoked();
199: long end;
200: if (_query.getEndRange() == Long.MAX_VALUE)
201: end = Long.MAX_VALUE;
202: else
203: end = startPosition
204: + (_query.getEndRange() - _query.getStartRange());
205: _query.setRange(startPosition, end);
206: return this ;
207: }
208:
209: public int getMaxResults() {
210: return asInt(_query.getEndRange() - _query.getStartRange());
211: }
212:
213: public OpenJPAQuery setMaxResults(int max) {
214: _em.assertNotCloseInvoked();
215: long start = _query.getStartRange();
216: if (max == Integer.MAX_VALUE)
217: _query.setRange(start, Long.MAX_VALUE);
218: else
219: _query.setRange(start, start + max);
220: return this ;
221: }
222:
223: public OpenJPAQuery compile() {
224: _em.assertNotCloseInvoked();
225: _query.compile();
226: return this ;
227: }
228:
229: private Object execute() {
230: if (_query.getOperation() != QueryOperations.OP_SELECT)
231: throw new InvalidStateException(_loc.get(
232: "not-select-query", _query.getQueryString()), null,
233: null, false);
234:
235: validateParameters();
236:
237: // handle which types of parameters we are using, if any
238: if (_positional != null)
239: return _query.execute(_positional.toArray());
240: if (_named != null)
241: return _query.execute(_named);
242: return _query.execute();
243: }
244:
245: /**
246: * Validate that the types of the parameters are correct.
247: */
248: private void validateParameters() {
249: if (_positional != null) {
250: LinkedMap types = _query.getParameterTypes();
251: for (int i = 0, size = Math.min(_positional.size(), types
252: .size()); i < size; i++)
253: validateParameter(String.valueOf(i), (Class) types
254: .getValue(i), _positional.get(i));
255: } else if (_named != null) {
256: Map types = _query.getParameterTypes();
257: for (Iterator i = _named.entrySet().iterator(); i.hasNext();) {
258: Map.Entry entry = (Map.Entry) i.next();
259: String name = (String) entry.getKey();
260: validateParameter(name, (Class) types.get(name), entry
261: .getValue());
262: }
263: }
264: }
265:
266: private void validateParameter(String paramDesc, Class type,
267: Object param) {
268: // null parameters are allowed, so are not validated
269: if (param == null || type == null)
270: return;
271:
272: // check the parameter against the wrapped type
273: if (!Filters.wrap(type).isInstance(param))
274: throw new ArgumentException(_loc.get("bad-param-type",
275: paramDesc, param.getClass().getName(), type
276: .getName()), null, null, false);
277: }
278:
279: public List getResultList() {
280: _em.assertNotCloseInvoked();
281: Object ob = execute();
282: if (ob instanceof List) {
283: List ret = (List) ob;
284: if (ret instanceof ResultList)
285: return new DelegatingResultList((ResultList) ret,
286: PersistenceExceptions
287: .getRollbackTranslator(_em));
288: else
289: return ret;
290: }
291:
292: return Collections.singletonList(ob);
293: }
294:
295: /**
296: * Execute a query that returns a single result.
297: */
298: public Object getSingleResult() {
299: _em.assertNotCloseInvoked();
300: // temporarily set query to unique so that a single result is validated
301: // and returned; unset again in case the user executes query again
302: // via getResultList
303: _query.setUnique(true);
304: try {
305: return execute();
306: } finally {
307: _query.setUnique(false);
308: }
309: }
310:
311: public int executeUpdate() {
312: _em.assertNotCloseInvoked();
313: if (_query.getOperation() == QueryOperations.OP_DELETE) {
314: // handle which types of parameters we are using, if any
315: if (_positional != null)
316: return asInt(_query.deleteAll(_positional.toArray()));
317: if (_named != null)
318: return asInt(_query.deleteAll(_named));
319: return asInt(_query.deleteAll());
320: }
321: if (_query.getOperation() == QueryOperations.OP_UPDATE) {
322: // handle which types of parameters we are using, if any
323: if (_positional != null)
324: return asInt(_query.updateAll(_positional.toArray()));
325: if (_named != null)
326: return asInt(_query.updateAll(_named));
327: return asInt(_query.updateAll());
328: }
329: throw new InvalidStateException(_loc.get(
330: "not-update-delete-query", _query.getQueryString()),
331: null, null, false);
332: }
333:
334: /**
335: * Cast the specified long down to an int, first checking for overflow.
336: */
337: private static int asInt(long l) {
338: if (l > Integer.MAX_VALUE)
339: return Integer.MAX_VALUE;
340: if (l < Integer.MIN_VALUE) // unlikely, but we might as well check
341: return Integer.MIN_VALUE;
342: return (int) l;
343: }
344:
345: public FlushModeType getFlushMode() {
346: return EntityManagerImpl.fromFlushBeforeQueries(_query
347: .getFetchConfiguration().getFlushBeforeQueries());
348: }
349:
350: public OpenJPAQuery setFlushMode(FlushModeType flushMode) {
351: _em.assertNotCloseInvoked();
352: _query.getFetchConfiguration().setFlushBeforeQueries(
353: EntityManagerImpl.toFlushBeforeQueries(flushMode));
354: return this ;
355: }
356:
357: public OpenJPAQuery setHint(String key, Object value) {
358: _em.assertNotCloseInvoked();
359: if (key == null || !key.startsWith("openjpa."))
360: return this ;
361: String k = key.substring("openjpa.".length());
362:
363: try {
364: if ("Subclasses".equals(k)) {
365: if (value instanceof String)
366: value = Boolean.valueOf((String) value);
367: setSubclasses(((Boolean) value).booleanValue());
368: } else if ("FilterListener".equals(k))
369: addFilterListener(Filters.hintToFilterListener(value,
370: _query.getBroker().getClassLoader()));
371: else if ("FilterListeners".equals(k)) {
372: FilterListener[] arr = Filters.hintToFilterListeners(
373: value, _query.getBroker().getClassLoader());
374: for (int i = 0; i < arr.length; i++)
375: addFilterListener(arr[i]);
376: } else if ("AggregateListener".equals(k))
377: addAggregateListener(Filters.hintToAggregateListener(
378: value, _query.getBroker().getClassLoader()));
379: else if ("FilterListeners".equals(k)) {
380: AggregateListener[] arr = Filters
381: .hintToAggregateListeners(value, _query
382: .getBroker().getClassLoader());
383: for (int i = 0; i < arr.length; i++)
384: addAggregateListener(arr[i]);
385: } else if (k.startsWith("FetchPlan.")) {
386: k = k.substring("FetchPlan.".length());
387: hintToSetter(getFetchPlan(), k, value);
388: } else if (k.startsWith("hint.")) {
389: if ("hint.OptimizeResultCount".equals(k)) {
390: if (value instanceof String) {
391: try {
392: value = new Integer((String) value);
393: } catch (NumberFormatException nfe) {
394: }
395: }
396: if (!(value instanceof Number)
397: || ((Number) value).intValue() < 0)
398: throw new ArgumentException(_loc.get(
399: "bad-query-hint-value", key, value),
400: null, null, false);
401: }
402: _query.getFetchConfiguration().setHint(key, value);
403: } else
404: throw new ArgumentException(_loc.get("bad-query-hint",
405: key), null, null, false);
406: return this ;
407: } catch (Exception e) {
408: throw PersistenceExceptions.toPersistenceException(e);
409: }
410: }
411:
412: private void hintToSetter(FetchPlan fetchPlan, String k,
413: Object value) {
414: if (fetchPlan == null || k == null)
415: return;
416:
417: Method setter = Reflection.findSetter(fetchPlan.getClass(), k,
418: true);
419: Class paramType = setter.getParameterTypes()[0];
420: if (Enum.class.isAssignableFrom(paramType)
421: && value instanceof String)
422: value = Enum.valueOf(paramType, (String) value);
423:
424: Filters.hintToSetter(fetchPlan, k, value);
425: }
426:
427: public OpenJPAQuery setParameter(int position, Calendar value,
428: TemporalType t) {
429: return setParameter(position, value);
430: }
431:
432: public OpenJPAQuery setParameter(int position, Date value,
433: TemporalType type) {
434: return setParameter(position, value);
435: }
436:
437: public OpenJPAQuery setParameter(int position, Object value) {
438: _query.assertOpen();
439: _em.assertNotCloseInvoked();
440: _query.lock();
441: try {
442: // not allowed to mix positional and named parameters (EDR2 3.6.4)
443: if (_named != null)
444: throw new InvalidStateException(_loc.get(
445: "no-pos-named-params-mix", _query
446: .getQueryString()), null, null, false);
447:
448: if (position < 1)
449: throw new InvalidStateException(_loc.get(
450: "illegal-index", position), null, null, false);
451:
452: if (_positional == null)
453: _positional = new ArrayList();
454:
455: // make sure it is at least the requested size
456: while (_positional.size() < position)
457: _positional.add(null);
458:
459: // note that we add it to position - 1, since setPosition
460: // starts at 1, while List starts at 0
461: _positional.set(position - 1, value);
462: return this ;
463: } finally {
464: _query.unlock();
465: }
466: }
467:
468: public OpenJPAQuery setParameter(String name, Calendar value,
469: TemporalType t) {
470: return setParameter(name, value);
471: }
472:
473: public OpenJPAQuery setParameter(String name, Date value,
474: TemporalType type) {
475: return setParameter(name, value);
476: }
477:
478: public OpenJPAQuery setParameter(String name, Object value) {
479: _query.assertOpen();
480: _em.assertNotCloseInvoked();
481: _query.lock();
482: try {
483: // not allowed to mix positional and named parameters (EDR2 3.6.4)
484: if (_positional != null)
485: throw new InvalidStateException(_loc.get(
486: "no-pos-named-params-mix", _query
487: .getQueryString()), null, null, false);
488:
489: if (_named == null)
490: _named = new HashMap();
491: _named.put(name, value);
492: return this ;
493: } finally {
494: _query.unlock();
495: }
496: }
497:
498: public boolean hasPositionalParameters() {
499: return _positional != null;
500: }
501:
502: public Object[] getPositionalParameters() {
503: _query.lock();
504: try {
505: return (_positional == null) ? EMPTY_ARRAY : _positional
506: .toArray();
507: } finally {
508: _query.unlock();
509: }
510: }
511:
512: public OpenJPAQuery setParameters(Object... params) {
513: _query.assertOpen();
514: _em.assertNotCloseInvoked();
515: _query.lock();
516: try {
517: _positional = null;
518: _named = null;
519: if (params != null)
520: for (int i = 0; i < params.length; i++)
521: setParameter(i + 1, params[i]);
522: return this ;
523: } finally {
524: _query.unlock();
525: }
526: }
527:
528: public Map getNamedParameters() {
529: _query.lock();
530: try {
531: return (_named == null) ? Collections.EMPTY_MAP
532: : Collections.unmodifiableMap(_named);
533: } finally {
534: _query.unlock();
535: }
536: }
537:
538: public OpenJPAQuery setParameters(Map params) {
539: _query.assertOpen();
540: _em.assertNotCloseInvoked();
541: _query.lock();
542: try {
543: _positional = null;
544: _named = null;
545: if (params != null)
546: for (Map.Entry e : (Set<Map.Entry>) params.entrySet())
547: setParameter((String) e.getKey(), e.getValue());
548: return this ;
549: } finally {
550: _query.unlock();
551: }
552: }
553:
554: public OpenJPAQuery closeAll() {
555: _query.closeAll();
556: return this ;
557: }
558:
559: public String[] getDataStoreActions(Map params) {
560: return _query.getDataStoreActions(params);
561: }
562:
563: public int hashCode() {
564: return _query.hashCode();
565: }
566:
567: public boolean equals(Object other) {
568: if (other == this )
569: return true;
570: if (!(other instanceof QueryImpl))
571: return false;
572: return _query.equals(((QueryImpl) other)._query);
573: }
574: }
|