Author: ppoddar
Date: Tue Feb 17 23:21:37 2009
New Revision: 745293
URL: http://svn.apache.org/viewvc?rev=745293&view=rev
Log:
OPENJPA-924: Support cache of Finder queries.
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderQueryImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderCache.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderQuery.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestFinderCache.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/annotations/common/apps/annotApp/annotype/TxRollbackEntity.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestDefaultInheritanceStrategy.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfigurationImpl.java
Tue Feb 17 23:21:37 2009
@@ -40,6 +40,8 @@
import org.apache.openjpa.jdbc.sql.DBDictionaryFactory;
import org.apache.openjpa.jdbc.sql.SQLFactory;
import org.apache.openjpa.kernel.BrokerImpl;
+import org.apache.openjpa.kernel.FinderCache;
+import org.apache.openjpa.kernel.PreparedQueryCache;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.lib.conf.IntValue;
import org.apache.openjpa.lib.conf.ObjectValue;
@@ -317,6 +319,17 @@
preparedQueryCachePlugin.setDynamic(true);
preparedQueryCachePlugin.setInstantiatingGetter("getQuerySQLCacheInstance");
+ finderCachePlugin = addPlugin("jdbc.FinderCache", true);
+ aliases = new String[] {
+ "true", "org.apache.openjpa.jdbc.kernel.FinderCacheImpl",
+ "false", null
+ };
+ finderCachePlugin.setAliases(aliases);
+ finderCachePlugin.setAliasListComprehensive(true);
+ finderCachePlugin.setDefault(aliases[0]);
+ finderCachePlugin.setClassName(aliases[1]);
+ finderCachePlugin.setDynamic(true);
+ finderCachePlugin.setInstantiatingGetter("getFinderCacheInstance");
// this static initializer is to get past a weird
// ClassCircularityError that happens only under IBM's
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java?rev=745293&view=auto
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java
(added)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderCacheImpl.java
Tue Feb 17 23:21:37 2009
@@ -0,0 +1,418 @@
+/*
+ * 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.jdbc.kernel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.SelectExecutor;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.FinderCache;
+import org.apache.openjpa.kernel.FinderQuery;
+import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.kernel.QueryStatistics;
+import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+
+/**
+ * Implementation of FinderCache for JDBC.
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ *
+ */
+public class FinderCacheImpl
+ implements FinderCache<ClassMapping, SelectExecutor, Result> {
+ private static final String PATTERN_SEPARATOR = "\\;";
+ private static final String EXLUDED_BY_USER = "Excluded by user";
+
+ private final Map<ClassMapping,
+ FinderQuery<ClassMapping, SelectExecutor, Result>> _delegate;
+ // Key: class name Value: Reason why excluded
+ private final Map<String, String> _uncachables;
+ private List<String> _exclusionPatterns;
+ private QueryStatistics<FinderQuery<ClassMapping,SelectExecutor,Result>>
+ _stats;
+ private ReentrantLock _lock = new ReentrantLock();
+ private Log _log;
+ private Localizer _loc = Localizer.forPackage(FinderCacheImpl.class);
+
+
+ public FinderCacheImpl() {
+ _delegate = new HashMap<ClassMapping,
+ FinderQuery<ClassMapping, SelectExecutor, Result>>();
+ _uncachables = new HashMap<String, String>();
+ _stats = new QueryStatistics.Default<FinderQuery<ClassMapping,
+ SelectExecutor,Result>>();
+ }
+
+ /**
+ * Get a map-oriented view of the cache.
+ *
+ * @return a map of the query string with class names as key.
+ */
+ public Map<String, String> getMapView() {
+ lock();
+ try {
+ Map<String, String> view = new TreeMap<String, String>();
+ for (ClassMapping mapping : _delegate.keySet())
+ view.put(mapping.getDescribedType().getName(),
+ _delegate.get(mapping).getQueryString());
+ return view;
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Gets basic statistics of execution and hit count of finder queries.
+ */
+ public QueryStatistics<FinderQuery<ClassMapping,SelectExecutor,Result>>
+ getStatistics() {
+ return _stats;
+ }
+
+ /**
+ * Gets the finder query for the given mapping. The get operation can be
+ * controlled by FetchConfiguration hints.
+ * {...@link QueryHints#HINT_IGNORE_FINDER HINT_IGNORE_FINDER} will ignore
+ * any cached finder that may exist in this cache and will return null.
+ * {...@link QueryHints#HINT_INVALIDATE_FINDER HINT_INVALIDATE_FINDER}
will
+ * invalidate any cached finder that may exist in this cache and will
return
+ * null.
+ *
+ */
+ public FinderQuery<ClassMapping,SelectExecutor,Result>
+ get(ClassMapping mapping, FetchConfiguration fetch) {
+ boolean ignore = isHinted(fetch, QueryHints.HINT_IGNORE_FINDER);
+ boolean invalidate = isHinted(fetch,
QueryHints.HINT_INVALIDATE_FINDER);
+ if (invalidate)
+ invalidate(mapping);
+ if (ignore)
+ return null;
+ FinderQuery<ClassMapping, SelectExecutor, Result> result =
+ _delegate.get(mapping);
+ _stats.recordExecution(result, result != null);
+ return result;
+ }
+
+ /**
+ * Cache a Finder Query for the given mapping and select. The put
operation
+ * can be controlled by FetchConfiguration hints.
+ * If no entry exists for the given mapping then an attempt is made to
+ * create a new FinderQuery. The attempt, however, may not be successful
+ * because all Selects can not be cached.
+ * @see FinderQueryImpl#newFinder(ClassMapping, Select).
+ *
+ * If a entry for the given mapping exists then the value of
+ * {...@link QueryHints#HINT_RECACHE_FINDER HINT_RECACHE_FINDER} hint
+ * determines whether the existing entry is returned or a new FinderQuery
+ * with the given argument overwrites the existing one.
+ *
+ * @param mapping the class for which the finder is to be cached
+ * @param select the finder query
+ * @param fetch may contain hints to control cache operation
+ */
+ public FinderQuery<ClassMapping, SelectExecutor, Result> cache
+ (ClassMapping mapping, SelectExecutor select, FetchConfiguration fetch)
{
+ lock();
+ try {
+ boolean recache = isHinted(fetch, QueryHints.HINT_RECACHE_FINDER);
+ if (isExcluded(mapping)) {
+ return recache ? put(mapping, select) : null;
+ }
+ if (_delegate.containsKey(mapping)) {
+ return recache ? put(mapping, select) : _delegate.get(mapping);
+ }
+ return put(mapping, select);
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Creates and puts a FinderQuery in the internal map indexed by the
+ * given ClassMapping.
+ * If a new FinderQuery can not be created for the given Select (because
+ * some Select are not cached), then the mapping is marked invalid.
+ *
+ */
+ private FinderQuery<ClassMapping, SelectExecutor, Result> put
+ (ClassMapping mapping, SelectExecutor select) {
+ FinderQuery<ClassMapping, SelectExecutor, Result> finder =
+ FinderQueryImpl.newFinder(mapping, select);
+ if (finder != null) {
+ _delegate.put(mapping, finder);
+ if (_log != null && _log.isTraceEnabled())
+ _log.trace(_loc.get("finder-cached", finder));
+ } else {
+ if (_log != null && _log.isWarnEnabled())
+ _log.warn(_loc.get("finder-not-cachable", mapping));
+ invalidate(mapping);
+ }
+ return finder;
+ }
+
+ /**
+ * Affirms if the given mapping is excluded from being cached.
+ */
+ public boolean isExcluded(ClassMapping mapping) {
+ return mapping != null &&
+ isExcluded(mapping.getDescribedType().getName());
+ }
+
+ /**
+ * Searches the exclusion patterns to find out if the given string matches
+ * any element.
+ */
+ private boolean isExcluded(String target) {
+ if (_exclusionPatterns != null && _exclusionPatterns.contains(target))
+ return true;
+ return getMatchedExclusionPattern(target) != null;
+ }
+
+ /**
+ * Adds a pattern for exclusion. Any cached finder whose class name
+ * matches the given pattern will be marked invalidated as a side-effect.
+ */
+ public void addExclusionPattern(String pattern) {
+ lock();
+ try {
+ if (_exclusionPatterns == null)
+ _exclusionPatterns = new ArrayList<String>();
+ _exclusionPatterns.add(pattern);
+ Collection<ClassMapping> invalidMappings = getMatchedKeys(pattern,
+ _delegate.keySet());
+ if (!invalidMappings.isEmpty()
+ && _log != null && _log.isInfoEnabled())
+ _log.info(_loc.get("finder-add-pattern", pattern,
+ invalidMappings.size(), invalidMappings));
+ for (ClassMapping invalidMapping : invalidMappings)
+ markUncachable(invalidMapping, pattern);
+ } finally {
+ unlock();
+ }
+ }
+ /**
+ * Removes a pattern for exclusion. Any query identifier marked as not
+ * cachable due to the given pattern will now be removed from the list of
+ * uncachables as a side-effect.
+ */
+ public void removeExclusionPattern(String pattern) {
+ lock();
+ try {
+ if (_exclusionPatterns == null)
+ return;
+ _exclusionPatterns.remove(pattern);
+ Collection<String> reborns = getMatchedKeys(pattern,
+ _uncachables.keySet());
+ if (!reborns.isEmpty() && _log != null && _log.isInfoEnabled())
+ _log.info(_loc.get("finder-remove-pattern", pattern,
+ reborns.size(), reborns));
+ for (String rebornKey : reborns)
+ _uncachables.remove(rebornKey);
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Gets the pattern that matches the given identifier.
+ */
+ private String getMatchedExclusionPattern(String id) {
+ if (_exclusionPatterns == null || _exclusionPatterns.isEmpty())
+ return null;
+ for (String pattern : _exclusionPatterns)
+ if (matches(pattern, id))
+ return pattern;
+ return null;
+ }
+
+ /**
+ * Gets the elements of the given set that match the given pattern.
+ */
+ private Collection<ClassMapping> getMatchedKeys(String pattern,
+ Set<ClassMapping> set) {
+ List<ClassMapping> result = new ArrayList<ClassMapping>();
+ for (ClassMapping entry : set) {
+ if (matches(pattern, entry)) {
+ result.add(entry);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the elements of the given list which match the given pattern.
+ */
+ private Collection<String> getMatchedKeys(String pattern,
+ Collection<String> coll) {
+ List<String> result = new ArrayList<String>();
+ for (String key : coll) {
+ if (matches(pattern, key)) {
+ result.add(key);
+ }
+ }
+ return result;
+ }
+
+ boolean matches(String pattern, ClassMapping mapping) {
+ return matches(pattern, mapping.getDescribedType().getName());
+ }
+
+ boolean matches(String pattern, String target) {
+ return target != null && (target.equals(pattern)
+ || target.matches(pattern));
+ }
+
+ public boolean invalidate(ClassMapping mapping) {
+ lock();
+ try {
+ if (_log.isTraceEnabled())
+ _log.trace(_loc.get("finder-invalidate", mapping));
+ return _delegate.remove(mapping) != null;
+ } finally {
+ unlock();
+ }
+ }
+
+ public FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
+ ClassMapping mapping) {
+ return markUncachable(mapping.getDescribedType().getName());
+ }
+
+ public FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
+ String id) {
+ return markUncachable(id, EXLUDED_BY_USER);
+ }
+
+ private FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
+ String cls, String reason) {
+ lock();
+ try {
+ boolean excludedByUser = _uncachables.get(cls) == EXLUDED_BY_USER;
+ if (!excludedByUser)
+ _uncachables.put(cls, reason);
+ if (_log != null && _log.isInfoEnabled()) {
+ if (excludedByUser)
+ _log.info(_loc.get("finder-uncache-strong", cls));
+ else
+ _log.info(_loc.get("finder-uncache-weak", cls,
+ reason));
+ }
+ return _delegate.remove(searchMappingByName(cls));
+ } finally {
+ unlock();
+ }
+ }
+
+ private FinderQuery<ClassMapping, SelectExecutor, Result> markUncachable(
+ ClassMapping mapping, String reason) {
+ lock();
+ try {
+ String cls = mapping.getDescribedType().getName();
+ boolean excludedByUser = _uncachables.get(cls) == EXLUDED_BY_USER;
+ if (!excludedByUser)
+ _uncachables.put(cls, reason);
+ if (_log != null && _log.isInfoEnabled()) {
+ if (excludedByUser)
+ _log.info(_loc.get("finder-uncache-strong", cls));
+ else
+ _log.info(_loc.get("finder-uncache-weak", cls, reason));
+ }
+ return _delegate.remove(mapping);
+ } finally {
+ unlock();
+ }
+ }
+
+ ClassMapping searchMappingByName(String cls) {
+ for (ClassMapping mapping : _delegate.keySet())
+ if (matches(cls, mapping))
+ return mapping;
+ return null;
+ }
+
+
+ public void setExcludes(String excludes) {
+ lock();
+ try {
+ if (StringUtils.isEmpty(excludes))
+ return;
+ if (_exclusionPatterns == null)
+ _exclusionPatterns = new ArrayList<String>();
+ String[] patterns = excludes.split(PATTERN_SEPARATOR);
+ for (String pattern : patterns)
+ addExclusionPattern(pattern);
+ } finally {
+ unlock();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getExcludes() {
+ return (List<String>)_exclusionPatterns == null
+ ? Collections.EMPTY_LIST
+ : Collections.unmodifiableList(_exclusionPatterns);
+ }
+
+ boolean isHinted(FetchConfiguration fetch, String hint) {
+ if (fetch == null)
+ return false;
+ Object result = fetch.getHint(hint);
+ return result != null && "true".equalsIgnoreCase(result.toString());
+ }
+
+ void lock() {
+ if (_lock != null)
+ _lock.lock();
+ }
+
+ void unlock() {
+ if (_lock != null && _lock.isLocked())
+ _lock.unlock();
+ }
+
+ // ----------------------------------------------------
+ // Configuration contract
+ // ----------------------------------------------------
+ public void startConfiguration() {
+ }
+
+ public void setConfiguration(Configuration conf) {
+ _log = conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
+ }
+
+ public void endConfiguration() {
+ }
+
+}
Added:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderQueryImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderQueryImpl.java?rev=745293&view=auto
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderQueryImpl.java
(added)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/FinderQueryImpl.java
Tue Feb 17 23:21:37 2009
@@ -0,0 +1,165 @@
+/*
+ * 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.jdbc.kernel;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.Joinable;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.sql.LogicalUnion;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.SelectExecutor;
+import org.apache.openjpa.jdbc.sql.SelectImpl;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.FinderQuery;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreManager;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.Id;
+
+import serp.util.Numbers;
+
+/**
+ * Implements Finder Query identified by ClassMappping for SelectExecutor that
+ * can be executed to generate Result.
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ */
+public class FinderQueryImpl
+ implements FinderQuery<ClassMapping, SelectExecutor, Result> {
+ private final ClassMapping _mapping;
+ private final SelectImpl _select;
+ private final Column[] _pkCols;
+ private final Joinable[] _joins;
+ private final SQLBuffer _buffer;
+ private final String _sql;
+
+ /**
+ * Attempts to construct a FinderQuery from the given Select for the given
+ * mapping. The given Select may not be amenable for caching and then a
null
+ * value is returned.
+ */
+ static FinderQueryImpl newFinder(ClassMapping mapping,
+ SelectExecutor select) {
+ SelectImpl impl = extractImplementation(select);
+ if (impl == null)
+ return null;
+ SQLBuffer buffer = impl.getSQL();
+ Column[] pkCols = mapping.getPrimaryKeyColumns();
+ boolean canCache = pkCols.length == buffer.getParameters().size();
+ return (canCache)
+ ? new FinderQueryImpl(mapping, impl, buffer) : null;
+ }
+
+ private FinderQueryImpl(ClassMapping mapping, SelectImpl select,
+ SQLBuffer buffer) {
+ super();
+ _mapping = mapping;
+ _select = select;
+ _buffer = buffer;
+ _sql = _buffer.getSQL();
+ _pkCols = _mapping.getPrimaryKeyColumns();
+ _joins = new Joinable[_pkCols.length];
+ for (int i = 0; i < _pkCols.length; i++)
+ _joins[i] = _mapping.assertJoinable(_pkCols[i]);
+ }
+
+ public ClassMapping getIdentifier() {
+ return _mapping;
+ }
+
+ public SelectExecutor getDelegate() {
+ return _select;
+ }
+
+ public String getQueryString() {
+ return _sql;
+ }
+
+ private Object[] getPKValues(OpenJPAStateManager sm, JDBCStore store) {
+ Object[] pks = null;
+ Object oid = sm.getObjectId();
+ if (_mapping.getIdentityType() == ClassMapping.ID_APPLICATION)
+ pks = ApplicationIds.toPKValues(oid, _mapping);
+
+ Object[] val = new Object[_pkCols.length];
+ int count = 0;
+ for (int i = 0; i < _pkCols.length; i++, count++) {
+ if (pks == null)
+ val[0] = (oid == null)
+ ? null
+ : Numbers.valueOf(((Id) oid).getId());
+ else {
+ val[i] = pks[_mapping.getField(_joins[i].getFieldIndex()).
+ getPrimaryKeyIndex()];
+ val[i] = _joins[i].getJoinValue(val[i], _pkCols[i], store);
+ }
+ }
+ return val;
+ }
+
+ public Result execute(OpenJPAStateManager sm, StoreManager store,
+ FetchConfiguration fetch) {
+ boolean forUpdate = false;
+ Connection conn = ((JDBCStore)store).getConnection();
+ PreparedStatement stmnt = null;
+ ResultSet rs = null;
+ try {
+ stmnt = conn.prepareStatement(_sql);
+ Object[] params = getPKValues(sm, (JDBCStore)store);
+ int i = 0;
+ for (Object o : params) {
+ stmnt.setObject(++i, o);
+ }
+ rs = stmnt.executeQuery();
+ return _select.getEagerResult(conn, stmnt, rs, (JDBCStore)store,
+ (JDBCFetchConfiguration)fetch, forUpdate, _buffer);
+ } catch (SQLException se) {
+ if (stmnt != null)
+ try { stmnt.close(); } catch (SQLException se2) {}
+ try { conn.close(); } catch (SQLException se2) {}
+ throw new RuntimeException(se);
+ }
+ }
+
+ private static SelectImpl extractImplementation(SelectExecutor selector) {
+ if (selector == null || selector.hasMultipleSelects())
+ return null;
+ if (selector instanceof SelectImpl)
+ return (SelectImpl)selector;
+ if (selector instanceof LogicalUnion.UnionSelect)
+ return ((LogicalUnion.UnionSelect)selector).getDelegate();
+ if (selector instanceof Union)
+ return extractImplementation(((Union)selector).getSelects()[0]);
+
+ return null;
+ }
+
+ public String toString() {
+ return _mapping + ": [" + getQueryString() + "]";
+ }
+}
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
Tue Feb 17 23:21:37 2009
@@ -30,6 +30,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+
import javax.sql.DataSource;
import org.apache.openjpa.enhance.PersistenceCapable;
@@ -48,7 +49,10 @@
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.jdbc.sql.SelectExecutor;
import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.BrokerImpl;
import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.FinderCache;
+import org.apache.openjpa.kernel.FinderQuery;
import org.apache.openjpa.kernel.LockManager;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.PCState;
@@ -483,13 +487,18 @@
private Result getInitializeStateResult(OpenJPAStateManager sm,
ClassMapping mapping, JDBCFetchConfiguration fetch, int subs)
throws SQLException {
+ FinderQueryImpl fq = getFinder(mapping, fetch);
+ if (fq != null)
+ return fq.execute(sm, this, fetch);
Select sel = _sql.newSelect();
if (!select(sel, mapping, subs, sm, null, fetch,
JDBCFetchConfiguration.EAGER_JOIN, true, false))
return null;
sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
sel.setExpectedResultCount(1, false);
- return sel.execute(this, fetch);
+ Result result = sel.execute(this, fetch);
+ cacheFinder(mapping, sel, fetch);
+ return result;
}
/**
@@ -499,6 +508,9 @@
private Result getInitializeStateUnionResult(final OpenJPAStateManager sm,
ClassMapping mapping, final ClassMapping[] mappings,
final JDBCFetchConfiguration fetch) throws SQLException {
+ FinderQueryImpl fq = getFinder(mapping, fetch);
+ if (fq != null)
+ return fq.execute(sm, this, fetch);
final JDBCStoreManager store = this;
final int eager = Math.min(fetch.getEagerFetchMode(),
JDBCFetchConfiguration.EAGER_JOIN);
@@ -514,7 +526,9 @@
sel.wherePrimaryKey(sm.getObjectId(), mappings[i], store);
}
});
- return union.execute(this, fetch);
+ Result result = union.execute(this, fetch);
+ cacheFinder(mapping, union, fetch);
+ return result;
}
/**
@@ -1349,6 +1363,24 @@
private void afterExecuteStatement(Statement stmnt) {
_stmnts.remove(stmnt);
}
+
+ FinderQueryImpl getFinder(ClassMapping mapping, FetchConfiguration fetch) {
+ FinderCache cache = getFinderCache();
+ return cache == null
+ ? null : (FinderQueryImpl)cache.get(mapping, fetch);
+ }
+
+ boolean cacheFinder(ClassMapping mapping, SelectExecutor select,
+ FetchConfiguration fetch) {
+ FinderCache cache = getFinderCache();
+ return cache != null && cache.cache(mapping, select, fetch) != null;
+ }
+
+ FinderCache getFinderCache() {
+ return (((BrokerImpl)getContext()).getCacheFinderQuery())
+ ? getConfiguration().getFinderCacheInstance() : null;
+ }
+
/**
* Connection returned to client code. Makes sure its wrapped connection
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
Tue Feb 17 23:21:37 2009
@@ -199,10 +199,9 @@
}
public boolean hasMultipleSelects() {
- for (UnionSelect sel : sels)
- if (sel.hasMultipleSelects())
- return true;
- return false;
+ if (sels != null && sels.length > 1)
+ return true;
+ return sels[0].hasMultipleSelects();
}
public int getCount(JDBCStore store)
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
Tue Feb 17 23:21:37 2009
@@ -35,6 +35,7 @@
import org.apache.openjpa.kernel.BrokerImpl;
import org.apache.openjpa.kernel.ConnectionRetainModes;
import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.FinderCache;
import org.apache.openjpa.kernel.InverseManager;
import org.apache.openjpa.kernel.LockManager;
import org.apache.openjpa.kernel.PreparedQueryCache;
@@ -1603,6 +1604,26 @@
*
* @since 2.0.0
*/
- public void setQuerySQLCache(String config);
-
+ public void setQuerySQLCache(String config);
+
+ /**
+ * Get the cache of finder queries.
+ *
+ * @since 2.0.0
+ */
+ public FinderCache getFinderCacheInstance();
+
+ /**
+ * Get the string configuration of the finder cache.
+ *
+ * @since 2.0.0
+ */
+ public String getFinderCache();
+
+ /**
+ * Set the finder cache from a string configuration.
+ *
+ * @since 2.0.0
+ */
+ public void setFinderCache(String cache);
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
Tue Feb 17 23:21:37 2009
@@ -36,6 +36,7 @@
import org.apache.openjpa.kernel.AutoClear;
import org.apache.openjpa.kernel.BrokerImpl;
import org.apache.openjpa.kernel.ConnectionRetainModes;
+import org.apache.openjpa.kernel.FinderCache;
import org.apache.openjpa.kernel.InverseManager;
import org.apache.openjpa.kernel.LockLevels;
import org.apache.openjpa.kernel.LockManager;
@@ -148,6 +149,7 @@
public CacheMarshallersValue cacheMarshallerPlugins;
public BooleanValue eagerInitialization;
public PluginValue preparedQueryCachePlugin;
+ public PluginValue finderCachePlugin;
public ObjectValue specification;
public IntValue queryTimeout;
@@ -1550,5 +1552,18 @@
return (PreparedQueryCache)preparedQueryCachePlugin.get();
}
-
+ public void setFinderCache(String finderCache) {
+ finderCachePlugin.setString(finderCache);
+ }
+
+ public String getFinderCache() {
+ return finderCachePlugin.getString();
+ }
+
+ public FinderCache getFinderCacheInstance() {
+ if (finderCachePlugin.get() == null) {
+ finderCachePlugin.instantiate(FinderCache.class, this);
+ }
+ return (FinderCache)finderCachePlugin.get();
+ }
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
Tue Feb 17 23:21:37 2009
@@ -225,6 +225,7 @@
private boolean _detachedNew = true;
private boolean _orderDirty = false;
private boolean _cachePreparedQuery = true;
+ private boolean _cacheFinderQuery = true;
// Map of properties whose values have been changed
@@ -4890,4 +4891,23 @@
unlock();
}
}
+
+ public boolean getCacheFinderQuery() {
+ lock();
+ try {
+ return _cacheFinderQuery
+ && _conf.getFinderCacheInstance() != null;
+ } finally {
+ unlock();
+ }
+ }
+
+ public void setCacheFinderQuery(boolean flag) {
+ lock();
+ try {
+ _cachePreparedQuery = flag;
+ } finally {
+ unlock();
+ }
+ }
}
Added:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderCache.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderCache.java?rev=745293&view=auto
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderCache.java
(added)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderCache.java
Tue Feb 17 23:21:37 2009
@@ -0,0 +1,144 @@
+/*
+ * 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.List;
+import java.util.Map;
+
+import org.apache.openjpa.lib.conf.Configurable;
+
+/**
+ * A cache to create and maintain {...@link FinderQuery finder queries}.
+ *
+ * A finder query is a query to find instance of a given class by its primary
+ * identity. This cache maintains finder queries by generic identifier of
+ * parameterized type K.
+ *
+ * A cached query by an identifier of parameterized type K.
+ *
+ * A query can be cached by an identifier and value represented by
parameterized
+ * type V. Caching results in creating a new instance of FinderQuery FQ from
the
+ * value V such that FQ can be executed to return a result of parameterized
type
+ * R. A request to cache may not be successful if this cache determines the
+ * value V to be not cachable.
+ *
+ * Both get() and put() operations can be controlled by the hints associated
+ * with FetchConfiguration.
+ *
+ * The target database query FQ associated to a cached finder query F
+ * <em>may</em> depend upon query execution context such as fetch plan or
+ * lock group. This cache, by design, does not monitor the context or
+ * automatically invalidate an entry when the original query F is executed
+ * with context parameters that affect the target query FQ.
+ *
+ * The user must notify this receiver to invalidate a cached entry when
+ * execution context changes in a way that will modify the resultant database
+ * language query FQ.
+ *
+ * One of the built-in mechanism (available in JPA facade) is to set query
hints
+ * to either invalidate the query entirely or ignore the cached version for
the
+ * current execution.
+ *
+ * @see QueryHints#HINT_IGNORE_FINDER
+ * @see QueryHints#HINT_INVALIDATE_FINDER
+ * @see QueryHints#HINT_RECACHE_FINDER
+ *
+ * This cache allows customization of whether a query can be cached or not
+ * via either explicit marking of certain classes as non-cachable (which is
+ * irreversible) or addition/removal of exclusion patterns (which is
reversible)
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ */
+public interface FinderCache<K,V,R> extends Configurable {
+ /**
+ * Get the FinderQuery for the given key.
+ *
+ * @param key for which the finder is looked up
+ * @param fecth may contain hints to control lookup operation
+ *
+ * @return FinderQuery for the given mapping.
+ */
+ public FinderQuery<K,V,R> get(K key, FetchConfiguration fetch);
+
+ /**
+ * Cache a FinderQuery for the given key and value.
+ *
+ * @param key for which the finder is cached.
+ * @param value used to construct the finder query
+ * @param fetch may contain hints to control cache operation.
+ *
+ * @return the finder query that has been cached. It may be newly
+ * constructed or an existing query. If the given key-value can not be
+ * cached, then return null.
+ */
+ public FinderQuery<K,V,R> cache(K key, V value, FetchConfiguration fetch);
+
+ /**
+ * Get a map view of the cached entries as strings.
+ */
+ public Map<String, String> getMapView();
+
+ /**
+ * Remove the FinderQuery for the given key from this cache.
+ */
+ public boolean invalidate(K key);
+
+ /**
+ * Marks the given key as not amenable to caching.
+ * Explicit marking helps to avoid repeated computational cost of
+ * determining whether finder for a key can be cached or not.
+ *
+ * Explicit marking can not be reversed by removal of exclusion patterns.
+ *
+ * @return finder query for the given class if it had been cached before.
+ * null otherwise.
+ */
+ public FinderQuery<K,V,R> markUncachable(K key);
+
+ /**
+ * Affirms if the given key matches any of the exclusion patterns.
+ */
+ public boolean isExcluded(K key);
+
+ /**
+ * Gets the excluded stringified keys.
+ */
+ public List<String> getExcludes();
+
+ /**
+ * Adds the given pattern to the list of excluded patterns. Any
existing
+ * cache entry whose key matches the given pattern will be marked
+ * non-cachable in a reversible manner.
+ */
+ public void addExclusionPattern(String pattern);
+
+ /**
+ * Removes the given pattern from the list of excluded patterns.
+ * Any excluded entry that matches the given pattern can now be cached
+ * again, unless it has been marked non-cachable explicitly.
+ */
+ public void removeExclusionPattern(String pattern);
+
+ /**
+ * Gets the simple statistics for executed finder queries.
+ */
+ public QueryStatistics<FinderQuery<K,V,R>> getStatistics();
+}
\ No newline at end of file
Added:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderQuery.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderQuery.java?rev=745293&view=auto
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderQuery.java
(added)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FinderQuery.java
Tue Feb 17 23:21:37 2009
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * A finder query is a query for an instance of a class by its primary key.
+ * A finder query is parameterized by the type of key K, type of value V and
+ * type of result R.
+ *
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ *
+ */
+public interface FinderQuery<K,V,R> {
+
+ /**
+ * Gets the identifier of this receiver.
+ *
+ */
+ public K getIdentifier();
+
+ /**
+ * Gets the value to which this receiver delegates its execution.
+ *
+ * @return
+ */
+ public V getDelegate();
+
+ /**
+ * Execute the query for a given instance.
+ *
+ * @param sm the StateManager for a given instance carrying the primary key
+ * values.
+ * @param store the data store against which the query is to be executed.
+ * @param fetch fetch parameters
+ *
+ * @return the result of execution.
+ *
+ */
+ public R execute(OpenJPAStateManager sm, StoreManager store,
+ FetchConfiguration fetch);
+
+ /**
+ * Gets the query string.
+ *
+ */
+ public String getQueryString();
+}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
Tue Feb 17 23:21:37 2009
@@ -75,4 +75,21 @@
public static final String HINT_IGNORE_PREPARED_QUERY =
"openjpa.hint.IgnorePreparedQuery";
+ /**
+ * A directive to ignore any cached finder query for find() operation.
+ * The cached entry, if any, remains in the cache.
+ */
+ public static final String HINT_IGNORE_FINDER =
"openjpa.hint.IgnoreFinder";
+
+ /**
+ * A directive to invalidate any cached finder query.
+ */
+ public static final String HINT_INVALIDATE_FINDER =
+ "openjpa.hint.InvalidateFinder";
+
+ /**
+ * A directive to overwrite a cached finder query by a new query.
+ */
+ public static final String HINT_RECACHE_FINDER =
+ "openjpa.hint.RecacheFinder";
}
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/annotations/common/apps/annotApp/annotype/TxRollbackEntity.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/annotations/common/apps/annotApp/annotype/TxRollbackEntity.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/annotations/common/apps/annotApp/annotype/TxRollbackEntity.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/annotations/common/apps/annotApp/annotype/TxRollbackEntity.java
Tue Feb 17 23:21:37 2009
@@ -41,6 +41,10 @@
@Basic
private String name;
+ protected TxRollbackEntity() {
+ this("?");
+ }
+
public TxRollbackEntity(String name)
{
this.name = name;
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestDefaultInheritanceStrategy.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestDefaultInheritanceStrategy.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestDefaultInheritanceStrategy.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/inheritance/TestDefaultInheritanceStrategy.java
Tue Feb 17 23:21:37 2009
@@ -77,7 +77,9 @@
BaseClass4.class, SubclassG.class,
BaseClass5.class, MidClass2.class, SubclassH.class,
AbstractClass.class, SubclassI.class, SubclassJ.class,
- BaseClass6.class, SubclassK.class);
+ BaseClass6.class, SubclassK.class,
+ "openjpa.jdbc.FinderCache", "true",
+ CLEAR_TABLES);
}
private Class[] classArray(Class... classes) {
@@ -550,6 +552,43 @@
em.close();
}
+
+ public void testFinder() {
+ EntityManager em = emf.createEntityManager();
+ SubclassK sck = new SubclassK();
+ sck.setId(479);
+ sck.setClassKName("SubclassKName");
+ sck.setMidClass3Name("SubclassKMidClass3Name");
+ sck.setName("SubclassKBaseClass6Name");
+
+ BaseClass6 bk6 = new BaseClass6();
+ bk6.setId(302);
+ bk6.setName("BaseClass6Name");
+
+ SubclassI sci = new SubclassI();
+ sci.setId(109);
+ sci.setClassIName("SubclassIName");
+ sci.setName("SubclassIBaseClassName");
+
+ SubclassJ scj = new SubclassJ();
+ scj.setId(238);
+ scj.setClassJName("SubclassJName");
+ scj.setName("SubclassJBaseClassName");
+
+ em.getTransaction().begin();
+ em.persist(sck);
+ em.persist(bk6);
+ em.persist(sci);
+ em.persist(scj);
+ em.getTransaction().commit();
+
+ em.clear();
+
+ verifyInheritanceFinderResult(em, SubclassK.class, 479);
+ verifyInheritanceFinderResult(em, BaseClass6.class, 479, 302);
+ verifyInheritanceFinderResult(em, SubclassI.class, 109);
+ verifyInheritanceFinderResult(em, SubclassJ.class, 238);
+ }
/**
* Verifies that a table contains the specified number of entries
@@ -637,4 +676,16 @@
assertTrue("Returned expected entities",
count == expectedValues.length);
}
+
+ private void verifyInheritanceFinderResult(EntityManager em,
+ Class entityType, int... ids) {
+ for (int j = 0; j < 2; j++) {
+ em.clear();
+ for (int id : ids) {
+ Object pc = em.find(entityType, id);
+ assertTrue(entityType.isAssignableFrom(pc.getClass()));
+ }
+ }
+ }
+
}
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java
Tue Feb 17 23:21:37 2009
@@ -1,7 +1,6 @@
package org.apache.openjpa.persistence.jdbc.sqlcache;
import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@@ -10,7 +9,6 @@
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Merchandise {
@Id
- @GeneratedValue
private long id;
public long getId() {
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java?rev=745293&r1=745292&r2=745293&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java
Tue Feb 17 23:21:37 2009
@@ -18,6 +18,7 @@
*/
package org.apache.openjpa.persistence.jdbc.sqlcache;
+import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@@ -32,6 +33,7 @@
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
+...@discriminatorvalue("PERSON")
@NamedQueries({
@NamedQuery(name="JPQLNamedSelectPositionalParameter",
query="select p from Person p where p.firstName=?2" +
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestFinderCache.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestFinderCache.java?rev=745293&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestFinderCache.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestFinderCache.java
Tue Feb 17 23:21:37 2009
@@ -0,0 +1,116 @@
+/*
+ * 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.persistence.jdbc.sqlcache;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.kernel.FinderCache;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Basic test to check FinderQuery caches.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class TestFinderCache extends SingleEMFTestCase {
+ public static final long[] BOOK_IDS = {1000, 2000, 3000};
+ public static final String[] BOOK_NAMES = {"Argumentative Indian", "Tin
Drum", "Blink"};
+ public static final long[] CD_IDS = {1001, 2001, 3001};
+ public static final String[] CD_LABELS = {"Beatles", "Sinatra", "Don't
Rock My Boat"};
+
+ void createTestData() {
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ for (int i = 0; i < BOOK_IDS.length; i++) {
+ Book book = new Book();
+ book.setId(BOOK_IDS[i]);
+ book.setTitle(BOOK_NAMES[i]);
+ em.persist(book);
+ }
+ for (int i = 0; i < CD_IDS.length; i++) {
+ CD cd = new CD();
+ cd.setId(CD_IDS[i]);
+ cd.setLabel(CD_LABELS[i]);
+ em.persist(cd);
+ }
+ em.getTransaction().commit();
+ }
+
+ public void setUp() {
+ super.setUp(CLEAR_TABLES, Merchandise.class, Book.class, CD.class,
+ Author.class, Person.class, Singer.class, Address.class);
+ createTestData();
+ }
+
+ public void testFinder() {
+ int N = 200;
+
+ emf = createEMF("openjpa.jdbc.FinderCache", "false");
+ run(1, Book.class, BOOK_IDS); // for warmup
+
+ assertNull(getCache());
+ long without = run(N, Book.class, BOOK_IDS);
+
+ emf = createEMF("openjpa.jdbc.FinderCache", "true");
+ assertNotNull(getCache());
+ long with = run(N, Book.class, BOOK_IDS);
+
+ getCache().getStatistics().dump(System.out);
+
+ long pct = (without-with)*100/without;
+ System.err.println(BOOK_IDS.length*N + " find");
+ System.err.println("with " + with);
+ System.err.println("without " + without);
+ System.err.println("delta " + (pct > 0 ? "+" : "") + pct + "%");
+ }
+
+ /**
+ * Run a finder query for each identifiers N times and report the median
+ * execution time.
+ */
+ <T> long run(int N, Class<T> cls, long[] ids) {
+ EntityManager em = emf.createEntityManager();
+ List<Long> stats = new ArrayList<Long>();
+ for (int n = 0; n < N; n++) {
+ em.clear();
+ long start = System.nanoTime();
+ for (int i = 0; i < ids.length; i++) {
+ T pc = em.find(cls, ids[i]);
+ assertNotNull(pc);
+ assertTrue(cls.isInstance(pc));
+ }
+ long end = System.nanoTime();
+ stats.add(end-start);
+ }
+ Collections.sort(stats);
+ return stats.get(N/2);
+ }
+
+ FinderCache getCache() {
+ return
((JDBCConfiguration)emf.getConfiguration()).getFinderCacheInstance();
+ }
+
+}