Author: fancy
Date: Tue May 27 23:08:41 2008
New Revision: 660825
URL: http://svn.apache.org/viewvc?rev=660825&view=rev
Log:
OPENJPA-612 Add support for calculating update value in QueryImpl.updateInMemory
Help comitting patch provided by Fay Wang
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java
Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Tue May 27 23:08:41 2008
@@ -35,10 +35,12 @@
import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
import org.apache.openjpa.jdbc.kernel.exps.JDBCStringContains;
import org.apache.openjpa.jdbc.kernel.exps.JDBCWildcardMatch;
+import org.apache.openjpa.jdbc.kernel.exps.PCPath;
import org.apache.openjpa.jdbc.kernel.exps.QueryExpressionsState;
import org.apache.openjpa.jdbc.kernel.exps.SQLEmbed;
import org.apache.openjpa.jdbc.kernel.exps.SQLExpression;
import org.apache.openjpa.jdbc.kernel.exps.SQLValue;
+import org.apache.openjpa.jdbc.kernel.exps.Val;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
@@ -50,18 +52,24 @@
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.ExpressionStoreQuery;
+import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.kernel.exps.Constant;
import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.ExpressionParser;
import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.kernel.exps.Literal;
import org.apache.openjpa.kernel.exps.QueryExpressions;
import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
import org.apache.openjpa.lib.rop.RangeResultObjectProvider;
import org.apache.openjpa.lib.rop.ResultObjectProvider;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
+import org.apache.openjpa.util.UnsupportedException;
import org.apache.openjpa.util.UserException;
import serp.util.Numbers;
@@ -671,4 +679,105 @@
throws SQLException {
return sql.prepareStatement(conn);
}
+
+ public Object evaluate(Object value, Object ob, Object[] params,
+ OpenJPAStateManager sm) {
+ if (value instanceof org.apache.openjpa.jdbc.kernel.exps.Math) {
+ org.apache.openjpa.jdbc.kernel.exps.Math mathVal =
+ (org.apache.openjpa.jdbc.kernel.exps.Math) value;
+
+ Val value1 = mathVal.getVal1();
+ Object val1 = getValue(value1, ob, params, sm);
+ Class c1 = value1.getType();
+
+ Val value2 = mathVal.getVal2();
+ Object val2 = getValue(value2, ob, params, sm);
+ Class c2 = value2.getType();
+
+ String op = mathVal.getOperation();
+
+ if (op.equals(org.apache.openjpa.jdbc.kernel.exps.Math.ADD))
+ return Filters.add(val1, c1, val2, c2);
+ else if (op.equals(
+ org.apache.openjpa.jdbc.kernel.exps.Math.SUBTRACT))
+ return Filters.subtract(val1, c1, val2, c2);
+ else if (op.equals(
+ org.apache.openjpa.jdbc.kernel.exps.Math.MULTIPLY))
+ return Filters.multiply(val1, c1, val2, c2);
+ else if (op.equals(
+ org.apache.openjpa.jdbc.kernel.exps.Math.DIVIDE))
+ return Filters.divide(val1, c1, val2, c2);
+ else if (op.equals(org.apache.openjpa.jdbc.kernel.exps.Math.MOD))
+ return Filters.mod(val1, c1, val2, c2);
+ throw new UnsupportedException();
+ }
+ throw new UnsupportedException();
+ }
+
+ private Object getValue(Object ob, FieldMapping fmd,
+ OpenJPAStateManager sm) {
+ int i = fmd.getIndex();
+ switch (fmd.getDeclaredTypeCode()) {
+ case JavaTypes.BOOLEAN:
+ return sm.fetchBooleanField(i);
+ case JavaTypes.BYTE:
+ return sm.fetchByteField(i);
+ case JavaTypes.CHAR:
+ return sm.fetchCharField(i);
+ case JavaTypes.DOUBLE:
+ return sm.fetchDoubleField(i);
+ case JavaTypes.FLOAT:
+ return sm.fetchFloatField(i);
+ case JavaTypes.INT:
+ return sm.fetchIntField(i);
+ case JavaTypes.LONG:
+ return sm.fetchLongField(i);
+ case JavaTypes.SHORT:
+ return sm.fetchShortField(i);
+ case JavaTypes.STRING:
+ return sm.fetchStringField(i);
+ case JavaTypes.DATE:
+ case JavaTypes.NUMBER:
+ case JavaTypes.BOOLEAN_OBJ:
+ case JavaTypes.BYTE_OBJ:
+ case JavaTypes.CHAR_OBJ:
+ case JavaTypes.DOUBLE_OBJ:
+ case JavaTypes.FLOAT_OBJ:
+ case JavaTypes.INT_OBJ:
+ case JavaTypes.LONG_OBJ:
+ case JavaTypes.SHORT_OBJ:
+ case JavaTypes.BIGDECIMAL:
+ case JavaTypes.BIGINTEGER:
+ case JavaTypes.LOCALE:
+ case JavaTypes.OBJECT:
+ case JavaTypes.OID:
+ return sm.fetchObjectField(i);
+ default:
+ throw new UnsupportedException();
+ }
+ }
+
+ private Object eval(Object ob, Object value, Object[] params,
+ OpenJPAStateManager sm) {
+ Object val = null;
+ if (value instanceof Literal)
+ val = ((Literal) value).getValue();
+ else if (value instanceof Constant)
+ val = ((Constant) value).getValue(params);
+ else
+ val = evaluate(value, ob, params, sm);
+
+ return val;
+ }
+
+ private Object getValue(Object value, Object ob, Object[] params,
+ OpenJPAStateManager sm) {
+ if (value instanceof org.apache.openjpa.jdbc.kernel.exps.Math)
+ return evaluate(value, ob, params, sm);
+ else if (value instanceof PCPath) {
+ FieldMapping fm = (FieldMapping)((PCPath)value).last();
+ return getValue(ob, fm, sm);
+ } else
+ return eval(ob, value, params, sm);
+ }
}
Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java?rev=660825&r1=660824&r2=660825&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java Tue May 27 23:08:41 2008
@@ -33,7 +33,7 @@
*
* @author Abe White
*/
-class Math
+public class Math
extends AbstractVal {
public static final String ADD = "+";
@@ -57,6 +57,18 @@
_op = op;
}
+ public Val getVal1() {
+ return _val1;
+ }
+
+ public Val getVal2() {
+ return _val2;
+ }
+
+ public String getOperation() {
+ return _op;
+ }
+
public ClassMetaData getMetaData() {
return _meta;
}
Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java Tue May 27 23:08:41 2008
@@ -1,660 +1,667 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.openjpa.datacache;
-
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.commons.collections.map.LinkedMap;
-import org.apache.openjpa.kernel.FetchConfiguration;
-import org.apache.openjpa.kernel.LockLevels;
-import org.apache.openjpa.kernel.QueryContext;
-import org.apache.openjpa.kernel.StoreContext;
-import org.apache.openjpa.kernel.StoreQuery;
-import org.apache.openjpa.kernel.exps.AggregateListener;
-import org.apache.openjpa.kernel.exps.FilterListener;
-import org.apache.openjpa.lib.rop.ListResultObjectProvider;
-import org.apache.openjpa.lib.rop.ResultObjectProvider;
-import org.apache.openjpa.meta.ClassMetaData;
-import org.apache.openjpa.meta.JavaTypes;
-import org.apache.openjpa.meta.MetaDataRepository;
-import org.apache.openjpa.util.ObjectNotFoundException;
-import serp.util.Numbers;
-
-/**
- * A {@link StoreQuery} implementation that caches the OIDs involved in
- * the query, and can determine whether or not the query has been dirtied.
- *
- * @author Patrick Linskey
- * @since 0.2.5.0
- */
-public class QueryCacheStoreQuery
- implements StoreQuery {
-
- private final StoreQuery _query;
- private final QueryCache _cache;
- private StoreContext _sctx;
- private MetaDataRepository _repos;
-
- /**
- * Create a new instance that delegates to <code>query</code> if no
- * cached results are available.
- */
- public QueryCacheStoreQuery(StoreQuery query, QueryCache cache) {
- _query = query;
- _cache = cache;
- }
-
- /**
- * Return the {@link QueryCache} that this object is associated with.
- */
- public QueryCache getCache() {
- return _cache;
- }
-
- /**
- * Delegate.
- */
- public StoreQuery getDelegate() {
- return _query;
- }
-
- /**
- * Look in the query cache for a result for the given query
- * key. Only look if this query is being executed outside a
- * transaction or in a transaction with IgnoreChanges set to true
- * or in a transaction with IgnoreChanges set to false but in which
- * none of the classes involved in this query have been touched.
- * Caching is not used when using object locking.
- * This is because we must obtain locks on the
- * data, and it is likely that making n trips to the database to
- * make the locks will be slower than running the query against
- * the database.
- * If the fetch configuration has query caching disabled,
- * then this method returns <code>null</code>.
- * Return the list if we meet the above criteria and if a list
- * is found for <code>qk</code>. Else, return
- * <code>null</code>.
- * This implementation means that queries against the cache
- * are of READ_COMMITTED isolation level. It'd be nice to support
- * READ_SERIALIZABLE -- to do so, we'd just return false when in
- * a transaction.
- */
- private List checkCache(QueryKey qk) {
- if (qk == null)
- return null;
- FetchConfiguration fetch = getContext().getFetchConfiguration();
- if (!fetch.getQueryCacheEnabled())
- return null;
- if (fetch.getReadLockLevel() > LockLevels.LOCK_NONE)
- return null;
-
- // get the cached data
- QueryResult res = _cache.get(qk);
- if (res == null)
- return null;
- if (res.isEmpty())
- return Collections.EMPTY_LIST;
-
- int projs = getContext().getProjectionAliases().length;
- if (projs == 0) {
- // make sure the data cache contains the oids for the query result;
- // if it doesn't, then using the result could be slower than not
- // using it because of the individual by-oid lookups
- ClassMetaData meta = _repos.getMetaData(getContext().
- getCandidateType(), _sctx.getClassLoader(), true);
- if (meta.getDataCache() == null)
- return null;
-
- BitSet idxs = meta.getDataCache().containsAll(res);
-
- // eventually we should optimize this to figure out how many objects
- // the cache is missing and if only a few do a bulk fetch for them
- int len = idxs.length();
- if (len < res.size())
- return null;
- for (int i = 0; i < len; i++)
- if (!idxs.get(i))
- return null;
- }
- return new CachedList(res, projs != 0, _sctx);
- }
-
- /**
- * Wrap the result object provider returned by our delegate in a
- * caching provider.
- */
- private ResultObjectProvider wrapResult(ResultObjectProvider rop,
- QueryKey key) {
- if (key == null)
- return rop;
- return new CachingResultObjectProvider(rop, getContext().
- getProjectionAliases().length > 0, key);
- }
-
- /**
- * Copy a projection element for caching / returning.
- */
- private static Object copyProjection(Object obj, StoreContext ctx) {
- if (obj == null)
- return null;
- switch (JavaTypes.getTypeCode(obj.getClass())) {
- case JavaTypes.STRING:
- case JavaTypes.BOOLEAN_OBJ:
- case JavaTypes.BYTE_OBJ:
- case JavaTypes.CHAR_OBJ:
- case JavaTypes.DOUBLE_OBJ:
- case JavaTypes.FLOAT_OBJ:
- case JavaTypes.INT_OBJ:
- case JavaTypes.LONG_OBJ:
- case JavaTypes.SHORT_OBJ:
- case JavaTypes.BIGDECIMAL:
- case JavaTypes.BIGINTEGER:
- case JavaTypes.OID:
- return obj;
- case JavaTypes.DATE:
- return ((Date) obj).clone();
- case JavaTypes.LOCALE:
- return ((Locale) obj).clone();
- default:
- if (obj instanceof CachedObjectId)
- return fromObjectId(((CachedObjectId) obj).oid, ctx);
- Object oid = ctx.getObjectId(obj);
- if (oid != null)
- return new CachedObjectId(oid);
- return obj;
- }
- }
-
- /**
- * Return the result object based on its cached oid.
- */
- private static Object fromObjectId(Object oid, StoreContext sctx) {
- if (oid == null)
- return null;
-
- Object obj = sctx.find(oid, null, null, null, 0);
- if (obj == null)
- throw new ObjectNotFoundException(oid);
- return obj;
- }
-
- public Object writeReplace()
- throws ObjectStreamException {
- return _query;
- }
-
- public QueryContext getContext() {
- return _query.getContext();
- }
-
- public void setContext(QueryContext qctx) {
- _query.setContext(qctx);
- _sctx = qctx.getStoreContext();
- _repos = _sctx.getConfiguration().getMetaDataRepositoryInstance();
- }
-
- public boolean setQuery(Object query) {
- return _query.setQuery(query);
- }
-
- public FilterListener getFilterListener(String tag) {
- return _query.getFilterListener(tag);
- }
-
- public AggregateListener getAggregateListener(String tag) {
- return _query.getAggregateListener(tag);
- }
-
- public Object newCompilationKey() {
- return _query.newCompilationKey();
- }
-
- public Object newCompilation() {
- return _query.newCompilation();
- }
-
- public void populateFromCompilation(Object comp) {
- _query.populateFromCompilation(comp);
- }
-
- public void invalidateCompilation() {
- _query.invalidateCompilation();
- }
-
- public boolean supportsDataStoreExecution() {
- return _query.supportsDataStoreExecution();
- }
-
- public boolean supportsInMemoryExecution() {
- return _query.supportsInMemoryExecution();
- }
-
- public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs) {
- return _query.newInMemoryExecutor(meta, subs);
- }
-
- public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs) {
- Executor ex = _query.newDataStoreExecutor(meta, subs);
- return new QueryCacheExecutor(ex, meta, subs);
- }
-
- public boolean supportsAbstractExecutors() {
- return _query.supportsAbstractExecutors();
- }
-
- public boolean requiresCandidateType() {
- return _query.requiresCandidateType();
- }
-
- public boolean requiresParameterDeclarations() {
- return _query.requiresParameterDeclarations();
- }
-
- public boolean supportsParameterDeclarations() {
- return _query.supportsParameterDeclarations();
- }
-
- /**
- * Caching executor.
- */
- private static class QueryCacheExecutor
- implements Executor {
-
- private final Executor _ex;
- private final Class _candidate;
- private final boolean _subs;
-
- public QueryCacheExecutor(Executor ex, ClassMetaData meta,
- boolean subs) {
- _ex = ex;
- _candidate = (meta == null) ? null : meta.getDescribedType();
- _subs = subs;
- }
-
- public ResultObjectProvider executeQuery(StoreQuery q, Object[] params,
- Range range) {
- QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
- QueryKey key = QueryKey.newInstance(cq.getContext(),
- _ex.isPacking(q), params, _candidate, _subs, range.start,
- range.end);
- List cached = cq.checkCache(key);
- if (cached != null)
- return new ListResultObjectProvider(cached);
-
- ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(),
- params, range);
- return cq.wrapResult(rop, key);
- }
-
- /**
- * Clear the cached queries associated with the access path
- * classes in the query. This is done when bulk operations
- * (such as deletes or updates) are performed so that the
- * cache remains up-to-date.
- */
- private void clearAccessPath(StoreQuery q) {
- if (q == null)
- return;
-
- ClassMetaData[] cmd = getAccessPathMetaDatas(q);
- if (cmd == null || cmd.length == 0)
- return;
-
- List classes = new ArrayList(cmd.length);
- for (int i = 0; i < cmd.length; i++)
- classes.add(cmd[i].getDescribedType());
-
- // evict from the query cache
- QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
- cq.getCache().onTypesChanged(new TypesChangedEvent
- (q.getContext(), classes));
-
- // evict from the data cache
- for (int i = 0; i < cmd.length; i++) {
- if (cmd[i].getDataCache() != null)
- cmd[i].getDataCache().removeAll(
- cmd[i].getDescribedType(), true);
- }
- }
-
- public Number executeDelete(StoreQuery q, Object[] params) {
- try {
- return _ex.executeDelete(unwrap(q), params);
- } finally {
- clearAccessPath(q);
- }
- }
-
- public Number executeUpdate(StoreQuery q, Object[] params) {
- try {
- return _ex.executeUpdate(unwrap(q), params);
- } finally {
- clearAccessPath(q);
- }
- }
-
- public String[] getDataStoreActions(StoreQuery q, Object[] params,
- Range range) {
- return EMPTY_STRINGS;
- }
-
- public void validate(StoreQuery q) {
- _ex.validate(unwrap(q));
- }
-
- public void getRange(StoreQuery q, Object[] params, Range range) {
- _ex.getRange(q, params, range);
- }
-
- public Object getOrderingValue(StoreQuery q, Object[] params,
- Object resultObject, int orderIndex) {
- return _ex.getOrderingValue(unwrap(q), params, resultObject,
- orderIndex);
- }
-
- public boolean[] getAscending(StoreQuery q) {
- return _ex.getAscending(unwrap(q));
- }
-
- public boolean isPacking(StoreQuery q) {
- return _ex.isPacking(unwrap(q));
- }
-
- public String getAlias(StoreQuery q) {
- return _ex.getAlias(unwrap(q));
- }
-
- public Class getResultClass(StoreQuery q) {
- return _ex.getResultClass(unwrap(q));
- }
-
- public String[] getProjectionAliases(StoreQuery q) {
- return _ex.getProjectionAliases(unwrap(q));
- }
-
- public Class[] getProjectionTypes(StoreQuery q) {
- return _ex.getProjectionTypes(unwrap(q));
- }
-
- public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
- return _ex.getAccessPathMetaDatas(unwrap(q));
- }
-
- public int getOperation(StoreQuery q) {
- return _ex.getOperation(unwrap(q));
- }
-
- public boolean isAggregate(StoreQuery q) {
- return _ex.isAggregate(unwrap(q));
- }
-
- public boolean hasGrouping(StoreQuery q) {
- return _ex.hasGrouping(unwrap(q));
- }
-
- public LinkedMap getParameterTypes(StoreQuery q) {
- return _ex.getParameterTypes(unwrap(q));
- }
-
- public Map getUpdates(StoreQuery q) {
- return _ex.getUpdates(unwrap(q));
- }
-
- private static StoreQuery unwrap(StoreQuery q) {
- return ((QueryCacheStoreQuery) q).getDelegate();
- }
- }
-
- /**
- * Result list implementation for a cached query result. Package-protected
- * for testing.
- */
- public static class CachedList
- extends AbstractList
- implements Serializable {
-
- private final QueryResult _res;
- private final boolean _proj;
- private final StoreContext _sctx;
-
- public CachedList(QueryResult res, boolean proj, StoreContext ctx) {
- _res = res;
- _proj = proj;
- _sctx = ctx;
- }
-
- public Object get(int idx) {
- if (!_proj)
- return fromObjectId(_res.get(idx), _sctx);
-
- Object[] cached = (Object[]) _res.get(idx);
- if (cached == null)
- return null;
- Object[] uncached = new Object[cached.length];
- for (int i = 0; i < cached.length; i++)
- uncached[i] = copyProjection(cached[i], _sctx);
- return uncached;
- }
-
- public int size() {
- return _res.size();
- }
-
- public Object writeReplace()
- throws ObjectStreamException {
- return new ArrayList(this);
- }
- }
-
- /**
- * A wrapper around a {@link ResultObjectProvider} that builds up a list of
- * all the OIDs in this list and registers that list with the
- * query cache. Abandons monitoring and registering if one of the classes
- * in the access path is modified while the query results are being loaded.
- */
- private class CachingResultObjectProvider
- implements ResultObjectProvider, TypesChangedListener {
-
- private final ResultObjectProvider _rop;
- private final boolean _proj;
- private final QueryKey _qk;
- private final TreeMap _data = new TreeMap();
- private boolean _maintainCache = true;
- private int _pos = -1;
-
- // used to determine list size without necessarily calling size(),
- // which may require a DB trip or return Integer.MAX_VALUE
- private int _max = -1;
- private int _size = Integer.MAX_VALUE;
-
- /**
- * Constructor. Supply delegate result provider and our query key.
- */
- public CachingResultObjectProvider(ResultObjectProvider rop,
- boolean proj, QueryKey key) {
- _rop = rop;
- _proj = proj;
- _qk = key;
- _cache.addTypesChangedListener(this);
- }
-
- /**
- * Stop caching.
- */
- private void abortCaching() {
- if (!_maintainCache)
- return;
-
- // this can be called via an event from another thread
- synchronized (this) {
- // it's important that we set this flag first so that any
- // subsequent calls to this object are bypassed.
- _maintainCache = false;
- _cache.removeTypesChangedListener(this);
- _data.clear();
- }
- }
-
- /**
- * Check whether we've buffered all results, while optionally adding
- * the given result.
- */
- private void checkFinished(Object obj, boolean result) {
- // this can be called at the same time as abortCaching via
- // a types changed event
- boolean finished = false;
- synchronized (this) {
- if (_maintainCache) {
- if (result) {
- Integer index = Numbers.valueOf(_pos);
- if (!_data.containsKey(index)) {
- Object cached;
- if (obj == null)
- cached = null;
- else if (!_proj)
- cached = _sctx.getObjectId(obj);
- else {
- Object[] arr = (Object[]) obj;
- Object[] cp = new Object[arr.length];
- for (int i = 0; i < arr.length; i++)
- cp[i] = copyProjection(arr[i], _sctx);
- cached = cp;
- }
- if (cached != null)
- _data.put(index, cached);
- }
- }
- finished = _size == _data.size();
- }
- }
-
- if (finished) {
- // an abortCaching call can sneak in here via onExpire; the
- // cache is locked during event firings, so the lock here will
- // wait for it (or will force the next firing to wait)
- _cache.writeLock();
- try {
- // make sure we didn't abort
- if (_maintainCache) {
- QueryResult res = new QueryResult(_qk, _data.values());
- _cache.put(_qk, res);
- abortCaching();
- }
- }
- finally {
- _cache.writeUnlock();
- }
- }
- }
-
- public boolean supportsRandomAccess() {
- return _rop.supportsRandomAccess();
- }
-
- public void open()
- throws Exception {
- _rop.open();
- }
-
- public Object getResultObject()
- throws Exception {
- Object obj = _rop.getResultObject();
- checkFinished(obj, true);
- return obj;
- }
-
- public boolean next()
- throws Exception {
- _pos++;
- boolean next = _rop.next();
- if (!next && _pos == _max + 1) {
- _size = _pos;
- checkFinished(null, false);
- } else if (next && _pos > _max)
- _max = _pos;
- return next;
- }
-
- public boolean absolute(int pos)
- throws Exception {
- _pos = pos;
- boolean valid = _rop.absolute(pos);
- if (!valid && _pos == _max + 1) {
- _size = _pos;
- checkFinished(null, false);
- } else if (valid && _pos > _max)
- _max = _pos;
- return valid;
- }
-
- public int size()
- throws Exception {
- if (_size != Integer.MAX_VALUE)
- return _size;
- int size = _rop.size();
- _size = size;
- checkFinished(null, false);
- return size;
- }
-
- public void reset()
- throws Exception {
- _rop.reset();
- _pos = -1;
- }
-
- public void close()
- throws Exception {
- abortCaching();
- _rop.close();
- }
-
- public void handleCheckedException(Exception e) {
- _rop.handleCheckedException(e);
- }
-
- public void onTypesChanged(TypesChangedEvent ev) {
- if (_qk.changeInvalidatesQuery(ev.getTypes()))
- abortCaching();
- }
- }
-
- /**
- * Struct to recognize cached oids.
- */
- private static class CachedObjectId {
-
- public final Object oid;
-
- public CachedObjectId (Object oid)
- {
- this.oid = oid;
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.datacache;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.LockLevels;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.QueryContext;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.kernel.StoreQuery;
+import org.apache.openjpa.kernel.exps.AggregateListener;
+import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.lib.rop.ListResultObjectProvider;
+import org.apache.openjpa.lib.rop.ResultObjectProvider;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.util.ObjectNotFoundException;
+
+import serp.util.Numbers;
+
+/**
+ * A {@link StoreQuery} implementation that caches the OIDs involved in
+ * the query, and can determine whether or not the query has been dirtied.
+ *
+ * @author Patrick Linskey
+ * @since 0.2.5.0
+ */
+public class QueryCacheStoreQuery
+ implements StoreQuery {
+
+ private final StoreQuery _query;
+ private final QueryCache _cache;
+ private StoreContext _sctx;
+ private MetaDataRepository _repos;
+
+ /**
+ * Create a new instance that delegates to <code>query</code> if no
+ * cached results are available.
+ */
+ public QueryCacheStoreQuery(StoreQuery query, QueryCache cache) {
+ _query = query;
+ _cache = cache;
+ }
+
+ /**
+ * Return the {@link QueryCache} that this object is associated with.
+ */
+ public QueryCache getCache() {
+ return _cache;
+ }
+
+ /**
+ * Delegate.
+ */
+ public StoreQuery getDelegate() {
+ return _query;
+ }
+
+ /**
+ * Look in the query cache for a result for the given query
+ * key. Only look if this query is being executed outside a
+ * transaction or in a transaction with IgnoreChanges set to true
+ * or in a transaction with IgnoreChanges set to false but in which
+ * none of the classes involved in this query have been touched.
+ * Caching is not used when using object locking.
+ * This is because we must obtain locks on the
+ * data, and it is likely that making n trips to the database to
+ * make the locks will be slower than running the query against
+ * the database.
+ * If the fetch configuration has query caching disabled,
+ * then this method returns <code>null</code>.
+ * Return the list if we meet the above criteria and if a list
+ * is found for <code>qk</code>. Else, return
+ * <code>null</code>.
+ * This implementation means that queries against the cache
+ * are of READ_COMMITTED isolation level. It'd be nice to support
+ * READ_SERIALIZABLE -- to do so, we'd just return false when in
+ * a transaction.
+ */
+ private List checkCache(QueryKey qk) {
+ if (qk == null)
+ return null;
+ FetchConfiguration fetch = getContext().getFetchConfiguration();
+ if (!fetch.getQueryCacheEnabled())
+ return null;
+ if (fetch.getReadLockLevel() > LockLevels.LOCK_NONE)
+ return null;
+
+ // get the cached data
+ QueryResult res = _cache.get(qk);
+ if (res == null)
+ return null;
+ if (res.isEmpty())
+ return Collections.EMPTY_LIST;
+
+ int projs = getContext().getProjectionAliases().length;
+ if (projs == 0) {
+ // make sure the data cache contains the oids for the query result;
+ // if it doesn't, then using the result could be slower than not
+ // using it because of the individual by-oid lookups
+ ClassMetaData meta = _repos.getMetaData(getContext().
+ getCandidateType(), _sctx.getClassLoader(), true);
+ if (meta.getDataCache() == null)
+ return null;
+
+ BitSet idxs = meta.getDataCache().containsAll(res);
+
+ // eventually we should optimize this to figure out how many objects
+ // the cache is missing and if only a few do a bulk fetch for them
+ int len = idxs.length();
+ if (len < res.size())
+ return null;
+ for (int i = 0; i < len; i++)
+ if (!idxs.get(i))
+ return null;
+ }
+ return new CachedList(res, projs != 0, _sctx);
+ }
+
+ /**
+ * Wrap the result object provider returned by our delegate in a
+ * caching provider.
+ */
+ private ResultObjectProvider wrapResult(ResultObjectProvider rop,
+ QueryKey key) {
+ if (key == null)
+ return rop;
+ return new CachingResultObjectProvider(rop, getContext().
+ getProjectionAliases().length > 0, key);
+ }
+
+ /**
+ * Copy a projection element for caching / returning.
+ */
+ private static Object copyProjection(Object obj, StoreContext ctx) {
+ if (obj == null)
+ return null;
+ switch (JavaTypes.getTypeCode(obj.getClass())) {
+ case JavaTypes.STRING:
+ case JavaTypes.BOOLEAN_OBJ:
+ case JavaTypes.BYTE_OBJ:
+ case JavaTypes.CHAR_OBJ:
+ case JavaTypes.DOUBLE_OBJ:
+ case JavaTypes.FLOAT_OBJ:
+ case JavaTypes.INT_OBJ:
+ case JavaTypes.LONG_OBJ:
+ case JavaTypes.SHORT_OBJ:
+ case JavaTypes.BIGDECIMAL:
+ case JavaTypes.BIGINTEGER:
+ case JavaTypes.OID:
+ return obj;
+ case JavaTypes.DATE:
+ return ((Date) obj).clone();
+ case JavaTypes.LOCALE:
+ return ((Locale) obj).clone();
+ default:
+ if (obj instanceof CachedObjectId)
+ return fromObjectId(((CachedObjectId) obj).oid, ctx);
+ Object oid = ctx.getObjectId(obj);
+ if (oid != null)
+ return new CachedObjectId(oid);
+ return obj;
+ }
+ }
+
+ /**
+ * Return the result object based on its cached oid.
+ */
+ private static Object fromObjectId(Object oid, StoreContext sctx) {
+ if (oid == null)
+ return null;
+
+ Object obj = sctx.find(oid, null, null, null, 0);
+ if (obj == null)
+ throw new ObjectNotFoundException(oid);
+ return obj;
+ }
+
+ public Object writeReplace()
+ throws ObjectStreamException {
+ return _query;
+ }
+
+ public QueryContext getContext() {
+ return _query.getContext();
+ }
+
+ public void setContext(QueryContext qctx) {
+ _query.setContext(qctx);
+ _sctx = qctx.getStoreContext();
+ _repos = _sctx.getConfiguration().getMetaDataRepositoryInstance();
+ }
+
+ public boolean setQuery(Object query) {
+ return _query.setQuery(query);
+ }
+
+ public FilterListener getFilterListener(String tag) {
+ return _query.getFilterListener(tag);
+ }
+
+ public AggregateListener getAggregateListener(String tag) {
+ return _query.getAggregateListener(tag);
+ }
+
+ public Object newCompilationKey() {
+ return _query.newCompilationKey();
+ }
+
+ public Object newCompilation() {
+ return _query.newCompilation();
+ }
+
+ public void populateFromCompilation(Object comp) {
+ _query.populateFromCompilation(comp);
+ }
+
+ public void invalidateCompilation() {
+ _query.invalidateCompilation();
+ }
+
+ public boolean supportsDataStoreExecution() {
+ return _query.supportsDataStoreExecution();
+ }
+
+ public boolean supportsInMemoryExecution() {
+ return _query.supportsInMemoryExecution();
+ }
+
+ public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs) {
+ return _query.newInMemoryExecutor(meta, subs);
+ }
+
+ public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs) {
+ Executor ex = _query.newDataStoreExecutor(meta, subs);
+ return new QueryCacheExecutor(ex, meta, subs);
+ }
+
+ public boolean supportsAbstractExecutors() {
+ return _query.supportsAbstractExecutors();
+ }
+
+ public boolean requiresCandidateType() {
+ return _query.requiresCandidateType();
+ }
+
+ public boolean requiresParameterDeclarations() {
+ return _query.requiresParameterDeclarations();
+ }
+
+ public boolean supportsParameterDeclarations() {
+ return _query.supportsParameterDeclarations();
+ }
+
+ public Object evaluate(Object value, Object ob, Object[] params,
+ OpenJPAStateManager sm) {
+ return _query.evaluate(value, ob, params, sm);
+ }
+
+ /**
+ * Caching executor.
+ */
+ private static class QueryCacheExecutor
+ implements Executor {
+
+ private final Executor _ex;
+ private final Class _candidate;
+ private final boolean _subs;
+
+ public QueryCacheExecutor(Executor ex, ClassMetaData meta,
+ boolean subs) {
+ _ex = ex;
+ _candidate = (meta == null) ? null : meta.getDescribedType();
+ _subs = subs;
+ }
+
+ public ResultObjectProvider executeQuery(StoreQuery q, Object[] params,
+ Range range) {
+ QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
+ QueryKey key = QueryKey.newInstance(cq.getContext(),
+ _ex.isPacking(q), params, _candidate, _subs, range.start,
+ range.end);
+ List cached = cq.checkCache(key);
+ if (cached != null)
+ return new ListResultObjectProvider(cached);
+
+ ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(),
+ params, range);
+ return cq.wrapResult(rop, key);
+ }
+
+ /**
+ * Clear the cached queries associated with the access path
+ * classes in the query. This is done when bulk operations
+ * (such as deletes or updates) are performed so that the
+ * cache remains up-to-date.
+ */
+ private void clearAccessPath(StoreQuery q) {
+ if (q == null)
+ return;
+
+ ClassMetaData[] cmd = getAccessPathMetaDatas(q);
+ if (cmd == null || cmd.length == 0)
+ return;
+
+ List classes = new ArrayList(cmd.length);
+ for (int i = 0; i < cmd.length; i++)
+ classes.add(cmd[i].getDescribedType());
+
+ // evict from the query cache
+ QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
+ cq.getCache().onTypesChanged(new TypesChangedEvent
+ (q.getContext(), classes));
+
+ // evict from the data cache
+ for (int i = 0; i < cmd.length; i++) {
+ if (cmd[i].getDataCache() != null)
+ cmd[i].getDataCache().removeAll(
+ cmd[i].getDescribedType(), true);
+ }
+ }
+
+ public Number executeDelete(StoreQuery q, Object[] params) {
+ try {
+ return _ex.executeDelete(unwrap(q), params);
+ } finally {
+ clearAccessPath(q);
+ }
+ }
+
+ public Number executeUpdate(StoreQuery q, Object[] params) {
+ try {
+ return _ex.executeUpdate(unwrap(q), params);
+ } finally {
+ clearAccessPath(q);
+ }
+ }
+
+ public String[] getDataStoreActions(StoreQuery q, Object[] params,
+ Range range) {
+ return EMPTY_STRINGS;
+ }
+
+ public void validate(StoreQuery q) {
+ _ex.validate(unwrap(q));
+ }
+
+ public void getRange(StoreQuery q, Object[] params, Range range) {
+ _ex.getRange(q, params, range);
+ }
+
+ public Object getOrderingValue(StoreQuery q, Object[] params,
+ Object resultObject, int orderIndex) {
+ return _ex.getOrderingValue(unwrap(q), params, resultObject,
+ orderIndex);
+ }
+
+ public boolean[] getAscending(StoreQuery q) {
+ return _ex.getAscending(unwrap(q));
+ }
+
+ public boolean isPacking(StoreQuery q) {
+ return _ex.isPacking(unwrap(q));
+ }
+
+ public String getAlias(StoreQuery q) {
+ return _ex.getAlias(unwrap(q));
+ }
+
+ public Class getResultClass(StoreQuery q) {
+ return _ex.getResultClass(unwrap(q));
+ }
+
+ public String[] getProjectionAliases(StoreQuery q) {
+ return _ex.getProjectionAliases(unwrap(q));
+ }
+
+ public Class[] getProjectionTypes(StoreQuery q) {
+ return _ex.getProjectionTypes(unwrap(q));
+ }
+
+ public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
+ return _ex.getAccessPathMetaDatas(unwrap(q));
+ }
+
+ public int getOperation(StoreQuery q) {
+ return _ex.getOperation(unwrap(q));
+ }
+
+ public boolean isAggregate(StoreQuery q) {
+ return _ex.isAggregate(unwrap(q));
+ }
+
+ public boolean hasGrouping(StoreQuery q) {
+ return _ex.hasGrouping(unwrap(q));
+ }
+
+ public LinkedMap getParameterTypes(StoreQuery q) {
+ return _ex.getParameterTypes(unwrap(q));
+ }
+
+ public Map getUpdates(StoreQuery q) {
+ return _ex.getUpdates(unwrap(q));
+ }
+
+ private static StoreQuery unwrap(StoreQuery q) {
+ return ((QueryCacheStoreQuery) q).getDelegate();
+ }
+ }
+
+ /**
+ * Result list implementation for a cached query result. Package-protected
+ * for testing.
+ */
+ public static class CachedList
+ extends AbstractList
+ implements Serializable {
+
+ private final QueryResult _res;
+ private final boolean _proj;
+ private final StoreContext _sctx;
+
+ public CachedList(QueryResult res, boolean proj, StoreContext ctx) {
+ _res = res;
+ _proj = proj;
+ _sctx = ctx;
+ }
+
+ public Object get(int idx) {
+ if (!_proj)
+ return fromObjectId(_res.get(idx), _sctx);
+
+ Object[] cached = (Object[]) _res.get(idx);
+ if (cached == null)
+ return null;
+ Object[] uncached = new Object[cached.length];
+ for (int i = 0; i < cached.length; i++)
+ uncached[i] = copyProjection(cached[i], _sctx);
+ return uncached;
+ }
+
+ public int size() {
+ return _res.size();
+ }
+
+ public Object writeReplace()
+ throws ObjectStreamException {
+ return new ArrayList(this);
+ }
+ }
+
+ /**
+ * A wrapper around a {@link ResultObjectProvider} that builds up a list of
+ * all the OIDs in this list and registers that list with the
+ * query cache. Abandons monitoring and registering if one of the classes
+ * in the access path is modified while the query results are being loaded.
+ */
+ private class CachingResultObjectProvider
+ implements ResultObjectProvider, TypesChangedListener {
+
+ private final ResultObjectProvider _rop;
+ private final boolean _proj;
+ private final QueryKey _qk;
+ private final TreeMap _data = new TreeMap();
+ private boolean _maintainCache = true;
+ private int _pos = -1;
+
+ // used to determine list size without necessarily calling size(),
+ // which may require a DB trip or return Integer.MAX_VALUE
+ private int _max = -1;
+ private int _size = Integer.MAX_VALUE;
+
+ /**
+ * Constructor. Supply delegate result provider and our query key.
+ */
+ public CachingResultObjectProvider(ResultObjectProvider rop,
+ boolean proj, QueryKey key) {
+ _rop = rop;
+ _proj = proj;
+ _qk = key;
+ _cache.addTypesChangedListener(this);
+ }
+
+ /**
+ * Stop caching.
+ */
+ private void abortCaching() {
+ if (!_maintainCache)
+ return;
+
+ // this can be called via an event from another thread
+ synchronized (this) {
+ // it's important that we set this flag first so that any
+ // subsequent calls to this object are bypassed.
+ _maintainCache = false;
+ _cache.removeTypesChangedListener(this);
+ _data.clear();
+ }
+ }
+
+ /**
+ * Check whether we've buffered all results, while optionally adding
+ * the given result.
+ */
+ private void checkFinished(Object obj, boolean result) {
+ // this can be called at the same time as abortCaching via
+ // a types changed event
+ boolean finished = false;
+ synchronized (this) {
+ if (_maintainCache) {
+ if (result) {
+ Integer index = Numbers.valueOf(_pos);
+ if (!_data.containsKey(index)) {
+ Object cached;
+ if (obj == null)
+ cached = null;
+ else if (!_proj)
+ cached = _sctx.getObjectId(obj);
+ else {
+ Object[] arr = (Object[]) obj;
+ Object[] cp = new Object[arr.length];
+ for (int i = 0; i < arr.length; i++)
+ cp[i] = copyProjection(arr[i], _sctx);
+ cached = cp;
+ }
+ if (cached != null)
+ _data.put(index, cached);
+ }
+ }
+ finished = _size == _data.size();
+ }
+ }
+
+ if (finished) {
+ // an abortCaching call can sneak in here via onExpire; the
+ // cache is locked during event firings, so the lock here will
+ // wait for it (or will force the next firing to wait)
+ _cache.writeLock();
+ try {
+ // make sure we didn't abort
+ if (_maintainCache) {
+ QueryResult res = new QueryResult(_qk, _data.values());
+ _cache.put(_qk, res);
+ abortCaching();
+ }
+ }
+ finally {
+ _cache.writeUnlock();
+ }
+ }
+ }
+
+ public boolean supportsRandomAccess() {
+ return _rop.supportsRandomAccess();
+ }
+
+ public void open()
+ throws Exception {
+ _rop.open();
+ }
+
+ public Object getResultObject()
+ throws Exception {
+ Object obj = _rop.getResultObject();
+ checkFinished(obj, true);
+ return obj;
+ }
+
+ public boolean next()
+ throws Exception {
+ _pos++;
+ boolean next = _rop.next();
+ if (!next && _pos == _max + 1) {
+ _size = _pos;
+ checkFinished(null, false);
+ } else if (next && _pos > _max)
+ _max = _pos;
+ return next;
+ }
+
+ public boolean absolute(int pos)
+ throws Exception {
+ _pos = pos;
+ boolean valid = _rop.absolute(pos);
+ if (!valid && _pos == _max + 1) {
+ _size = _pos;
+ checkFinished(null, false);
+ } else if (valid && _pos > _max)
+ _max = _pos;
+ return valid;
+ }
+
+ public int size()
+ throws Exception {
+ if (_size != Integer.MAX_VALUE)
+ return _size;
+ int size = _rop.size();
+ _size = size;
+ checkFinished(null, false);
+ return size;
+ }
+
+ public void reset()
+ throws Exception {
+ _rop.reset();
+ _pos = -1;
+ }
+
+ public void close()
+ throws Exception {
+ abortCaching();
+ _rop.close();
+ }
+
+ public void handleCheckedException(Exception e) {
+ _rop.handleCheckedException(e);
+ }
+
+ public void onTypesChanged(TypesChangedEvent ev) {
+ if (_qk.changeInvalidatesQuery(ev.getTypes()))
+ abortCaching();
+ }
+ }
+
+ /**
+ * Struct to recognize cached oids.
+ */
+ private static class CachedObjectId {
+
+ public final Object oid;
+
+ public CachedObjectId (Object oid)
+ {
+ this.oid = oid;
+ }
+ }
+}
Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java Tue May 27 23:08:41 2008
@@ -1,184 +1,190 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.openjpa.kernel;
-
-import java.util.Map;
-
-import org.apache.commons.collections.map.LinkedMap;
-import org.apache.openjpa.kernel.exps.AggregateListener;
-import org.apache.openjpa.kernel.exps.FilterListener;
-import org.apache.openjpa.meta.ClassMetaData;
-import org.apache.openjpa.util.InternalException;
-
-/**
- * Abstract {@link StoreQuery} that implements most methods as no-ops.
- *
- * @author Abe White
- * @since 0.4.0
- */
-public abstract class AbstractStoreQuery
- implements StoreQuery {
-
- protected QueryContext ctx = null;
-
- public QueryContext getContext() {
- return ctx;
- }
-
- public void setContext(QueryContext ctx) {
- this.ctx = ctx;
- }
-
- public boolean setQuery(Object query) {
- return false;
- }
-
- public FilterListener getFilterListener(String tag) {
- return null;
- }
-
- public AggregateListener getAggregateListener(String tag) {
- return null;
- }
-
- public Object newCompilationKey() {
- return null;
- }
-
- public Object newCompilation() {
- return null;
- }
-
- public void populateFromCompilation(Object comp) {
- }
-
- public void invalidateCompilation() {
- }
-
- public boolean supportsDataStoreExecution() {
- return false;
- }
-
- public boolean supportsInMemoryExecution() {
- return false;
- }
-
- public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs) {
- throw new InternalException();
- }
-
- public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs) {
- throw new InternalException();
- }
-
- public boolean supportsAbstractExecutors() {
- return false;
- }
-
- public boolean requiresCandidateType() {
- return true;
- }
-
- public boolean requiresParameterDeclarations() {
- return true;
- }
-
- public boolean supportsParameterDeclarations() {
- return true;
- }
-
- /**
- * Abstract {@link Executor} that implements most methods as no-ops.
- */
- public static abstract class AbstractExecutor
- implements Executor {
-
- public Number executeDelete(StoreQuery q, Object[] params) {
- return q.getContext().deleteInMemory(q, this, params);
- }
-
- public Number executeUpdate(StoreQuery q, Object[] params) {
- return q.getContext().updateInMemory(q, this, params);
- }
-
- public String[] getDataStoreActions(StoreQuery q, Object[] params,
- Range range) {
- return EMPTY_STRINGS;
- }
-
- public void validate(StoreQuery q) {
- }
-
- public void getRange(StoreQuery q, Object[] params, Range range) {
- }
-
- public Object getOrderingValue(StoreQuery q, Object[] params,
- Object resultObject, int orderIndex) {
- return null;
- }
-
- public boolean[] getAscending(StoreQuery q) {
- return EMPTY_BOOLEANS;
- }
-
- public boolean isPacking(StoreQuery q) {
- return false;
- }
-
- public String getAlias(StoreQuery q) {
- return null;
- }
-
- public String[] getProjectionAliases(StoreQuery q) {
- return EMPTY_STRINGS;
- }
-
- public Class[] getProjectionTypes(StoreQuery q) {
- return EMPTY_CLASSES;
- }
-
- public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
- return EMPTY_METAS;
- }
-
- public int getOperation(StoreQuery q) {
- return OP_SELECT;
- }
-
- public boolean isAggregate(StoreQuery q) {
- return false;
- }
-
- public boolean hasGrouping(StoreQuery q) {
- return false;
- }
-
- public LinkedMap getParameterTypes(StoreQuery q) {
- return EMPTY_PARAMS;
- }
-
- public Class getResultClass(StoreQuery q) {
- return null;
- }
-
- public Map getUpdates(StoreQuery q) {
- return null;
- }
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel;
+
+import java.util.Map;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.openjpa.kernel.exps.AggregateListener;
+import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.UnsupportedException;
+
+/**
+ * Abstract {@link StoreQuery} that implements most methods as no-ops.
+ *
+ * @author Abe White
+ * @since 0.4.0
+ */
+public abstract class AbstractStoreQuery
+ implements StoreQuery {
+
+ protected QueryContext ctx = null;
+
+ public QueryContext getContext() {
+ return ctx;
+ }
+
+ public void setContext(QueryContext ctx) {
+ this.ctx = ctx;
+ }
+
+ public boolean setQuery(Object query) {
+ return false;
+ }
+
+ public FilterListener getFilterListener(String tag) {
+ return null;
+ }
+
+ public AggregateListener getAggregateListener(String tag) {
+ return null;
+ }
+
+ public Object newCompilationKey() {
+ return null;
+ }
+
+ public Object newCompilation() {
+ return null;
+ }
+
+ public void populateFromCompilation(Object comp) {
+ }
+
+ public void invalidateCompilation() {
+ }
+
+ public boolean supportsDataStoreExecution() {
+ return false;
+ }
+
+ public boolean supportsInMemoryExecution() {
+ return false;
+ }
+
+ public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs) {
+ throw new InternalException();
+ }
+
+ public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs) {
+ throw new InternalException();
+ }
+
+ public boolean supportsAbstractExecutors() {
+ return false;
+ }
+
+ public boolean requiresCandidateType() {
+ return true;
+ }
+
+ public boolean requiresParameterDeclarations() {
+ return true;
+ }
+
+ public boolean supportsParameterDeclarations() {
+ return true;
+ }
+
+ public Object evaluate(Object value, Object ob, Object[] params,
+ OpenJPAStateManager sm) {
+ throw new UnsupportedException();
+ }
+
+ /**
+ * Abstract {@link Executor} that implements most methods as no-ops.
+ */
+ public static abstract class AbstractExecutor
+ implements Executor {
+
+ public Number executeDelete(StoreQuery q, Object[] params) {
+ return q.getContext().deleteInMemory(q, this, params);
+ }
+
+ public Number executeUpdate(StoreQuery q, Object[] params) {
+ return q.getContext().updateInMemory(q, this, params);
+ }
+
+ public String[] getDataStoreActions(StoreQuery q, Object[] params,
+ Range range) {
+ return EMPTY_STRINGS;
+ }
+
+ public void validate(StoreQuery q) {
+ }
+
+ public void getRange(StoreQuery q, Object[] params, Range range) {
+ }
+
+ public Object getOrderingValue(StoreQuery q, Object[] params,
+ Object resultObject, int orderIndex) {
+ return null;
+ }
+
+ public boolean[] getAscending(StoreQuery q) {
+ return EMPTY_BOOLEANS;
+ }
+
+ public boolean isPacking(StoreQuery q) {
+ return false;
+ }
+
+ public String getAlias(StoreQuery q) {
+ return null;
+ }
+
+ public String[] getProjectionAliases(StoreQuery q) {
+ return EMPTY_STRINGS;
+ }
+
+ public Class[] getProjectionTypes(StoreQuery q) {
+ return EMPTY_CLASSES;
+ }
+
+ public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
+ return EMPTY_METAS;
+ }
+
+ public int getOperation(StoreQuery q) {
+ return OP_SELECT;
+ }
+
+ public boolean isAggregate(StoreQuery q) {
+ return false;
+ }
+
+ public boolean hasGrouping(StoreQuery q) {
+ return false;
+ }
+
+ public LinkedMap getParameterTypes(StoreQuery q) {
+ return EMPTY_PARAMS;
+ }
+
+ public Class getResultClass(StoreQuery q) {
+ return null;
+ }
+
+ public Map getUpdates(StoreQuery q) {
+ return null;
+ }
+ }
+}
Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java?rev=660825&r1=660824&r2=660825&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java Tue May 27 23:08:41 2008
@@ -1048,7 +1048,7 @@
int size = 0;
for (Iterator i = ((Collection) o).iterator(); i.hasNext(); size++)
- updateInMemory(i.next(), params);
+ updateInMemory(i.next(), params, q);
return Numbers.valueOf(size);
} catch (OpenJPAException ke) {
throw ke;
@@ -1063,12 +1063,13 @@
* @param ob the persistent instance to change
* @param params the parameters passed to the query
*/
- private void updateInMemory(Object ob, Object[] params) {
+ private void updateInMemory(Object ob, Object[] params, StoreQuery q) {
for (Iterator it = getUpdates().entrySet().iterator();
it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
Path path = (Path) e.getKey();
FieldMetaData fmd = (FieldMetaData) path.last();
+ OpenJPAStateManager sm = _broker.getStateManager(ob);
Object val;
Object value = e.getValue();
@@ -1080,10 +1081,13 @@
} else if (value instanceof Constant) {
val = ((Constant) value).getValue(params);
} else {
- throw new UserException(_loc.get("only-update-primitives"));
+ try {
+ val = q.evaluate(value, ob, params, sm);
+ } catch (UnsupportedException e1) {
+ throw new UserException(_loc.get("fail-to-get-update-value"));
+ }
}
- OpenJPAStateManager sm = _broker.getStateManager(ob);
int i = fmd.getIndex();
PersistenceCapable into = ImplHelper.toPersistenceCapable(ob,
_broker.getConfiguration());
|