openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mik...@apache.org
Subject svn commit: r652913 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ openjpa-jdbc/src/main/java/org/...
Date Fri, 02 May 2008 21:09:15 GMT
Author: mikedd
Date: Fri May  2 14:09:14 2008
New Revision: 652913

URL: http://svn.apache.org/viewvc?rev=652913&view=rev
Log:
OPENJPA-407 committing patch provided by Fay Wang and Jeremy Bauer

Added:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java   (with props)
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfiguration.java
    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/meta/strats/RelationFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/LogicalUnion.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/strats/localizer.properties
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfiguration.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfiguration.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCConfiguration.java Fri May  2 14:09:14 2008
@@ -18,6 +18,8 @@
  */
 package org.apache.openjpa.jdbc.conf;
 
+import java.util.Map;
+
 import javax.sql.DataSource;
 
 import org.apache.openjpa.conf.OpenJPAConfiguration;
@@ -602,4 +604,23 @@
      * @see #getDataSource
      */
     public DataSource getDataSource2(StoreContext ctx);
+    
+    /**
+     * Return QuerySQLCacheValue.
+     * @since 1.2.0
+     */
+    public QuerySQLCacheValue getQuerySQLCache();
+        
+    /**
+     * Whether querySQLCache is enabled or not.
+     * @since 1.2.0
+     */
+    public boolean isQuerySQLCacheOn();
+    
+    /**
+     * Return QuerySQLCacheInstance.
+     * @since 1.2.0
+     */
+    public Map getQuerySQLCacheInstance();     
+    
 }

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=652913&r1=652912&r2=652913&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 Fri May  2 14:09:14 2008
@@ -20,6 +20,9 @@
 
 import java.sql.Connection;
 import java.sql.ResultSet;
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.sql.DataSource;
 
 import org.apache.commons.lang.StringUtils;
@@ -82,6 +85,8 @@
     public ObjectValue mappingDefaultsPlugin;
     public PluginValue driverDataSourcePlugin;
     public MappingFactoryValue mappingFactoryPlugin;
+    public QuerySQLCacheValue querySQLCache;
+    private Map querySQLCacheInstance = new HashMap(); 
 
     // used internally
     private String firstUser = null;
@@ -302,6 +307,9 @@
         seqPlugin.setDefault(JDBCSeqValue.ALIASES[0]);
         seqPlugin.setString(JDBCSeqValue.ALIASES[0]);
 
+        querySQLCache = new QuerySQLCacheValue("jdbc.QuerySQLCache");
+        addValue(querySQLCache);
+
         // this static initializer is to get past a weird
         // ClassCircularityError that happens only under IBM's
         // JDK 1.3.1 on Linux from within the JRun ClassLoader;
@@ -856,4 +864,21 @@
                 return true; 
         return false;
     }
+    
+    public void setQuerySQLCache(String querySQLCache) {
+        this.querySQLCache.setString(querySQLCache);
+    }
+
+    public QuerySQLCacheValue getQuerySQLCache() {
+        return querySQLCache;
+    }
+    
+    public boolean isQuerySQLCacheOn() {
+        return querySQLCache.isSQLCacheOn();
+    }
+
+    public Map getQuerySQLCacheInstance() {
+        return querySQLCacheInstance;
+    }
+    
 }

Added: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java?rev=652913&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java Fri May  2 14:09:14 2008
@@ -0,0 +1,86 @@
+/*
+ * 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.conf;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.openjpa.lib.conf.Configurations;
+import org.apache.openjpa.lib.conf.PluginValue;
+import org.apache.openjpa.util.CacheMap;
+
+
+/**
+ * A cache of sql queries.
+ *
+ * @since 1.2
+ * @nojavadoc
+ */
+public class QuerySQLCacheValue
+    extends PluginValue {
+
+    public static final String[] ALIASES = {
+        "true", CacheMap.class.getName(),
+        "all", ConcurrentHashMap.class.getName(),
+        "false", null,
+    };
+    
+    public QuerySQLCacheValue(String prop) {
+        super(prop, true); 
+        setAliases(ALIASES);
+        setDefault(ALIASES[0]);
+        setClassName(ALIASES[1]);
+    }
+    
+    public boolean isSQLCacheOn() {
+        if (getClassName() == null) 
+            return false;
+        return true;
+    }
+    
+    public Object newInstance() {
+        // make sure map handles concurrency
+        String clsName = getClassName();
+        if (clsName == null)
+            return null;
+        Map map = null;
+
+        try {
+            // Use the "OpenJPA" classloader first...
+            map = (Map) Configurations.newInstance(clsName, this.getClass()
+                    .getClassLoader());
+        } catch (Exception e) {
+            // If the "OpenJPA" classloader fails, then try the classloader
+            // that was used to load java.util.Map...
+            map = (Map) Configurations.newInstance(clsName,
+                    Map.class.getClassLoader());
+        }
+        if (map != null
+                && !(map instanceof Hashtable)
+                && !(map instanceof CacheMap)
+                && !(map instanceof 
+                        org.apache.openjpa.lib.util.concurrent.ConcurrentMap)
+                && !(map instanceof java.util.concurrent.ConcurrentMap))
+            map = Collections.synchronizedMap(map);
+        return map;
+    }
+
+}

Propchange: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/QuerySQLCacheValue.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=652913&r1=652912&r2=652913&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 Fri May  2 14:09:14 2008
@@ -29,15 +29,20 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
+
 import javax.sql.DataSource;
 
 import org.apache.openjpa.event.OrphanedKeyAction;
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.QuerySQLCacheValue;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.meta.Discriminator;
 import org.apache.openjpa.jdbc.meta.FieldMapping;
 import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
 import org.apache.openjpa.jdbc.sql.Joins;
@@ -60,10 +65,12 @@
 import org.apache.openjpa.lib.jdbc.DelegatingConnection;
 import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
 import org.apache.openjpa.lib.jdbc.DelegatingStatement;
+import org.apache.openjpa.lib.log.Log;
 import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
 import org.apache.openjpa.lib.rop.ResultObjectProvider;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FetchGroup;
 import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.meta.ValueStrategies;
@@ -99,7 +106,11 @@
 
     // track the pending statements so we can cancel them
     private Set _stmnts = Collections.synchronizedSet(new HashSet());
-
+    
+    private Map _sqlCache = null;
+    private boolean _isQuerySQLCache = true;
+    private static final Object _nullCacheValue = new Object();
+    
     public StoreContext getContext() {
         return _ctx;
     }
@@ -125,6 +136,9 @@
 
         if (_conf.getUpdateManagerInstance().orderDirty())
             ctx.setOrderDirtyObjects(true);
