blob: c8ff3e7d4040212881510980d3cb30a955a70ddd [file] [log] [blame]
/*
* 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.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
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.QueryStatistics;
import org.apache.openjpa.kernel.jpql.JPQLParser;
import org.apache.openjpa.lib.jdbc.AbstractJDBCListener;
import org.apache.openjpa.lib.jdbc.JDBCEvent;
import org.apache.openjpa.lib.jdbc.JDBCListener;
import org.apache.openjpa.persistence.ArgumentException;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.OpenJPAQuery;
import org.apache.openjpa.persistence.jdbc.sqlcache.Employee.Category;
import org.apache.openjpa.persistence.test.AbstractPersistenceTestCase;
/**
* Tests correctness and performance of queries with and without Prepared Query Cache.
*
* This test uses a single EntityManagerFactory initialized with fixed set of entity classes
* and appropriate configuration parameters for Prepared Query Cache.
*
* The entity classes are specified in persistence unit <code>"PreparedQuery"</code> in a
* <code>META-INF/persistence.xml</code> that must be available to the classpath.
*
* Configures the EntityManagerFactory with properties that are relevant to testing PreparedQuery caches.
* Switches off runtime enhancement to avoid unintended consequences.
* Provides facilities to run a JPQL query with and without cache and compute the performance delta.
*
* Uses a hard-coded data set initialized once for the entire suite.
*
* @author Pinaki Poddar
*
*/
public class TestPreparedQueryCache extends AbstractPersistenceTestCase {
private static String RESOURCE = "META-INF/persistence.xml";
private static String UNIT_NAME = "PreparedQuery";
protected static final int SAMPLE_SIZE = 100; // no. of observations for performance statistics
public static final boolean USE_CACHE = true; // mnemonic for using cache
private static Object[] NO_PARAMS = null; // mnemonic for no query parameters
public static final boolean IS_NAMED_QUERY = true; // mnemonic for named query
private static boolean FAIL_IF_PERF_DEGRADE = false;
private static Company IBM;
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 Date[] EMPLOYEE_START_DATES = {new GregorianCalendar(2017, Calendar.FEBRUARY, 12).getTime(),
new GregorianCalendar(2014, Calendar.JANUARY, 1).getTime(),
new GregorianCalendar(2014, Calendar.JANUARY, 1).getTime()};
public static final Date[] EMPLOYEE_END_DATES = {new GregorianCalendar(2999, Calendar.JANUARY, 1).getTime(),
new GregorianCalendar(2999, Calendar.JANUARY, 1).getTime(),
new GregorianCalendar(2016, Calendar.DECEMBER, 31).getTime()};
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";
public static final long[] BOOK_IDS = {1000, 2000, 3000};
public static final String[] BOOK_NAMES = {"Argumentative Indian", "Tin Drum", "Blink"};
public static final long[] CD_IDS = {1001, 2001, 3001};
public static final String[] CD_LABELS = {"Beatles", "Sinatra", "Don't Rock My Boat"};
protected static OpenJPAEntityManagerFactorySPI emf;
protected static SQLAuditor auditor;
protected static int TEST_COUNT = 0;
private OpenJPAEntityManagerSPI em;
/**
* Sets up the test suite with a statically initialized EntityManagerFactory.
* Creates data once for all other tests to use.
*/
@Override
public void setUp() throws Exception {
super.setUp();
if (emf == null) {
Properties config = new Properties();
config.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true,SchemaAction='drop,add')");
config.put("openjpa.jdbc.JDBCListeners", new JDBCListener[] { auditor = new SQLAuditor()});
config.put("openjpa.jdbc.QuerySQLCache", "true(EnableStatistics=true)");
config.put("openjpa.RuntimeUnenhancedClasses", "unsupported");
config.put("openjpa.DynamicEnhancementAgent", "false");
emf = (OpenJPAEntityManagerFactorySPI)OpenJPAPersistence.createEntityManagerFactory(
UNIT_NAME, RESOURCE, config);
em = emf.createEntityManager();
createTestData();
} else {
em = emf.createEntityManager();
getPreparedQueryCache().clear();
}
TEST_COUNT++;
}
/**
* Create data for the entire test suite to use.
*/
void createTestData() {
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 (String departmentName : DEPARTMENT_NAMES) {
Department dept = new Department();
dept.setName(departmentName);
company.addDepartment(dept);
em.persist(dept);
for (int k = 0; k < EMPLOYEE_NAMES.length; k++) {
Employee emp = new Employee();
emp.setName(EMPLOYEE_NAMES[k]);
emp.setStartDate(EMPLOYEE_START_DATES[k]);
emp.setEndDate(EMPLOYEE_END_DATES[k]);
Address addr = new Address();
addr.setCity(CITY_NAMES[k]);
em.persist(emp);
em.persist(addr);
emp.setAddress(addr);
dept.addEmployees(emp);
}
}
}
Person p1 = new Person("John", "Doe", (short)45, 1964);
Person p2 = new Person("John", "Doe", (short)42, 1967);
Person p3 = new Person("Harry", "Doe", (short)12, 1995);
Person p4 = new Person("Barry", "Doe", (short)22, 1985);
em.persist(p1);
em.persist(p2);
em.persist(p3);
em.persist(p4);
Author a1 = new Author("Author1", "", (short)40, 1960);
Author a2 = new Author("Author2", "", (short)41, 1961);
Author a3 = new Author("Author3", "", (short)42, 1962);
Singer s1 = new Singer("Singer1", "", (short)21, 1991);
Singer s2 = new Singer("Singer2", "", (short)22, 1992);
long id = 100;
Book b1 = new Book("Book1");
Book b2 = new Book("Book2");
CD c1 = new CD("CD1");
CD c2 = new CD("CD2");
b1.setId(id++); b1.setTitle("title-1"); b1.setToken("LARGE");
b2.setId(id++); b2.setTitle("title-2"); b2.setToken("MEDIUM");
c1.setId(id++);
c2.setId(id++);
b1.addAuthor(a1);
b1.addAuthor(a2);
b2.addAuthor(a2);
b2.addAuthor(a3);
c1.setSinger(s1);
c2.setSinger(s2);
em.persist(a1); em.persist(a2); em.persist(a3);
em.persist(s1); em.persist(s2);
em.persist(b1); em.persist(b2);
em.persist(c1); em.persist(c2);
id = (int)System.currentTimeMillis();
OrderJPA o1 = new OrderJPA();
o1.setOrderId(id++);
o1.setCustomerId(339);
o1.setDistrictId(3);
o1.setWarehouseId(23);
OrderJPA o2 = new OrderJPA();
o2.setOrderId(id++);
o2.setCustomerId(2967);
o2.setDistrictId(5);
o2.setWarehouseId(22);
em.persist(o1);
em.persist(o2);
for (int i = 1; i < 10; i++) {
Parent parent = new Parent();
parent.setId(i);
parent.setName(new String("Parent "+i));
Address addr = new Address();
addr.setCity("Address "+i+i);
parent.setAddrId(addr);
em.persist(addr);
for (int j = 1; j < 5; j++) {
Child child = new Child();
child.setName("Child "+i+j);
child.setParent(parent);
parent.add(child);
}
em.persist(parent);
}
em.getTransaction().commit();
}
@Override
public void tearDown() throws Exception {
closeEM(em);
em = null;
if (TEST_COUNT >= 50) {
auditor.clear();
auditor = null;
closeEMF(emf);
emf = null;
}
super.tearDown();
}
public void testCollectionValuedParameterOfEntities() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql1 = "select d from Department d where d.name in ('Marketing', 'Sales') order by d.name";
String jpql2 = "select d from Department d where d.name in ('Engineering', 'Marketing') order by d.name";
List<Department> param1 = (List<Department>) em.createQuery(jpql1).getResultList();
List<Department> param2 = (List<Department>) em.createQuery(jpql2).getResultList();
em.clear();
String jpql = "select e from Employee e where e.department in :param";
List<Employee> rs1 = em.createQuery(jpql).setParameter("param", param1).getResultList();
for (Employee employee2 : rs1) {
Employee e = (Employee) employee2;
assertFalse(e.getDepartment().getName().equals("Engineering"));
}
List<Employee> rs2 = (List<Employee>) em.createQuery(jpql).setParameter("param", param2).getResultList();
for (Employee employee1 : rs2) {
Employee e = (Employee) employee1;
assertFalse(e.getDepartment().getName().equals("Sales"));
}
em.clear();
String jpql3 = "select e from Employee e where e.department in (:p1, :p2, :p3)";
Query query = em.createQuery(jpql3);
query.setParameter("p1", param1.get(0));
query.setParameter("p2", param1.get(1));
query.setParameter("p3", param1.get(2));
List<Employee> rs3 = query.getResultList();
for (Employee element : rs3) {
Employee e = (Employee) element;
assertTrue(e.getDepartment().getName().equals("Marketing"));
}
em.clear();
query = em.createQuery(jpql3);
query.setParameter("p1", param2.get(0));
query.setParameter("p2", param2.get(1));
query.setParameter("p3", param2.get(2));
List<Employee> rs4 = query.getResultList();
for (Employee employee : rs4) {
Employee e = (Employee) employee;
assertTrue(e.getDepartment().getName().equals("Engineering"));
}
em.clear();
String jpql4 = "select p from Parent p where p.id < 3";
String jpql5 = "select p from Parent p where p.id > 4";
List<Parent> parm1 = em.createQuery(jpql4).getResultList();
List<Parent> parm2 = em.createQuery(jpql5).getResultList();
assertTrue("Size of two result list " + parm1.size() + " and " + parm2.size() +
" must not be same", parm1.size() != parm2.size());
em.clear();
String jpql6 = "select c from Child c where c.parent in ?1";
Query qry = em.createQuery(jpql6);
qry.setParameter(1, parm1);
List<Child> c1 = qry.getResultList();
for (Child item : c1) {
Child child = (Child) item;
assertTrue(child.getParent().getId() < 3);
}
em.clear();
qry = em.createQuery(jpql6);
qry.setParameter(1, parm2);
List<Child> c2 = qry.getResultList();
for (Child value : c2) {
Child child = (Child) value;
assertTrue(child.getParent().getId() > 4);
}
}
public void testCollectionValuedParameterOfEntitiesWithEmptyList() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql1 =
"select d from Department d where d.name in ('Marketing', 'Sales') order by d.name";
List<Department> param1 =
(List<Department>) em.createQuery(jpql1).getResultList();
em.clear();
String jpql = "select e from Employee e where e.department in :param";
List<Employee> rs1 =
em.createQuery(jpql).setParameter("param", param1).getResultList();
for (Employee employee : rs1) {
Employee e = (Employee) employee;
assertFalse(e.getDepartment().getName().equals("Engineering"));
}
// Prior to OPENJPA-2118, the following query would yeild a
// 'ArithmeticException: divide
// by zero' exception (see JIRA for details).
try {
// Pass an empty list to 'param'.
em.createQuery(jpql).setParameter("param",
new ArrayList<Department>()).getResultList();
} catch (ArgumentException ae) {
assertEquals(ae.getCause().getMessage(),
"Input parameter \"param\" is empty.");
}
}
public void testRepeatedParameterInSubqueryInDifferentOrderSubQLast() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql = "SELECT o from OrderJPA o " +
"WHERE (o.CustomerId = :customerId) " +
"AND (o.WarehouseId = :warehouseId) " +
"AND (o.DistrictId = :districtId) " +
"AND o.OrderId IN (SELECT MAX (o1.OrderId) from OrderJPA o1 " +
"WHERE ((o1.CustomerId = :customerId) " +
"AND (o1.DistrictId = :districtId) " +
"AND (o1.WarehouseId = :warehouseId)))";
em.getTransaction().begin();
TypedQuery<OrderJPA> q1 = em.createQuery(jpql, OrderJPA.class);
q1.setParameter("customerId", 339)
.setParameter("districtId", 3)
.setParameter("warehouseId", 23);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
assertFalse(q1.getResultList().isEmpty());
TypedQuery<OrderJPA> q2 = em.createQuery(jpql, OrderJPA.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
q2.setParameter("customerId", 2967)
.setParameter("districtId", 5)
.setParameter("warehouseId", 22);
assertFalse(q2.getResultList().isEmpty());
em.getTransaction().rollback();
}
public void testPreparedQueryCacheIsActiveByDefault() {
assertNotNull(getPreparedQueryCache());
}
public void testPreparedQueryCacheIsPerUnitSingleton() {
PreparedQueryCache c1 = getPreparedQueryCache();
PreparedQueryCache c2 = getPreparedQueryCache();
assertSame(c1, c2);
}
public void testPreparedQueryIdentifierIsOriginalJPQLQuery() {
String jpql = "select p from Company p";
OpenJPAQuery<?> q1 = em.createQuery(jpql);
q1.getResultList();
PreparedQuery pq = getPreparedQueryCache().get(jpql);
assertNotNull(pq);
assertEquals(jpql, pq.getIdentifier());
assertEquals(jpql, pq.getOriginalQuery());
}
public void testOriginalJPQLQueryStringIsSetOnPreparedQuery() {
String jpql = "select p from Company p";
OpenJPAQuery<?> q1 = em.createQuery(jpql);
q1.getResultList();
PreparedQuery pq = getPreparedQueryCache().get(jpql);
assertNotNull(pq);
OpenJPAQuery<?> q2 = em.createQuery(jpql);
assertEquals(jpql,q2.getQueryString());
}
public void testOrderByElementsAbsentInProjection() {
String jpql = "select c.name from Company c ORDER BY c.startYear";
OpenJPAQuery<?> q1 = em.createQuery(jpql);
List l1 = q1.getResultList();
PreparedQuery pq = getPreparedQueryCache().get(jpql);
assertNotNull(pq);
OpenJPAQuery<?> q2 = em.createQuery(jpql);
List l2 = q2.getResultList();
assertEquals(l1.size(), l2.size());
assertEquals(l1.toString(), l2.toString());
}
public void testExclusionPattern() {
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 = getPreparedQueryCache().get(id);
assertNotNull(getPreparedQueryCache() + ": " + getPreparedQueryCache().getMapView() +
" does not contain " + id, cached);
}
void assertNotCached(String id) {
PreparedQueryCache cache = getPreparedQueryCache();
if (cache != null) {
assertNull(cache.get(id));
}
}
public void testPreparedQueryIsCachedOnExecution() {
String jpql = "select p from Company p";
OpenJPAQuery<?> q1 = em.createQuery(jpql);
assertNotCached(jpql);
assertLanguage(q1, JPQLParser.LANG_JPQL);
q1.getResultList();
assertCached(jpql);
assertLanguage(q1, JPQLParser.LANG_JPQL);
PreparedQuery cached = getPreparedQueryCache().get(jpql);
assertEquals(jpql, cached.getIdentifier());
assertFalse(jpql.equalsIgnoreCase(cached.getTargetQuery()));
}
public void testPreparedQueryIsCachedAcrossExecution() {
String jpql = "select p from Company p";
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";
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";
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() {
QueryStatistics<String> stats = getPreparedQueryCache().getStatistics();
stats.reset();
String jpql1 = "select c from Company c";
String jpql2 = "select c from Company c where c.name = 'PObject'";
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();
}
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() {
QueryStatistics<String> stats = getPreparedQueryCache().getStatistics();
stats.reset();
String jpql1 = "select c from Company c";
String jpql2 = "select c from Company c where c.name = 'PObject'";
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();
}
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();
}
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";
compare(jpql, !IS_NAMED_QUERY);
}
public void testQueryWithLiteral() {
String jpql = "select p from Company p where p.name = " + literal(COMPANY_NAMES[0]);
compare(jpql, !IS_NAMED_QUERY);
}
public void testQueryWithParameter() {
String jpql = "select p from Company p where p.name = :param";
Object[] params = {"param", COMPANY_NAMES[0]};
compare(jpql, !IS_NAMED_QUERY, 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 "
+ literal(COMPANY_NAMES[0])
+ " and e.address.city = :city";
Object[] params = { "emp", EMPLOYEE_NAMES[0],
"dept", DEPARTMENT_NAMES[0],
"city", CITY_NAMES[0]};
compare(jpql, !IS_NAMED_QUERY, params);
}
public void testNamedQueryWithNoParameter() {
String namedQuery = "Company.PreparedQueryWithNoParameter";
compare(namedQuery, IS_NAMED_QUERY);
}
public void testNamedQueryWithLiteral() {
String namedQuery = "Company.PreparedQueryWithLiteral";
compare(namedQuery, IS_NAMED_QUERY);
}
public void testNamedQueryWithPositionalParameter() {
String namedQuery = "Company.PreparedQueryWithPositionalParameter";
Object[] params = {1, COMPANY_NAMES[0], 2, START_YEARS[0]};
compare(namedQuery, IS_NAMED_QUERY, params);
}
public void testNamedQueryWithNamedParameter() {
String namedQuery = "Company.PreparedQueryWithNamedParameter";
Object[] params = {"name", COMPANY_NAMES[0], "startYear",
START_YEARS[0]};
compare(namedQuery, IS_NAMED_QUERY, params);
}
public void testPersistenceCapableParameter() {
String jpql = "select e from Employee e " +
"where e.department.company=:company";
Object[] params = {"company", IBM};
compare(jpql, !IS_NAMED_QUERY, params);
}
/**
* Project results are returned with different types of ROP.
*/
public void testProjectionResult() {
String jpql = "select e.name from Employee e " +
"where e.address.city=:city";
Object[] params = {"city", CITY_NAMES[0]};
compare(jpql, !IS_NAMED_QUERY, params);
}
public void testCollectionValuedParameters() {
String jpql = "select e from Employee e where e.name in :names";
Object[] params1 = {"names",
Arrays.asList(new String[]{EMPLOYEE_NAMES[0],
EMPLOYEE_NAMES[1]})};
Object[] params2 = {"names",
Arrays.asList(new String[]{EMPLOYEE_NAMES[2]})};
Object[] params3 = {"names", Arrays.asList(EMPLOYEE_NAMES)};
int expectedCount = 2 * COMPANY_NAMES.length * DEPARTMENT_NAMES.length;
run(jpql, !IS_NAMED_QUERY, params1, expectedCount, USE_CACHE, 1);
expectedCount = 1 * COMPANY_NAMES.length * DEPARTMENT_NAMES.length;
run(jpql, !IS_NAMED_QUERY, params2, expectedCount, USE_CACHE, 1);
expectedCount = EMPLOYEE_NAMES.length * COMPANY_NAMES.length * DEPARTMENT_NAMES.length;
run(jpql, !IS_NAMED_QUERY, params3, expectedCount, USE_CACHE, 1);
}
public void testQueryProjectionNotCandidateClass() {
String jpql = "select e.department from Employee e";
compare(jpql, !IS_NAMED_QUERY);
}
public void testQueryMultipleProjectionClass() {
String jpql = "select d, e from Department d, in (d.employees) e";
compare(jpql, !IS_NAMED_QUERY);
}
public void testQueryWithOrderByClause() {
String jpql = "select e.name from Employee e order by e.id";
compare(jpql, !IS_NAMED_QUERY);
}
public void testQueryCount() {
String jpql = "select count(e),d from Department d join d.employees e group by d";
compare(jpql, !IS_NAMED_QUERY);
}
public void testProjectRepeatsTerm() {
String jpql = "select e.name, e.name from Employee e";
compare(jpql, !IS_NAMED_QUERY);
}
public void testProjectEmbedded() {
String jpql = "select e.address from Employee e";
compare(jpql, !IS_NAMED_QUERY);
}
public void testNeedsTypeConversion() {
String jpql = "select e.name, e.isManager from Employee e";
compare(jpql, !IS_NAMED_QUERY);
}
String literal(String s) {
return "'"+s+"'";
}
public void testPositional() {
String jpql = "select p from Person p where p.firstName=?1" +
" and p.lastName='Doe' and p.age > ?2";
EntityManager em = emf.createEntityManager();
OpenJPAQuery<?> q1 = OpenJPAPersistence.cast(em.createQuery(jpql));
assertEquals(JPQLParser.LANG_JPQL, q1.getLanguage());
List<?> result1 = q1.setParameter(1, "John")
.setParameter(2, (short)40)
.getResultList();
assertEquals(2, result1.size());
OpenJPAQuery<?> q2 = OpenJPAPersistence.cast(em.createQuery(jpql));
assertEquals(QueryLanguages.LANG_PREPARED_SQL, q2.getLanguage());
List<?> result2 = q2.setParameter(1, "Harry")
.setParameter(2, (short)10)
.getResultList();
assertEquals(1, result2.size());
}
public void testNamed() {
String jpql = "select p from Person p where p.firstName=:first" +
" and p.lastName='Doe' and p.age > :age";
EntityManager em = emf.createEntityManager();
OpenJPAQuery<?> q1 = OpenJPAPersistence.cast(em.createQuery(jpql));
assertEquals(JPQLParser.LANG_JPQL, q1.getLanguage());
List<?> result1 = q1.setParameter("first", "John")
.setParameter("age", (short)40)
.getResultList();
assertEquals(2, result1.size());
OpenJPAQuery<?> q2 = OpenJPAPersistence.cast(em.createQuery(jpql));
assertEquals(QueryLanguages.LANG_PREPARED_SQL, q2.getLanguage());
List<?> result2 = q2.setParameter("first", "Barry")
.setParameter("age", (short)20)
.getResultList();
assertEquals(1, result2.size());
}
public void testWrongParameterValueTypeThrowException() {
String jpql = "select p from Person p where p.firstName=:first"
+ " and p.age > :age";
EntityManager em = emf.createEntityManager();
OpenJPAQuery<?> q1 = OpenJPAPersistence.cast(em.createQuery(jpql));
try {
List<?> result1 = q1.setParameter("first", (short)40)
.setParameter("age", "John")
.getResultList();
fail("Expected to fail with wrong parameter value");
} catch (IllegalArgumentException e) {
// good
}
}
public void testNullParameterValueForPrimitiveTypeThrowsException() {
String jpql = "select p from Person p where p.firstName=:first"
+ " and p.age > :age";
EntityManager em = emf.createEntityManager();
OpenJPAQuery<?> q1 = OpenJPAPersistence.cast(em.createQuery(jpql));
try {
List<?> result1 = q1.setParameter("first", "John")
.setParameter("age", null)
.getResultList();
fail("Expected to fail with null parameter value for primitives");
} catch (RuntimeException e) {
// good
}
}
public void testQueryWithLazyRelationIsCached() {
// Author is lazily related to Book
String jpql = "select p from Author p";
EntityManager em = emf.createEntityManager();
Query q1 = em.createQuery(jpql);
assertEquals(OpenJPAPersistence.cast(q1).getLanguage(),
JPQLParser.LANG_JPQL);
List<Author> authors1 = q1.getResultList();
assertFalse(authors1.isEmpty());
Author author1 = authors1.iterator().next();
em.close(); // nothing will be loaded by chance
assertNull(author1.getBooks());
// do the same thing again, this time query should be cached
em = emf.createEntityManager();
Query q2 = em.createQuery(jpql);
assertEquals(OpenJPAPersistence.cast(q2).getLanguage(),
QueryLanguages.LANG_PREPARED_SQL);
List<Author> authors2 = q2.getResultList();
assertFalse(authors2.isEmpty());
Author author2 = authors2.iterator().next();
em.close();
assertNull(author2.getBooks());
}
public void testQueryWithEagerRelationIsNotCached() {
// Book is eagerly related to Author
String jpql = "select b from Book b";
EntityManager em = emf.createEntityManager();
Query q1 = em.createQuery(jpql);
assertEquals(OpenJPAPersistence.cast(q1).getLanguage(),
JPQLParser.LANG_JPQL);
List<Book> books = q1.getResultList();
assertFalse(books.isEmpty());
Book book1 = books.iterator().next();
em.close(); // nothing will be loaded by chance
assertNotNull(book1.getAuthors());
assertFalse(book1.getAuthors().isEmpty());
// do the same thing again, this time query should not be cached
// because it requires multiple selects
em = emf.createEntityManager();
Query q2 = em.createQuery(jpql);
assertEquals(OpenJPAPersistence.cast(q2).getLanguage(),
JPQLParser.LANG_JPQL);
List<Book> books2 = q2.getResultList();
assertFalse(books2.isEmpty());
Book book2 = books2.iterator().next();
em.close();
assertNotNull(book2.getAuthors());
assertFalse(book2.getAuthors().isEmpty());
}
public void testQueryWithUserDefinedAndInternalParamtersInSubquery() {
String jpql = "Select a From Address a Where Not Exists ("
+ " Select s.id From Singer As s Where "
+ " s.address = a And "
+ " Not ("
+ " (s.firstName = :firstName) "
+ " Or "
+ " ("
+ " ("
+ " exists (select c.id from CD c where c.singer = s and c.status = 1) And "
+ " s.lastName = :lastName"
+ " ) "
+ " Or "
+ " ("
+ " not exists (Select c.id from CD c where c.singer = s and c.status = 2)"
+ " )"
+ " )"
+ " )"
+ " )";
Query jQ = em.createQuery(jpql);
jQ.setParameter("lastName", "LastName");
jQ.setParameter("firstName", "FirstName");
List jList = jQ.getResultList();
Query jQ1 = em.createQuery(jpql);
jQ1.setParameter("lastName", "LastName1");
jQ1.setParameter("firstName", "FirstName1");
try {
List jList1 = jQ1.getResultList();
} catch (Exception e) {
System.err.println(jQ1.getParameters());
e.printStackTrace();
fail("Fail to execute again - Parameters are messed up:" + e.getMessage());
}
}
public void testPreparedQueryIgnoredWhenLockModeIsSet() {
String jpql = "select p from Author p";
EntityManager em = emf.createEntityManager();
Query q1 = em.createQuery(jpql);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
List<Author> authors1 = q1.getResultList();
// do the same thing again, this time query should be cached
em.getTransaction().begin();
Query q2 = em.createQuery(jpql);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
LockModeType lmode1 = q2.getLockMode();
q2.setLockMode(LockModeType.OPTIMISTIC);
LockModeType lmode2 = q2.getLockMode();
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q2).getLanguage());
assertFalse(lmode1.equals(lmode2));
List<Author> authors2 = q2.getResultList();
em.getTransaction().rollback();
}
public void testEnumParameter() {
String jpql = "select e from Employee e where e.status=:current and e.hireStatus=:hire";
EntityManager em = emf.createEntityManager();
TypedQuery<Employee> q1 = em.createQuery(jpql, Employee.class);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
List<Employee> emps = q1.setParameter("current", Category.PERMANENT)
.setParameter("hire", Category.CONTRACTOR).getResultList();
// do the same thing again, this time query should be cached
em.getTransaction().begin();
TypedQuery<Employee> q2 = em.createQuery(jpql, Employee.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
List<Employee> emps2 = q2.setParameter("current", Category.PERMANENT)
.setParameter("hire", Category.CONTRACTOR).getResultList();
em.getTransaction().rollback();
}
public void testMultithreadedAccess() {
OpenJPAEntityManager em1 = emf.createEntityManager();
String jpql = "select p from Author p where p.name=:name";
int N = 5;
Thread[] threads = new Thread[N];
QueryThread[] qts = new QueryThread[N];
for (int i = 0; i < N; i++) {
OpenJPAEntityManager emt = emf.createEntityManager();
qts[i] = new QueryThread(emt, jpql);
threads[i] = new Thread(qts[i]);
threads[i].setDaemon(true);
}
for (Thread t : threads) {
t.start();
}
for (int i = 0; i < N; i++) {
try {
threads[i].join();
assertFalse(qts[i].isFailed());
} catch (InterruptedException e) {
e.printStackTrace();
fail();
}
}
}
public void testParameterOnExternalizedFieldIsExcluded() {
String jpql = "select b from Book b where b.title=:title and b.token=:token";
Query q1 = em.createQuery(jpql)
.setParameter("title", "title-1")
.setParameter("token", "LARGE");
// default fetches authors eagerly and thus creates multiple SQL and hence not caches anyway
OpenJPAPersistence.cast(q1).getFetchPlan().removeFetchGroup("default");
assertFalse(q1.getResultList().isEmpty());
assertNotCached(jpql);
Query q2 = em.createQuery(jpql)
.setParameter("title", "title-2")
.setParameter("token", "MEDIUM");
assertFalse(q2.getResultList().isEmpty());
}
public void testNoParameterOnExternalizedFieldIsIncluded() {
String jpql = "select b from Book b where b.title=:title";
Query q1 = em.createQuery(jpql)
.setParameter("title", "title-1");
// default fetches authors eagerly and thus creates multiple SQL and hence not caches anyway
OpenJPAPersistence.cast(q1).getFetchPlan().removeFetchGroup("default");
assertFalse(q1.getResultList().isEmpty());
assertCached(jpql);
Query q2 = em.createQuery(jpql)
.setParameter("title", "title-2");
assertFalse(q2.getResultList().isEmpty());
}
public void testSubqueryParameters() {
EntityManager em = emf.createEntityManager();
String query = "select e from Employee e "
+ "inner join e.department d "
+ "inner join d.company c "
+ "where mod(c.startYear, 100) = 0 "
+ "and exists (select e2 from Employee e2 "
+ "inner join e2.department d2 "
+ "inner join d2.company c2 "
+ "where e2.address.city = e.address.city "
+ "and e2.isManager = false "
+ "and d2.name = d.name "
+ "and c2.name = :companyName) "
+ "and d.name = :departmentName";
em.getTransaction().begin();
TypedQuery<Employee> q1 = em.createQuery(query, Employee.class);
q1.setParameter("companyName", "acme.org");
q1.setParameter("departmentName", "Engineering");
assertEquals(q1.getResultList().size(), 6);
TypedQuery<Employee> q2 = em.createQuery(query, Employee.class);
q2.setParameter("companyName", "acme.org");
q2.setParameter("departmentName", "Engineering");
assertEquals(q2.getResultList().size(), 6);
em.getTransaction().rollback();
}
public void testRepeatedParameterInSubqueryInDifferentOrder() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql = "select o from OrderJPA o "
+ "where o.OrderId in (select max(o1.OrderId) from OrderJPA o1 "
+ "where ((o1.CustomerId = :customerId) "
+ "and (o1.DistrictId = :districtId) "
+ "and (o1.WarehouseId = :warehouseId))) "
+ "and (o.CustomerId = :customerId) "
+ "and (o.WarehouseId = :warehouseId) "
+ "and (o.DistrictId = :districtId)";
em.getTransaction().begin();
TypedQuery<OrderJPA> q1 = em.createQuery(jpql, OrderJPA.class);
q1.setParameter("customerId", 339)
.setParameter("districtId", 3)
.setParameter("warehouseId", 23);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
assertFalse(q1.getResultList().isEmpty());
TypedQuery<OrderJPA> q2 = em.createQuery(jpql, OrderJPA.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
q2.setParameter("customerId", 2967)
.setParameter("districtId", 5)
.setParameter("warehouseId", 22);
assertFalse(q2.getResultList().isEmpty());
em.getTransaction().rollback();
}
public void testRepeatedParameterInSubqueryInSameOrder() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql = "select o from OrderJPA o "
+ "where o.OrderId in (select max(o1.OrderId) from OrderJPA o1 "
+ "where ((o1.CustomerId = :customerId) "
+ "and (o1.DistrictId = :districtId) "
+ "and (o1.WarehouseId = :warehouseId))) "
+ "and (o.CustomerId = :customerId) "
+ "and (o.DistrictId = :districtId) "
+ "and (o.WarehouseId = :warehouseId)";
em.getTransaction().begin();
TypedQuery<OrderJPA> q1 = em.createQuery(jpql, OrderJPA.class);
q1.setParameter("customerId", 339)
.setParameter("districtId", 3)
.setParameter("warehouseId", 23);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
assertFalse(q1.getResultList().isEmpty());
TypedQuery<OrderJPA> q2 = em.createQuery(jpql, OrderJPA.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
q2.setParameter("customerId", 2967)
.setParameter("districtId", 5)
.setParameter("warehouseId", 22);
assertFalse(q2.getResultList().isEmpty());
em.getTransaction().rollback();
}
public void testPartiallyRepeatedParameterInSubquery() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql = "select o from OrderJPA o "
+ "where o.OrderId in (select max(o1.OrderId) from OrderJPA o1 "
+ "where ((o1.CustomerId = :customerId) "
+ "and (o1.WarehouseId = :warehouseId))) "
+ "and (o.CustomerId = :customerId) "
+ "and (o.DistrictId = :districtId) "
+ "and (o.WarehouseId = :warehouseId)";
em.getTransaction().begin();
TypedQuery<OrderJPA> q1 = em.createQuery(jpql, OrderJPA.class);
q1.setParameter("customerId", 339)
.setParameter("districtId", 3)
.setParameter("warehouseId", 23);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
assertFalse(q1.getResultList().isEmpty());
TypedQuery<OrderJPA> q2 = em.createQuery(jpql, OrderJPA.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
q2.setParameter("customerId", 2967)
.setParameter("districtId", 5)
.setParameter("warehouseId", 22);
assertFalse(q2.getResultList().isEmpty());
em.getTransaction().rollback();
}
public void testPartiallyRepeatedParameterInMainquery() {
OpenJPAEntityManager em = emf.createEntityManager();
String jpql = "select o from OrderJPA o "
+ "where o.OrderId in (select max(o1.OrderId) from OrderJPA o1 "
+ "where ((o1.CustomerId = :customerId) "
+ "and (o1.DistrictId = :districtId) "
+ "and (o1.WarehouseId = :warehouseId))) "
+ "and (o.CustomerId = :customerId) "
+ "and (o.WarehouseId = :warehouseId)";
em.getTransaction().begin();
TypedQuery<OrderJPA> q1 = em.createQuery(jpql, OrderJPA.class);
q1.setParameter("customerId", 339)
.setParameter("districtId", 3)
.setParameter("warehouseId", 23);
assertEquals(JPQLParser.LANG_JPQL, OpenJPAPersistence.cast(q1).getLanguage());
assertFalse(q1.getResultList().isEmpty());
TypedQuery<OrderJPA> q2 = em.createQuery(jpql, OrderJPA.class);
assertEquals(QueryLanguages.LANG_PREPARED_SQL, OpenJPAPersistence.cast(q2).getLanguage());
q2.setParameter("customerId", 2967)
.setParameter("districtId", 5)
.setParameter("warehouseId", 22);
assertFalse(q2.getResultList().isEmpty());
em.getTransaction().rollback();
}
public void testRangeIsExcluded() {
List<Company> l = null;
l = getAllCompaniesPaged(0, 1);
assertEquals(1, l.size());
assertEquals(1900, l.get(0).getStartYear());
l = getAllCompaniesPaged(1, 1);
assertEquals(1, l.size());
assertEquals(2000, l.get(0).getStartYear());
l = getAllCompaniesPaged(2, 1);
assertEquals(1, l.size());
assertEquals(2010, l.get(0).getStartYear());
}
public List<Company> getAllCompaniesPaged(int start, int max) {
EntityManager em = emf.createEntityManager();
Query q = em.createQuery("select p from Company p order by p.startYear");
q.setFirstResult(start);
q.setMaxResults(max);
return (List<Company>) q.getResultList();
}
PreparedQueryCache getPreparedQueryCache() {
return emf.getConfiguration().getQuerySQLCacheInstance();
}
/**
* Compare the result of execution of the non-parameterized query with and without PreparedQuery cache.
*
* @param jpql a String representing either a JPQL or the name of a NamedQuery
* @param isNamedQuery flags if the first input represents a JPQL string or a NamedQuery name
*/
void compare(String jpql, boolean isNamed) {
compare(jpql, isNamed, -1, NO_PARAMS);
}
/**
* Compare the result of execution of the non-parameterized query with and without PreparedQuery cache.
*
* @param jpql a String representing either a JPQL or the name of a NamedQuery
* @param isNamedQuery flags if the first input represents a JPQL string or a NamedQuery name
* @param expectedCount expected number of results
*/
void compare(String jpql, boolean isNamed, int expectedCount) {
compare(jpql, isNamed, expectedCount, NO_PARAMS);
}
/**
* Compare the result of execution of the query with and without PreparedQuery cache.
*
*
* @param jpql a String representing either a JPQL or the name of a NamedQuery
* @param isNamedQuery flags if the first input represents a JPQL string or a NamedQuery name
* @param params a even sized array whose even-indexed elements are keys and odd-indexed elements are corresponding
* parameter value for the query. A null array denotes the query is not parameterized.
*/
void compare(String jpql, boolean isNamed, Object... params) {
compare(jpql, isNamed, -1, params);
}
/**
* Compare the result of execution of the query with and without PreparedQuery cache.
*
*
* @param jpql a String representing either a JPQL or the name of a NamedQuery
* @param isNamedQuery flags if the first input represents a JPQL string or a NamedQuery name
* @param params a even sized array whose even-indexed elements are keys and odd-indexed elements are corresponding
* parameter value for the query. A null array denotes the query is not parameterized.
* @param expectedCount expected number of results. Supply a negative number to ignore.
*/
void compare(String query, boolean isNamed, int expectedCount, Object... params) {
run(query, isNamed, params, expectedCount, !USE_CACHE, 1); // run the query once for warming up
// run N times without cache
auditor.clear();
long without = run(query, isNamed, params, expectedCount, !USE_CACHE, SAMPLE_SIZE);
List<String> originalSQLs = auditor.getSQLs();
// run N times with cache
auditor.clear();
long with = run(query, isNamed, params, expectedCount, USE_CACHE, SAMPLE_SIZE);
List<String> cachedSQLs = auditor.getSQLs();
compareSQLs(originalSQLs, cachedSQLs);
long delta = (without == 0) ? 0 : (without - with) * 100 / without;
String jpql = getJPQLString(query, isNamed);
System.err.println((delta < 0 ? "***WARN " : "") + Math.abs(delta) + "% " +
(delta < 0 ? "degradtion" : "improvement") + " for ["+ jpql + "]");
assertTrue(Math.abs(delta) + "% degradtion for ["+ jpql + "]", !FAIL_IF_PERF_DEGRADE || delta > 0);
}
void compareSQLs(List<String> a, List<String> b) {
assertEquals(a.size(), b.size());
for (int i = 0; i < a.size(); i++) {
assertEquals(a.get(i), b.get(i));
}
}
/**
* Create a query from the given string and execute it for given number of times.
*
* @param jpql a String representing either a JPQL or the name of a NamedQuery
* @param isNamedQuery flags if the first input represents a JPQL string or a NamedQuery name
* @param params a even sized array whose even-indexed elements are keys and odd-indexed elements are corresponding
* parameter value for the query. A null array denotes the query is not parameterized.
* @param expectedCount expected number of results. Supply a negative number to ignore.
* @param useCache flags if the PreparedQuery cache is to be activated.
* @param N number of times the query is to be executed to calculate reasonable statistics.
*
* @return median time to execute a query and iterate through its results
*/
long run(String jpql, boolean isNamedQuery, Object[] params, int expectedCount, boolean useCache, int N) {
List<Long> stats = new ArrayList<>();
String cacheKey = getJPQLString(jpql, isNamedQuery);
QueryStatistics<String> cacheStats = getPreparedQueryCache().getStatistics();
getPreparedQueryCache().clear();
assertEquals(0, cacheStats.getExecutionCount(cacheKey));
assertEquals(0, cacheStats.getHitCount(cacheKey));
for (int i = 0; i < N; i++) {
OpenJPAEntityManagerSPI em = (OpenJPAEntityManagerSPI)emf.createEntityManager();
em.setQuerySQLCache(useCache);
assertEquals(useCache, em.getQuerySQLCache());
// measure time
long start = System.nanoTime();
OpenJPAQuery<?> q = isNamedQuery ? em.createNamedQuery(jpql) : em.createQuery(jpql);
parameterize(q, params);
List<?> list = q.getResultList();
if (expectedCount >= 0)
assertEquals(expectedCount, list.size());
else
assertFalse(list.isEmpty());
iterate(list);
long end = System.nanoTime();
assertEquals(useCache ? i+1 : 0, cacheStats.getExecutionCount(cacheKey));
assertEquals(useCache ? i : 0, cacheStats.getHitCount(cacheKey));
q.closeAll();
stats.add(end - start);
em.close();
}
assertEquals("Execution Count [" + cacheKey + "]", useCache ? N : 0, cacheStats.getTotalExecutionCount());
assertEquals("Hit Count [" + cacheKey + "]", useCache ? N-1 : 0, cacheStats.getTotalHitCount());
Collections.sort(stats);
return stats.get(N/2);
}
public void testRepeatedQueryInBetweenParameters() {
OpenJPAEntityManager em = emf.createEntityManager();
em.clear();
String jpql1 = "SELECT e FROM Employee e";
int employeeSize = em.createQuery(jpql1, Employee.class).getResultList().size();
String jpql2 = "SELECT e FROM Employee e WHERE :baseDate between e.startDate AND e.endDate";
TypedQuery<Employee> q1 = em.createQuery(jpql2, Employee.class);
q1.setParameter("baseDate", new GregorianCalendar(2016, Calendar.JUNE, 1).getTime());
int count2016 = (int)(employeeSize * ((double)2/3));
assertEquals(count2016, q1.getResultList().size());
TypedQuery<Employee> q2 = em.createQuery(jpql2, Employee.class);
q2.setParameter("baseDate", new GregorianCalendar(2017, Calendar.JUNE, 1).getTime());
int count2017 = (int)(employeeSize * ((double)2/3));
assertEquals(count2017, q2.getResultList().size());
}
void parameterize(Query q, Object[] params) {
if (params == null)
return;
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
fail("key " + key + " is neither Number nor String");
}
}
void iterate(List<?> list) {
Iterator<?> i = list.iterator();
while (i.hasNext())
i.next();
}
/**
* Gets the JPQL String of a NamedQuery of the given name.
*/
String getJPQLString(String name, boolean isNamedQuery) {
if (!isNamedQuery)
return name;
return emf.getConfiguration()
.getMetaDataRepositoryInstance()
.getQueryMetaData(null, name, null, true)
.getQueryString();
}
/**
* A JDBC Listener to audit target SQL executed per JPQL query.
*
*/
public class SQLAuditor extends AbstractJDBCListener {
private List<String> sqls = new ArrayList<>();
@Override
public void beforeExecuteStatement(JDBCEvent event) {
if (event.getSQL() != null && sqls != null) {
sqls.add(event.getSQL());
}
}
void clear() {
sqls.clear();
}
List<String> getSQLs() {
return new ArrayList<>(sqls);
}
}
public static class QueryThread implements Runnable {
private final OpenJPAEntityManager em;
private final String jpql;
private boolean failed = false;
public QueryThread(OpenJPAEntityManager em, String jpql) {
super();
this.em = em;
this.jpql = jpql;
}
@Override
public void run() {
try {
for (int i = 0; i < 10 && !failed; i++) {
OpenJPAQuery q = em.createQuery(jpql);
q.setParameter("name", "Author-"+i);
q.getResultList();
if (i > 1)
assertEquals(QueryLanguages.LANG_PREPARED_SQL, q.getLanguage());
}
} catch (Exception e) {
e.printStackTrace();
failed = true;
}
}
public boolean isFailed() {
return failed;
}
}
}