openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ppod...@apache.org
Subject svn commit: r739123 [2/3] - 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/kernel/exps/ openjpa-jdbc/src/main/jav...
Date Fri, 30 Jan 2009 01:27:37 GMT
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=739123&r1=739122&r2=739123&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 Fri Jan 30 01:27:35 2009
@@ -36,6 +36,7 @@
 import org.apache.openjpa.kernel.FetchConfiguration;
 import org.apache.openjpa.kernel.InverseManager;
 import org.apache.openjpa.kernel.LockManager;
+import org.apache.openjpa.kernel.PreparedQueryCache;
 import org.apache.openjpa.kernel.QueryFlushModes;
 import org.apache.openjpa.kernel.RestoreState;
 import org.apache.openjpa.kernel.SavepointManager;
@@ -1548,4 +1549,33 @@
      * @since 1.3.0
      */
     public void setInitializeEagerly(boolean flag);
+    
+    /**
+     * Return PreparedQueryCache used for caching datastore queries.
+     * 
+     * @since 2.0.0
+     */
+    public PreparedQueryCache getQuerySQLCacheInstance();
+        
+    /**
+     * Gets the configuration of QuerySQLCache.
+     * 
+     * @since 2.0.0
+     */
+    public String getQuerySQLCache();
+    
+    /**
+     * Sets QuerySQLCache with the given cache.
+     * 
+     * @since 2.0.0
+     */
+    public void setQuerySQLCache(PreparedQueryCache cache);   
+    
+    /**
+     * Sets QuerySQLCache with the given configuration.
+     * 
+     * @since 2.0.0
+     */
+    public void setQuerySQLCache(String config);     
+
 }

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=739123&r1=739122&r2=739123&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 Fri Jan 30 01:27:35 2009
@@ -38,6 +38,7 @@
 import org.apache.openjpa.kernel.InverseManager;
 import org.apache.openjpa.kernel.LockLevels;
 import org.apache.openjpa.kernel.LockManager;
+import org.apache.openjpa.kernel.PreparedQueryCache;
 import org.apache.openjpa.kernel.QueryFlushModes;
 import org.apache.openjpa.kernel.RestoreState;
 import org.apache.openjpa.kernel.SavepointManager;
@@ -137,6 +138,7 @@
     public IntValue runtimeUnenhancedClasses;
     public CacheMarshallersValue cacheMarshallerPlugins;
     public BooleanValue eagerInitialization;
+    public PluginValue preparedQueryCachePlugin;
 
     // custom values
     public BrokerFactoryValue brokerFactoryPlugin;
@@ -526,7 +528,7 @@
             addValue(new CacheMarshallersValue(this));
         
         eagerInitialization = addBoolean("InitializeEagerly");
-
+        
         // initialize supported options that some runtimes may not support
         supportedOptions.add(OPTION_NONTRANS_READ);
         supportedOptions.add(OPTION_OPTIMISTIC);
@@ -1489,4 +1491,25 @@
     public Log getConfigurationLog() {
         return getLog(LOG_RUNTIME);
     }
+    
+    public void setQuerySQLCache(String querySQLCache) {
+        preparedQueryCachePlugin.setString(querySQLCache);
+    }
+    
+    public void setQuerySQLCache(PreparedQueryCache querySQLCache) {
+        preparedQueryCachePlugin.set(querySQLCache);
+    }
+
+    public String getQuerySQLCache() {
+        return preparedQueryCachePlugin.getString();
+    }
+    
+    public PreparedQueryCache getQuerySQLCacheInstance() {
+        if (preparedQueryCachePlugin.get() == null) {
+            preparedQueryCachePlugin.instantiate(PreparedQueryCache.class, this);
+        }
+        return (PreparedQueryCache)preparedQueryCachePlugin.get();
+    }
+
+
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java Fri Jan 30 01:27:35 2009
@@ -284,6 +284,23 @@
      */
     public void setLifecycleListenerCallbackMode(int mode);
 
+    
+    /**
+     * Affirms if this receiver is caching prepared queries.
+     *  
+     * @since 2.0.0
+     */
+    public boolean getCachePreparedQuery();
+    
+    /**
+     * Sets whether this receiver will cache prepared queries during its 
+     * lifetime. The cache configured at BrokerFactory level is not affected by 
+     * setting it inactive for this receiver. 
+     * 
+     * @since 2.0.0
+     */
+    public void setCachePreparedQuery(boolean flag);
+
     /**
      * Begin a transaction.
      */

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=739123&r1=739122&r2=739123&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 Fri Jan 30 01:27:35 2009
@@ -222,6 +222,8 @@
     private int _detachState = DETACH_LOADED;
     private boolean _detachedNew = true;
     private boolean _orderDirty = false;
+    private boolean _cachePreparedQuery = true;
+    
 
     // status
     private int _flags = 0;
@@ -4774,4 +4776,23 @@
             throw new ObjectExistsException(_loc.get("cache-exists",
                 obj.getClass().getName(), id)).setFailedObject(obj);
     }
+    
+    public boolean getCachePreparedQuery() {
+        lock();
+        try {
+            return _cachePreparedQuery 
+               && _conf.getQuerySQLCacheInstance() != null;
+        } finally {
+            unlock();
+        }
+    }
+    
+    public void setCachePreparedQuery(boolean flag) {
+        lock();
+        try {
+            _cachePreparedQuery = flag;
+        } finally {
+            unlock();
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java Fri Jan 30 01:27:35 2009
@@ -1384,4 +1384,13 @@
             throw translate(re);
         }
     }
+        
+    public boolean getCachePreparedQuery() {
+        return _broker.getCachePreparedQuery();
+    }
+    
+    public void setCachePreparedQuery(boolean flag) {
+        _broker.setCachePreparedQuery(flag);
+    }
+
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingResultList.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingResultList.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingResultList.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingResultList.java Fri Jan 30 01:27:35 2009
@@ -115,6 +115,22 @@
         }
     }
 