+        
+        _sqlCache = _conf.getQuerySQLCacheInstance();
+        _isQuerySQLCache = _conf.isQuerySQLCacheOn();
     }
 
     public JDBCConfiguration getConfiguration() {
@@ -402,13 +416,86 @@
     private Result getInitializeStateResult(OpenJPAStateManager sm,
         ClassMapping mapping, JDBCFetchConfiguration fetch, int subs)
         throws SQLException {
+        List params = new ArrayList();
+        Select sel = newSelect(sm, mapping, fetch, subs, params);
+        if (sel == null) return null;
+        return sel.execute(this, fetch, params);
+    }
+
+    private Select newSelect(OpenJPAStateManager sm,
+        ClassMapping mapping, JDBCFetchConfiguration fetch, int subs,
+        List params) {
+        if (!_isQuerySQLCache) 
+            return newSelect(sm, mapping, fetch, subs);       
+           
+        Map<SelectKey, Select> selectImplCacheMap = 
+            getCacheMapFromQuerySQLCache(JDBCStoreManager.class);
+        JDBCFetchConfiguration fetchClone = new JDBCFetchConfigurationImpl();
+        fetchClone.copy(fetch);
+        SelectKey selKey = new SelectKey(mapping, null, fetchClone);
+        Select sel = null;
+        boolean found = true;
+        Object obj = selectImplCacheMap.get(selKey);
+        if (obj == null) {
+            synchronized (selectImplCacheMap) {
+                obj = selectImplCacheMap.get(selKey);
+                if (obj == null) {
+                    // Not found in cache, create a new select
+                    obj = newSelect(sm, mapping, fetch, subs);
+                    found = false;
+                }
+                    
+                if (obj == null) {
+                    // If the generated SelectImpl is null, store a generic
+                    // known object in the cache as a placeholder. Some map 
+                    // implementations do not allow null values.
+                    obj = _nullCacheValue;
+                    found = false;
+                }
+                else if (obj != _nullCacheValue)
+                {
+                    sel = (Select)obj;
+                    if (sel.getSQL() == null) {
+                        sel.setSQL(this, fetch);
+                        found = false;
+                    }
+                }
+                if (!found) {
+                    addToSqlCache(selectImplCacheMap, selKey, obj);
+                }
+            }
+        }
+
+        if (obj != null && obj != _nullCacheValue)
+            sel = (Select) obj;
+
+        Log log = _conf.getLog(JDBCConfiguration.LOG_JDBC);
+        if (log.isTraceEnabled()) {
+            if (!found)
+                log.trace(_loc.get("cache-missed", mapping, this.getClass()));
+            else
+                log.trace(_loc.get("cache-hit", mapping, this.getClass()));
+        }
+
+        if (sel == null)
+            return null;
+        
+        Object oid = sm.getObjectId();
+        Column[] cols = mapping.getPrimaryKeyColumns();
+        sel.wherePrimaryKey(mapping, cols, cols, oid, this, 
+        	null, null, params);
+        return sel;
+    }
+
+    protected Select newSelect(OpenJPAStateManager sm,
+        ClassMapping mapping, JDBCFetchConfiguration fetch, int subs) {
         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);
+        return sel;
     }
 
     /**
@@ -1419,4 +1506,163 @@
             }
         }
     }
+    
+    public Map getCacheMapFromQuerySQLCache(Object key) {
+        synchronized(_sqlCache) {
+            //sqlCache is a map of map
+            Map cacheMap = (Map)_sqlCache.get(key);
+            if (cacheMap == null) {
+                cacheMap = createSQLCache();
+                _sqlCache.put(key, cacheMap);
+            }
+            return cacheMap;
+        }
+    }
+    
+    public void addToSqlCache(Map cacheMap, Object key, Object value) {
+        cacheMap.put(key, value);
+    }
+    
+    public Map createSQLCache() {
+        QuerySQLCacheValue querySQLCache = _conf.getQuerySQLCache();
+        return (Map)querySQLCache.newInstance();
+    }
+
+    public boolean isQuerySQLCacheOn() {
+        return _isQuerySQLCache;  
+    }
+    
+    public Map getQuerySQLCache() {
+        return _sqlCache;
+    }
+    
+    public static class SelectKey {
+        public ClassMapping mapping;
+        public FieldMapping fm;
+        public JDBCFetchConfiguration fetch;
+        
+        public SelectKey (ClassMapping mapping, FieldMapping fm, 
+            JDBCFetchConfiguration fetch) {
+            this.mapping = mapping;
+            this.fm = fm;
+            this.fetch = fetch;
+        }
+        
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            SelectKey selectKey = (SelectKey) o;
+            if (fetch != null ? !equals(fetch, selectKey.fetch) :
+                selectKey.fetch != null) return false;
+            if (mapping != null ? !mapping.equals(selectKey.mapping) :
+                selectKey.mapping != null) return false;
+            if (fm != null ? !fm.equals(selectKey.fm) :
+                selectKey.fm != null) return false;
+            return true;
+        }
+        
+        public boolean equals(JDBCFetchConfiguration fetch1,
+        	JDBCFetchConfiguration fetch2) {
+            if (fetch1 == fetch2) 
+            	return true;
+
+            if (fetch1.getIsolation() != fetch2.getIsolation()) 
+            	return false;
+            if (fetch1.getFetchDirection() != fetch2.getFetchDirection()) 
+            	return false;
+            if (fetch1.getEagerFetchMode() != fetch2.getEagerFetchMode()) 
+            	return false;
+            if (fetch1.getSubclassFetchMode() != fetch2.getSubclassFetchMode()) 
+            	return false;
+            if (fetch1.getJoinSyntax() != fetch2.getJoinSyntax()) 
+            	return false;
+            Set joins1 = fetch1.getJoins();
+            Set joins2 = fetch2.getJoins();
+            if (joins1 != null ? !joins1.equals(joins2) : joins2 != null)
+                return false;
+            
+            if (fetch1.getMaxFetchDepth() != fetch2.getMaxFetchDepth()) 
+            	return false;
+            if (fetch1.getReadLockLevel() != fetch2.getReadLockLevel()) 
+            	return false;
+            if (fetch1.getWriteLockLevel() != fetch2.getWriteLockLevel()) 
+            	return false;
+            
+            boolean sameFetchGroup = false;
+            boolean hasFetchGroupAll = ((JDBCFetchConfigurationImpl)fetch1).
+            	hasFetchGroupAll();
+            boolean hasFetchGroupAll1 = ((JDBCFetchConfigurationImpl)fetch2).
+            	hasFetchGroupAll();
+            if (hasFetchGroupAll && hasFetchGroupAll1) 
+                sameFetchGroup = true;
+            else if (!hasFetchGroupAll && !hasFetchGroupAll1){
+                boolean hasFetchGroupDefault = 
+                	((JDBCFetchConfigurationImpl)fetch1).hasFetchGroupDefault();
+                boolean hasFetchGroupDefault1 = 
+                	((JDBCFetchConfigurationImpl)fetch2).hasFetchGroupDefault();
+                if (hasFetchGroupDefault && hasFetchGroupDefault1) 
+                    sameFetchGroup = true;
+            }
+            
+            if (!sameFetchGroup) {
+                Set fetchGroups = fetch1.getFetchGroups();
+                Set fetchGroups1 = fetch2.getFetchGroups();
+                if (fetchGroups != null ? !fetchGroups.equals(fetchGroups1) : 
+                	fetchGroups1 != null)
+                    return false;
+            }
+            
+            Set fields = fetch1.getFields();
+            Set fields1 = fetch2.getFields();
+            int size = fields.size();
+            int size1 = fields1.size();
+            if (size == 0 && size1 == 0)
+                return true;
+            else if (size != size1) 
+                return false;   
+            
+            if (fields != null ? !fields.equals(fields1) : fields1 != null)
+                return false;
+            
+            return true;
+        }
+        
+        
+        public int hashCode() {
+            int result = 0;
+            result = 31 * result + (mapping != null ? mapping.hashCode() : 0);
+            result = 31 * result + (fm != null ? fm.hashCode() : 0);
+            result = 31 * result + fetch.getIsolation();
+            result = 31 * result + fetch.getFetchDirection();
+            result = 31 * result + fetch.getEagerFetchMode();
+            result = 31 * result + fetch.getSubclassFetchMode();
+            result = 31 * result + fetch.getJoinSyntax();
+            Set joins = fetch.getJoins();
+            result = 31 * result + (joins != null ? joins.hashCode() : 0);
+            
+            result = 31 * result + fetch.getMaxFetchDepth();
+            result = 31 * result + fetch.getReadLockLevel();
+            result = 31 * result + fetch.getWriteLockLevel();
+        	
+            if (((JDBCFetchConfigurationImpl)fetch).hasFetchGroupAll()) 
+            	result = 31 * result + FetchGroup.NAME_ALL.hashCode();
+            else {
+                Set fetchGroups = fetch.getFetchGroups();
+                if (((JDBCFetchConfigurationImpl)fetch).hasFetchGroupDefault() 
+                	&& fetchGroups != null && fetchGroups.size() == 1)
+                    result = 31 * result + FetchGroup.NAME_DEFAULT.hashCode();
+                else {
+                    result = 31 * result + (fetchGroups != null && 
+                        fetchGroups.size() > 0 ? 
+                        fetchGroups.hashCode() : 0);
+                }
+            }
+            Set fields = fetch.getFields();
+        	result = 31 * result + (fields != null &&  fields.size() > 0 ? 
+        		fields.hashCode() : 0);
+            
+            return result;
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java Fri May  2 14:09:14 2008
@@ -19,11 +19,16 @@
 package org.apache.openjpa.jdbc.meta.strats;
 
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfigurationImpl;
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.meta.Embeddable;
 import org.apache.openjpa.jdbc.meta.FieldMapping;
@@ -38,12 +43,14 @@
 import org.apache.openjpa.jdbc.schema.Table;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.LogicalUnion;
 import org.apache.openjpa.jdbc.sql.Result;
 import org.apache.openjpa.jdbc.sql.Row;
 import org.apache.openjpa.jdbc.sql.RowManager;
 import org.apache.openjpa.jdbc.sql.SQLBuffer;
 import org.apache.openjpa.jdbc.sql.Select;
 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.OpenJPAStateManager;
 import org.apache.openjpa.lib.log.Log;
@@ -55,6 +62,7 @@
 import org.apache.openjpa.util.MetaDataException;
 import org.apache.openjpa.util.OpenJPAId;
 import org.apache.openjpa.util.UnsupportedException;
+
 import serp.util.Numbers;
 
 /**
@@ -579,9 +587,97 @@
         final int subs = field.getSelectSubclasses();
         final Joins[] resJoins = new Joins[rels.length];
 
-        // select related mapping columns; joining from the related type
-        // back to our fk table if not an inverse mapping (in which case we
-        // can just make sure the inverse cols == our pk values)
+        //cache union for field here
+        //select data for this sm
+        Union union = null;
+        SelectImpl sel = null;
+        List parmList = null;
+
+        if (!((JDBCStoreManager)store).isQuerySQLCacheOn())
+            union = newUnion(sm, store, fetch, rels, subs, resJoins);
+        else {
+            Map<JDBCStoreManager.SelectKey, Object[]> relationFieldUnionCache = 
+                ((JDBCStoreManager)store).getCacheMapFromQuerySQLCache(
+                RelationFieldStrategy.class);
+            boolean found = true;
+            JDBCFetchConfiguration fetchClone = new JDBCFetchConfigurationImpl();
+            fetchClone.copy(fetch);
+            JDBCStoreManager.SelectKey selKey = 
+                new JDBCStoreManager.SelectKey(null, field, fetch);
+            Object[] obj = relationFieldUnionCache.get(selKey);
+            if (obj != null) {
+                union = (Union) obj[0];
+                resJoins[0] = (Joins)obj[1];
+            } else {
+                synchronized(relationFieldUnionCache) {
+                    obj = relationFieldUnionCache.get(selKey);
+                    if (obj != null) {
+                        union = (Union) obj[0];
+                        resJoins[0] = (Joins) obj[1];
+                    } else {
+                        // select related mapping columns; joining from the 
+                        // related type back to our fk table if not an inverse 
+                        // mapping (in which case we can just make sure the 
+                        // inverse cols == our pk values)
+                        union = newUnion(sm, store, fetch, rels, subs, 
+                                resJoins);
+                        found = false;                
+                    }
+                    sel = ((LogicalUnion.UnionSelect)union.getSelects()[0]).
+                        getDelegate();
+                    SQLBuffer buf = sel.getSQL();
+                    if (buf == null) {
+                    	((SelectImpl)sel).setSQL(store, fetch);
+                        found = false;
+                    }
+
+                    // only cache the union when elems length is 1 for now
+                    if (!found && rels.length == 1) {
+                        Object[] obj1 = new Object[2];
+                        obj1[0] = union;
+                        obj1[1] = resJoins[0];
+                        ((JDBCStoreManager)store).addToSqlCache(
+                            relationFieldUnionCache, selKey, obj1);
+                    }
+                }
+            }
+            Log log = store.getConfiguration().
+                getLog(JDBCConfiguration.LOG_JDBC);
+            if (log.isTraceEnabled()){
+                if (found) 
+                    log.trace(_loc.get("cache-hit", field, this.getClass()));                        
+                else
+                    log.trace(_loc.get("cache-missed", field, this.getClass()));
+            }
+
+            parmList = new ArrayList();
+            ClassMapping mapping = field.getDefiningMapping();
+            Object oid = sm.getObjectId();
+            Column[] cols = mapping.getPrimaryKeyColumns();
+            if (sel == null)
+                sel = ((LogicalUnion.UnionSelect)union.getSelects()[0]).
+                getDelegate();
+
+            sel.wherePrimaryKey(mapping, cols, cols, oid, store, 
+            	null, null, parmList);
+        }
+        
+        Result res = union.execute(store, fetch, parmList);
+        try {
+            Object val = null;
+            if (res.next())
+                val = res.load(rels[res.indexOf()], store, fetch,
+                    resJoins[res.indexOf()]);
+            sm.storeObject(field.getIndex(), val);
+        } finally {
+            res.close();
+        }
+    }
+    
+    protected Union newUnion(final OpenJPAStateManager sm, 
+        final JDBCStore store, final JDBCFetchConfiguration fetch, 
+        final ClassMapping[] rels, final int subs, 
+        final Joins[] resJoins) {
         Union union = store.getSQLFactory().newUnion(rels.length);
         union.setExpectedResultCount(1, false);
         if (fetch.getSubclassFetchMode(field.getTypeMapping())
@@ -602,17 +698,7 @@
                     resJoins[idx]);
             }
         });
-
-        Result res = union.execute(store, fetch);
-        try {
-            Object val = null;
-            if (res.next())
-                val = res.load(rels[res.indexOf()], store, fetch,
-                    resJoins[res.indexOf()]);
-            sm.storeObject(field.getIndex(), val);
-        } finally {
-            res.close();
-        }
+        return union;
     }
 
     public Object toDataStoreValue(Object val, JDBCStore store) {

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StoreCollectionFieldStrategy.java Fri May  2 14:09:14 2008
@@ -22,21 +22,29 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfigurationImpl;
 import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
 import org.apache.openjpa.jdbc.meta.ClassMapping;
 import org.apache.openjpa.jdbc.meta.FieldMapping;
 import org.apache.openjpa.jdbc.meta.FieldStrategy;
 import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.jdbc.schema.ForeignKey;
 import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.LogicalUnion;
 import org.apache.openjpa.jdbc.sql.Result;
 import org.apache.openjpa.jdbc.sql.Select;
 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.OpenJPAStateManager;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.ChangeTracker;
 import org.apache.openjpa.util.Id;
@@ -55,6 +63,9 @@
 public abstract class StoreCollectionFieldStrategy
     extends ContainerFieldStrategy {
 
+    private static final Localizer _loc = Localizer.forPackage
+        (StoreCollectionFieldStrategy.class);
+    
     /**
      * Return the foreign key used to join to the owning field for the given
      * element mapping from {@link #getIndependentElementMappings} (or null).
@@ -445,19 +456,83 @@
             return;
         }
 
+        //cache union for field here
         // select data for this sm
+        boolean found = true;
         final ClassMapping[] elems = getIndependentElementMappings(true);
         final Joins[] resJoins = new Joins[Math.max(1, elems.length)];
-        Union union = store.getSQLFactory().newUnion
-            (Math.max(1, elems.length));
-        union.select(new Union.Selector() {
-            public void select(Select sel, int idx) {
-                ClassMapping elem = (elems.length == 0) ? null : elems[idx];
-                resJoins[idx] = selectAll(sel, elem, sm, store, fetch,
-                    JDBCFetchConfiguration.EAGER_PARALLEL);
+        List parmList = null;
+        Union union = null;
+        SelectImpl sel = null;
+        Map<JDBCStoreManager.SelectKey, Object[]> storeCollectionUnionCache = null;
+        JDBCStoreManager.SelectKey selKey = null;
+        if (!((JDBCStoreManager)store).isQuerySQLCacheOn())
+            union = newUnion(sm, store, fetch, elems, resJoins);
+        else {
+            parmList = new ArrayList();
+            JDBCFetchConfiguration fetchClone = new JDBCFetchConfigurationImpl();
+            fetchClone.copy(fetch);
+           
+            // to specify the type so that no cast is needed
+            storeCollectionUnionCache = ((JDBCStoreManager)store).
+                getCacheMapFromQuerySQLCache(StoreCollectionFieldStrategy.class);
+            selKey = 
+                new JDBCStoreManager.SelectKey(null, field, fetchClone);
+            Object[] objs = storeCollectionUnionCache.get(selKey);
+            if (objs != null) {
+                union = (Union) objs[0];
+                resJoins[0] = (Joins) objs[1];
             }
-        });
+            else {
+                synchronized(storeCollectionUnionCache) {
+                    objs = storeCollectionUnionCache.get(selKey);
+                    if (objs == null) {
+                        // select data for this sm
+                        union = newUnion(sm, store, fetch, elems, resJoins);
+                        found = false;
+                    } else {
+                        union = (Union) objs[0];
+                        resJoins[0] = (Joins) objs[1];
+                    }
+
+                    sel = ((LogicalUnion.UnionSelect)union.getSelects()[0]).
+                        getDelegate();
+                    if (sel.getSQL() == null) {
+                    	((SelectImpl)sel).setSQL(store, fetch);
+                        found = false;
+                    }
+
+                    // only cache the union when elems length is 1 for now
+                    if (!found && elems.length == 1) { 
+                        Object[] objs1 = new Object[2];
+                        objs1[0] = union;
+                        objs1[1] = resJoins[0];
+                        ((JDBCStoreManager)store).addToSqlCache(
+                            storeCollectionUnionCache, selKey, objs1);
+                     }
+                }
+            }
+            
+            Log log = store.getConfiguration().
+                getLog(JDBCConfiguration.LOG_JDBC);
+            if (log.isTraceEnabled()) {
+                if (found)
+                    log.trace(_loc.get("cache-hit", field, this.getClass()));
+                else
+                    log.trace(_loc.get("cache-missed", field, this.getClass())); 
+            }
+            
+            ClassMapping mapping = field.getDefiningMapping();
+            Object oid = sm.getObjectId();
+            Column[] cols = mapping.getPrimaryKeyColumns();
+            if (sel == null)
+                sel = ((LogicalUnion.UnionSelect)union.getSelects()[0]).
+                getDelegate();
 
+            sel.wherePrimaryKey(mapping, cols, cols, oid, store, 
+                	null, null, parmList);
+        }
+        
         // create proxy
         Object coll;
         ChangeTracker ct = null;
@@ -470,14 +545,14 @@
         }
 
         // load values
-        Result res = union.execute(store, fetch);
+        Result res = union.execute(store, fetch, parmList);
         try {
             int seq = -1;
             while (res.next()) {
                 if (ct != null && field.getOrderColumn() != null)
                     seq = res.getInt(field.getOrderColumn());
-                add(store, coll, loadElement(sm, store, fetch, res,
-                    resJoins[res.indexOf()]));
+               	add(store, coll, loadElement(sm, store, fetch, res,
+           	        resJoins[res.indexOf()]));
             }
             if (ct != null && field.getOrderColumn() != null)
                 ct.setNextSequence(seq + 1);
@@ -493,6 +568,21 @@
             sm.storeObject(field.getIndex(), coll);
     }
 
+    protected Union newUnion(final OpenJPAStateManager sm, final JDBCStore store,
+        final JDBCFetchConfiguration fetch, final ClassMapping[] elems,
+        final Joins[] resJoins) {
+        Union union = store.getSQLFactory().newUnion
+        (Math.max(1, elems.length));
+        union.select(new Union.Selector() {
+            public void select(Select sel, int idx) {
+                ClassMapping elem = (elems.length == 0) ? null : elems[idx];
+                resJoins[idx] = selectAll(sel, elem, sm, store, fetch,
+                        JDBCFetchConfiguration.EAGER_PARALLEL);
+            }
+        });
+        return union;
+    }
+    
     /**
      * Select data for loading, starting in field table.
      */

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=652913&r1=652912&r2=652913&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 Fri May  2 14:09:14 2008
@@ -202,20 +202,32 @@
     }
 
     public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
