Added: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java?rev=739123&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
 Fri Jan 30 01:27:35 2009
@@ -0,0 +1,495 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.persistence.jdbc.sqlcache;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.kernel.PreparedQuery;
+import org.apache.openjpa.kernel.QueryHints;
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.kernel.PreparedQueryCache;
+import org.apache.openjpa.kernel.QueryStatistics;
+import org.apache.openjpa.kernel.jpql.JPQLParser;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Performance oriented test to see comparative difference in response time 
with
+ * or with SQL caching.
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+public class TestPreparedQueryCache extends SingleEMFTestCase {
+       // Fail if performance degrades with cache compared to without cache
+       public static final boolean FAIL_ON_PERF_DEGRADE = 
+               Boolean.getBoolean("FailOnPerformanceRegression");
+       
+       // # observations to compute timing statistics
+       public static final int SAMPLE_SIZE = 100;
+       
+       public static final boolean USE_CACHE                  = true;
+       public static final boolean BIND_DIFFERENT_PARM_VALUES = true;
+       public static final boolean IS_NAMED_QUERY             = true;
+       
+       public static final String[] COMPANY_NAMES = { "acme.org" };
+       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 p from Address p";
+       
+       public void setUp() throws Exception {
+               super.setUp(Company.class, Department.class, Employee.class,
+                               Address.class, 
+                               "openjpa.Log", "SQL=WARN",
+                               "openjpa.QuerySQLCache", 
+                               "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().getQuerySQLCacheInstance();
+       }
+
+       public void testPreparedQueryCacheIsActiveByDefault() {
+               OpenJPAConfiguration conf = emf.getConfiguration();
+               assertTrue(conf.getQuerySQLCache().startsWith("true"));
+               assertNotNull(getCache());
+       }
+       
+       public void testPreparedQueryCacheCanBeDeactivatedDynamically() {
+               OpenJPAConfiguration conf = emf.getConfiguration();
+               assertNotNull(getCache());
+               conf.setQuerySQLCache("false");
+               assertNull(getCache());
+       }
+       
+       public void testPreparedQueryCacheIsPerUnitSingleton() {
+               PreparedQueryCache c1 = getCache();
+               PreparedQueryCache c2 = getCache();
+               assertSame(c1, c2);
+       }
+       
+       public void testPreparedQueryIdentifierIsOriginalJPQLQuery() {
+        String jpql = "select p from Company p";
+        OpenJPAEntityManager em = emf.createEntityManager();
+        OpenJPAQuery q1 = em.createQuery(jpql);
+        q1.getResultList();
+        PreparedQuery pq = getCache().get(jpql);
+        assertNotNull(pq);
+        assertEquals(jpql, pq.getIdentifier());
+        assertEquals(jpql, pq.getOriginalQuery());
+       }
+       
+       public void testExclusionPattern() {
+               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);
+               
+               q1.getResultList();
+               assertCached(jpql);
+               assertLanguage(q1, JPQLParser.LANG_JPQL);
+               
+               PreparedQuery cached = getCache().get(jpql);
+               assertEquals(jpql, cached.getIdentifier());
+               assertNotEquals(jpql, cached.getTargetQuery());
+       }
+
+       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);
+               
+               
+               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);
+               
+               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);
+               
+               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 testQueryStatistics() {
+               String jpql1 = "select c from Company c";
+               String jpql2 = "select c from Company c where c.name = 
'PObject'";
+               OpenJPAEntityManager em = emf.createEntityManager();
+               int N1 = 5;
+               int N2 = 8;
+               for (int i = 0; i < N1; i++) {
+               OpenJPAQuery q1 = em.createQuery(jpql1);
+                       q1.getResultList();
+               }
+               for (int i = 0; i < N2; i++) {
+               OpenJPAQuery q2 = em.createQuery(jpql2);
+                       q2.getResultList();
+               }
+               
+               QueryStatistics stats = getCache().getStatistics();
+               stats.dump(System.out);
+
+               assertEquals(N1,      stats.getExecutionCount(jpql1));
+               assertEquals(N2,      stats.getExecutionCount(jpql2));
+               assertEquals(N1+N2,   stats.getExecutionCount());
+               assertEquals(N1-1,    stats.getHitCount(jpql1));
+               assertEquals(N2-1,    stats.getHitCount(jpql2));
+               assertEquals(N1+N2-2, stats.getHitCount());
+               
+       }
+
+       public void testResetQueryStatistics() {
+               String jpql1 = "select c from Company c";
+               String jpql2 = "select c from Company c where c.name = 
'PObject'";
+               OpenJPAEntityManager em = emf.createEntityManager();
+               int N10 = 4;
+               int N20 = 7;
+               for (int i = 0; i < N10; i++) {
+               OpenJPAQuery q1 = em.createQuery(jpql1);
+                       q1.getResultList();
+               }
+               for (int i = 0; i < N20; i++) {
+               OpenJPAQuery q2 = em.createQuery(jpql2);
+                       q2.getResultList();
+               }
+               
+               QueryStatistics stats = getCache().getStatistics();
+               assertEquals(N10,       stats.getExecutionCount(jpql1));
+               assertEquals(N20,       stats.getExecutionCount(jpql2));
+               assertEquals(N10+N20,   stats.getExecutionCount());
+               assertEquals(N10-1,     stats.getHitCount(jpql1));
+               assertEquals(N20-1,     stats.getHitCount(jpql2));
+               assertEquals(N10+N20-2, stats.getHitCount());
+               
+               stats.reset();
+               
+               int N11 = 7;
+               int N21 = 4;
+               for (int i = 0; i < N11; i++) {
+            OpenJPAQuery q1 = em.createQuery(jpql1);
+                       q1.getResultList();
+               }
+               for (int i = 0; i < N21; i++) {
+            OpenJPAQuery q2 = em.createQuery(jpql2);
+                       q2.getResultList();
+               }
+
+               stats.dump(System.out);
+               
+               assertEquals(N11,     stats.getExecutionCount(jpql1));
+               assertEquals(N21,     stats.getExecutionCount(jpql2));
+               assertEquals(N11+N21, stats.getExecutionCount());
+               assertEquals(N11,     stats.getHitCount(jpql1));
+               assertEquals(N21,     stats.getHitCount(jpql2));
+               assertEquals(N11+N21, stats.getHitCount());
+               
+               assertEquals(N10+N11,     stats.getTotalExecutionCount(jpql1));
+               assertEquals(N20+N21,     stats.getTotalExecutionCount(jpql2));
+               assertEquals(N10+N11+N20+N21, stats.getTotalExecutionCount());
+               assertEquals(N10+N11-1,     stats.getTotalHitCount(jpql1));
+               assertEquals(N20+N21-1,     stats.getTotalHitCount(jpql2));
+               assertEquals(N10+N11+N20+N21-2, stats.getTotalHitCount());
+               
+       }
+
+       public void testQueryWithNoParameter() {
+               String jpql = "select p from Company p";
+               Object[] params = null;
+               compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testQueryWithLiteral() {
+               String jpql = "select p from Company p where p.name = 
'PObject'";
+               Object[] params = null;
+               compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testQueryWithParameter() {
+               String jpql = "select p from Company p where p.name = :param";
+               Object[] params = {"param", "x"};
+               compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testQueryWithJoinsAndParameters() {
+               String jpql = "select e from Employee e " + "where e.name = 
:emp "
+                                       + "and e.department.name = :dept "
+                                       + "and e.department.company.name LIKE 
'IBM' " 
+                                       + "and e.department.company.name = 
:company " 
+                                       + "and e.address.zip = :zip";
+               Object[] params = { "emp", "John", 
+                                                       "dept", "Engineering",
+                                                       "company", "acme.org",
+                                                       "zip", 12345};
+               compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testNamedQueryWithNoParameter() {
+               String namedQuery = "Company.PreparedQueryWithNoParameter";
+               Object[] params = null;
+               compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testNamedQueryWithLiteral() {
+               String namedQuery = "Company.PreparedQueryWithLiteral";
+               Object[] params = null;
+               compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+
+       public void testNamedQueryWithPositionalParameter() {
+               String namedQuery = 
"Company.PreparedQueryWithPositionalParameter";
+               Object[] params = {1, "x", 2, 1960};
+               compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+       
+       public void testNamedQueryWithNamedParameter() {
+               String namedQuery = "Company.PreparedQueryWithNamedParameter";
+               Object[] params = {"name", "x", "startYear", 1960};
+               compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES, 
params);
+       }
+       
+       /**
+        * Compare the result of execution of the same query with and without
+        * Prepared Query Cache.
+        * 
+        */
+       void compare(boolean isNamed, String jpql, boolean append, Object... 
params) {
+               String realJPQL = isNamed ? getJPQL(jpql) : jpql;
+               // run the query once for warming up 
+               run(jpql, params, !USE_CACHE, 1, isNamed, append);
+               
+               // run N times without cache
+               long without = run(jpql, params, !USE_CACHE, SAMPLE_SIZE, 
isNamed, append);
+               assertNotCached(realJPQL);
+               
+               // run N times with cache
+               long with = run(jpql, params, USE_CACHE, SAMPLE_SIZE, isNamed, 
append);
+               assertCached(realJPQL);
+               
+               long delta = (without == 0) ? 0 : (without - with) * 100 / 
without;
+               
+               String sql = getSQL(realJPQL);
+               System.err.println("Execution time in nanos for " + SAMPLE_SIZE
+                               + " query execution with and without SQL 
cache:" + with + " "
+                               + without + " (" + delta + "%)");
+               System.err.println("JPQL: " + realJPQL);
+               System.err.println("SQL : " + sql);
+               if (delta < 0) {
+                       if (FAIL_ON_PERF_DEGRADE)
+                               assertFalse("change in execution time = " + 
delta + "%", 
+                                               delta < 0);
+                       else 
+                               System.err.println("*** WARN: Perforamce 
regression with cache." + 
+                                       " Execution time degrades by " + delta 
+ "%");
+               } else {
+                   System.err.println("change in execution time = +" + delta + 
"%");
+               }
+       }
+
+       /**
+        * Create and run a query N times with the given parameters. The time 
for 
+        * 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 useCache, int N, 
+                       boolean isNamedQuery, boolean 
appendIndexValuetoParameters) {
+               OpenJPAEntityManager em = emf.createEntityManager();
+               ((OpenJPAEntityManagerSPI)em).setQuerySQLCache(useCache);
+               assertEquals(useCache, 
((OpenJPAEntityManagerSPI)em).getQuerySQLCache());
+               List<Long> stats = new ArrayList<Long>();
+               for (int i = 0; i < N; i++) {
+                       long start = System.nanoTime();
+                       OpenJPAQuery q = isNamedQuery 
+                               ? em.createNamedQuery(jpql) : 
em.createQuery(jpql);
+                       for (int j = 0; params != null && j < params.length - 
1; j += 2) {
+                               Object key = params[j];
+                               Object val = params[j + 1];
+                               if (key instanceof Integer)
+                                       
q.setParameter(((Number)key).intValue(), val); 
+                               else if (key instanceof String)
+                                       q.setParameter(key.toString(), val); 
+                               else
+                                       throw new RuntimeException("key " + key 
+ " is neither Number nor String");
+                       }
+                       q.getResultList();
+                       q.closeAll();
+                       long end = System.nanoTime();
+                       stats.add(end - start);
+               }
+               em.close();
+               Collections.sort(stats);
+               return stats.get(N/2);
+       }       
+       
+       /**
+        * Get the SQL corresponding to the given query key.
+        * @param jpql
+        * @return
+        */
+       String getSQL(String queryKey) {
+               PreparedQueryCache cache = 
emf.getConfiguration().getQuerySQLCacheInstance();
+               if (cache == null)
+                       return "null";
+               PreparedQuery query = cache.get(queryKey);
+               return (query != null) ? query.getTargetQuery() : "null";
+       }
+       
+       String getJPQL(String namedQuery) {
+               return emf.getConfiguration().getMetaDataRepositoryInstance()
+                                 .getQueryMetaData(null, namedQuery, null, 
true)
+                                 .getQueryString();
+       }
+
+
+
+       public static void main(String[] args) throws Exception {
+               TestPreparedQueryCache _this = new TestPreparedQueryCache();
+               _this.setUp();
+               String jpql = "select e from Employee e where e.name = :emp and 
"
+                               + "e.department.name = :dept and "
+                               + "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, 
+                               USE_CACHE, 
+                               SAMPLE_SIZE, 
+                               !IS_NAMED_QUERY, 
+                               BIND_DIFFERENT_PARM_VALUES);
+               long end = System.nanoTime();
+               System.err.println("Time taken " + (end-start) + "ns");
+       }
+
+}

Added: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCacheExclusion.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCacheExclusion.java?rev=739123&view=auto
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCacheExclusion.java
 (added)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCacheExclusion.java
 Fri Jan 30 01:27:35 2009
@@ -0,0 +1,124 @@
+/*
+ * 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.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.openjpa.jdbc.kernel.PreparedQueryCacheImpl;
+import org.apache.openjpa.jdbc.kernel.PreparedQueryImpl;
+import org.apache.openjpa.kernel.PreparedQuery;
+import org.apache.openjpa.kernel.PreparedQueryCache;
+
+/**
+ * Test exclusion patterns of PreparedQueryCache in isolation.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestPreparedQueryCacheExclusion extends TestCase {
+       private PreparedQueryCache cache;
+       private String[] keys   = {"jpql1", "jpql2", "jpql3"};
+       private String[] values = {"sql1", "sql2", "sql3"};
+       
+       protected void setUp() throws Exception {
+               super.setUp();
+               cache = new PreparedQueryCacheImpl();
+               for (int i = 0; i < keys.length; i++) {
+                       PreparedQuery p = new PreparedQueryImpl(keys[i], 
values[i], null);
+                       cache.cache(p);
+               }
+       }
+
+       protected void tearDown() throws Exception {
+               super.tearDown();
+       }
+       
+       public void testExclusionPatternsAreSet() {
+               String excludes = "a;b;c";
+               cache.setExcludes(excludes);
+               assertEquals(3, cache.getExcludes().size());
+               assertTrue(cache.isExcluded("a"));
+               assertTrue(cache.isExcluded("b"));
+               assertTrue(cache.isExcluded("c"));
+               assertFalse(cache.isExcluded("d"));
+       }
+
+       public void testCachePopulationSetUp() {
+               assertContent(keys, values);
+       }
+       
+       public void testAddExclusionPatternDisallowsCacheing() {
+               int EXCLUDED = 1;
+               cache.addExclusionPattern(keys[EXCLUDED]);
+               
+               PreparedQuery p = new PreparedQueryImpl(keys[EXCLUDED], 
values[EXCLUDED], null);
+               assertFalse("Must not cache excluded key " + keys[EXCLUDED], 
cache.cache(p));
+       }
+
+       public void testAddExclusionPatternMakesExistingEntryInvalid() {
+               int EXCLUDED = 1;
+               cache.addExclusionPattern(keys[EXCLUDED]);
+               Map<String, String> view = cache.getMapView();
+               for (int i = 0; i < keys.length; i++) {
+                       if (i == EXCLUDED) {
+                               assertFalse(view.containsKey(keys[i]));
+                               assertFalse(view.containsValue(values[i]));
+                       } else {
+                               assertTrue(view.containsKey(keys[i]));
+                               assertTrue(view.containsValue(values[i]));
+                       }
+               }
+       }
+       
+       public void testRemoveExclusionPatternAllowsCacheing() {
+               int EXCLUDED = 1;
+               cache.addExclusionPattern(keys[EXCLUDED]);
+               
+               PreparedQuery p = new PreparedQueryImpl(keys[EXCLUDED], 
values[EXCLUDED], null);
+               assertFalse("Must not cache excluded key " + keys[EXCLUDED], 
cache.cache(p));
+               
+               cache.removeExclusionPattern(keys[EXCLUDED]);
+               assertTrue("Must cache remove excluded key " + keys[EXCLUDED], 
cache.cache(p));
+       }
+
+       public void testRemoveExclusionPatternDoesNotRemoveUserProhbitedKeys() {
+               String USER_MARKED_UNCACHABLE = "[user prohibited]";
+               cache.markUncachable(USER_MARKED_UNCACHABLE);
+               
+               PreparedQuery p = new PreparedQueryImpl(USER_MARKED_UNCACHABLE, 
"xyz", null);
+               assertFalse("Must not cache user-prohibited key " + 
+                               USER_MARKED_UNCACHABLE, cache.cache(p));
+               
+               cache.removeExclusionPattern(USER_MARKED_UNCACHABLE);
+               assertFalse("Must not cache user-prohibited key even when 
removed " + 
+                               USER_MARKED_UNCACHABLE, cache.cache(p));
+       }
+
+       void assertContent(String[] keys, String[] values) {
+               Map<String, String> view = cache.getMapView();
+               for (int i = 0; i < keys.length; i++) {
+                       assertTrue("key " + keys[i] + " not in " + view, 
+                                       view.containsKey(keys[i]));
+                       assertTrue("value " + values[i] + " not in " + view,
+                                       view.containsValue(values[i]));
+               }
+       }
+}

Modified: 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/expressions/TestEntityTypeExpression.java
 Fri Jan 30 01:27:35 2009
@@ -48,7 +48,7 @@
             new Address("22 Montgomery", "SF", null, "50054") };
 
         CompUser user1 = createUser("Seetha", "MAC", add[0], 36, true);
-        CompUser user2 = createUser("Shannon ", "PC", add[1], 36, false);
+        CompUser user2 = createUser("Shannon", "PC", add[1], 36, false);
         CompUser user3 = createUser("Ugo", "PC", add[2], 19, true);
         CompUser user4 = createUser("_Jacob", "LINUX", add[3], 10, true);
         CompUser user5 = createUser("Famzy", "UNIX", add[4], 29, false);
@@ -91,26 +91,26 @@
         String param3 = "PC";
 
         query = "SELECT e FROM CompUser e where TYPE(e) in ?1 and e.age in ?2" 
+
-                " and e.computerName = ?3";
+                " and e.computerName = ?3 ORDER By e.name";
         rs = em.createQuery(query).
             setParameter(1, params).
             setParameter(2, params2).
             setParameter(3, param3).getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("Shannon", user.getName());
 
-        query = "SELECT e FROM CompUser e where TYPE(e) in ?1 and e.age in ?2";
+        query = "SELECT e FROM CompUser e where TYPE(e) in ?1 and e.age in ?2 
ORDER By e.name";
         rs = em.createQuery(query).
             setParameter(1, params).
             setParameter(2, params2).getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("Famzy", user.getName());
 
-        query = "SELECT e FROM CompUser e where TYPE(e) in :params";
+        query = "SELECT e FROM CompUser e where TYPE(e) in :params ORDER BY 
e.name DESC";
         rs = em.createQuery(query).
             setParameter("params", params).getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("_Jacob", user.getName());
         
         query = "SELECT TYPE(e) FROM MaleUser e where TYPE(e) = MaleUser";
         rs = em.createQuery(query).getResultList();
@@ -139,26 +139,26 @@
         rs =  em.createQuery(query).
             setParameter("typeName", FemaleUser.class).getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("Shannon", user.getName());
 
-        query = "SELECT e FROM CompUser e where TYPE(e) = ?1";
+        query = "SELECT e FROM CompUser e where TYPE(e) = ?1 ORDER BY e.name";
         rs = em.createQuery(query).
             setParameter(1, FemaleUser.class).getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("Famzy", user.getName());
         
-        query = "SELECT e FROM CompUser e where TYPE(e) in (?1)";
+        query = "SELECT e FROM CompUser e where TYPE(e) in (?1) ORDER BY 
e.name";
         rs = em.createQuery(query).
             setParameter(1, MaleUser.class).getResultList();
         user = rs.get(0);
-        assertEquals(user.getName(), "Seetha");
+        assertEquals("Seetha", user.getName());
         
-        query = "SELECT e FROM CompUser e where TYPE(e) in (?1, ?2)";
+        query = "SELECT e FROM CompUser e where TYPE(e) in (?1, ?2) ORDER BY 
e.name";
         rs = em.createQuery(query).
             setParameter(1, FemaleUser.class).setParameter(2, MaleUser.class).
             getResultList();
         user = rs.get(0);
-        assertEquals("the name is not shannon", "Shannon ", user.getName());
+        assertEquals("Famzy", user.getName());
 
         query = "select sum(e.age) FROM CompUser e GROUP BY e.age" +
             " HAVING ABS(e.age) = :param";

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
 Fri Jan 30 01:27:35 2009
@@ -31,16 +31,14 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.EnumSet;
-import java.util.Map;
-import java.util.HashMap;
 import java.util.IdentityHashMap;
+import java.util.Map;
 import java.util.Set;
 
 import javax.persistence.EntityManager;
 import javax.persistence.FlushModeType;
 import javax.persistence.LockModeType;
 import javax.persistence.Query;
-import javax.persistence.QueryBuilder;
 import javax.persistence.QueryDefinition;
 
 import org.apache.commons.lang.StringUtils;
@@ -51,16 +49,17 @@
 import org.apache.openjpa.kernel.AbstractBrokerFactory;
 import org.apache.openjpa.kernel.Broker;
 import org.apache.openjpa.kernel.DelegatingBroker;
+import org.apache.openjpa.kernel.FetchConfiguration;
 import org.apache.openjpa.kernel.FindCallbacks;
 import org.apache.openjpa.kernel.LockLevels;
 import org.apache.openjpa.kernel.OpCallbacks;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.PreparedQuery;
+import org.apache.openjpa.kernel.PreparedQueryCache;
 import org.apache.openjpa.kernel.QueryFlushModes;
 import org.apache.openjpa.kernel.QueryLanguages;
 import org.apache.openjpa.kernel.Seq;
-import org.apache.openjpa.kernel.FetchConfiguration;
 import org.apache.openjpa.kernel.jpql.JPQLParser;
-import org.apache.openjpa.kernel.jpql.ParseException;
 import org.apache.openjpa.lib.util.Closeable;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.ClassMetaData;
@@ -875,17 +874,26 @@
     public OpenJPAQuery createQuery(String language, String query) {
         assertNotCloseInvoked();
         try {
+            String qid = query;
+            PreparedQuery cached = getPreparedQuery(qid);
+            if (cached != null) {
+                language = QueryLanguages.LANG_PREPARED_SQL;
+                query = cached.getTargetQuery();
+            }
             org.apache.openjpa.kernel.Query q = _broker.newQuery(language, 
                 query);
             // have to validate JPQL according to spec
             if (JPQLParser.LANG_JPQL.equals(language))
                 q.compile(); 
-            return new QueryImpl(this, _ret, q);
+            if (cached != null) {
+                cached.setInto(q);
+            }
+            return new QueryImpl(this, _ret, q).setId(qid);
         } catch (RuntimeException re) {
             throw PersistenceExceptions.toPersistenceException(re);
         }
     }
-
+    
     public OpenJPAQuery createQuery(Query query) {
         if (query == null)
             return createQuery((String) null);
@@ -902,12 +910,25 @@
             QueryMetaData meta = _broker.getConfiguration().
                 getMetaDataRepositoryInstance().getQueryMetaData(null, name,
                 _broker.getClassLoader(), true);
-            org.apache.openjpa.kernel.Query del =
-                _broker.newQuery(meta.getLanguage(), null);
-            meta.setInto(del);
+            String query = null;
+            String language = meta.getLanguage();
+            String qid = meta.getQueryString();
+            
+            PreparedQuery cached = getPreparedQuery(qid);
+            if (cached != null) {
+                language = QueryLanguages.LANG_PREPARED_SQL;
+                query = cached.getTargetQuery();
+            }
+            org.apache.openjpa.kernel.Query del = _broker.newQuery(language, 
+                query);
+            if (cached != null) {
+                cached.setInto(del);
+            } else {
+                meta.setInto(del);
+            }
             del.compile();
-
-            OpenJPAQuery q = new QueryImpl(this, _ret, del);
+            
+            OpenJPAQuery q = new QueryImpl(this, _ret, del).setId(qid);
             String[] hints = meta.getHintKeys();
             Object[] values = meta.getHintValues();
             for (int i = 0; i < hints.length; i++)
@@ -916,7 +937,7 @@
         } catch (RuntimeException re) {
             throw PersistenceExceptions.toPersistenceException(re);
         }
-    }
+    }    
 
     public OpenJPAQuery createNativeQuery(String query) {
         validateSQL(query);
@@ -943,7 +964,23 @@
         if (StringUtils.trimToNull(query) == null)
             throw new ArgumentException(_loc.get("no-sql"), null, null, false);
     }
+    
+    PreparedQueryCache getPreparedQueryCache() {
+        return _broker.getCachePreparedQuery() ?
+            getConfiguration().getQuerySQLCacheInstance() : null;
+    }
+
+    /**
+     * Gets the prepared query cached by the given key. 
+     * 
+     * @return the cached PreparedQuery or null if none exists.
+     */
+    PreparedQuery getPreparedQuery(String id) {
+        PreparedQueryCache cache = getPreparedQueryCache();
+        return (cache == null) ? null : cache.get(id);
+    }
 
+    
     public void setFlushMode(FlushModeType flushMode) {
         assertNotCloseInvoked();
         _broker.assertOpen();
@@ -1445,4 +1482,18 @@
         throw new UnsupportedOperationException(
             "JPA 2.0 - Method not yet implemented");
     }
+    
+    
+    public void setQuerySQLCache(boolean flag) {
+        _broker.setCachePreparedQuery(flag);
+    }
+    
+    public boolean getQuerySQLCache() {
+        return _broker.getCachePreparedQuery();
+    }
+    
+    RuntimeExceptionTranslator getExceptionTranslator() {
+        return _ret;
+    }
+
 }

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManagerSPI.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManagerSPI.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManagerSPI.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAEntityManagerSPI.java
 Fri Jan 30 01:27:35 2009
@@ -105,4 +105,22 @@
      * previous settings.
      */
     public void setLifecycleListenerCallbackMode(EnumSet<CallbackMode> modes);
+    
+    
+    /**
+     * Affirms if this receiver is caching database queries.
+     *  
+     * @since 2.0.0
+     */
+    public boolean getQuerySQLCache();
+    
+    /**
+     * Sets whether this receiver will cache database 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 setQuerySQLCache(boolean flag);
+
 }

Modified: 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
 (original)
+++ 
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
 Fri Jan 30 01:27:35 2009
@@ -23,13 +23,11 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,19 +40,26 @@
 import javax.persistence.Query;
 import javax.persistence.TemporalType;
 
-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.FetchConfiguration;
 import org.apache.openjpa.kernel.Filters;
+import org.apache.openjpa.kernel.PreparedQuery;
+import org.apache.openjpa.kernel.PreparedQueryCache;
+import org.apache.openjpa.kernel.QueryHints;
 import org.apache.openjpa.kernel.QueryLanguages;
 import org.apache.openjpa.kernel.QueryOperations;
+import org.apache.openjpa.kernel.QueryStatistics;
 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;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
+import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
 
 /**
  * Implementation of {...@link Query} interface.
@@ -69,14 +74,13 @@
 
        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 Map<String, Object> _named;
        private Map<Integer, Object> _positional;
-
-       private static Object GAP_FILLER = new Object();
+       private String _id;
 
        /**
         * Constructor; supply factory exception translator and delegate.
@@ -249,183 +253,32 @@
                if (_query.getOperation() != QueryOperations.OP_SELECT)
                        throw new 
InvalidStateException(_loc.get("not-select-query", _query
                                        .getQueryString()), null, null, false);
-
-               validateParameters();
-
-               // handle which types of parameters we are using, if any
-               if (_positional != null)
-                       return _query.execute(_positional);
-               if (_named != null)
-                       return _query.execute(_named);
-               return _query.execute();
-       }
-       
-       /**
-        * Validate that the types of the parameters are correct.
-        * The idea is to catch as many validation error as possible at the 
facade
-        * layer itself.
-        * For native SQL queries, however, parameter validation is bypassed as
-        * we do not parse SQL.
-        * 
-        * The expected parameters are parsed from the query and in a LinkedMap 
-        *      key   : name of the parameter as declared in query
-        *  value : expected Class of allowed value
-        *  
-        * The bound parameters depends on positional or named parameter style
-        * 
-        * TreeMap<Integer, Object> for positional parameters:
-        *   key   : 1-based Integer index
-        *   value : bound value. GAP_FILLER if the position is not set. This
-        *   simplifies validation at the kernel layer
-        *   
-        * Map<String, Object> for named parameters:
-        *   key   : parameter name
-        *   value : the bound value
-        *   
-        *  Validation accounts for 
-        *    a) gaps in positional parameters
-        *       SELECT p FROM PObject p WHERE p.a1=?1 AND p.a3=?3
-        *    
-        *    b) repeated parameters
-        *       SELECT p FROM PObject p WHERE p.a1=?1 AND p.a2=?1 AND p.a3=?2
-        *       
-        *    c) parameter is bound but not declared
-        *    
-        *    d) parameter is declared but not bound
-        *    
-        *    e) parameter does not match the value type
-        *    
-        *    f) parameter is primitive type but bound to null value
-        */
-       private void validateParameters() {
-               if (isNative()) {
-                       removeGaps(_positional);
-                       return;
-               }
-               String query = getQueryString();
-               if (_positional != null) {
-                       LinkedMap expected = _query.getParameterTypes();
-                       Map<Integer, Object> actual = _positional;
-                       for (Object o : expected.keySet()) {
-                               String position = (String) o;
-                               Class expectedParamType = (Class) 
expected.get(position);
-                               try {
-                                       Integer.parseInt(position);
-                               } catch (NumberFormatException ex) {
-                                       
newValidationException("param-style-mismatch", query,
-                                                       expected.asList(),
-                                                       
Arrays.toString(actual.keySet().toArray()));
-                               }
-                               Object actualValue = 
actual.get(Integer.parseInt(position));
-                               boolean valueUnspecified = (actualValue == 
GAP_FILLER)
-                                               || (actualValue == null && 
(actual.size() < expected
-                                                               .size()));
-                               if (valueUnspecified) 
-                                       newValidationException("param-missing", 
position, query,
-                                                       
Arrays.toString(actual.keySet().toArray()));
-                               
-                               if (expectedParamType.isPrimitive() && 
actualValue == null)
-                                       
newValidationException("param-type-null", 
-                                                       position, query, 
expectedParamType.getName());
-                               if (actualValue != null &&
-                                  
!Filters.wrap(expectedParamType).isInstance(actualValue)) 
-                                       
newValidationException("param-type-mismatch",
-                                                       position, query, 
actualValue,
-                                                       
actualValue.getClass().getName(),
-                                                       
expectedParamType.getName());
-                               
-                       }
-                       for (Integer position : actual.keySet()) {
-                               Object actualValue = actual.get(position);
-                               Class expectedParamType = (Class) 
expected.get("" + position);
-                               boolean paramExpected = expected.containsKey("" 
+ position);
-                               if (actualValue == GAP_FILLER) {
-                                       if (paramExpected) {
-                                               
newValidationException("param-missing", position, query,
-                                                               
Arrays.toString(actual.keySet().toArray()));
-                                       }
-                               } else {
-                                       if (!paramExpected)
-                                               
newValidationException("param-extra", position, query,
-                                                               
expected.asList());
-                                       if (expectedParamType.isPrimitive() && 
actualValue == null)
-                                               
newValidationException("param-type-null", 
-                                                               position, 
query, expectedParamType.getName());
-                                       if (actualValue != null 
-                                        && 
!Filters.wrap(expectedParamType).isInstance(actualValue)) 
-                                               
newValidationException("param-type-mismatch",
-                                                               position, 
query, actualValue,
-                                                               
actualValue.getClass().getName(),
-                                                               
expectedParamType.getName());
-                                       
-                               }
-                       }
-
-               } else if (_named != null) {
-                       LinkedMap expected = _query.getParameterTypes();
-                       // key : name of the parameter used while binding
-                       // value : user supplied parameter value. null may mean 
either
-                       // user has supplied a value or not specified at all
-                       Map<String, Object> actual = _named;
-                       for (Object o : expected.keySet()) {
-                               String expectedName = (String) o;
-                               Class expectedParamType = (Class) 
expected.get(expectedName);
-                               Object actualValue = actual.get(expectedName);
-                               boolean valueUnspecified = 
!actual.containsKey(expectedName);
-                               if (valueUnspecified) {
-                                       newValidationException("param-missing", 
expectedName, query,
-                                                       
Arrays.toString(actual.keySet().toArray()));
-                               }
-                               if (expectedParamType.isPrimitive() && 
actualValue == null)
-                                       
newValidationException("param-type-null", 
-                                                       expectedName, query, 
expectedParamType.getName());
-                               if (actualValue != null 
-                                && 
!Filters.wrap(expectedParamType).isInstance(actualValue)) {
-                                       
newValidationException("param-type-mismatch",
-                                                       expectedName, query, 
actualValue,
-                                                       
actualValue.getClass().getName(),
-                                                       
expectedParamType.getName());
-                               }
-                       }
-                       for (String actualName : actual.keySet()) {
-                               Object actualValue = actual.get(actualName);
-                               Class expectedParamType = (Class) 
expected.get(actualName);
-                               boolean paramExpected = 
expected.containsKey(actualName);
-                               if (!paramExpected) {
-                                       newValidationException("param-extra", 
actualName, query,
-                                                       expected.asList());
-                               }
-                               if (expectedParamType.isPrimitive() && 
actualValue == null)
-                                       
newValidationException("param-type-null", 
-                                                       actualName, query, 
expectedParamType.getName());
-                               if (actualValue != null 
-                                && 
!Filters.wrap(expectedParamType).isInstance(actualValue)) {
-                                       
newValidationException("param-type-mismatch",
-                                                       actualName, query, 
actualValue,
-                                                       
actualValue.getClass().getName(),
-                                                       
expectedParamType.getName());
-                               }
-                       }
-               }
+               
+        Map params = _positional != null ? _positional : _named;
+        Boolean registered = null;
+               PreparedQueryCache cache = _em.getPreparedQueryCache();
+               if (cache != null) {
+                   FetchConfiguration fetch = _query.getFetchConfiguration();
+                   registered = cache.register(_id, _query, fetch);
+                   boolean alreadyCached = (registered == null);
+                   String lang = _query.getLanguage();
+                   QueryStatistics stats = cache.getStatistics();
+                   if (alreadyCached && LANG_PREPARED_SQL.equals(lang)) {
+                       PreparedQuery pq = _em.getPreparedQuery(_id);
+                       params = pq.reparametrize(params);
+                       stats.recordExecution(pq.getOriginalQuery(), 
alreadyCached);
+                   } else {
+                stats.recordExecution(_query.getQueryString(), alreadyCached);
+                   }
+               }
+        Object result = _query.execute(params);
+        
+        if (registered == Boolean.TRUE) {
+            cache.initialize(_id, result);
+        }
+        return result;
        }
        
-       Map<Integer, Object> removeGaps(Map<Integer, Object> map) {
-               if (map == null || !map.containsValue(GAP_FILLER))
-                       return map;
-               List<Integer> gaps = new ArrayList<Integer>();
-               for (Integer key : map.keySet())
-                       if (map.get(key) == GAP_FILLER)
-                               gaps.add(key);
-               for (Integer gap : gaps) {
-                       map.remove(gap);
-               }
-               return map;
-       }
-
-       void newValidationException(String msgKey, Object...args) {
-               throw new ArgumentException(_loc.get(msgKey, args), null, null, 
false);
-       }
-
        public List getResultList() {
                _em.assertNotCloseInvoked();
                Object ob = execute();
@@ -548,9 +401,17 @@
                                                throw new 
ArgumentException(_loc.get(
                                                                
"bad-query-hint-value", key, value), null,
                                                                null, false);
-                               }
-                               _query.getFetchConfiguration().setHint(key, 
value);
-                       } else
+                               }  else if 
(QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
+                    (key)) {
+                    _query.getFetchConfiguration().setHint(key, 
(Boolean)value);
+                    invalidatePreparedQuery();
+                } else if (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
+                    _query.getFetchConfiguration().setHint(key, 
(Boolean)value);
+                    ignorePreparedQuery();
+                } else {
+                    _query.getFetchConfiguration().setHint(key, value);
+                }
+            } else
                                throw new 
