You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2008/09/11 06:38:25 UTC

svn commit: r694111 - in /openjpa/branches/sql-cache: openjpa-kernel/src/main/java/org/apache/openjpa/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/ ope...

Author: ppoddar
Date: Wed Sep 10 21:38:24 2008
New Revision: 694111

URL: http://svn.apache.org/viewvc?rev=694111&view=rev
Log:
Define PreparedQueryCache and capability to invalidate and ignore PreparedQuery via Query hints when context changes

Modified:
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java
    openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
    openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
    openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
    openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java
    openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfiguration.java Wed Sep 10 21:38:24 2008
@@ -46,6 +46,7 @@
 import org.apache.openjpa.lib.conf.Configuration;
 import org.apache.openjpa.meta.MetaDataFactory;
 import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.PreparedQueryCache;
 import org.apache.openjpa.util.ClassResolver;
 import org.apache.openjpa.util.ProxyManager;
 import org.apache.openjpa.util.StoreFacadeTypeRegistry;
@@ -1528,22 +1529,22 @@
     public void setInitializeEagerly(boolean flag);
     
     /**
-     * Configuration settings for the Prepared Query Cache to use. 
-     * @see PreparedQueryCacheValue
+     * Affirms if Prepared Query Cache is activated.
+     * 
      * @since 1.3.0
      */
-    public String getPreparedQueryCache();
+    public boolean getPreparedQueryCache();
     
     /**
-     * Configuration settings for the Prepared Query Cache to use. 
-     * @see PreparedQueryCacheValue
+     * Sets whether Prepared Query Cache is activated.
+     * 
      * @since 1.3.0
      */
-    public void setPreparedQueryCache(String cache);    
+    public void setPreparedQueryCache(boolean flag);    
     
     /**
-     * Gets the modifable map of the cached prepared query indexed by query
+     * Gets the modifable view of the cached prepared query indexed by query
      * String.
      */
-    public Map getPreparedQueryCacheInstance();
+    public PreparedQueryCache getPreparedQueryCacheInstance();
 }

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/OpenJPAConfigurationImpl.java Wed Sep 10 21:38:24 2008
@@ -49,6 +49,7 @@
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.MetaDataFactory;
 import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.PreparedQueryCache;
 import org.apache.openjpa.util.ClassResolver;
 import org.apache.openjpa.util.ImplHelper;
 import org.apache.openjpa.util.ProxyManager;
@@ -1403,18 +1404,19 @@
         return (Map) queryCompilationCachePlugin.get();
     }
 