+            throws SQLException {
+        return execute(store, fetch, null);
+    }    
+
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+        int lockLevel)
+        throws SQLException {
+        return execute(store, fetch, lockLevel, null);
+    }
+    
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch, 
+        List params)
         throws SQLException {
         if (fetch == null)
             fetch = store.getFetchConfiguration();
-        return execute(store, fetch, fetch.getReadLockLevel());
+        return execute(store, fetch, fetch.getReadLockLevel(), params);
     }
 
     public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
-        int lockLevel)
+        int lockLevel, List params)
         throws SQLException {
         if (fetch == null)
             fetch = store.getFetchConfiguration();
 
         if (sels.length == 1) {
-            Result res = sels[0].execute(store, fetch, lockLevel);
+            Result res = sels[0].execute(store, fetch, lockLevel, params);
             ((AbstractResult) res).setBaseMapping(mappings[0]);
             return res;
         }
@@ -224,7 +236,7 @@
             AbstractResult res;
             for (int i = 0; i < sels.length; i++) {
                 res = (AbstractResult) sels[i].execute(store, fetch,
-                    lockLevel);
+                    lockLevel, params);
                 res.setBaseMapping(mappings[i]);
                 res.setIndexOf(i);
 
@@ -256,7 +268,7 @@
             List l;
             for (int i = 0; i < res.length; i++) {
                 res[i] = (AbstractResult) sels[i].execute(store, fetch,
-                    lockLevel);
+                    lockLevel, params);
                 res[i].setBaseMapping(mappings[i]);
                 res[i].setIndexOf(i);
 
@@ -303,7 +315,7 @@
     /**
      * A select that is part of a logical union.
      */
-    protected class UnionSelect
+    public class UnionSelect
         implements Select {
 
         protected final SelectImpl sel;
@@ -396,6 +408,18 @@
             return sel.getCount(store);
         }
 
+        public Result execute(JDBCStore store, JDBCFetchConfiguration fetch, 
+            List params)
+            throws SQLException {
+            return sel.execute(store, fetch, params);
+        }
+
+        public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+            int lockLevel, List params)
+            throws SQLException {
+            return sel.execute(store, fetch, lockLevel, params);
+        }
+
         public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
             throws SQLException {
             return sel.execute(store, fetch);
@@ -406,7 +430,7 @@
             throws SQLException {
             return sel.execute(store, fetch, lockLevel);
         }
-
+        
         public List getSubselects() {
             return Collections.EMPTY_LIST;
         }
@@ -475,6 +499,14 @@
             return sel.getHaving();
         }
 
