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();
+    }
+
+}


Reply via email to