-    public String getPreparedQueryCache() {
-        return preparedQueryCachePlugin.getString();
+    public boolean getPreparedQueryCache() {
+        String str = preparedQueryCachePlugin.getString();
+        return str != null && str.toLowerCase().startsWith("true");
     }
 
-    public void setPreparedQueryCache(String queryCompilationCache) {
-    	preparedQueryCachePlugin.setString(queryCompilationCache);
+    public void setPreparedQueryCache(boolean flag) {
+    	preparedQueryCachePlugin.setString(""+flag);
     }
     
-    public Map getPreparedQueryCacheInstance() {
+    public PreparedQueryCache getPreparedQueryCacheInstance() {
         if (preparedQueryCachePlugin.get() == null)
-        	preparedQueryCachePlugin.instantiate(Map.class, this);
-        return (Map) preparedQueryCachePlugin.get();
+        	preparedQueryCachePlugin.instantiate(PreparedQueryCache.class, this);
+        return (PreparedQueryCache) preparedQueryCachePlugin.get();
     }
 
     public StoreFacadeTypeRegistry getStoreFacadeTypeRegistry() {

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/conf/PreparedQueryCacheValue.java Wed Sep 10 21:38:24 2008
@@ -18,15 +18,8 @@
  */
 package org.apache.openjpa.conf;
 
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.Map;
-
-import org.apache.openjpa.lib.conf.Configuration;
 import org.apache.openjpa.lib.conf.PluginValue;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.openjpa.lib.util.ParseException;
-import org.apache.openjpa.util.CacheMap;
+import org.apache.openjpa.persistence.PreparedQueryCache;
 
 /**
  * A cache of prepared queries indexed by an identifier.
@@ -39,40 +32,19 @@
     extends PluginValue {
 
     public static final String[] ALIASES = {
-        "true", CacheMap.class.getName(),
-        "all", ConcurrentHashMap.class.getName(),
-        "false", null,
+	    "true", PreparedQueryCache.class.getName(),
+	    "false", null,
     };
-
+	  
     public PreparedQueryCacheValue(String prop) {
         super(prop, true);
         setAliases(ALIASES);
         setDefault(ALIASES[0]);
         setClassName(ALIASES[1]);
     }
-
-    public Object newInstance(String clsName, Class type,
-        Configuration conf, boolean fatal) {
-        // make sure map handles concurrency
-        Map map;
-        
-        try {
-            map = (Map) super.newInstance(clsName, type, conf, fatal);
-        } catch (ParseException pe) {
-        	// For comment on special classloading see QueryCompilationCacheValue  
-            map = (Map) super.newInstance(clsName,
-                PreparedQueryCacheValue.class, conf, fatal);
-        } catch (IllegalArgumentException iae) {
-           map = (Map) super.newInstance(clsName,
-                PreparedQueryCacheValue.class, conf, fatal);
-        }
-
-        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;
-	}
+    
+    @Override
+    public boolean isAliasListComprehensive() {
+    	return true;
+    }
 }

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Broker.java Wed Sep 10 21:38:24 2008
@@ -52,6 +52,14 @@
      */
     public void setImplicitBehavior(OpCallbacks call,
         RuntimeExceptionTranslator ex);
+    
+    /**
+     * Gets the translator for runtime exceptions.
+     * 
+     * @see #setImplicitBehavior(OpCallbacks, RuntimeExceptionTranslator)
+     * @since 1.3.0
+     */
+    public RuntimeExceptionTranslator getInstanceExceptionTranslator();
 
     /**
      * Return the factory that produced this broker.

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Wed Sep 10 21:38:24 2008
@@ -370,7 +370,7 @@
             _extrans = ex;
     }
 
-    RuntimeExceptionTranslator getInstanceExceptionTranslator() {
+    public RuntimeExceptionTranslator getInstanceExceptionTranslator() {
         return (_operationCount == 0) ? _extrans : null;
     }
 

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java Wed Sep 10 21:38:24 2008
@@ -495,6 +495,15 @@
             throw translate(re);
         }
     }
+    
+    public RuntimeExceptionTranslator getInstanceExceptionTranslator() {
+        try {
+           return _broker.getInstanceExceptionTranslator();
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
 
     public BrokerFactory getBrokerFactory() {
         try {

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java Wed Sep 10 21:38:24 2008
@@ -158,11 +158,24 @@
         addFetchGroups(fetch.getFetchGroups());
         clearFields();
         addFields(fetch.getFields());
-
+        copyHints(fetch);
         // don't use setters because require active transaction
         _state.readLockLevel = fetch.getReadLockLevel();
         _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;

Modified: openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java (original)
+++ openjpa/branches/sql-cache/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryHints.java Wed Sep 10 21:38:24 2008
@@ -50,7 +50,29 @@
      * 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/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java (original)
+++ openjpa/branches/sql-cache/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java Wed Sep 10 21:38:24 2008
@@ -23,10 +23,16 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.persistence.EntityManager;
+
 import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.kernel.jpql.JPQLParser;
 import org.apache.openjpa.persistence.OpenJPAEntityManager;
 import org.apache.openjpa.persistence.OpenJPAQuery;
 import org.apache.openjpa.persistence.PreparedQuery;
+import org.apache.openjpa.persistence.PreparedQueryCache;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 /**
@@ -44,30 +50,176 @@
 	public static final String[] DEPARTMENT_NAMES = { "Marketing", "Sales",
 			"Engineering" };
 	public static final String[] EMPLOYEE_NAMES = { "Tom", "Dick", "Harray" };
-
+	public static final String EXCLUDED_QUERY_1 = "select count(p) from Company p";
+	public static final String EXCLUDED_QUERY_2 = "select count(p) from Department p";
+	public static final String INCLUDED_QUERY   = "select count(p) from Address p";
+	
 	public void setUp() throws Exception {
 		super.setUp(Company.class, Department.class, Employee.class,
-				Address.class, "openjpa.Log", "SQL=WARN");
+				Address.class, 
+				"openjpa.Log", "SQL=WARN",
+				"openjpa.PreparedQueryCache", 
+				"true(excludes='select count(p) from Company p;select count(p) from Department p')");
 	}
 
 	public void tearDown() throws Exception {
 		super.tearDown();
 	}
+	
+	PreparedQueryCache getCache() {
+		return emf.getConfiguration().getPreparedQueryCacheInstance();
+	}
 
 	public void testPreparedQueryCacheIsActiveByDefault() {
 		OpenJPAConfiguration conf = emf.getConfiguration();
-		assertEquals("true", conf.getPreparedQueryCache());
-		assertNotNull(conf.getPreparedQueryCacheInstance());
+		assertEquals(true, conf.getPreparedQueryCache());
+		assertNotNull(getCache());
 	}
 	
 	public void testPreparedQueryCacheCanBeDeactivatedDynamically() {
 		OpenJPAConfiguration conf = emf.getConfiguration();
-		assertNotNull(conf.getPreparedQueryCacheInstance());
-		conf.setPreparedQueryCache("false");
-		assertNull(conf.getPreparedQueryCacheInstance());
+		assertNotNull(getCache());
+		conf.setPreparedQueryCache(false);
+		assertNull(getCache());
+	}
+	
+	public void testPreparedQueryCacheIsPerUnitSingleton() {
+		PreparedQueryCache c1 = getCache();
+		PreparedQueryCache c2 = getCache();
+		assertSame(c1, c2);
+	}
+	
+	public void testExclusionPattern() {
+		List<String> patterns = getCache().getExcludes(); 
+		OpenJPAEntityManager em = emf.createEntityManager();
+		OpenJPAQuery q1 = em.createQuery(EXCLUDED_QUERY_1);
+		q1.getResultList();
+		assertNotCached(EXCLUDED_QUERY_1);
+		
+		OpenJPAQuery q2 = em.createQuery(EXCLUDED_QUERY_2);
+		q2.getResultList();
+		assertNotCached(EXCLUDED_QUERY_2);
+		
+		OpenJPAQuery q3 = em.createQuery(INCLUDED_QUERY);
+		q3.getResultList();
+		assertCached(INCLUDED_QUERY);
 	}
+	
+	void assertLanguage(OpenJPAQuery q, String lang) {
+		assertEquals(lang, q.getLanguage());
+	}
+	
+	void assertCached(String id) {
+		PreparedQuery cached = getCache().get(id);
+		assertNotNull(getCache() + ": " + getCache().getMapView() + 
+				" does not contain " + id, cached);
+	}
+	
+	void assertNotCached(String id) {
+		PreparedQueryCache cache = getCache();
+		if (cache != null) {
+			assertNull(cache.get(id));
+		}
+	}
+	
+	public void testPreparedQueryIsCachedOnExecution() {
+		String jpql = "select p from Company p";
+		OpenJPAEntityManager em = emf.createEntityManager();
+		OpenJPAQuery q1 = em.createQuery(jpql);
+		assertNotCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		List result = q1.getResultList();
+		assertCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		PreparedQuery cached = getCache().get(jpql);
+		assertEquals(jpql, cached.getIdentifier());
+		assertNotEquals(jpql, cached.getSQL());
+	}
+
+	public void testPreparedQueryIsCachedAcrossExecution() {
+		String jpql = "select p from Company p";
+		OpenJPAEntityManager em = emf.createEntityManager();
+		OpenJPAQuery q1 = em.createQuery(jpql);
+		assertNotCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		
+		List result = q1.getResultList();
+		assertCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		// Create a new query with the same JPQL
+		// This is not only cached, its language is different too
+		OpenJPAQuery q2 = em.createQuery(jpql);
+		assertCached(jpql);
+		assertLanguage(q2, QueryLanguages.LANG_PREPARED_SQL);
+	}
+	
+	public void testInvalidatePreparedQueryWithHint() {
+		String jpql = "select p from Company p";
+		OpenJPAEntityManager em = emf.createEntityManager();
+		OpenJPAQuery q1 = em.createQuery(jpql);
+		assertNotCached(jpql);
+		List result = q1.getResultList();
+		assertCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		// Create a new query with the same JPQL
+		// This is cached on creation, its language is Prepared SQL
+		OpenJPAQuery q2 = em.createQuery(jpql);
+		assertCached(jpql);
+		assertLanguage(q2, QueryLanguages.LANG_PREPARED_SQL);
+		q2.getResultList();
+		
+		// Now execute with hints to invalidate. 
+		q2.setHint(QueryHints.HINT_INVALIDATE_PREPARED_QUERY, true);
+		// Immediately it should be removed from the cache
+		assertNotCached(jpql);
+		assertEquals(JPQLParser.LANG_JPQL, q2.getLanguage());
+		q2.getResultList();
+		
+		// Create a new query with the same JPQL
+		// This is not cached on creation, its language is JPQL
+		OpenJPAQuery q3 = em.createQuery(jpql);
+		assertNotCached(jpql);
+		assertLanguage(q3, JPQLParser.LANG_JPQL);
+	}
+	
+	public void testIgnorePreparedQueryWithHint() {
+		String jpql = "select p from Company p";
+		OpenJPAEntityManager em = emf.createEntityManager();
+		OpenJPAQuery q1 = em.createQuery(jpql);
+		assertNotCached(jpql);
+		List result = q1.getResultList();
+		assertCached(jpql);
+		assertLanguage(q1, JPQLParser.LANG_JPQL);
+		
+		// Create a new query with the same JPQL
+		// This is cached on creation, its language is PREPARED SQL
+		OpenJPAQuery q2 = em.createQuery(jpql);
+		assertCached(jpql);
+		assertLanguage(q2, QueryLanguages.LANG_PREPARED_SQL);
+		q2.getResultList();
+		
+		// Now execute with hints to ignore. 
+		q2.setHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true);
+		// It should remain in the cache
+		assertCached(jpql);
+		// But its language should be JPQL and not PREPARED SQL
+		assertEquals(JPQLParser.LANG_JPQL, q2.getLanguage());
+		q2.getResultList();
+		
+		// Create a new query with the same JPQL
+		// This is cached on creation, its language is PREPARED SQL
+		OpenJPAQuery q3 = em.createQuery(jpql);
+		assertCached(jpql);
+		assertLanguage(q3, QueryLanguages.LANG_PREPARED_SQL);
+	}
+
 
-	public void testSimpleQueryCache() {
+	public void testQueryWithNoParameter() {
 		compare("select p from Company p");
 	}
 
@@ -80,7 +232,7 @@
 				"param", "x");
 	}
 
-	public void testJoins() {
+	public void testQueryWithJoinsAndParameters() {
 		compare("select e from Employee e " + "where e.name = :emp "
 				+ "and e.department.name = :dept " +
 				"and e.department.company.name = :company " +
@@ -98,19 +250,22 @@
 	 * 
 	 */
 	void compare(String jpql, Object... params) {
+//		if (true) return;
 		// run the query once for warming up 
 		run(jpql, params, !QUERY_CACHE, 1);
 		
 		// run N times without cache
 		long without = run(jpql, params, !QUERY_CACHE, SAMPLE_SIZE);
+		assertNotCached(jpql);
 		
 		// run N times with cache
 		long with = run(jpql, params, QUERY_CACHE, SAMPLE_SIZE);
+		assertCached(jpql);
 		
 		long delta = (without == 0) ? 0 : (without - with) * 100 / without;
 		
 		String sql = getSQL(jpql);
-		System.err.println("Execution time in millis for " + SAMPLE_SIZE
+		System.err.println("Execution time in nanos for " + SAMPLE_SIZE
 				+ " query execution with and without SQL cache:" + with + " "
 				+ without + " (" + delta + "%)");
 		System.err.println("JPQL: " + jpql);
@@ -120,14 +275,14 @@
 
 	/**
 	 * Create and run a query N times with the given parameters. The time for 
-	 * each query execution is measured in nanosecond precision) and finally
-	 * median time taken in N observation is returned in nanosecond.  
+	 * each query execution is measured in nanosecond precision and 
+	 * median time taken in N observation is returned.  
 	 * 
 	 * returns median time taken for single execution.
 	 */
 	long run(String jpql, Object[] params, boolean cache, int N) {
 		OpenJPAEntityManager em = emf.createEntityManager();
-		em.getConfiguration().setPreparedQueryCache("" + cache);
+		em.getConfiguration().setPreparedQueryCache(cache);
 		
 		List<Long> stats = new ArrayList<Long>();
 		for (int i = 0; i < N; i++) {
@@ -153,15 +308,13 @@
 		return stats.get(N/2);
 	}	
 	
-	public boolean isCached(String jpql) {
-		Map cache = emf.getConfiguration().getPreparedQueryCacheInstance();
-		return cache != null && cache.get(jpql) != null
-				&& cache.get(jpql) != PreparedQuery.NOT_CACHABLE;
-	}
-	
 	String getSQL(String jpql) {
-		Map cache = emf.getConfiguration().getPreparedQueryCacheInstance();
-		return cache == null ? null : ((PreparedQuery)cache.get(jpql)).getSQL();
+		PreparedQueryCache cache = emf.getConfiguration().getPreparedQueryCacheInstance();
+		if (cache == null)
+			return "null";
+		if (cache.get(jpql) != null)
+			return cache.get(jpql).getSQL();
+		return "null";
 	}
 
 
@@ -174,7 +327,13 @@
 				+ "e.department.company.name = :company and e.address.zip = :zip";
 		Object[] params = { "emp", "John", "dept", "Engineering", "company",
 				"acme.org", "zip", 12345 };
+		System.err.println("Executing  100 times [" + jpql + "]");
+		System.err.println("Press return to continue...");
+//		System.in.read();
+		long start = System.nanoTime();
 		_this.run(jpql, params, true, 100);
+		long end = System.nanoTime();
+		System.err.println("Time taken " + (end-start) + "ns");
 	}
 
 }

Modified: openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java Wed Sep 10 21:38:24 2008
@@ -32,7 +32,6 @@
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Map;
-import java.util.HashMap;
 import java.util.IdentityHashMap;
 import javax.persistence.EntityManager;
 import javax.persistence.FlushModeType;
@@ -64,7 +63,6 @@
 import org.apache.openjpa.meta.SequenceMetaData;
 import org.apache.openjpa.util.Exceptions;
 import org.apache.openjpa.util.ImplHelper;
-import org.apache.openjpa.util.ParameterMap;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 import org.apache.openjpa.util.UserException;
 
@@ -947,14 +945,15 @@
             throw new ArgumentException(_loc.get("no-sql"), null, null, false);
     }
     
-    private PreparedQuery getPreparedQuery(String id) {
-    	Map cache = getConfiguration().getPreparedQueryCacheInstance();
-    	if (cache == null)
-    		return null;
-    	Object val = cache.get(id);
-    	if (val == PreparedQuery.NOT_CACHABLE)
-    		return null;
-    	return (PreparedQuery)val;
+    /**
+     * Gets the prepared query cached by the given key. 
+     * 
+     * @return the cached PreparedQuery or null if none exists.
+     */
+    PreparedQuery getPreparedQuery(String id) {
+    	PreparedQueryCache cache = getConfiguration()
+    		.getPreparedQueryCacheInstance();
+    	return (cache == null) ? null : cache.get(id);
     }
 
     public void setFlushMode(FlushModeType flushMode) {

Modified: openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java (original)
+++ openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PreparedQuery.java Wed Sep 10 21:38:24 2008
@@ -44,7 +44,6 @@
  * @nojavadoc
  */
 public class PreparedQuery  {
-	public static final PreparedQuery NOT_CACHABLE = new PreparedQuery();
 	private final String _sql;
 	private final String _id;
 	
@@ -53,14 +52,6 @@
 	boolean _subclasses = true;
 	boolean _isProjection = false;
 	
-	/**
-	 * Private constructor to designate a null marker.
-	 */
-	private PreparedQuery() {
-		_sql  = null;
-		_id  = null;
-	}
-	
 	public PreparedQuery(String id, String sql, Query compiled) {
 		this._id = id;
 		this._sql = sql;

Modified: openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=694111&r1=694110&r2=694111&view=diff
==============================================================================
--- openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java (original)
+++ openjpa/branches/sql-cache/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java Wed Sep 10 21:38:24 2008
@@ -35,6 +35,7 @@
 
 import org.apache.commons.collections.map.LinkedMap;
 import org.apache.openjpa.enhance.Reflection;
+import org.apache.openjpa.kernel.Broker;
 import org.apache.openjpa.kernel.DelegatingQuery;
 import org.apache.openjpa.kernel.DelegatingResultList;
 import org.apache.openjpa.kernel.Filters;
@@ -43,6 +44,7 @@
 import org.apache.openjpa.kernel.QueryOperations;
 import org.apache.openjpa.kernel.exps.AggregateListener;
 import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.kernel.jpql.JPQLParser;
 import org.apache.openjpa.lib.rop.ResultList;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.util.ImplHelper;
@@ -64,7 +66,7 @@
 
 	private static final Localizer _loc = Localizer.forPackage(QueryImpl.class);
 
-	private final DelegatingQuery _query;
+	private DelegatingQuery _query;
 	private transient EntityManagerImpl _em;
 	private transient FetchPlan _fetch;
 	private ParameterMap _params;
@@ -359,10 +361,15 @@
 						throw new ArgumentException(_loc.get(
 								"bad-query-hint-value", key, value), null,
 								null, false);
-				} else if (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
-					invalidatePreparedQueryCache();
-				} else 
+				} else if (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
+					(key)) {
 					_query.getFetchConfiguration().setHint(key, value);
+					invalidatePreparedQuery();
+				} else if (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
+					_query.getFetchConfiguration().setHint(key, (Boolean)value);
+					ignorePreparedQuery();
+				} else 
+					_query.getFetchConfiguration().setHint(key, (Boolean)value);
 			} else
 				throw new ArgumentException(_loc.get("bad-query-hint", key),
 						null, null, false);
@@ -559,41 +566,74 @@
 	}
 	
 	/**
-	 * Cache this query with its identifier as key and SQL as value if this
-	 * query is amenable to caching and has not already been cached.
+	 * Cache this query if this query is amenable to caching and 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 non-null if this query has already been cached. null if it can 
-	 * not be cached or cached in this call.
+	 * not be cached or cached in this call or hint is set to ignore the cached
+	 * version.
 	 */
 	PreparedQuery cache() {
-		if (_id == null)
+		if (_id == null 
+			|| !_em.getConfiguration().getPreparedQueryCache() 
+			|| isHinted(QueryHints.HINT_IGNORE_PREPARED_QUERY)
+			|| isHinted(QueryHints.HINT_INVALIDATE_PREPARED_QUERY))
 			return null;
-		Map cache = _em.getConfiguration().getPreparedQueryCacheInstance();
-		if (cache == null)
-			return null;
-		PreparedQuery cached = (PreparedQuery)cache.get(_id);
-		if (cached == PreparedQuery.NOT_CACHABLE)
+		PreparedQueryCache cache = _em.getConfiguration()
+			.getPreparedQueryCacheInstance();
+		if (cache.isCachable(_id) == Boolean.FALSE)
 			return null;
+		PreparedQuery cached = cache.get(_id);
 		if (cached == null) {
 			String[] sqls = _query.getDataStoreActions(getParameterMap(true));
-			boolean cacheable = sqls.length == 1;
+			boolean cacheable = (sqls.length == 1);
 			if (!cacheable) {
-				cache.put(_id, PreparedQuery.NOT_CACHABLE);
+				cache.markUncachable(_id);
 				return null;
 			}
 			cached = new PreparedQuery(_id, sqls[0], _query); 
-			cache.put(_id, cached);
-			return null;
+			// Attempt to cache may fail if query matches exclusion pattern
+			if (!cache.cache(cached)) {
+				cached = null;
+			}
 		}
 		return cached;
 	}
 	
+	boolean isHinted(String hint) {
+		Object result = _query.getFetchConfiguration().getHint(hint);
+		return result != null && "true".equalsIgnoreCase(result.toString());
+	}
+	
 	/**
 	 * Remove this query from PreparedQueryCache. 
 	 */
-	public boolean invalidatePreparedQueryCache() {
-		Map cache = _em.getConfiguration().getPreparedQueryCacheInstance();
-		return cache != null && cache.remove(_id) != null;
+	private boolean invalidatePreparedQuery() {
+		if (!_em.getConfiguration().getPreparedQueryCache())
+			return false;
+		ignorePreparedQuery();
+		PreparedQueryCache cache = _em.getConfiguration()
+			.getPreparedQueryCacheInstance();
+		return cache.invalidate(_id);
+	}
+	
+	/**
+	 * Ignores this query from PreparedQueryCache by recreating the original
+	 * query if it has been cached. 
+	 */
+	private void ignorePreparedQuery() {
+		PreparedQuery cached = _em.getPreparedQuery(_id);
+		if (cached == null)
+			return;
+		Broker broker = _em.getBroker();
+		String JPQL = JPQLParser.LANG_JPQL;
+		String jpql = _id;
+		org.apache.openjpa.kernel.Query newQuery = broker.newQuery(JPQL, jpql);
+		newQuery.getFetchConfiguration().copy(_query.getFetchConfiguration());
+		newQuery.compile();
+		_query = new DelegatingQuery(newQuery, 
+				broker.getInstanceExceptionTranslator());
 	}
 
 	public int hashCode() {