+        public SQLBuffer getSQL() {
+            return sel.getSQL();
+        }
+        
+        public void setSQL(JDBCStore store, JDBCFetchConfiguration fetch) {
+            sel.setSQL(store, fetch);
+        }
+        
         public void addJoinClassConditions() {
             sel.addJoinClassConditions();
         }
@@ -717,6 +749,15 @@
             JDBCStore store) {
             sel.wherePrimaryKey(oid, mapping, store);
         }
+        
+        public int wherePrimaryKey(ClassMapping mapping, Column[] toCols, 
+            Column[] fromCols, Object oid, JDBCStore store, PathJoins pj,
+            SQLBuffer buf, List parmList) {
+            return sel.wherePrimaryKey(mapping, toCols, fromCols, oid, store, pj, 
+                buf, parmList);
+        }
+        
+        
 
         public void whereForeignKey(ForeignKey fk, Object oid,
             ClassMapping mapping, JDBCStore store) {

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java Fri May  2 14:09:14 2008
@@ -450,9 +450,19 @@
      * the SQL in this buffer.
      */
     public PreparedStatement prepareStatement(Connection conn, int rsType,
+        int rsConcur, List parms)
+        throws SQLException {
+        return prepareStatement(conn, null, rsType, rsConcur, parms);
+    }
+    
+    /**
+     * Create and populate the parameters of a prepared statement using
+     * the SQL in this buffer.
+     */
+    public PreparedStatement prepareStatement(Connection conn, int rsType,
         int rsConcur)
         throws SQLException {
-        return prepareStatement(conn, null, rsType, rsConcur);
+        return prepareStatement(conn, rsType, rsConcur, null);
     }
 
     /**
@@ -462,6 +472,16 @@
     public PreparedStatement prepareStatement(Connection conn,
         JDBCFetchConfiguration fetch, int rsType, int rsConcur)
         throws SQLException {
+        return prepareStatement(conn, fetch, rsType, rsConcur, null);
+    }
+    
+    /**
+     * Create and populate the parameters of a prepred statement using the
+     * SQL in this buffer and the given fetch configuration.
+     */
+    public PreparedStatement prepareStatement(Connection conn,
+        JDBCFetchConfiguration fetch, int rsType, int rsConcur, List parms)
+        throws SQLException {
         if (rsType == -1 && fetch == null)
             rsType = ResultSet.TYPE_FORWARD_ONLY;
         else if (rsType == -1)
@@ -476,7 +496,7 @@
         else
             stmnt = conn.prepareStatement(getSQL(), rsType, rsConcur);
         try {
-            setParameters(stmnt);
+            setParameters(stmnt, parms);
             if (fetch != null) {
                 if (fetch.getFetchBatchSize() > 0)
                     stmnt.setFetchSize(fetch.getFetchBatchSize());
@@ -559,13 +579,25 @@
      */
     public void setParameters(PreparedStatement ps)
         throws SQLException {
-        if (_params == null)
+        setParameters(ps, null);
+    }
+    
+    /**
+     * Populate the parameters of an existing PreparedStatement
+     * with values from this buffer.
+     */
+    public void setParameters(PreparedStatement ps, List cacheParams)
+        throws SQLException {
+        List params = ((cacheParams != null && cacheParams.size() > 0) ? 
+            cacheParams : _params);    
+        
+        if (params == null)
             return;
 
         Column col;
-        for (int i = 0; i < _params.size(); i++) {
+        for (int i = 0; i < params.size(); i++) {
             col = (_cols == null) ? null : (Column) _cols.get(i);
-            _dict.setUnknown(ps, i + 1, _params.get(i), col);
+            _dict.setUnknown(ps, i + 1, params.get(i), col);
         }
     }
 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Select.java Fri May  2 14:09:14 2008
@@ -193,6 +193,19 @@
     public SQLBuffer getHaving();
 
     /**
+     * Return the SQL for this select. This buffer contains
+     * the final SQL to be executed/cached.
+     */
+    public SQLBuffer getSQL();    
+    
+    /**
+     * Create and set the SQLBuffer object to this select. This buffer contains
+     * the final SQL to be executed/cached.
+     */
+    public void setSQL(JDBCStore store, JDBCFetchConfiguration fetch); 
+    
+    
+    /**
      * Apply class conditions from relation joins.  This may affect the return
      * values of {@link #getJoins}, {@link #getJoinIterator}, and
      * {@link #getWhere}.
@@ -515,6 +528,19 @@
      */
     public void wherePrimaryKey(Object oid, ClassMapping mapping,
         JDBCStore store);
+    
+    
+    /**
+     * Add where conditions setting the mapping's primary key to the given
+     * oid values. If the parmList is not null, the value of the primary
+     * key will be collected and saved into the parmList. If the parmList is 
+     * null, this method will build the where clause with the value
+     * incorporated in the where clause.
+     */
+    public int wherePrimaryKey(ClassMapping mapping, Column[] toCols, 
+            Column[] fromCols, Object oid, JDBCStore store, PathJoins pj,
+            SQLBuffer buf, List parmList);
+    
 
     /**
      * Add where conditions setting the given foreign key to the given

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectExecutor.java Fri May  2 14:09:14 2008
@@ -19,6 +19,7 @@
 package org.apache.openjpa.jdbc.sql;
 
 import java.sql.SQLException;
+import java.util.List;
 
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
@@ -133,6 +134,20 @@
      * Execute this select in the context of the given store manager.
      */
     public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+        List params) 
+        throws SQLException;
+
+    /**
+     * Execute this select in the context of the given store manager.
+     */
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+        int lockLevel, List params)
+        throws SQLException;
+
+    /**
+     * Execute this select in the context of the given store manager.
+     */
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
         int lockLevel)
         throws SQLException;
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java Fri May  2 14:09:14 2008
@@ -118,6 +118,9 @@
     // 'parent.address.street' for the purposes of comparisons
     private Map _aliases = null;
 
+    // to cache table alias using Table as the key
+    private Map _tableAliases = null;
+
     // map of indexes to table aliases like 'TABLENAME t0'
     private SortedMap _tables = null;
 
@@ -167,7 +170,10 @@
     // if the bit is set, the corresponding alias has been removed from parent
     // and recorded under subselect.
     private BitSet _removedAliasFromParent = new BitSet(16);
-     
+
+    //contains final sql statement to be executed/cached
+    private SQLBuffer _sql = null;
+    
     /**
      * Helper method to return the proper table alias for the given alias index.
      */
@@ -300,7 +306,7 @@
             stmnt = prepareStatement(conn, sql, null, 
                 ResultSet.TYPE_FORWARD_ONLY, 
                 ResultSet.CONCUR_READ_ONLY, false);
-            rs = executeQuery(conn, stmnt, sql, false, store);
+            rs = executeQuery(conn, stmnt, sql, false, store, null);
             return getCount(rs);
         } finally {
             if (rs != null)
@@ -312,20 +318,31 @@
         }
     }
 