ArgumentException(_loc.get("bad-query-hint", key),
                                                null, null, false);
                        return this;
@@ -624,10 +485,6 @@
                                _positional = new TreeMap<Integer, Object>();
 
                        _positional.put(position, value);
-                       for (int i = 1; i < position; i++)
-                               if (!_positional.containsKey(i))
-                                       _positional.put(i, GAP_FILLER);
-
                        return this;
                } finally {
                        _query.unlock();
@@ -778,4 +635,50 @@
         throw new UnsupportedOperationException(
             "JPA 2.0 - Method not yet implemented");
     }
+    
+    
+    /**
+     * Remove this query from PreparedQueryCache. 
+     */
+    private boolean invalidatePreparedQuery() {
+        PreparedQueryCache cache = _em.getPreparedQueryCache();
+        if (cache == null)
+            return false;
+        ignorePreparedQuery();
+        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();
+        // Critical assumption: Only JPQL queries are cached and more 
+        // importantly, the identifier of the prepared query is the original
+        // JPQL String
+        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, _em.getExceptionTranslator());
+    }
+    
+    private void recordStatistics(String query, boolean usingCachedVersion) {
+        PreparedQueryCache cache = _em.getPreparedQueryCache();
+        if (cache == null)
+            return;
+        cache.getStatistics().recordExecution(query,usingCachedVersion);
+    }
+    
+    QueryImpl setId(String id) {
+        _id = id;
+        return this;
+    }
+
 }

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml
URL: 
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml?rev=739123&r1=739122&r2=739123&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml 
(original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_caching.xml Fri Jan 
30 01:27:35 2009
@@ -1035,73 +1035,61 @@
                 query sql cache
             </secondary>
         </indexterm>
-        <para>
-The query SQL cache is a <classname>Map</classname> used to cache 
-pushed-down SQL query strings for the find operation.  As a result, 
-the SQL queries are only generated once in OpenJPA, and cached thereafter.  
-This query SQL cache is shared across entity managers and the fetch plan 
-is part of the cache key. You can control the SQL cache through 
-the <link linkend="openjpa.jdbc.QuerySQLCache"><literal>
-openjpa.jdbc.QuerySQLCache</literal></link> configuration property.  This 
-property accepts a plugin string (see <xref 
linkend="ref_guide_conf_plugins"/>) 
-describing the <classname>Map</classname> used to associate query strings and 
-their parsed form.  This property accepts the following aliases:
-        </para>
-        <table>
-            <title>
-                Pre-defined aliases
-            </title>
-            <tgroup cols="2" align="left" colsep="1" rowsep="1">
-                <colspec colname="alias"/>
-                <colspec colname="value"/>
-                <colspec colname="notes"/>
-                <thead>
-                    <row>
-                        <entry colname="alias">Alias</entry>
-                        <entry colname="value">Value</entry>
-                        <entry colname="notes">Notes</entry>
-                    </row>
-                </thead>
-                <tbody>
-                    <row>
-                        <entry colname="alias">
-<literal>true</literal>
-                        </entry>
-                        <entry colname="value">
-<literal>org.apache.openjpa.util.CacheMap</literal>
-                        </entry>
-                        <entry colname="notes">
-The default option.  Uses a 
-<ulink url="../javadoc/org/apache/openjpa/util/CacheMap.html"> 
-<literal>CacheMap</literal></ulink> to store sql string.  
-<literal>CacheMap</literal> maintains a fixed number of cache entries, and an 
-optional soft reference map for entries that are moved out of the LRU space. 
-So, for applications that have a monotonically increasing number of distinct 
-queries, this option can be used to ensure that a fixed amount of memory is 
-used by the cache.
-                        </entry>
-                    </row>
-                    <row>
-                        <entry colname="alias"><literal>all</literal></entry>
-                        <entry colname="value">
-<literal>org.apache.openjpa.lib.util.ConcurrentHashMap</literal>
-                        </entry>
-                        <entry colname="notes">
-This is the fastest option, but sql string is never dropped from the 
-cache, so if you use a large number of dynamic queries, this option may result 
-in ever-increasing memory usage. Note that if your queries only differ in the 
-values of the parameters, this should not be an issue.
-                        </entry>
-                    </row>
-                    <row>
-                        <entry colname="alias"><literal>false</literal></entry>
-                        <entry 
colname="value"><emphasis>none</emphasis></entry>
-                        <entry colname="notes">
-Disables the sql cache.
-                        </entry>
-                    </row>
-                </tbody>
-            </tgroup>
-        </table>
+<para>
+The Query SQL Cache caches SQL statements corresponding to JPQL queries.
+If a JPQL query is executed more than once in the same or different persistence
+context, the SQL statement generated during the first execution is cached and
+executed directly for subsequent execution. Direct execution of SQL offers
+significant performance gain as it saves the cost of parsing query string and,
+more importantly, populating the query expression tree during every execution.
+</para>
+<para>
+The Query SQL Cache is configured by the <link 
linkend="openjpa.jdbc.QuerySQLCache">
+<literal>openjpa.QuerySQLCache</literal></link> configuration property. This
+property accepts a a plugin string (see <xref 
linkend="ref_guide_conf_plugins"/>)
+with value of <literal>true</literal> or <literal>false</literal>. 
+</para>
+<para>
+Depending upon the mapping of your persistent domain object model, 
+lazy/eager fetch settings on the relation paths and fetch configuration, a 
+single JPQL may result in multiple SQL statements. If a JPQL query requires
+more than one SQL statement to be executed, then the JPQL query is not cached.
+The other critical point to remember while using the Query SQL Cache is that
+change in fetch plan or locking mode may cause a different target SQL 
statement 
+than the original. But Query SQL Cache does not track such changes and reuses
+the cached SQL under the assumption that the query execution context has 
+remained unchanged. 
+</para>
+<para>
+Several mechanisms are available to the application to deactivate SQL cacheing
+for a query. 
+       <listitem>A user application can deactivate SQL Query Cache
+       for entire lifetime of a persistence context by invoking the following
+       method on OpenJPA's EntityMananger interface:
+       <programlisting>
+       <methodname>OpenJPAEntityManager.setQuerySQLCache(boolean)</methodname> 
+       </programlisting>
+       </listitem>
+       <listitem>
+       A user application can instruct a particular execution of a JPQL query 
to
+       ignore any cached SQL query, by setting 
+       <literal>QueryHints.HINT_IGNORE_PREPARED_QUERY</literal> or 
+       <literal>"openjpa.hint.IgnorePreparedQuery"</literal> to 
<literal>true</literal>
+       via standard <literal>javax.persistence.Query.setHints()</literal>. If a
+       SQL query has been cached corresponding to the JPQL query prior to this 
+       execution, then the cached SQL remains in the cache and will be reused
+       for any subsequent execution of the same JPQL query.
+       </listitem>
+       <listitem>
+       A user application can instruct all subsequent execution of a JPQL 
query to
+       ignore any cached SQL query, by setting 
+       <literal>QueryHints.HINT_INVALIDATE_PREPARED_QUERY</literal> or 
+       <literal>"openjpa.hint.InvalidatePreparedQuery"</literal> to 
<literal>true</literal>
+       The SQL query is removed from the cache and the JPQL query will never be
+       cached again during the lifetime of the entire persistence unit i.e.
+       EntityManagerFactory.
+       </listitem>
+</para>
+ 
     </section>
 </chapter>


Reply via email to