+    public Object getUserObject() {
+        try {
+            return _del.getUserObject();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+    
+    public void setUserObject(Object opaque) {
+        try {
+            _del.setUserObject(opaque);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
     public void close() {
         try {
             _del.close();

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=739123&r1=739122&r2=739123&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 Jan 30 01:27:35 2009
@@ -82,9 +82,6 @@
         public Set rootClasses;
         public Set rootInstances;
         public Map hints = null;
-        
-        public boolean fetchGroupContainsDefault = false;
-        public boolean fetchGroupContainsAll = false;
     }
 
     private final ConfigurationState _state;
@@ -95,7 +92,7 @@
     private boolean _load = true;
     private int _availableRecursion;
     private int _availableDepth;
-    
+
     public FetchConfigurationImpl() {
         this(null);
     }
@@ -160,6 +157,7 @@
         clearFetchGroups();
         addFetchGroups(fetch.getFetchGroups());
         clearFields();
+        copyHints(fetch);
         addFields(fetch.getFields());
 
         // don't use setters because require active transaction
@@ -167,6 +165,20 @@
         _state.writeLockLevel = fetch.getWriteLockLevel();
     }
 
+    
+    void copyHints(FetchConfiguration fetch) {
+        if (fetch instanceof FetchConfigurationImpl == false)
+            return;
+        FetchConfigurationImpl from = (FetchConfigurationImpl)fetch;
+        if (from._state == null || from._state.hints == null)
+            return;
+        if (this._state == null)
+            return;
+        if (this._state.hints == null)
+            this._state.hints = new HashMap();
+        this._state.hints.putAll(from._state.hints);
+    }
+    
     public int getFetchBatchSize() {
         return _state.fetchBatchSize;
     }
@@ -224,22 +236,10 @@
 
     public boolean hasFetchGroup(String group) {
         return _state.fetchGroups != null
-            && (hasFetchGroupAll()
-            ||  _state.fetchGroups.contains(group));
+            && (_state.fetchGroups.contains(group)
+            || _state.fetchGroups.contains(FetchGroup.NAME_ALL));
     }
 
-    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"));
@@ -249,10 +249,6 @@
             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();
         }
@@ -270,13 +266,8 @@
     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();
         }
@@ -286,9 +277,8 @@
     public FetchConfiguration removeFetchGroups(Collection groups) {
         lock();
         try {
-            if (_state.fetchGroups != null && groups != null)
-                for (Object group : groups)
-                    removeFetchGroup(group.toString());
+            if (_state.fetchGroups != null)
+                _state.fetchGroups.removeAll(groups);
         } finally {
             unlock();
         }
@@ -298,11 +288,8 @@
     public FetchConfiguration clearFetchGroups() {
         lock();
         try {
-            if (_state.fetchGroups != null) {
+            if (_state.fetchGroups != null)
                 _state.fetchGroups.clear();
-                _state.fetchGroupContainsAll = false;
-                _state.fetchGroupContainsDefault = true;
-            }
         } finally {
             unlock();
         }
@@ -590,9 +577,9 @@
      * Whether our configuration state includes the given field.
      */
     private boolean includes(FieldMetaData fmd) {
-        if (hasFetchGroupAll()
-            || (fmd.isInDefaultFetchGroup() 
-            && hasFetchGroupDefault())
+        if ((fmd.isInDefaultFetchGroup() 
+            && hasFetchGroup(FetchGroup.NAME_DEFAULT))
+            || hasFetchGroup(FetchGroup.NAME_ALL)
             || hasField(fmd.getFullName(false))
             || hasField(fmd.getRealName()) // OPENJPA-704
             || (_fromField != null 

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.kernel;
+
+import java.util.Map;
+
+/**
+ * A prepared query associates a compiled query to a <em>parsed state</em> that
+ * can be executed possibly with more efficiency. An obvious example is to 
+ * associate a compiled query to an executable SQL string. 
+ * 
+ * The query expressed in target language can be executed directly bypassing 
+ * the critical translation cost to the data store target language on every 
+ * execution. 
+ * 
+ * As the subsequent execution of a cached query will bypass normal query 
+ * compilation, the post-compilation state of the original query is captured by 
+ * this receiver to be transferred to the executable query instance.  
+ * 
+ * This receiver must not hold any context-sensitive reference or dependency.
+ * Because the whole idea of preparing a query (for a cost) is to be able to
+ * execute the same logical query in different persistence contexts. However,
+ * a prepared query may not be valid when some parameters of execution context  
+ * such as lock group or fetch plan changes in a way that will change the target
+ * query. Refer to {@link PreparedQueryCache} for invalidation mechanism on
+ * possible actions under such circumstances.
+ * 
+ * The query execution model <em>does</em> account for context changes that do 
+ * not impact the target query e.g. bind variables. 
+ * 
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ */
+public interface PreparedQuery  {
+    /**
+     * Get the immutable identifier of this receiver used for 
+     * * {@link PreparedQueryCache cache}.
+     */
+	public String getIdentifier();
+	
+	/**
+	 * Get the target database query.
+	 */
+    public String getTargetQuery();
+    
+    /**
+     * Get the original query.
+     */
+    public String getOriginalQuery();
+    
+    /**
+     * Fill in the post-compilation state of the given Query. This must be
+     * called when a original query is substituted by this receiver and hence 
+     * the original query is not parsed or compiled.
+     * 
+     * @param q A Query which has been substituted by this receiver and hence
+     * does not have its post-compilation state.
+     */
+	public void setInto(Query q);
+	
+	/**
+	 * Initialize from the given argument.  
+	 * 
+	 * @param o an opaque instance supposed to carry post-execution data such
+	 * as target database query, parameters of the query etc.
+	 * 
+	 * @return true if this receiver can initialize itself from the given
+	 * argument. false otherwise.
+	 */
+	public boolean initialize(Object o);
+	
+	/**
+	 * Get the list of parameters in a map where an entry represents a parameter
+	 * key and value after replacing with the given user parameters. 
+	 * 
+	 * @param user the map of parameter key and value set by the user on the
+	 * original query.
+	 */
+	public Map reparametrize(Map user);
+	
+}

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQueryCache.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQueryCache.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQueryCache.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,199 @@
+/*
+ * 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 PreparedQuery prepared queries}. 
+ * 
+ * To cache a PreparedQuery is two-stage process. In the first stage, 
+ * {@link #register(String, Query, FetchConfiguration)} register} 
+ * an identification key and a compiled Query Q to create a 
+ * hollow PreparedQuery instance P in the cache. In the second stage, after Q 
+ * executes, {@link #initialize(String, Object)} initialize} the hollow 
+ * Prepared Query P from the result of execution such as the target
+ * database query PQ and its parameters. After initialization, P  can 
+ * be used with re-parameterization for subsequent execution of the original 
+ * Query Q. 
+ * 
+ * The target database query PQ associated to a cached prepared query P  
+ * <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 P when the original query Q is executed  
+ * with context parameters that affect the target query PQ. 
+ * 
+ * The user must notify this receiver to invalidate a cached entry P when 
+ * execution context changes in a way that will modify the resultant database 
+ * language query PQ.
+ * 
+ * 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_PREPARED_QUERY 
+ * @see QueryHints#HINT_INVALIDATE_PREPARED_QUERY
+ * 
+ * This cache allows customization of whether a query can be cached or not
+ * via either explicit marking of certain keys as non-cachable (which is 
+ * irreversible) or addition/removal of exclusion patterns (which is reversible).
+ * 
+ * @see #markUncachable(String)
+ * @see #addExclusionPattern(String)
+ * @see #setExcludes(String)
+ * @see #removeExclusionPattern(String)
+ * 
+ * @author Pinaki Poddar
+ *
+ * @since 2.0.0
+ */
+public interface PreparedQueryCache extends Configurable {
+
+    /**
+     * Register the given query for caching against the given key if it has not 
+     * already been cached. If the query can not be cached, then mark it as such
+     * to avoid computing for the same key again.
+     * 
+     * @return TRUE the query is registered in the cache by this call
+     *         null if the query is already registered in the cache
+     *         FALSE if can not be registered in the cache, either because
+     *         it is known not to be cacheable from a previous attempt or
+     *         a hint is given to ignore the cached version.
+     */
+    public Boolean register(String key, Query query, FetchConfiguration hints);
+    
+    /**
+     * Initialize the cached Prepared Query registered with the given
+     * key earlier by the given execution result. If it is not possible to
+     * initialize the Prepared Query from the given execution result, then
+     * the corresponding key will be marked as invalid for caching. 
+     * 
+     * @param key the key used during registration
+     * @param executionResult an opaque instance carrying the execution result 
+     * of the original query. 
+     *  
+     * @return the initialized Prepared Query. If it is not possible to 
+     * initialize the cached, possibly hollow Prepared Query from the given
+     * result, return null.
+     */
+    public PreparedQuery initialize(String key, Object executionResult);
+    
+	/**
+	 * Get a map view of the cached queries indexed by identifier.
+	 */
+	public Map<String, String> getMapView();
+
+	/**
+	 * Cache the given PreparedQuery.
+	 * The key is the identifier of the given PreparedQuery itself.
+	 * The query must not be cached if either the key matches any exclusion
+	 * pattern or the key has been marked non-cachable.
+	 * 
+	 * @return true if the given query is cached. false if it can not be cached
+	 * due to exclusion.
+	 * 
+	 * @see #markUncachable(String)
+	 * @see #setExcludes(String)
+	 * @see #addExclusionPattern(String)
+	 */
+	public boolean cache(PreparedQuery q);
+
+	/**
+	 * Remove the PreparedQuery with the given identifier from this cache.
+	 */
+	public boolean invalidate(String id);
+
+	/**
+	 * Get the PreparedQuery with the given identifier if it exists. null
+	 * otherwise.
+	 */
+	public PreparedQuery get(String id);
+	
+	/**
+	 * Get the PreparedQuery for the finder query of the given class.
+	 * 
+	 * @param c class for which a find-by-primary-key query is looked up
+	 */
+	public PreparedQuery get(Class c);
+
+	/**
+	 * Affirms if a PreparedQuery can be cached against the given key.
+	 * 
+	 * @return Boolean.FALSE if the given key is explicitly marked before as not
+	 * be cached or matches any of the exclusion patterns. 
+	 * Boolean.TRUE if the given key currently exists in the cache. 
+	 * Otherwise, return null implying this receiver can not determine whether
+	 * this key can be cached on not. 
+	 * 
+	 */
+	public Boolean isCachable(String id);
+
+	/**
+	 * Marks the given key as not amenable to caching.
+	 * Explicit marking helps to avoid repeated computational cost of 
+	 * determining whether a query can be cached or not.
+	 * 
+	 * Explicit marking can not be reversed by removal of exclusion patterns.
+	 * 
+	 * @return The value for the given key if it had been cached before. null
+	 * otherwise.
+	 */
+	public PreparedQuery markUncachable(String id);
+
+	/**
+	 * Affirms if the given key matches any of the exclusion patterns.
+	 */
+	public boolean isExcluded(String id);
+
+	/**
+	 * Gets the exclusion patterns.
+	 */
+	public List<String> getExcludes();
+	
+	/**
+	 * Sets one or more exclusion regular expression patterns separated by 
+	 * semicolon. Any existing cache entry whose key matches any of the given
+	 * pattern will be marked non-cachable in a reversible manner. 
+	 */
+	public void setExcludes(String excludes);
+
+	/**
+	 * 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 key that matches the given pattern can now be cached
+	 * again, unless it has been marked non-cachable explicitly.
+	 * 
+	 * @see #markUncachable(String)
+	 */
+	public void removeExclusionPattern(String pattern);
+	
+	/**
+	 * Gets the simple statistics for executed queries.
+	 */
+	public QueryStatistics getStatistics();
+}
\ No newline at end of file

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=739123&r1=739122&r2=739123&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 Fri Jan 30 01:27:35 2009
@@ -28,4 +28,51 @@
      */
     public static final String HINT_RESULT_COUNT =
         "openjpa.hint.OptimizeResultCount";
+    
+    /**
+     * Hints to signal that the JPQL/SQL query string contains a parameter
+     * marker <code>?</code> character. By default, the query string is parsed
+     * to count number of parameters assuming that all <code>?</code> characters
+     * designate a bind parameter. This assumption makes the parse faster.
+     */
+    public static final String HINT_PARAM_MARKER_IN_QUERY =
+        "openjpa.hint.ParameterMarkerInQuery";
+    
+    /**
+     * A directive to invalidate any prepared SQL that might have been cached
+     * against a JPQL query. The target SQL corresponding to a JPQL depends on
+     * several context parameters such as fetch configuration, lock mode etc.
+     * If a query is executed repeatedly and hence its SQL is cached for faster
+     * execution then if any of the contextual parameters change across query
+     * execution then the user must supply this hint to invalidate the cached
+     * SQL query. 
+     * The alternative to monitor any such change for automatic invalidation 
+     * has a constant performance penalty for the frequent use case where a 
+     * query is repeatedly executed in different persistent context with the 
+     * same fetch plan or locking.  
+     * 
+     * @see #HINT_IGNORE_PREPARED_QUERY
+     */
+    public static final String HINT_INVALIDATE_PREPARED_QUERY =
+        "openjpa.hint.InvalidatePreparedQuery";
+    
+    /**
+     * A directive to ignore any prepared SQL that might have been cached
+     * against a JPQL query. The target SQL corresponding to a JPQL depends on
+     * several context parameters such as fetch configuration, lock mode etc.
+     * If a query is executed repeatedly and hence its SQL is cached for faster
+     * execution then if any of the contextual parameters change across query
+     * execution then the user must supply this hint to ignore the cached
+     * SQL query for the current execution.
+     * This is in contrast with invalidation hint that removes the cached 
+     * version from cache altogether.
+     * 
+     * The cached SQL is retained and subsequent execution of the same query
+     * string without this hint will reuse the cached SQL. 
+     * 
+     * @see #HINT_INVALIDATE_PREPARED_QUERY
+     */
+    public static final String HINT_IGNORE_PREPARED_QUERY =
+        "openjpa.hint.IgnorePreparedQuery";
+
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java Fri Jan 30 01:27:35 2009
@@ -836,9 +836,9 @@
                 StoreQuery.Executor ex = (isInMemory(operation))
                     ? compileForInMemory(comp) : compileForDataStore(comp);
 
+                assertParameters(_storeQuery, ex, params);
                 Object[] arr = (params.isEmpty()) ? StoreQuery.EMPTY_OBJECTS :
                     toParameterArray(ex.getParameterTypes(_storeQuery), params);
-                assertParameters(_storeQuery, ex, arr);
                 if (_log.isTraceEnabled())
                     logExecution(operation, params);
 
@@ -887,6 +887,12 @@
         return ((Number) execute(OP_UPDATE, params)).longValue();
     }
 
+    /**
+     * 
+     * @param paramTypes parameter key mapped to parameter value type
+     * @param params parameter key mapped to actual parameter value
+     * @return
+     */
     private Object[] toParameterArray(LinkedMap paramTypes, Map params) {
         if (params == null || params.isEmpty())
             return StoreQuery.EMPTY_OBJECTS;
@@ -1230,7 +1236,7 @@
         boolean lrs = range.lrs && !ex.isAggregate(q) && !ex.hasGrouping(q);
         ResultList res = (!detach && lrs) ? _fc.newResultList(rop)
             : new EagerResultList(rop);
-
+        res.setUserObject(rop);
         _resultLists.add(decorateResultList(res));
         return res;
     }
@@ -1401,9 +1407,9 @@
             assertOpen();
 
             StoreQuery.Executor ex = compileForExecutor();
+            assertParameters(_storeQuery, ex, params);
             Object[] arr = toParameterArray(ex.getParameterTypes(_storeQuery),
                 params);
-            assertParameters(_storeQuery, ex, arr);
             StoreQuery.Range range = new StoreQuery.Range(_startIdx, _endIdx);
             if (!_rangeSet)
                 ex.getRange(_storeQuery, arr, range);
@@ -1702,6 +1708,35 @@
                     entry.getKey()));
         }
     }
+    
+    protected void assertParameters(StoreQuery q, StoreQuery.Executor ex, 
+        Map params) {
+        if (!q.requiresParameterDeclarations())
+            return;
+
+        LinkedMap paramTypes = ex.getParameterTypes(q);
+        for (Object actual : params.keySet()) {
+            if (!paramTypes.containsKey(actual))
+            throw new UserException(_loc.get("unbound-params",
+                actual, paramTypes.keySet()));
+        }
+        for (Object expected : paramTypes.keySet()) {
+            if (!params.containsKey(expected))
+            throw new UserException(_loc.get("unbound-params",
+                params.keySet()));
+        }
+
+        Iterator itr = paramTypes.entrySet().iterator();
+        Map.Entry entry;
+        for (int i = 0; itr.hasNext(); i++) {
+            entry = (Map.Entry) itr.next();
+            if (((Class) entry.getValue()).isPrimitive() 
+                && params.get(entry.getKey()) == null)
+                throw new UserException(_loc.get("null-primitive-param",
+                    entry.getKey()));
+        }
+    }
+
 
     public String toString() {
         StringBuffer buf = new StringBuffer(64);
@@ -2032,6 +2067,14 @@
         public boolean isProviderOpen() {
             return _res.isProviderOpen();
         }
+        
+        public Object getUserObject() {
+            return _res.getUserObject();
+        }
+        
+        public void setUserObject(Object opaque) {
+            _res.setUserObject(opaque);
+        }
 
         public boolean isClosed() {
             return _res.isClosed();

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryLanguages.java Fri Jan 30 01:27:35 2009
@@ -34,6 +34,7 @@
 public class QueryLanguages {
 
     public static final String LANG_SQL = "openjpa.SQL";
+    public static final String LANG_PREPARED_SQL = "openjpa.prepared.SQL";
     public static final String LANG_METHODQL = "openjpa.MethodQL";
 
     private static Map _expressionParsers = new HashMap();

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryStatistics.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,237 @@
+/*
+ * 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.io.PrintStream;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Records query execution statistics.
+ * 
+ * Statistics can be reset.
+ * 
+ * Gathers both accumulated statistics since start as well as statistics since
+ * last reset.
+ *  
+ * @since 1.3.0
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+public interface QueryStatistics extends Serializable {
+	
+	/**
+	 * Record that the given query has been executed. The boolean parameter
+	 * designates whether the executed query is a cached version.  
+	 */
+	void recordExecution(String query, boolean cached);
+		
+	/**
+	 * Gets number of total query execution since last reset.
+	 */
+	public long getExecutionCount();
+
+	/**
+	 * Gets number of total query execution since start.
+	 */
+	public long getTotalExecutionCount();
+
+	/**
+	 * Gets number of executions for the given query since last reset.
+	 */
+	public long getExecutionCount(String query);
+
+	/**
+	 * Gets number of executions for the given query since start.
+	 */
+	public long getTotalExecutionCount(String query);
+
+	/**
+	 * Gets number of total query execution that are cached since last reset.
+	 */
+	public long getHitCount();
+
+	/**
+	 * Gets number of total query execution that are cached since start.
+	 */
+	public long getTotalHitCount();
+
+	/**
+	 * Gets number of executions for the given query that are cached since 
+	 * last reset.
+	 */
+	public long getHitCount(String query);
+
+	/**
+	 * Gets number of executions for the given query that are cached since 
+	 * start.
+	 */
+	public long getTotalHitCount(String query);
+
+	/**
+	 * Gets the time of last reset.
+	 */
+	public Date since();
+
+	/**
+	 * Gets the time of start.
+	 */
+	public Date start();
+
+	/**
+	 * Clears all accumulated statistics.
+	 */
+	public void reset();
+	
+	/**
+	 * Dumps on the given output stream.
+	 */
+	public void dump(PrintStream out);
+	
+	/**
+	 * A default implementation.
+	 *
+	 */
+	public static class Default implements QueryStatistics {
+		private static final int ARRAY_SIZE = 2;
+		private long[] astat = new long[ARRAY_SIZE];
+		private long[] stat  = new long[ARRAY_SIZE];
+		private Map<String, long[]> stats  = new HashMap<String, long[]>();
+		private Map<String, long[]> astats = new HashMap<String, long[]>();
+		private Date start = new Date();
+		private Date since = start;
+
+		private static final int READ  = 0;
+		private static final int HIT   = 1;
+
+		public long getExecutionCount() {
+			return stat[READ];
+		}
+
+		public long getTotalExecutionCount() {
+			return astat[READ];
+		}
+
+		public long getExecutionCount(String query) {
+			return getCount(stats, query, READ);
+		}
+
+		public long getTotalExecutionCount(String query) {
+			return getCount(astats, query, READ);
+		}
+
+		public long getHitCount() {
+			return stat[HIT];
+		}
+
+		public long getTotalHitCount() {
+			return astat[HIT];
+		}
+
+		public long getHitCount(String query) {
+			return getCount(stats, query, HIT);
+		}
+
+		public long getTotalHitCount(String query) {
+			return getCount(astats, query, HIT);
+		}
+
+		private long getCount(Map<String, long[]> target, String query, int i) {
+			long[] row = target.get(query);
+			return (row == null) ? 0 : row[i];
+		}
+
+		public Date since() {
+			return since;
+		}
+
+		public Date start() {
+			return start;
+		}
+
+		public void reset() {
+			stat = new long[ARRAY_SIZE];
+			stats.clear();
+			since = new Date();
+		}
+
+		private void addSample(String query, int index) {
+			stat[index]++;
+			astat[index]++;
+			addSample(stats, query, index);
+			addSample(astats, query, index);
+		}
+		
+		private void addSample(Map<String, long[]> target, String query, int i) {
+			long[] row = target.get(query);
+			if (row == null) {
+				row = new long[ARRAY_SIZE];
+			}
+			row[i]++;
+			target.put(query, row);
+		}
+		
+		public void recordExecution(String query, boolean cached) {
+			addSample(query, READ);
+			if (cached)
+				addSample(query, HIT);
+		}
+		
+		public void dump(PrintStream out) {
+			String header = "Query Statistics starting from " + start;
+			out.print(header);
+			if (since == start) {
+				out.println();
+				out.println("Total Query Execution: " + toString(astat)); 
+				out.println("\tTotal \t\tQuery");
+			} else {
+				out.println(" last reset on " + since);
+				out.println("Total Query Execution since start " 
+					+ toString(astat)  + " since reset " +  toString(stat));
+				out.println("\tSince Start \tSince Reset \t\tQuery");
+			}
+			int i = 0;
+			for (String key : stats.keySet()) {
+				i++;
+				long[] arow = astats.get(key);
+				if (since == start) {
+					out.println(i + ". \t" + toString(arow) + " \t"	+ key);
+				} else {
+					long[] row  = stats.get(key);
+					out.println(i + ". \t" + toString(arow) + " \t"  
+					    + toString(row) + " \t\t" + key);
+				}
+			}
+		}
+		
+		long pct(long per, long cent) {
+			if (cent <= 0)
+				return 0;
+			return (100*per)/cent;
+		}
+		
+		String toString(long[] row) {
+			return row[READ] + ":" + row[HIT] + "(" + pct(row[HIT], row[READ]) 
+			+ "%)";
+		}
+	}
+}

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/CollectionParam.java Fri Jan 30 01:27:35 2009
@@ -29,20 +29,20 @@
     extends Val
     implements Parameter {
 
-    private String _name = null;
+    private Object _key = null;
     private Class _type = null;
     private int _index = -1;
 
     /**
      * Constructor. Provide parameter name and type.
      */
-    public CollectionParam(String name, Class type) {
-        _name = name;
+    public CollectionParam(Object name, Class type) {
+        _key = name;
         _type = type;
     }
 
-    public String getParameterName() {
-        return _name;
+    public Object getParameterKey() {
+        return _key;
     }
 
     public Class getType() {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/ExpressionFactory.java Fri Jan 30 01:27:35 2009
@@ -245,13 +245,13 @@
      * Return a value representing a parameter for the given value. The
      * type may be <code>Object</code> if the parameter is not declared.
      */
-    public Parameter newParameter(String name, Class type);
+    public Parameter newParameter(Object name, Class type);
 
     /**
      * Return a value representing a collection-valued parameter. The
      * type may be <code>Object</code> if the parameter is not declared.
      */
-    public Parameter newCollectionValuedParameter(String name, Class type);
+    public Parameter newCollectionValuedParameter(Object name, Class type);
 
     /**
      * Return the value of the given extension.

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/InMemoryExpressionFactory.java Fri Jan 30 01:27:35 2009
@@ -504,11 +504,11 @@
         return CURRENT_DATE;
     }
 
-    public Parameter newParameter(String name, Class type) {
+    public Parameter newParameter(Object name, Class type) {
         return new Param(name, type);
     }
 
-    public Parameter newCollectionValuedParameter(String name, Class type) {
+    public Parameter newCollectionValuedParameter(Object name, Class type) {
         return new CollectionParam(name, type);
     }
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Param.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Param.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Param.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Param.java Fri Jan 30 01:27:35 2009
@@ -29,20 +29,20 @@
     extends Val
     implements Parameter {
 
-    private String _name = null;
+    private Object _key = null;
     private Class _type = null;
     private int _index = -1;
 
     /**
      * Constructor. Provide parameter name and type.
      */
-    public Param(String name, Class type) {
-        _name = name;
+    public Param(Object key, Class type) {
+        _key = key;
         _type = type;
     }
 
-    public String getParameterName() {
-        return _name;
+    public Object getParameterKey() {
+        return _key;
     }
 
     public Class getType() {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Parameter.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Parameter.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Parameter.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Parameter.java Fri Jan 30 01:27:35 2009
@@ -33,7 +33,7 @@
     public void setIndex(int index);
 
     /**
-     * Returns the name of the parameter.
+     * Returns the key of the parameter.
      */
-    public String getParameterName();
+    public Object getParameterKey();
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java Fri Jan 30 01:27:35 2009
@@ -1241,13 +1241,13 @@
         if (type == null)
             return;
 
-        String paramName = param.getParameterName();
-        if (paramName == null)
+        Object paramKey = param.getParameterKey();
+        if (paramKey == null)
             return;
 
         // make sure we have already declared the parameter
-        if (parameterTypes.containsKey(paramName))
-            parameterTypes.put(paramName, type);
+        if (parameterTypes.containsKey(paramKey))
+            parameterTypes.put(paramKey, type);
     }
 
     private Value getStringValue(JPQLNode node) {
@@ -1293,8 +1293,9 @@
     private Parameter getParameter(String id, boolean positional) {
         if (parameterTypes == null)
             parameterTypes = new LinkedMap(6);
-        if (!parameterTypes.containsKey(id))
-            parameterTypes.put(id, TYPE_OBJECT);
+        Object paramKey = positional ? Integer.parseInt(id) : id;
+        if (!parameterTypes.containsKey(paramKey))
+            parameterTypes.put(paramKey, TYPE_OBJECT);
 
         Class type = Object.class;
         ClassMetaData meta = null;
@@ -1317,8 +1318,7 @@
             // otherwise the index is just the current size of the params
             index = parameterTypes.indexOf(id);
         }
-
-        Parameter param = factory.newParameter(id, type);
+        Parameter param = factory.newParameter(paramKey, type);
         param.setMetaData(meta);
         param.setIndex(index);
 
@@ -1335,8 +1335,9 @@
 
         if (parameterTypes == null)
             parameterTypes = new LinkedMap(6);
+        Object paramKey = positional ? Integer.parseInt(id) : id;
         if (!parameterTypes.containsKey(id))
-            parameterTypes.put(id, TYPE_OBJECT);
+            parameterTypes.put(paramKey, TYPE_OBJECT);
 
         Class type = Object.class;
         ClassMetaData meta = null;

Modified: openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties (original)
+++ openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties Fri Jan 30 01:27:35 2009
@@ -285,9 +285,9 @@
 	to perform bulk updates.
 unbound-param: Cannot execute query; the declared parameter "{0}" was \
 	not given a value.
-unbound-params: Cannot execute query; some declared parameters were not given \
+unbound-params: Cannot execute query; declared parameters "{0}" were not given \
 	values.  You must supply a value for each of the following parameters, \
-	in the given order: {0}
+	in the given order: {1}
 extra-params: More parameters were passed to execute() than were declared: \
 	{1} parameters were specified for query execution, but only {0} \
 	parameters were declared in the query.

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/AbstractResultList.java Fri Jan 30 01:27:35 2009
@@ -32,7 +32,8 @@
  */
 @SuppressWarnings("serial")
 public abstract class AbstractResultList<E> implements ResultList<E> {
-
+    private Object _userObject;
+    
     private static final Localizer _loc = Localizer.forPackage
         (AbstractResultList.class);
 
@@ -89,4 +90,13 @@
         if (isClosed())
             throw new NoSuchElementException(_loc.get("closed").getMessage());
     }
+    
+    public final Object getUserObject() {
+        return _userObject;
+    }
+    
+    public final void setUserObject(Object opaque) {
+        _userObject = opaque;
+    }
+    
 }

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/rop/ResultList.java Fri Jan 30 01:27:35 2009
@@ -43,6 +43,9 @@
      * Returns true if the provider backing this list is open.
      */
     public boolean isProviderOpen();
+    
+    public Object getUserObject();
+    public void   setUserObject(Object opaque);
 
     /**
      * Close the list.

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestQueryParameterBinding.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestQueryParameterBinding.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestQueryParameterBinding.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/query/TestQueryParameterBinding.java Fri Jan 30 01:27:35 2009
@@ -23,6 +23,7 @@
 
 import org.apache.openjpa.persistence.ArgumentException;
 import org.apache.openjpa.persistence.jdbc.query.domain.Binder;
+import org.apache.openjpa.persistence.test.AllowFailure;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 /**
@@ -100,6 +101,7 @@
 		assertEquals(1,q.getResultList().size());
 	}
 	
+	@AllowFailure
 	public void testPositionalParameterWithGapSucceeds() {
 		String JPQL_POSITIONAL_GAP_IN_PARAM  = 
 			JPQL + "WHERE p.p1=?1 AND p.p2=?3";

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Address.java Fri Jan 30 01:27:35 2009
@@ -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.persistence.jdbc.sqlcache;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Address {
+	@Id
+	@GeneratedValue
+	private long id;
+	
+	private String street;
+	
+	private String city;
+	
+	private String state;
+	
+	private int zip;
+	
+	public Address() {
+		
+	}
+
+	public Address(String street, String city, String state, int zip) {
+		super();
+		this.street = street;
+		this.city = city;
+		this.state = state;
+		this.zip = zip;
+	}
+
+	public String getStreet() {
+		return street;
+	}
+
+	public void setStreet(String street) {
+		this.street = street;
+	}
+
+	public String getCity() {
+		return city;
+	}
+
+	public void setCity(String city) {
+		this.city = city;
+	}
+
+	public String getState() {
+		return state;
+	}
+
+	public void setState(String state) {
+		this.state = state;
+	}
+
+	public int getZip() {
+		return zip;
+	}
+
+	public void setZip(int zip) {
+		this.zip = zip;
+	}
+
+	public long getId() {
+		return id;
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Author.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Author.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Author.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Author.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,46 @@
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.ManyToMany;
+
+@Entity
+@DiscriminatorValue("AUTHOR")
+public class Author extends Person {
+    private String name;
+    
+    @ManyToMany(fetch=FetchType.LAZY)
+    private Set<Book> books;
+    
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Author() {
+        super();
+    }
+
+    public Author(String firstName, String lastName, short age, int yob) {
+        super(firstName, lastName, age, yob);
+    }
+
+    public Set<Book> getBooks() {
+        return books;
+    }
+    
+    public void addBook(Book b) {
+        if (books == null)
+            books = new HashSet<Book>();
+        if (books.add(b)) {
+            b.addAuthor(this);
+        }
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Book.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Book.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Book.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Book.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,46 @@
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.ManyToMany;
+
+@Entity
+@DiscriminatorValue("BOOK")
+public class Book extends Merchandise {
+    private String title;
+    
+    @ManyToMany(fetch=FetchType.EAGER)
+    private Set<Author> authors;
+
+    public Book() {
+        this("?");
+    }
+    
+    public Book(String title) {
+        setTitle(title);
+    }
+    
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Set<Author> getAuthors() {
+        return authors;
+    }
+
+    public void addAuthor(Author a) {
+        if (authors == null)
+            authors = new HashSet<Author>();
+        if (authors.add(a)) {
+            a.addBook(this);
+        }
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/CD.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/CD.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/CD.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/CD.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,39 @@
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+
+@Entity
+@DiscriminatorValue("CD")
+public class CD extends Merchandise {
+    private String label;
+    
+    @ManyToOne
+    private Singer singer;
+
+    public CD() {
+        this("?");
+    }
+    
+    public CD(String label) {
+        setLabel(label);
+    }
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public Singer getSinger() {
+        return singer;
+    }
+
+    public void setSinger(Singer singer) {
+        this.singer = singer;
+        singer.addCd(this);
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,73 @@
+/*
+ * 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.Collection;
+import java.util.HashSet;
+
+import javax.persistence.*;
+
+@Entity
+@NamedQueries({
+	@NamedQuery(name="Company.PreparedQueryWithNoParameter", query="select x from Company x"),
+	@NamedQuery(name="Company.PreparedQueryWithNamedParameter", query="select x from Company x where x.name=:name and x.startYear=:startYear"),
+	@NamedQuery(name="Company.PreparedQueryWithPositionalParameter", query="select x from Company x where x.name=?1 and x.startYear=?2"),
+	@NamedQuery(name="Company.PreparedQueryWithLiteral", query="select x from Company x where x.name='X' and x.startYear=1960")
+})
+public class Company {
+	@Id
+	@GeneratedValue
+	private long id;
+	
+	private String name;
+	
+	private int startYear;
+	
+	@OneToMany(mappedBy="company", cascade=CascadeType.ALL)
+	private Collection<Department> departments = new HashSet<Department>();
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Collection<Department> getDepartments() {
+		return departments;
+	}
+
+	public void addDepartment(Department dept) {
+		this.departments.add(dept);
+		dept.setCompany(this);
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public int getStartYear() {
+		return startYear;
+	}
+
+	public void setStartYear(int startYear) {
+		this.startYear = startYear;
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Department.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,70 @@
+/*
+ * 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.Collection;
+import java.util.HashSet;
+
+import javax.persistence.*;
+
+@Entity
+public class Department {
+	@Id
+	@GeneratedValue
+	private long id;
+	
+	private String name;
+	
+	@ManyToOne
+	private Company company;
+	
+	@OneToMany
+	private Collection<Employee> employees = new HashSet<Employee>();
+
+	public Company getCompany() {
+		return company;
+	}
+
+	public void setCompany(Company company) {
+		this.company = company;
+	}
+
+	public Collection<Employee> getEmployees() {
+		return employees;
+	}
+
+	public void addEmployees(Employee emp) {
+		this.employees.add(emp);
+		emp.setDepartment(this);
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Employee.java Fri Jan 30 01:27:35 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.persistence.jdbc.sqlcache;
+
+import javax.persistence.*;
+
+@Entity
+public class Employee {
+	@Id
+	@GeneratedValue
+	private long id;
+	
+	private String name;
+	
+	@ManyToOne
+	private Department department;
+	
+	@OneToOne
+	private Address address;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Department getDepartment() {
+		return department;
+	}
+
+	public void setDepartment(Department department) {
+		this.department = department;
+	}
+
+	public Address getAddress() {
+		return address;
+	}
+
+	public void setAddress(Address address) {
+		this.address = address;
+	}
+
+	public long getId() {
+		return id;
+	}
+
+}

Added: 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=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Merchandise.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,23 @@
+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;
+
+@Entity
+@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
+public class Merchandise {
+    @Id
+    @GeneratedValue
+    private long id;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+}

Added: 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=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Person.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,59 @@
+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;
+
+
+@Entity
+@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
+public class Person {
+    @Id
+    @GeneratedValue
+    private long id;
+    
+    private String firstName;
+    private String lastName;
+    private short age;
+    private int   yob;
+    
+    public Person() {
+        this("?", "?", (short)0, 0);
+    }
+    public Person(String firstName, String lastName, short age, int yob) {
+        super();
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.age = age;
+        this.yob = yob;
+    }
+    public String getFirstName() {
+        return firstName;
+    }
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+    public String getLastName() {
+        return lastName;
+    }
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+    public short getAge() {
+        return age;
+    }
+    public void setAge(short age) {
+        this.age = age;
+    }
+    public int getBirthYear() {
+        return yob;
+    }
+    public void setBirthYear(int yob) {
+        this.yob = yob;
+    }
+    public long getId() {
+        return id;
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Singer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Singer.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Singer.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Singer.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,35 @@
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+
+@Entity
+@DiscriminatorValue("SINGER")
+public class Singer extends Person {
+    
+    @OneToMany
+    private Set<CD> cds;
+
+    public Singer() {
+        super();
+    }
+
+    public Singer(String firstName, String lastName, short age, int yob) {
+        super(firstName, lastName, age, yob);
+    }
+    
+    public Set<CD> getCds() {
+        return cds;
+    }
+
+    public void addCd(CD cd) {
+        if (cds == null)
+            cds = new HashSet<CD>();
+        if (cds.add(cd))
+            cd.setSinger(this);
+    }
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestEagerQueries.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestEagerQueries.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestEagerQueries.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestEagerQueries.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,128 @@
+/*
+ * 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.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.kernel.jpql.JPQLParser;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests that if a query generates more than one SQL statements, then it is
+ * not cached.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestEagerQueries extends SingleEMFTestCase {
+    public void setUp() {
+        super.setUp(DROP_TABLES, 
+            Person.class, Author.class, Singer.class,
+            Merchandise.class, Book.class, CD.class);
+        createTestData();
+    }
+    
+    private void createTestData() {
+        EntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        Author a1 = new Author("Author1", "", (short)40, 1960);
+        Author a2 = new Author("Author1", "", (short)40, 1960);
+        Author a3 = new Author("Author1", "", (short)40, 1960);
+        Singer s1 = new Singer("Singer1", "", (short)40, 1960);
+        Singer s2 = new Singer("Singer1", "", (short)40, 1960);
+        Book   b1 = new Book();
+        Book   b2 = new Book();
+        CD     c1 = new CD();
+        CD     c2 = new CD();
+        
+        b1.addAuthor(a1);
+        b1.addAuthor(a2);
+        b2.addAuthor(a2);
+        b2.addAuthor(a3);
+        c1.setSinger(s1);
+        c2.setSinger(s2);
+        
+        em.persist(a1); em.persist(a2);   em.persist(a3);
+        em.persist(s1); em.persist(s2);
+        em.persist(b1); em.persist(b2);
+        em.persist(c1); em.persist(c2);
+        em.getTransaction().commit();
+    }
+    
+    public void testQueryWithLazyRelationIsCached() {
+        // Author is lazily related to Book
+        String jpql = "select p from Author p";
+        EntityManager em = emf.createEntityManager();
+        
+        Query q1 = em.createQuery(jpql);
+        assertEquals(OpenJPAPersistence.cast(q1).getLanguage(), JPQLParser.LANG_JPQL);
+        List<Author> authors1 = q1.getResultList();
+        assertFalse(authors1.isEmpty());
+        Author author1 = authors1.iterator().next();
+        em.close(); // nothing will be loaded by chance
+        
+        assertNull(author1.getBooks());
+        
+        // do the same thing again, this time query should be cached
+        em = emf.createEntityManager();
+        Query q2 = em.createQuery(jpql);
+        assertEquals(OpenJPAPersistence.cast(q2).getLanguage(), QueryLanguages.LANG_PREPARED_SQL);
+        List<Author> authors2 = q2.getResultList();
+        assertFalse(authors2.isEmpty());
+        Author author2 = authors2.iterator().next();
+        em.close();
+        
+        assertNull(author2.getBooks());
+    }
+    
+    public void testQueryWithEagerRelationIsNotCached() {
+        // Book is eagerly related to Author
+        String jpql = "select b from Book b";
+        EntityManager em = emf.createEntityManager();
+        
+        Query q1 = em.createQuery(jpql);
+        assertEquals(OpenJPAPersistence.cast(q1).getLanguage(), JPQLParser.LANG_JPQL);
+        List<Book> books = q1.getResultList();
+        assertFalse(books.isEmpty());
+        Book book1 = books.iterator().next();
+        em.close(); // nothing will be loaded by chance
+        
+        assertNotNull(book1.getAuthors());
+        assertFalse(book1.getAuthors().isEmpty());
+        
+        // do the same thing again, this time query should not be cached
+        // because it requires multiple selects
+        em = emf.createEntityManager();
+        Query q2 = em.createQuery(jpql);
+        assertEquals(OpenJPAPersistence.cast(q2).getLanguage(), JPQLParser.LANG_JPQL);
+        List<Book> books2 = q2.getResultList();
+        assertFalse(books2.isEmpty());
+        Book book2 = books2.iterator().next();
+        em.close();
+        
+        assertNotNull(book2.getAuthors());
+        assertFalse(book2.getAuthors().isEmpty());
+    }
+
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestParameterProcessing.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestParameterProcessing.java?rev=739123&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestParameterProcessing.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestParameterProcessing.java Fri Jan 30 01:27:35 2009
@@ -0,0 +1,80 @@
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Query;
+
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.kernel.jpql.JPQLParser;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+public class TestParameterProcessing extends SingleEMFTestCase {
+    public void setUp() {
+        super.setUp(CLEAR_TABLES, Person.class, "openjpa.LogLevel=SQL=TRACE,Query=TRACE");
+        createTestData();
+//        super.setUp(Person.class, "openjpa.LogLevel=SQL=TRACE,Query=TRACE");
+    }
+    
+    private void createTestData() {
+        if (count(Person.class)>0) return;
+        EntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        Person p1 = new Person("John", "Doe", (short)45, 1964);
+        Person p2 = new Person("John", "Doe", (short)42, 1967);
+        Person p3 = new Person("Harry", "Doe", (short)12, 1995);
+        Person p4 = new Person("Barry", "Doe", (short)22, 1985);
+        em.persist(p1);
+        em.persist(p2);
+        em.persist(p3);
+        em.persist(p4);
+        em.getTransaction().commit();
+    }
+    
+    public void testPositional() {
+        String jpql = "select p from Person p where p.firstName=?1 and p.lastName='Doe' and p.age > ?2";
+        EntityManager em = emf.createEntityManager();
+        
+        Query q1 = em.createQuery(jpql);
+        assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
+        
+        List result1 = q1.setParameter(1, "John")
+                       .setParameter(2, (short)40)
+                       .getResultList();
+        
+        assertEquals(2, result1.size());
+        
+        Query q2 = em.createQuery(jpql);
+        assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
+        List result2 = q2.setParameter(1, "Harry")
+                  .setParameter(2, (short)10)
+                  .getResultList();
+        
+        assertEquals(1, result2.size());
+    }
+    
+    public void testNamed() {
+        String jpql = "select p from Person p where p.firstName=:first and p.lastName='Doe' and p.age > :age";
+        EntityManager em = emf.createEntityManager();
+        
+        Query q1 = em.createQuery(jpql);
+        assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
+        
+        List result1 = q1.setParameter("first", "John")
+                       .setParameter("age", (short)40)
+                       .getResultList();
+        
+        assertEquals(2, result1.size());
+        
+        Query q2 = em.createQuery(jpql);
+        assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
+        List result2 = q2.setParameter("first", "Barry")
+                  .setParameter("age", (short)20)
+                  .getResultList();
+        
+        assertEquals(1, result2.size());
+    }
+
+}



Mime
View raw message