-    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch)
-        throws SQLException {
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch, 
+        List parms) throws SQLException {
         if (fetch == null)
             fetch = store.getFetchConfiguration();
         return execute(store.getContext(), store, fetch,
-            fetch.getReadLockLevel());
+            fetch.getReadLockLevel(), parms);
+    }
+
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch) 
+        throws SQLException {
+        return execute(store, fetch, null);
+     }
+
+    public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
+        int lockLevel, List parms)
+        throws SQLException {
+            if (fetch == null)
+                fetch = store.getFetchConfiguration();
+            return execute(store.getContext(), store, fetch, lockLevel, parms);
     }
 
     public Result execute(JDBCStore store, JDBCFetchConfiguration fetch,
         int lockLevel)
         throws SQLException {
-        if (fetch == null)
-            fetch = store.getFetchConfiguration();
-        return execute(store.getContext(), store, fetch, lockLevel);
+        return execute(store, fetch, lockLevel, null);
     }
 
     /**
@@ -333,16 +350,21 @@
      * context is passed in separately for profiling purposes.
      */
     protected Result execute(StoreContext ctx, JDBCStore store, 
-        JDBCFetchConfiguration fetch, int lockLevel)
+        JDBCFetchConfiguration fetch, int lockLevel, List params)
         throws SQLException {
-        boolean forUpdate = false;
-        if (!isAggregate() && _grouping == null) {
-            JDBCLockManager lm = store.getLockManager();
-            if (lm != null)
-                forUpdate = lm.selectForUpdate(this, lockLevel);
-        }
-
-        SQLBuffer sql = toSelect(forUpdate, fetch);
+        boolean forUpdate = isForUpdate(store, lockLevel);
+        
+        // A non-null _sql indicates that this SelectImpl object
+        // is obtained from cache. The _sql is constructed
+        // under the assumption that isAggregate() is false
+        // and _grouping is null. If neither of these holds,
+        // we need to re-construct the _sql
+        if (_sql != null && (isAggregate() || _grouping != null)) 
+            _sql = null;
+        
+        if (_sql == null) 
+        	_sql = toSelect(forUpdate, fetch);
+        
         boolean isLRS = isLRS();
         int rsType = (isLRS && supportsRandomAccess(forUpdate))
             ? -1 : ResultSet.TYPE_FORWARD_ONLY;
@@ -351,13 +373,15 @@
         ResultSet rs = null;
         try {
             if (isLRS) 
-                stmnt = prepareStatement(conn, sql, fetch, rsType, -1, true); 
+                stmnt = prepareStatement(conn, _sql, fetch, rsType, -1, true, 
+                        params); 
             else
-                stmnt = prepareStatement(conn, sql, null, rsType, -1, false);
+                stmnt = prepareStatement(conn, _sql, null, rsType, -1, false, 
+                        params);
             
             setTimeout(stmnt, forUpdate, fetch);
             
-            rs = executeQuery(conn, stmnt, sql, isLRS, store);
+            rs = executeQuery(conn, stmnt, _sql, isLRS, store, params);
         } catch (SQLException se) {
             // clean up statement
             if (stmnt != null)
@@ -367,7 +391,17 @@
         }
 
         return getEagerResult(conn, stmnt, rs, store, fetch, forUpdate, 
-            sql.getSQL());
+            _sql.getSQL());
+    }
+    
+    private boolean isForUpdate(JDBCStore store, int lockLevel) {
+    	boolean forUpdate = false;
+        if (!isAggregate() && _grouping == null) {
+            JDBCLockManager lm = store.getLockManager();
+            if (lm != null)
+                forUpdate = lm.selectForUpdate(this, lockLevel);
+        }
+        return forUpdate;
     }
 
     /**
@@ -413,10 +447,22 @@
     protected PreparedStatement prepareStatement(Connection conn, 
         SQLBuffer sql, JDBCFetchConfiguration fetch, int rsType, 
         int rsConcur, boolean isLRS) throws SQLException {
+        // add comments why we pass in null as the last parameter
+        return prepareStatement(conn, sql, fetch, rsType, rsConcur, isLRS, 
+                null);
+    }
+
+    /**
+     * This method is to provide override for non-JDBC or JDBC-like 
+     * implementation of preparing statement.
+     */
+    protected PreparedStatement prepareStatement(Connection conn, 
+        SQLBuffer sql, JDBCFetchConfiguration fetch, int rsType, 
+        int rsConcur, boolean isLRS, List params) throws SQLException {
         if (fetch == null)
-            return sql.prepareStatement(conn, rsType, rsConcur);
+            return sql.prepareStatement(conn, rsType, rsConcur, params);
         else
-            return sql.prepareStatement(conn, fetch, rsType, -1);
+            return sql.prepareStatement(conn, fetch, rsType, -1, params);
     }
     
     /**
@@ -445,7 +491,8 @@
      * implementation of executing query.
      */
     protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, 
