Author: ppoddar
Date: Sat Jan 31 19:37:01 2009
New Revision: 739589
URL: http://svn.apache.org/viewvc?rev=739589&view=rev
Log:
OPENJPA-703: Support PersistenceCapable as query parameter during
reparameterization of Prepared Query.
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java?rev=739589&r1=739588&r2=739589&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java
Sat Jan 31 19:37:01 2009
@@ -24,11 +24,17 @@
import java.util.List;
import java.util.Map;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.MappingRepository;
+import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.jdbc.sql.SelectExecutor;
+import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.kernel.PreparedQuery;
import org.apache.openjpa.kernel.Query;
import org.apache.openjpa.lib.rop.ResultList;
+import org.apache.openjpa.util.ImplHelper;
+import org.apache.openjpa.util.InternalException;
/**
* Implements {...@link PreparedQuery} for SQL queries.
@@ -138,9 +144,9 @@
* must be compatible with the user parameters extracted during
* {...@link #initialize(Object) initialization}.
*
- * @return key index starting from 1 and corresponding values.
+ * @return 0-based parameter index mapped to corresponding values.
*/
- public Map<Integer, Object> reparametrize(Map user) {
+ public Map<Integer, Object> reparametrize(Map user, Broker broker) {
Map<Integer, Object> result = new HashMap<Integer, Object>();
for (int i = 0; i < _params.size(); i++) {
result.put(i, _params.get(i));
@@ -151,13 +157,54 @@
int[] indices = _userParamPositions.get(key);
if (indices == null)
continue;
- for (int j : indices)
- result.put(j, user.get(key));
+ Object value = user.get(key);
+ if (ImplHelper.isManageable(value)) {
+ setPersistenceCapableParameter(result, value, indices, broker);
+ } else {
+ for (int j : indices)
+ result.put(j, value);
+ }
}
return result;
}
/**
+ * Calculate primary key identity value(s) of the given managable instance
+ * and fill in the given map.
+ *
+ * @param values a map of integer parameter index to parameter value
+ * @param pc a manageable instance
+ * @param indices the indices of the column values
+ * @param broker used to obtain the primary key values
+ */
+ private void setPersistenceCapableParameter(Map<Integer,Object> result,
+ Object pc, int[] indices, Broker broker) {
+ JDBCStore store = (JDBCStore)broker.getStoreManager()
+ .getInnermostDelegate();
+ MappingRepository repos = store.getConfiguration()
+ .getMappingRepositoryInstance();
+ ClassMapping mapping = repos.getMapping(pc.getClass(),
+ broker.getClassLoader(), true);
+ Column[] pks = mapping.getPrimaryKeyColumns();
+ Object cols = mapping.toDataStoreValue(pc, pks, store);
+ if (cols instanceof Object[]) {
+ Object[] array = (Object[])cols;
+ int n = array.length;
+ if (n > indices.length || indices.length%n != 0)
+ throw new InternalException();
+ int k = 0;
+ for (int j : indices) {
+ result.put(j, array[k%n]);
+ k++;
+ }
+ } else {
+ for (int j : indices) {
+ result.put(j, cols);
+ }
+ }
+ }
+
+ /**
* Marks the positions of user parameters.
*
* @param list odd elements are numbers representing the position of a
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java?rev=739589&r1=739588&r2=739589&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PreparedQuery.java
Sat Jan 31 19:37:01 2009
@@ -93,6 +93,6 @@
* @param user the map of parameter key and value set by the user on the
* original query.
*/
- public Map reparametrize(Map user);
+ public Map reparametrize(Map user, Broker broker);
}
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java?rev=739589&r1=739588&r2=739589&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Company.java
Sat Jan 31 19:37:01 2009
@@ -25,10 +25,14 @@
@Entity
@NamedQueries({
- @NamedQuery(name="Company.PreparedQueryWithNoParameter", query="select
x from Company x"),
- @NamedQuery(name="Company.PreparedQueryWithNamedParameter",
query="select x from Company x where x.name=:name and x.startYear=:startYear"),
- @NamedQuery(name="Company.PreparedQueryWithPositionalParameter",
query="select x from Company x where x.name=?1 and x.startYear=?2"),
- @NamedQuery(name="Company.PreparedQueryWithLiteral", query="select x
from Company x where x.name='X' and x.startYear=1960")
+ @NamedQuery(name="Company.PreparedQueryWithNoParameter",
+ query="select x from Company x"),
+ @NamedQuery(name="Company.PreparedQueryWithNamedParameter",
+ query="select x from Company x where x.name=:name and
x.startYear=:startYear"),
+ @NamedQuery(name="Company.PreparedQueryWithPositionalParameter",
+ query="select x from Company x where x.name=?1 and x.startYear=?2"),
+ @NamedQuery(name="Company.PreparedQueryWithLiteral",
+ query="select x from Company x where x.name='IBM' and
x.startYear=1900")
})
public class Company {
@Id
Modified:
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=739589&r1=739588&r2=739589&view=diff
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
(original)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java
Sat Jan 31 19:37:01 2009
@@ -20,8 +20,11 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import javax.persistence.EntityManager;
+
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.kernel.PreparedQuery;
import org.apache.openjpa.kernel.QueryHints;
@@ -33,6 +36,7 @@
import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.test.SQLListenerTestCase;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
@@ -42,33 +46,65 @@
* @author Pinaki Poddar
*
*/
-public class TestPreparedQueryCache extends SingleEMFTestCase {
+public class TestPreparedQueryCache extends SQLListenerTestCase {
// Fail if performance degrades with cache compared to without cache
- public static final boolean FAIL_ON_PERF_DEGRADE =
- Boolean.getBoolean("FailOnPerformanceRegression");
+ 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[] COMPANY_NAMES = {"IBM", "BEA", "acme.org" };
+ public static final int[] START_YEARS = {1900, 2000, 2010 };
public static final String[] DEPARTMENT_NAMES = { "Marketing", "Sales",
"Engineering" };
public static final String[] EMPLOYEE_NAMES = { "Tom", "Dick", "Harray"
};
+ public static final String[] CITY_NAMES = {"Tulsa", "Durban", "Harlem"};
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";
+ private Company IBM;
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')");
+ createTestData();
+ }
+
+ void createTestData() {
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ for (int i = 0; i < COMPANY_NAMES.length; i++) {
+ Company company = new Company();
+ if (i == 0)
+ IBM = company;
+ company.setName(COMPANY_NAMES[i]);
+ company.setStartYear(START_YEARS[i]);
+ em.persist(company);
+ for (int j = 0; j < DEPARTMENT_NAMES.length; j++) {
+ Department dept = new Department();
+ dept.setName(DEPARTMENT_NAMES[j]);
+ company.addDepartment(dept);
+ em.persist(dept);
+ for (int k = 0; k < EMPLOYEE_NAMES.length; k++) {
+ Employee emp = new Employee();
+ emp.setName(EMPLOYEE_NAMES[k]);
+ Address addr = new Address();
+ addr.setCity(CITY_NAMES[k]);
+ em.persist(emp);
+ em.persist(addr);
+ emp.setAddress(addr);
+ dept.addEmployees(emp);
+ }
+ }
+ }
+ em.getTransaction().commit();
}
public void tearDown() throws Exception {
@@ -323,56 +359,60 @@
public void testQueryWithNoParameter() {
String jpql = "select p from Company p";
Object[] params = null;
- compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES,
params);
+ compare(!IS_NAMED_QUERY, jpql, COMPANY_NAMES.length, params);
}
public void testQueryWithLiteral() {
- String jpql = "select p from Company p where p.name =
'PObject'";
+ String jpql = "select p from Company p where p.name = " +
literal(COMPANY_NAMES[0]);
Object[] params = null;
- compare(!IS_NAMED_QUERY, jpql, BIND_DIFFERENT_PARM_VALUES,
params);
+ compare(!IS_NAMED_QUERY, jpql, 1, 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);
+ Object[] params = {"param", COMPANY_NAMES[0]};
+ compare(!IS_NAMED_QUERY, jpql, 1, 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);
+ String jpql = "select e from Employee e " + "where e.name =
:emp"
+ + " and e.department.name = :dept"
+ + " and e.department.company.name LIKE
" + literal(COMPANY_NAMES[0])
+ + " and e.address.city = :city";
+ Object[] params = { "emp", EMPLOYEE_NAMES[0],
+ "dept",
DEPARTMENT_NAMES[0],
+ "city", CITY_NAMES[0]};
+ compare(!IS_NAMED_QUERY, jpql, 1, params);
}
public void testNamedQueryWithNoParameter() {
String namedQuery = "Company.PreparedQueryWithNoParameter";
Object[] params = null;
- compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES,
params);
+ compare(IS_NAMED_QUERY, namedQuery, COMPANY_NAMES.length,
params);
}
public void testNamedQueryWithLiteral() {
String namedQuery = "Company.PreparedQueryWithLiteral";
Object[] params = null;
- compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES,
params);
+ compare(IS_NAMED_QUERY, namedQuery, 1, params);
}
public void testNamedQueryWithPositionalParameter() {
String namedQuery =
"Company.PreparedQueryWithPositionalParameter";
- Object[] params = {1, "x", 2, 1960};
- compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES,
params);
+ Object[] params = {1, COMPANY_NAMES[0], 2, START_YEARS[0]};
+ compare(IS_NAMED_QUERY, namedQuery, 1, params);
}
public void testNamedQueryWithNamedParameter() {
String namedQuery = "Company.PreparedQueryWithNamedParameter";
- Object[] params = {"name", "x", "startYear", 1960};
- compare(IS_NAMED_QUERY, namedQuery, BIND_DIFFERENT_PARM_VALUES,
params);
+ Object[] params = {"name", COMPANY_NAMES[0], "startYear",
START_YEARS[0]};
+ compare(IS_NAMED_QUERY, namedQuery, 1, params);
+ }
+
+ public void testPersistenceCapableParameter() {
+ String jpql = "select e from Employee e where
e.department.company=:company";
+ Object[] params = {"company", IBM};
+ compare(!IS_NAMED_QUERY, jpql,
EMPLOYEE_NAMES.length*DEPARTMENT_NAMES.length, params);
}
/**
@@ -380,36 +420,36 @@
* Prepared Query Cache.
*
*/
- void compare(boolean isNamed, String jpql, boolean append, Object...
params) {
+ void compare(boolean isNamed, String jpql, int expectedCount, Object...
params) {
String realJPQL = isNamed ? getJPQL(jpql) : jpql;
// run the query once for warming up
- run(jpql, params, !USE_CACHE, 1, isNamed, append);
+ run(jpql, params, !USE_CACHE, 1, isNamed, expectedCount);
// run N times without cache
- long without = run(jpql, params, !USE_CACHE, SAMPLE_SIZE,
isNamed, append);
+ long without = run(jpql, params, !USE_CACHE, SAMPLE_SIZE,
isNamed, expectedCount);
assertNotCached(realJPQL);
// run N times with cache
- long with = run(jpql, params, USE_CACHE, SAMPLE_SIZE, isNamed,
append);
+ long with = run(jpql, params, USE_CACHE, SAMPLE_SIZE, isNamed,
expectedCount);
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
+ log("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);
+ log("JPQL: " + realJPQL);
+ log("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." +
+ log("*** WARN: Perforamce regression with
cache." +
" Execution time degrades by " + delta
+ "%");
} else {
- System.err.println("change in execution time = +" + delta +
"%");
+ log("change in execution time = +" + delta + "%");
}
}
@@ -421,12 +461,14 @@
* 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());
+ boolean isNamedQuery, int expectedCount) {
+ trace("Executing " + N + " times " + (useCache ? " with " :
"without") + " cache");
List<Long> stats = new ArrayList<Long>();
+ sql.clear();
for (int i = 0; i < N; i++) {
+ OpenJPAEntityManager em = emf.createEntityManager();
+ ((OpenJPAEntityManagerSPI)em).setQuerySQLCache(useCache);
+ assertEquals(useCache,
((OpenJPAEntityManagerSPI)em).getQuerySQLCache());
long start = System.nanoTime();
OpenJPAQuery q = isNamedQuery
? em.createNamedQuery(jpql) :
em.createQuery(jpql);
@@ -438,15 +480,26 @@
else if (key instanceof String)
q.setParameter(key.toString(), val);
else
- throw new RuntimeException("key " + key
+ " is neither Number nor String");
+ fail("key " + key + " is neither Number
nor String");
}
- q.getResultList();
+ List list = q.getResultList();
+ walk(list);
+ int actual = list.size();
q.closeAll();
+ assertEquals(expectedCount, actual);
long end = System.nanoTime();
stats.add(end - start);
+ em.close();
}
- em.close();
- Collections.sort(stats);
+ if (useCache) {
+ String cacheKey = isNamedQuery ? getJPQL(jpql) : jpql;
+ long total =
getCache().getStatistics().getExecutionCount(cacheKey);
+ long hits = getCache().getStatistics().getHitCount(cacheKey);
+ assertEquals(N, total);
+ assertEquals(N-1, hits);
+ }
+ assertEquals(N, sql.size());
+ Collections.sort(stats);
return stats.get(N/2);
}
@@ -468,28 +521,23 @@
.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");
+
+ String literal(String s) {
+ return "'"+s+"'";
+ }
+
+ void log(String s) {
+ System.err.println(s);
+ }
+
+ void trace(String s) {
+ if (Boolean.getBoolean("trace"))
+ System.err.println(s);
+ }
+
+ void walk(List list) {
+ Iterator i = list.iterator();
+ while (i.hasNext())
+ i.next();
}
-
}
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=739589&r1=739588&r2=739589&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
Sat Jan 31 19:37:01 2009
@@ -265,7 +265,7 @@
QueryStatistics stats = cache.getStatistics();
if (alreadyCached && LANG_PREPARED_SQL.equals(lang)) {
PreparedQuery pq = _em.getPreparedQuery(_id);
- params = pq.reparametrize(params);
+ params = pq.reparametrize(params, _em.getBroker());
stats.recordExecution(pq.getOriginalQuery(),
alreadyCached);
} else {
stats.recordExecution(_query.getQueryString(), alreadyCached);