-        SQLBuffer sql, boolean isLRS, JDBCStore store) throws SQLException {
+        SQLBuffer sql, boolean isLRS, JDBCStore store, List params) 
+        throws SQLException {
         return stmnt.executeQuery();
     }
     
@@ -589,6 +636,19 @@
         return _having;
     }
 
+    public SQLBuffer getSQL() {
+        return _sql;
+    }
+
+    public void setSQL(SQLBuffer sql) {
+        _sql = sql;
+    }
+
+    public void setSQL(JDBCStore store, JDBCFetchConfiguration fetch) {
+        boolean forUpdate = isForUpdate(store, fetch.getReadLockLevel());
+        _sql = toSelect(forUpdate, fetch);
+    }
+    
     public void addJoinClassConditions() {
         if (_joins == null || _joins.joins() == null)
             return;
@@ -656,13 +716,30 @@
      * Return the alias for the given column.
      */
     private String getColumnAlias(String col, Table table, PathJoins pj) {
+        String tableAlias = null;
+        if (pj == null || pj.path() == null) {
+            if (_tableAliases == null)
+                _tableAliases = new HashMap();
+            tableAlias = (String) _tableAliases.get(table);
+            if (tableAlias == null) {
+                tableAlias = getTableAlias(table, pj).toString();
+                _tableAliases.put(table, tableAlias);
+            }
+            return new StringBuilder(tableAlias).append(col).toString();
+        }
+        return getTableAlias(table, pj).append(col).toString();
+    }
+    
+    private StringBuilder getTableAlias(Table table, PathJoins pj) {
+        StringBuilder buf = new StringBuilder();
         if (_from != null) {
             String alias = toAlias(_from.getTableIndex(table, pj, true));
             if (_dict.requiresAliasForSubselect)
-                return FROM_SELECT_ALIAS + "." + alias + "_" + col;
-            return alias + "_" + col;
+                return buf.append(FROM_SELECT_ALIAS).append(".").append(alias).
+                    append("_");
+            return buf.append(alias).append("_");
         }
-        return toAlias(getTableIndex(table, pj, true)) + "." + col;
+        return buf.append(toAlias(getTableIndex(table, pj, true))).append(".");
     }
 
     public boolean isAggregate() {
@@ -1263,12 +1340,38 @@
             return;
         }
 
+        SQLBuffer buf = new SQLBuffer(_dict);
+
+        // only bother to pack pk values into array if app id        
+        int count = wherePrimaryKey(mapping, toCols, fromCols, oid, store, pj, 
+        	buf, null);
+            	
+        if (constCols != null && constCols.length > 0) {
+            for (int i = 0; i < constCols.length; i++, count++) {
+                if (count > 0)
+                    buf.append(" AND ");
+                buf.append(getColumnAlias(constCols[i], pj));
+
+                if (vals[i] == null)
+                    buf.append(" IS ");
+                else
+                    buf.append(" = ");
+                buf.appendValue(vals[i], constCols[i]);
+            }
+        }
+
+        where(buf, pj);
+    }
+
+    public int wherePrimaryKey(ClassMapping mapping, Column[] toCols, 
+    	Column[] fromCols, Object oid, JDBCStore store, PathJoins pj,
+    	SQLBuffer buf, List parmList) {
         // only bother to pack pk values into array if app id
+    	boolean collectParmValueOnly = (parmList != null ? true : false);
         Object[] pks = null;
         if (mapping.getIdentityType() == ClassMapping.ID_APPLICATION)
             pks = ApplicationIds.toPKValues(oid, mapping);
 
-        SQLBuffer buf = new SQLBuffer(_dict);
         Joinable join;
         Object val;
         int count = 0;
@@ -1281,8 +1384,13 @@
                 val = pks[mapping.getField(join.getFieldIndex()).
                     getPrimaryKeyIndex()];
                 val = join.getJoinValue(val, toCols[i], store);
+                if (parmList != null)
+                	parmList.add(val);
             }
-
+            
+            if (collectParmValueOnly) 
+            	continue;
+            
             if (count > 0)
                 buf.append(" AND ");
             buf.append(getColumnAlias(fromCols[i], pj));
@@ -1292,24 +1400,9 @@
                 buf.append(" = ");
             buf.appendValue(val, fromCols[i]);
         }
-
-        if (constCols != null && constCols.length > 0) {
-            for (int i = 0; i < constCols.length; i++, count++) {
-                if (count > 0)
-                    buf.append(" AND ");
-                buf.append(getColumnAlias(constCols[i], pj));
-
-                if (vals[i] == null)
-                    buf.append(" IS ");
-                else
-                    buf.append(" = ");
-                buf.appendValue(vals[i], constCols[i]);
-            }
-        }
-
-        where(buf, pj);
+        return count;
     }
-
+    
     /**
      * Test to see if the given set of columns contains all the
      * columns in the given potential subset.

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties Fri May  2 14:09:14 2008
@@ -113,4 +113,5 @@
 batch_limit: The batch limit is set to {0}.
 batch_update_info: ExecuteBatch command returns update count {0} for \
 	statement {1}.
-    
+cache-hit: SQL Cache hit with key: {0} in {1}
+cache-missed: SQL Cache missed with key: {0} in {1}    

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/strats/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/strats/localizer.properties?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/strats/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/strats/localizer.properties Fri May  2 14:09:14 2008
@@ -134,3 +134,5 @@
 	its "{1}" primary key field does not use a simple mapping.
 unmapped-datastore-value: Instances of type "{0}" are not valid query \
 	parameters because the type is not mapped.
+cache-hit: SQL Cache hit with key: {0} in {1}
+cache-missed: SQL Cache missed with key: {0} in {1}

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java Fri May  2 14:09:14 2008
@@ -82,6 +82,9 @@
         public Set rootClasses;
         public Set rootInstances;
         public Map hints = null;
+        
+        public boolean fetchGroupContainsDefault = false;
+        public boolean fetchGroupContainsAll = false;
     }
 
     private final ConfigurationState _state;
@@ -92,7 +95,7 @@
     private boolean _load = true;
     private int _availableRecursion;
     private int _availableDepth;
-
+    
     public FetchConfigurationImpl() {
         this(null);
     }
@@ -221,10 +224,22 @@
 
     public boolean hasFetchGroup(String group) {
         return _state.fetchGroups != null
-            && (_state.fetchGroups.contains(group)
-            || _state.fetchGroups.contains(FetchGroup.NAME_ALL));
+            && (hasFetchGroupAll()
+            ||  _state.fetchGroups.contains(group));
     }
 
+    public boolean hasFetchGroupDefault()
+    {
+        // Fetch group All includes fetch group Default by definition
+        return _state.fetchGroupContainsDefault || 
+            _state.fetchGroupContainsAll;
+    }
+    
+    public boolean hasFetchGroupAll()
+    {
+        return _state.fetchGroupContainsAll;
+    }
+    
     public FetchConfiguration addFetchGroup(String name) {
         if (StringUtils.isEmpty(name))
             throw new UserException(_loc.get("null-fg"));
@@ -234,6 +249,10 @@
             if (_state.fetchGroups == null)
                 _state.fetchGroups = new HashSet();
             _state.fetchGroups.add(name);
+            if (FetchGroup.NAME_ALL.equals(name))
+                _state.fetchGroupContainsAll = true;
+            else if (FetchGroup.NAME_DEFAULT.equals(name))
+                _state.fetchGroupContainsDefault = true;
         } finally {
             unlock();
         }
@@ -251,8 +270,13 @@
     public FetchConfiguration removeFetchGroup(String group) {
         lock();
         try {
-            if (_state.fetchGroups != null)
+            if (_state.fetchGroups != null) {
                 _state.fetchGroups.remove(group);
+                if (FetchGroup.NAME_ALL.equals(group))
+                    _state.fetchGroupContainsAll = false;
+                else if (FetchGroup.NAME_DEFAULT.equals(group))
+                    _state.fetchGroupContainsDefault = false;
+            }
         } finally {
             unlock();
         }
@@ -262,8 +286,9 @@
     public FetchConfiguration removeFetchGroups(Collection groups) {
         lock();
         try {
-            if (_state.fetchGroups != null)
-                _state.fetchGroups.removeAll(groups);
+            if (_state.fetchGroups != null && groups != null)
+                for (Object group : groups)
+                    removeFetchGroup(group.toString());
         } finally {
             unlock();
         }
@@ -273,8 +298,11 @@
     public FetchConfiguration clearFetchGroups() {
         lock();
         try {
-            if (_state.fetchGroups != null)
+            if (_state.fetchGroups != null) {
                 _state.fetchGroups.clear();
+                _state.fetchGroupContainsAll = false;
+                _state.fetchGroupContainsDefault = true;
+            }
         } finally {
             unlock();
         }
@@ -562,9 +590,9 @@
      * Whether our configuration state includes the given field.
      */
     private boolean includes(FieldMetaData fmd) {
-        if ((fmd.isInDefaultFetchGroup() 
-            && hasFetchGroup(FetchGroup.NAME_DEFAULT))
-            || hasFetchGroup(FetchGroup.NAME_ALL)
+        if (hasFetchGroupAll()
+            || (fmd.isInDefaultFetchGroup() 
+            && hasFetchGroupDefault())
             || hasField(fmd.getFullName(false)))
             return true;
         String[] fgs = fmd.getCustomFetchGroups();

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java?rev=652913&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java Fri May  2 14:09:14 2008
@@ -0,0 +1,391 @@
+/*
+ * 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.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+import junit.framework.TestCase;
+
+import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
+import org.apache.openjpa.persistence.EntityManagerImpl;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.simple.Person;
+
+/*
+ * Verify multiple permutations of openjpa.jdbc.QuerySQLCache settings.
+ */
+public class TestQuerySQLCache
+    extends TestCase {
+    
+    final int nThreads = 5;
+    final int nPeople = 100;
+    final int nIterations = 10;
+
+    /*
+     * Verify QuerySQLCacheValue setting "all" is caching queries.
+     */
+    public void testAllCacheSetting() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", "all");
+        OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI)
+            OpenJPAPersistence.cast(
+                Persistence.createEntityManagerFactory("test", props));
+        
+        EntityManagerImpl em = (EntityManagerImpl)emf.createEntityManager();
+        BrokerImpl broker = (BrokerImpl) em.getBroker();
+        DelegatingStoreManager dstore = broker.getStoreManager();
+        JDBCStoreManager jstore = 
+            (JDBCStoreManager)dstore.getInnermostDelegate();
+
+        em.getTransaction().begin();
+        Person p = new Person();
+        p.setId(1);
+        em.persist(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        Person p1 = em.find(Person.class, 1);
+        Map sqlCache = jstore.getQuerySQLCache();
+        Set keys = sqlCache.keySet();
+        for (Iterator iter = keys.iterator(); iter.hasNext();) {
+            Map cacheMap = (Map) iter.next();
+            //make sure there is an entry in the cache
+            assertEquals(1, cacheMap.size());   
+        }
+        
+        em.getTransaction().begin();
+        em.remove(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        em.close();
+        emf.close();
+    }
+    
+    /*
+     * Verify QuerySQLCacheValue setting "true" uses the expected cache
+     * implementation and is caching.
+     */
+    public void testTrueCacheSetting() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", "true");
+        OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI)
+            OpenJPAPersistence.cast(
+                Persistence.createEntityManagerFactory("test", props));
+        
+        EntityManagerImpl em = (EntityManagerImpl)emf.createEntityManager();
+        BrokerImpl broker = (BrokerImpl) em.getBroker();
+        DelegatingStoreManager dstore = broker.getStoreManager();
+        JDBCStoreManager jstore = 
+            (JDBCStoreManager)dstore.getInnermostDelegate();
+        
+        em.getTransaction().begin();
+        Person p = new Person();
+        p.setId(1);
+        em.persist(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        Person p1 = em.find(Person.class, 1);
+        Map sqlCache = jstore.getQuerySQLCache();
+        Set keys = sqlCache.keySet();
+        for (Iterator iter = keys.iterator(); iter.hasNext();) {
+            Map cacheMap = (Map) iter.next();
+            //make sure there is an entry in the cache
+            assertEquals(1, cacheMap.size());   
+        }
+        
+        em.getTransaction().begin();
+        em.remove(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        em.close();
+        emf.close();
+    }
+
+    /*
+     * Verify caching is disabled when the QuerySQLCacheValue setting is 
+     * "false".
+     */
+    public void testFalseCacheSetting() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", "false");
+        OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI)
+            OpenJPAPersistence.cast(
+                Persistence.createEntityManagerFactory("test", props));
+        
+        EntityManagerImpl em = (EntityManagerImpl)emf.createEntityManager();
+        BrokerImpl broker = (BrokerImpl) em.getBroker();
+        DelegatingStoreManager dstore = broker.getStoreManager();
+        JDBCStoreManager jstore = 
+            (JDBCStoreManager)dstore.getInnermostDelegate();
+        
+        em.getTransaction().begin();
+        Person p = new Person();
+        p.setId(1);
+        em.persist(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        Person p1 = em.find(Person.class, 1);
+
+        assertFalse(jstore.isQuerySQLCacheOn());
+        
+        em.getTransaction().begin();
+        em.remove(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        em.close();
+        emf.close();
+    }
+
+    /*
+     * Verify QuerySQLCacheValue setting with a custom cache backend uses
+     * the expected cache implementation and is caching.
+     */
+    public void testCustomCacheSetting() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", 
+            "org.apache.openjpa.kernel.TestQuerySQLCache.CustomCacheMap");
+        OpenJPAEntityManagerFactorySPI emf = (OpenJPAEntityManagerFactorySPI)
+            OpenJPAPersistence.cast(
+                Persistence.createEntityManagerFactory("test", props));
+        
+        EntityManagerImpl em = (EntityManagerImpl)emf.createEntityManager();
+        BrokerImpl broker = (BrokerImpl) em.getBroker();
+        DelegatingStoreManager dstore = broker.getStoreManager();
+        JDBCStoreManager jstore = 
+            (JDBCStoreManager)dstore.getInnermostDelegate();
+        
+        em.getTransaction().begin();
+        Person p = new Person();
+        p.setId(1);
+        em.persist(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        Person p1 = em.find(Person.class, 1);
+
+        assertTrue(jstore.isQuerySQLCacheOn());
+
+        Map sqlCache = jstore.getQuerySQLCache();
+        Set keys = sqlCache.keySet();
+        for (Iterator iter = keys.iterator(); iter.hasNext();) {
+            Map cacheMap = (Map) iter.next();
+            assertTrue((cacheMap instanceof 
+                org.apache.openjpa.kernel.TestQuerySQLCache.CustomCacheMap));
+            //make sure there is an entry in the cache
+            assertEquals(1, cacheMap.size());   
+        }
+        
+        em.getTransaction().begin();
+        em.remove(p);
+        em.flush();
+        em.getTransaction().commit();
+        
+        em.close();
+        emf.close();
+    }
+
+    /*
+     * Verify an exception is thrown if a bad cache implementation class
+     * is specified.
+     */
+    public void testBadCustomCacheSetting() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", 
+            "org.apache.openjpa.kernel.TestQuerySQLCache.BadCacheMap");
+        
+        try {
+            OpenJPAEntityManagerFactorySPI emf = 
+                (OpenJPAEntityManagerFactorySPI)OpenJPAPersistence.cast(
+                        Persistence.createEntityManagerFactory("test", props));
+            // EMF creation should throw an exception because the cache 
+            // implementation class will not be found.
+            assertFalse(false);
+        } catch (Exception e) {
+            assertTrue(true);
+        }
+    }
+
+    /*
+     * Verify multi-threaded multi-entity manager finder works with the
+     * QuerySQLCache set to "all".
+     */
+    public void testMultiEMCachingAll() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", 
+            "all");
+        runMultiEMCaching(props);        
+    }
+
+    /*
+     * Verify multi-threaded multi-entity manager finder works with the
+     * QuerySQLCache set to "true".
+     */
+    public void testMultiEMCachingTrue() {
+        Map props = new HashMap(System.getProperties());
+        props.put("openjpa.MetaDataFactory", "jpa(Types=" 
+            + Person.class.getName() + ")");
+        props.put("openjpa.jdbc.QuerySQLCache", 
+            "true");
+        runMultiEMCaching(props);
+    }
+
+    private void runMultiEMCaching(Map props) {
+
+        EntityManagerFactory emfac = 
+                Persistence.createEntityManagerFactory("test", props);
+
+        EntityManager em = emfac.createEntityManager();            
+
+        // Create some entities
+        em.getTransaction().begin();
+        for (int i = 0; i < nPeople; i++)
+        {
+            Person p = new Person();
+            p.setId(i);
+            em.persist(p);
+        }
+        em.flush();
+        em.getTransaction().commit();
+        em.close();
+
+        Thread[] newThreads = new Thread[nThreads];
+        FindPeople[] customer = new FindPeople[nThreads];
+        
+        for (int i=0; i < nThreads; i++) {
+            customer[i] = new FindPeople(emfac, 0, nPeople, 
+                nIterations, i);
+            newThreads[i] = new Thread(customer[i]);
+            newThreads[i].start();
+        }
+        
+        // Wait for the worker threads to complete
+        for (int i = 0; i < nThreads; i++) {
+            try {
+                newThreads[i].join();
+            } catch (InterruptedException e) {
+                this.fail("Caught Interrupted Exception: " + e);
+            }
+        }   
+
+        // Run through the state of all runnables to assert if any of them
+        // failed.
+        for (int i = 0; i < nThreads; i++) {
+            assertFalse(customer[i].hadFailures());
+        }
+
+        // Clean up the entities used in this test
+        em = emfac.createEntityManager();            
+        em.getTransaction().begin();
+
+        for (int i = 0; i < nPeople; i++) {
+            Person p = em.find(Person.class, i);
+            em.remove(p);
+        }
+        em.flush();
+        em.getTransaction().commit();
+        em.close();
+    }
+    
+    /*
+     * Empty ConcurrentHashMap subclass. Useful for testing custom cache
+     * storage implementations.
+     */
+    public class CustomCacheMap extends ConcurrentHashMap {
+        
+    }
+
+    /*
+     * Simple runnable to test finder in a tight loop.  Multiple instances
+     * of this runnable will run simultaneously.
+     */
+    private class FindPeople implements Runnable {
+        
+        private int startId;
+        private int endId;
+        private int thread;
+        private int iterations;
+        private EntityManagerFactory emf;
+        private boolean failures = false;
+        
+        public FindPeople(EntityManagerFactory emf, 
+            int startId, int endId, int iterations, int thread) {
+            super();
+            this.startId = startId;
+            this.endId = endId;
+            this.thread = thread;
+            this.iterations = iterations;
+            this.emf = emf;
+        }
+        
+        public boolean hadFailures()
+        {
+            return failures;
+        }
+        
+        public void run() {
+            try {            
+                EntityManager em = emf.createEntityManager();            
+                for (int j = 0; j < iterations; j++) {
+                    
+                    for (int i = startId; i < endId; i++) {
+                        Person p1 = em.find(Person.class, i);
+                        if (p1.getId() != i) {
+                            System.out.println("Finder failed: " + i);
+                            failures = true;
+                            break;
+                        }                    
+                    }
+                    em.clear();  
+                }
+                em.close();  
+            } 
+            catch (Exception e) {
+               failures = true;
+               System.out.println("Thread " + thread + " exception :" +
+                   e );
+            }
+        }
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestQuerySQLCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml Fri May  2 14:09:14 2008
@@ -885,4 +885,85 @@
 			</tgroup>
 		</table>
 	</section>
+    <section id="ref_guide_cache_querysql">
+        <title>
+            Query SQL Cache
+        </title>
+        <indexterm zone="ref_guide_cache_querysql">
+            <primary>
+                caching
+            </primary>
+            <secondary>
+                query sql cache
+            </secondary>
+        </indexterm>
+        <para>
+The query SQL cache is a <classname>Map</classname> used to cache 
+pushed-down SQL query strings for the find operation.  As a result, 
+the SQL queries are only generated once in OpenJPA, and cached thereafter.  
+This query SQL cache is shared across entity managers and the fetch plan 
+is part of the cache key. You can control the SQL cache through 
+the <link linkend="openjpa.jdbc.QuerySQLCache"><literal>
+openjpa.jdbc.QuerySQLCache</literal></link> configuration property.  This 
+property accepts a plugin string (see <xref linkend="ref_guide_conf_plugins"/>) 
+describing the <classname>Map</classname> used to associate query strings and 
+their parsed form.  This property accepts the following aliases:
+        </para>
+        <table>
+            <title>
+                Pre-defined aliases
+            </title>
+            <tgroup cols="2" align="left" colsep="1" rowsep="1">
+                <colspec colname="alias"/>
+                <colspec colname="value"/>
+                <colspec colname="notes"/>
+                <thead>
+                    <row>
+                        <entry colname="alias">Alias</entry>
+                        <entry colname="value">Value</entry>
+                        <entry colname="notes">Notes</entry>
+                    </row>
+                </thead>
+                <tbody>
+                    <row>
+                        <entry colname="alias">
+<literal>true</literal>
+                        </entry>
+                        <entry colname="value">
+<literal>org.apache.openjpa.util.CacheMap</literal>
+                        </entry>
+                        <entry colname="notes">
+The default option.  Uses a 
+<ulink url="../javadoc/org/apache/openjpa/util/CacheMap.html"> 
+<literal>CacheMap</literal></ulink> to store sql string.  
+<literal>CacheMap</literal> maintains a fixed number of cache entries, and an 
+optional soft reference map for entries that are moved out of the LRU space. 
+So, for applications that have a monotonically increasing number of distinct 
+queries, this option can be used to ensure that a fixed amount of memory is 
+used by the cache.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry colname="alias"><literal>all</literal></entry>
+                        <entry colname="value">
+<literal>org.apache.openjpa.lib.util.ConcurrentHashMap</literal>
+                        </entry>
+                        <entry colname="notes">
+This is the fastest option, but sql string is never dropped from the 
+cache, so if you use a large number of dynamic queries, this option may result 
+in ever-increasing memory usage. Note that if your queries only differ in the 
+values of the parameters, this should not be an issue.
+                        </entry>
+                    </row>
+                    <row>
+                        <entry colname="alias"><literal>false</literal></entry>
+                        <entry colname="value"><emphasis>none</emphasis></entry>
+                        <entry colname="notes">
+Disables the sql cache.
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </section>
 </chapter>

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml?rev=652913&r1=652912&r2=652913&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml Fri May  2 14:09:14 2008
@@ -3328,6 +3328,42 @@
 classes. See <xref linkend="ref_guide_mapping_factory"/> for details.
             </para>
         </section>
+        <section id="openjpa.jdbc.QuerySQLCache">
+            <title>
+                openjpa.jdbc.QuerySQLCache
+            </title>
+            <indexterm zone="openjpa.jdbc.QuerySQLCache">
+                <primary>
+                    QuerySQLCache
+                </primary>
+            </indexterm>
+            <indexterm zone="openjpa.jdbc.QuerySQLCache">
+                <primary>
+                    caching
+                </primary>
+                <secondary>
+                    QuerySQLCache
+                </secondary>
+            </indexterm>
+            <para>
+<emphasis role="bold">Property name:</emphasis>
+<literal>openjpa.jdbc.QuerySQLCache</literal>
+            </para>
+            <para>
+<emphasis role="bold">Resource adaptor config-property:</emphasis> 
+<literal>QuerySQLCache</literal>
+            </para>
+            <para>
+<emphasis role="bold">Default:</emphasis> <literal>true</literal>.
+            </para>
+            <para>
+<emphasis role="bold">Description:</emphasis> A plugin string (see 
+<xref linkend="ref_guide_conf_plugins"/>) describing the 
+<classname>java.util.Map</classname> to use for caching of the SQL string 
+used by the find operation.  See <xref linkend="ref_guide_cache_querysql"/> for 
+details.
+            </para>
+        </section>
         <section id="openjpa.jdbc.ResultSetType">
             <title>
                 openjpa.jdbc.ResultSetType



Mime
View raw message