blob: c5d08f5b9bdfc59a7dfe0ded8dfbe80a66268ee2 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.jpa.jpql;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.NoResultException;
import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.PessimisticLockException;
import jakarta.persistence.Query;
import jakarta.persistence.EntityManager;
import jakarta.persistence.RollbackException;
import jakarta.persistence.TypedQuery;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.persistence.annotations.BatchFetchType;
import org.eclipse.persistence.config.CacheUsage;
import org.eclipse.persistence.config.PessimisticLock;
import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.config.QueryType;
import org.eclipse.persistence.config.ResultSetConcurrency;
import org.eclipse.persistence.config.ResultSetType;
import org.eclipse.persistence.config.ResultType;
import org.eclipse.persistence.descriptors.invalidation.DailyCacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.invalidation.TimeToLiveCacheInvalidationPolicy;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaQuery;
import org.eclipse.persistence.queries.Cursor;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.queries.ScrollableCursor;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.testing.framework.QuerySQLTracker;
import org.eclipse.persistence.testing.models.jpa.inheritance.Engineer;
import org.eclipse.persistence.testing.models.jpa.inheritance.InheritancePopulator;
import org.eclipse.persistence.testing.models.jpa.inheritance.InheritanceTableCreator;
import org.eclipse.persistence.testing.models.jpa.advanced.Buyer;
import org.eclipse.persistence.testing.models.jpa.advanced.Department;
import org.eclipse.persistence.testing.models.jpa.advanced.Employee;
import org.eclipse.persistence.testing.models.jpa.advanced.Address;
import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator;
import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator;
import org.eclipse.persistence.testing.models.jpa.advanced.Employee.Gender;
import org.eclipse.persistence.testing.models.jpa.inheritance.Person;
import org.eclipse.persistence.testing.models.jpa.relationships.Customer;
import org.eclipse.persistence.testing.models.jpa.relationships.RelationshipsExamples;
import org.eclipse.persistence.testing.models.jpa.relationships.RelationshipsTableManager;
import org.junit.Assert;
/**
* <p>
* <b>Purpose</b>: Test advanced JPA Query functionality.
* <p>
* <b>Description</b>: This tests query hints, caching and query optimization.
*
*/
public class AdvancedQueryTestSuite extends JUnitTestCase {
static JUnitDomainObjectComparer comparer; //the global comparer object used in all tests
public AdvancedQueryTestSuite() {
super();
}
public AdvancedQueryTestSuite(String name) {
super(name);
}
// This method is run at the start of EVERY test case method.
@Override
public void setUp() {
}
// This method is run at the end of EVERY test case method.
@Override
public void tearDown() {
clearCache();
}
//This suite contains all tests contained in this class
public static Test suite() {
TestSuite suite = new TestSuite();
suite.setName("AdvancedQueryTestSuite");
suite.addTest(new AdvancedQueryTestSuite("testSetup"));
suite.addTest(new AdvancedQueryTestSuite("testQueryCacheFirstCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testQueryCacheOnlyCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testQueryCacheOnlyCacheHitsOnSession"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPrimaryKeyCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testQueryExactPrimaryKeyCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testQueryTypeCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testQueryCache"));
suite.addTest(new AdvancedQueryTestSuite("testQueryREADLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryWRITELock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryOPTIMISTICLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryOPTIMISTIC_FORCE_INCREMENTLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTIC_READLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTIC_WRITELock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTIC_READ_TIMEOUTLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTIC_WRITE_TIMEOUTLock"));
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTICLockWithLimit"));
suite.addTest(new AdvancedQueryTestSuite("testPESSIMISTIC_LockWithDefaultTimeOutUnit"));
suite.addTest(new AdvancedQueryTestSuite("testPESSIMISTIC_LockWithSecondsTimeOutUnit"));
suite.addTest(new AdvancedQueryTestSuite("testObjectResultType"));
suite.addTest(new AdvancedQueryTestSuite("testNativeResultType"));
suite.addTest(new AdvancedQueryTestSuite("testCursors"));
suite.addTest(new AdvancedQueryTestSuite("testFetchGroups"));
suite.addTest(new AdvancedQueryTestSuite("testMultipleNamedJoinFetchs"));
suite.addTest(new AdvancedQueryTestSuite("testNativeQueryTransactions"));
suite.addTest(new AdvancedQueryTestSuite("testLockWithSecondaryTable"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingJOIN"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingEXISTS"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingIN"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingIN5"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingIN2"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingCursor"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingPagination"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingPagination2"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingReadObject"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingInheritance"));
suite.addTest(new AdvancedQueryTestSuite("testBasicMapBatchFetchingJOIN"));
suite.addTest(new AdvancedQueryTestSuite("testBasicMapBatchFetchingEXISTS"));
suite.addTest(new AdvancedQueryTestSuite("testBasicMapBatchFetchingIN"));
suite.addTest(new AdvancedQueryTestSuite("testMapBatchFetchingJOIN"));
suite.addTest(new AdvancedQueryTestSuite("testMapBatchFetchingEXISTS"));
suite.addTest(new AdvancedQueryTestSuite("testMapBatchFetchingIN"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchingINCache"));
suite.addTest(new AdvancedQueryTestSuite("testBasicMapJoinFetching"));
suite.addTest(new AdvancedQueryTestSuite("testBasicMapLeftJoinFetching"));
suite.addTest(new AdvancedQueryTestSuite("testBatchFetchOuterJoin"));
suite.addTest(new AdvancedQueryTestSuite("testJoinFetching"));
suite.addTest(new AdvancedQueryTestSuite("testMapJoinFetching"));
suite.addTest(new AdvancedQueryTestSuite("testJoinFetchingCursor"));
suite.addTest(new AdvancedQueryTestSuite("testJoinFetchingPagination"));
suite.addTest(new AdvancedQueryTestSuite("testMapKeyJoinFetching"));
suite.addTest(new AdvancedQueryTestSuite("testMapKeyBatchFetching"));
suite.addTest(new AdvancedQueryTestSuite("testJPQLCacheHits"));
suite.addTest(new AdvancedQueryTestSuite("testCacheIndexes"));
suite.addTest(new AdvancedQueryTestSuite("testSQLHint"));
suite.addTest(new AdvancedQueryTestSuite("testLoadGroup"));
suite.addTest(new AdvancedQueryTestSuite("testConcurrentLoadGroup"));
if (!isJPA10()) {
suite.addTest(new AdvancedQueryTestSuite("testQueryPESSIMISTIC_FORCE_INCREMENTLock"));
suite.addTest(new AdvancedQueryTestSuite("testVersionChangeWithReadLock"));
suite.addTest(new AdvancedQueryTestSuite("testVersionChangeWithWriteLock"));
suite.addTest(new AdvancedQueryTestSuite("testNamedQueryAnnotationOverwritePersistenceXML"));
}
suite.addTest(new AdvancedQueryTestSuite("testTearDown"));
return suite;
}
/**
* The setup is done as a test, both to record its failure, and to allow execution in the server.
*/
public void testSetup() {
clearCache();
DatabaseSession session = JUnitTestCase.getServerSession();
//create a new EmployeePopulator
EmployeePopulator employeePopulator = new EmployeePopulator();
new AdvancedTableCreator().replaceTables(session);
//initialize the global comparer object
comparer = new JUnitDomainObjectComparer();
//set the session for the comparer to use
comparer.setSession((AbstractSession)session.getActiveSession());
//Populate the tables
employeePopulator.buildExamples();
//Persist the examples in the database
employeePopulator.persistExample(session);
new RelationshipsTableManager().replaceTables(session);
//populate the relationships model and persist as well
new RelationshipsExamples().buildExamples(session);
new InheritanceTableCreator().replaceTables(session);
InheritancePopulator inheritancePopulator = new InheritancePopulator();
inheritancePopulator.buildExamples();
//Persist the examples in the database
inheritancePopulator.persistExample(session);
}
public void testTearDown() {
JUnitTestCase.closeEntityManagerFactory(getPersistenceUnitName());
}
/**
* Test that a cache hit will occur on a primary key query.
*/
public void testQueryPrimaryKeyCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheByPrimaryKey);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee)query.getSingleResult();
if (queryResult != employee) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test using the hint hint.
*/
public void testSQLHint() {
if (!getDatabaseSession().getPlatform().isOracle()) {
return;
}
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
counter = new QuerySQLTracker(getServerSession());
Query query = em.createNamedQuery("findAllAddressesByPostalCode");
query.setParameter("postalcode", "K2H8C2");
query.getResultList();
if ((counter.getSqlStatements().get(0)).indexOf("/*") == -1) {
fail("SQL hint was not used: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test that a cache hit will occur on a primary key query.
*/
public void testQueryTypeCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.QUERY_TYPE, QueryType.ReadObject);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee)query.getSingleResult();
if (queryResult != employee) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test fetch groups.
*/
public void testFetchGroups() {
if (!isWeavingEnabled()) {
return;
}
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
rollbackTransaction(em);
closeEntityManager(em);
clearCache();
em = createEntityManager();
beginTransaction(em);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.id = :id");
query.setHint(QueryHints.FETCH_GROUP_ATTRIBUTE, "firstName");
query.setHint(QueryHints.FETCH_GROUP_ATTRIBUTE, "lastName");
query.setParameter("id", employee.getId());
Employee queryResult = (Employee)query.getSingleResult();
if (counter.getSqlStatements().size() != 1) {
fail("More than fetch group selected: " + counter.getSqlStatements());
}
queryResult.getGender();
if (counter.getSqlStatements().size() != 2) {
fail("Access to unfetch did not cause fetch: " + counter.getSqlStatements());
}
verifyObject(employee);
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test multiple fetch joining from named queries.
*/
public void testMultipleNamedJoinFetchs() {
if (!isWeavingEnabled()) {
return;
}
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
clearCache();
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
Query query = em.createNamedQuery("findAllEmployeesJoinAddressPhones");
List<Employee> result = query.getResultList();
if (counter.getSqlStatements().size() != 1) {
fail("More than join fetches selected: " + counter.getSqlStatements());
}
for (Employee each : result) {
each.getAddress().getCity();
each.getPhoneNumbers().size();
}
if (counter.getSqlStatements().size() != 1) {
fail("Join fetches triggered query: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test cursored queries.
*/
public void testCursors() {
EntityManager em = createEntityManager();
beginTransaction(em);
try {
// Test cusored stream.
Query query = em.createQuery("Select employee from Employee employee");
query.setHint(QueryHints.CURSOR, true);
query.setHint(QueryHints.CURSOR_INITIAL_SIZE, 2);
query.setHint(QueryHints.CURSOR_PAGE_SIZE, 5);
query.setHint(QueryHints.CURSOR_SIZE, "Select count(*) from CMP3_EMPLOYEE");
Cursor cursor = (Cursor)query.getSingleResult();
cursor.nextElement();
cursor.size();
cursor.close();
// Test cursor result API.
JpaQuery jpaQuery = (JpaQuery)((EntityManager)em.getDelegate()).createQuery("Select employee from Employee employee");
jpaQuery.setHint(QueryHints.CURSOR, true);
cursor = jpaQuery.getResultCursor();
cursor.nextElement();
cursor.size();
cursor.close();
// Test scrollable cursor.
jpaQuery = (JpaQuery)((EntityManager)em.getDelegate()).createQuery("Select employee from Employee employee");
jpaQuery.setHint(QueryHints.SCROLLABLE_CURSOR, true);
jpaQuery.setHint(QueryHints.RESULT_SET_CONCURRENCY, ResultSetConcurrency.ReadOnly);
String resultSetType = ResultSetType.DEFAULT;
// HANA supports only TYPE_FORWARD_ONLY, see bug 384116
if (getPlatform().isHANA()) {
resultSetType = ResultSetType.ForwardOnly;
}
jpaQuery.setHint(QueryHints.RESULT_SET_TYPE, resultSetType);
ScrollableCursor scrollableCursor = (ScrollableCursor)jpaQuery.getResultCursor();
scrollableCursor.next();
scrollableCursor.close();
} finally {
rollbackTransaction(em);
closeEntityManager(em);
}
}
/**
* Test the result type of various queries.
*/
public void testObjectResultType() {
EntityManager em = createEntityManager();
beginTransaction(em);
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
// Test multi object, as an array.
query = em.createQuery("Select employee, employee.address, employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Object[] arrayResult = (Object[])query.getSingleResult();
if ((arrayResult.length != 3) && (arrayResult[0] != employee) || (arrayResult[1] != employee.getAddress()) || (!arrayResult[2].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
List listResult = query.getResultList();
arrayResult = (Object[])listResult.get(0);
if ((arrayResult.length != 3) || (arrayResult[0] != employee) || (arrayResult[1] != employee.getAddress()) || (!arrayResult[2].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
// Test single object, as an array.
query = em.createQuery("Select employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Array);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
arrayResult = (Object[])query.getSingleResult();
if ((arrayResult.length != 1) || (!arrayResult[0].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
listResult = query.getResultList();
arrayResult = (Object[])listResult.get(0);
if ((arrayResult.length != 1) || (!arrayResult[0].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
// Test multi object, as a Map.
query = em.createQuery("Select employee, employee.address, employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Map mapResult = (Map)query.getSingleResult();
if ((mapResult.size() != 3) ||(mapResult.get("employee") != employee) || (mapResult.get("address") != employee.getAddress()) || (!mapResult.get("id").equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
listResult = query.getResultList();
mapResult = (Map)listResult.get(0);
if ((mapResult.size() != 3) ||(mapResult.get("employee") != employee) || (mapResult.get("address") != employee.getAddress()) || (!mapResult.get("id").equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
// Test single object, as a Map.
query = em.createQuery("Select employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
mapResult = (Map)query.getSingleResult();
if ((mapResult.size() != 1) || (!mapResult.get("id").equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
listResult = query.getResultList();
mapResult = (Map)listResult.get(0);
if ((mapResult.size() != 1) || (!mapResult.get("id").equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
// Test single object, as an array.
query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.QUERY_TYPE, QueryType.Report);
query.setHint(QueryHints.RESULT_TYPE, ResultType.Array);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
arrayResult = (Object[])query.getSingleResult();
if (arrayResult[0] != employee) {
fail("Array result not correct: " + arrayResult);
}
// Test single object, as value.
query = em.createQuery("Select employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Object valueResult = query.getSingleResult();
if (! valueResult.equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
listResult = query.getResultList();
valueResult = listResult.get(0);
if (! valueResult.equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
// Test multi object, as value.
query = em.createQuery("Select employee.id, employee.firstName from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Value);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
valueResult = query.getSingleResult();
if (! valueResult.equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
// Test single object, as attribute.
query = em.createQuery("Select employee.id from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Attribute);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
valueResult = query.getSingleResult();
if (! valueResult.equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
listResult = query.getResultList();
valueResult = listResult.get(0);
if (! valueResult.equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
} finally {
rollbackTransaction(em);
}
}
/**
* Test the result type of various native queries.
*/
public void testNativeResultType() {
EntityManager em = createEntityManager();
beginTransaction(em);
try {
// Load an employee into the cache.
Query query = em.createNativeQuery("Select * from CMP3_EMPLOYEE employee", Employee.class);
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
// Test multi object, as an array.
query = em.createNativeQuery("Select employee.F_NAME, employee.EMP_ID from CMP3_EMPLOYEE employee where employee.EMP_ID = ? and employee.F_NAME = ?");
query.setParameter(1, employee.getId());
query.setParameter(2, employee.getFirstName());
Object[] arrayResult = (Object[])query.getSingleResult();
if ((arrayResult.length != 2) || (!arrayResult[0].equals(employee.getFirstName())) && (!arrayResult[1].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
List listResult = query.getResultList();
arrayResult = (Object[])listResult.get(0);
if ((arrayResult.length != 2) || (!arrayResult[0].equals(employee.getFirstName())) && (!arrayResult[1].equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
// Test single object, as an array.
query = em.createNativeQuery("Select employee.EMP_ID from CMP3_EMPLOYEE employee where employee.EMP_ID = ? and employee.F_NAME = ?");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Array);
query.setParameter(1, employee.getId());
query.setParameter(2, employee.getFirstName());
arrayResult = (Object[])query.getSingleResult();
if ((arrayResult.length != 1) || (!Integer.valueOf(((Number)arrayResult[0]).intValue()).equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
listResult = query.getResultList();
arrayResult = (Object[])listResult.get(0);
if ((arrayResult.length != 1) || (!Integer.valueOf(((Number)arrayResult[0]).intValue()).equals(employee.getId()))) {
fail("Array result not correct: " + arrayResult);
}
// Test multi object, as a Map.
query = em.createNativeQuery("Select employee.F_NAME, employee.EMP_ID from CMP3_EMPLOYEE employee where employee.EMP_ID = ? and employee.F_NAME = ?");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
query.setParameter(1, employee.getId());
query.setParameter(2, employee.getFirstName());
Map mapResult = (Map)query.getSingleResult();
if ((mapResult.size() != 2) || (!mapResult.get("F_NAME").equals(employee.getFirstName())) || (!(Integer.valueOf(((Number)mapResult.get("EMP_ID")).intValue())).equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
listResult = query.getResultList();
mapResult = (Map)listResult.get(0);
if ((mapResult.size() != 2) || (!mapResult.get("F_NAME").equals(employee.getFirstName())) || (!(Integer.valueOf(((Number)mapResult.get("EMP_ID")).intValue())).equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
// Test single object, as a Map.
query = em.createNativeQuery("Select employee.EMP_ID from CMP3_EMPLOYEE employee where employee.EMP_ID = ? and employee.F_NAME = ?");
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
query.setParameter(1, employee.getId());
query.setParameter(2, employee.getFirstName());
mapResult = (Map)query.getSingleResult();
if ((mapResult.size() != 1) || (!(Integer.valueOf(((Number)mapResult.get("EMP_ID")).intValue())).equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
listResult = query.getResultList();
mapResult = (Map)listResult.get(0);
if ((mapResult.size() != 1) || (!(Integer.valueOf(((Number)mapResult.get("EMP_ID")).intValue())).equals(employee.getId()))) {
fail("Map result not correct: " + mapResult);
}
// Test single object, as value.
query = em.createNativeQuery("Select employee.EMP_ID from CMP3_EMPLOYEE employee where employee.EMP_ID = ? and employee.F_NAME = ?");
query.setParameter(1, employee.getId());
query.setParameter(2, employee.getFirstName());
Object valueResult = query.getSingleResult();
if (!(Integer.valueOf(((Number)valueResult).intValue())).equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
listResult = query.getResultList();
valueResult = listResult.get(0);
if (!(Integer.valueOf(((Number)valueResult).intValue())).equals(employee.getId())) {
fail("Value result not correct: " + valueResult);
}
} finally {
rollbackTransaction(em);
}
}
/**
* Test that a cache hit will occur on a primary key query.
*/
public void testQueryExactPrimaryKeyCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(0);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.id = :id");
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheByExactPrimaryKey);
query.setParameter("id", employee.getId());
Employee queryResult = (Employee)query.getSingleResult();
if (queryResult != employee) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test that the transaction is committed for a single native query transaction.
*/
public void testNativeQueryTransactions() {
Employee emp = (Employee)getServerSession().readObject(Employee.class);
if (emp == null) {
fail("Test problem: no Employees in the db, nothing to update");
}
EntityManager em = createEntityManager();
beginTransaction(em);
try {
em.setFlushMode(FlushModeType.COMMIT);
Query query = em.createNativeQuery("Update CMP3_EMPLOYEE set F_NAME = 'Bobo' where EMP_ID = " + emp.getId());
query.executeUpdate();
commitTransaction(em);
closeEntityManager(em);
em = createEntityManager();
beginTransaction(em);
query = em.createNativeQuery("Select * from CMP3_EMPLOYEE where F_NAME = 'Bobo' AND EMP_ID = " + emp.getId());
if (query.getResultList().size() == 0) {
fail("Native query did not commit transaction.");
} else {
// clean up - bring back the original name
em.setFlushMode(FlushModeType.COMMIT);
query = em.createNativeQuery("Update CMP3_EMPLOYEE set F_NAME = '"+emp.getFirstName()+"' where EMP_ID = " + emp.getId());
query.executeUpdate();
commitTransaction(em);
}
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
/**
* Test that a cache hit will occur on a query.
*/
public void testQueryCacheFirstCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(result.size() - 1);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.firstName = :firstName");
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheThenDatabase);
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee)query.getSingleResult();
if (!queryResult.getFirstName().equals(employee.getFirstName())) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test that a cache hit will occur on a query.
*/
public void testQueryCacheOnlyCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(result.size() - 1);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.firstName = :firstName");
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
query.setParameter("firstName", employee.getFirstName());
// Test that list works as well.
query.getResultList();
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test that a cache hit will occur on a query when the object is not in the unit of work/em.
*/
public void testQueryCacheOnlyCacheHitsOnSession() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(result.size() - 1);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
rollbackTransaction(em);
closeEntityManager(em);
em = createEntityManager();
beginTransaction(em);
query = em.createQuery("Select employee from Employee employee where employee.id = :id");
query.setHint(QueryHints.QUERY_TYPE, QueryType.ReadObject);
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
query.setParameter("id", employee.getId());
if (query.getSingleResult() == null) {
fail("Query did not check session cache.");
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
rollbackTransaction(em);
closeEntityManager(em);
em = createEntityManager();
beginTransaction(em);
query = em.createQuery("Select employee from Employee employee where employee.id = :id");
query.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheOnly);
query.setParameter("id", employee.getId());
if (query.getResultList().size() != 1) {
fail("Query did not check session cache.");
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
if (counter != null) {
counter.remove();
}
rollbackTransaction(em);
closeEntityManager(em);
}
}
/**
* Test the query cache.
*/
public void testQueryCache() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
JpaQuery jpaQuery = (JpaQuery)((EntityManager)em.getDelegate()).createNamedQuery("CachedAllEmployees");
List result = jpaQuery.getResultList();
ReadQuery readQuery = (ReadQuery)jpaQuery.getDatabaseQuery();
if (readQuery.getQueryResultsCachePolicy() == null) {
fail("Query cache not set.");
}
if (readQuery.getQueryResultsCachePolicy().getMaximumCachedResults() != 200) {
fail("Query cache size not set.");
}
if (!(readQuery.getQueryResultsCachePolicy().getCacheInvalidationPolicy() instanceof TimeToLiveCacheInvalidationPolicy)) {
fail("Query cache invalidation not set.");
}
if (((TimeToLiveCacheInvalidationPolicy)readQuery.getQueryResultsCachePolicy().getCacheInvalidationPolicy()).getTimeToLive() != 50000) {
fail("Query cache invalidation time not set.");
}
jpaQuery = (JpaQuery)((EntityManager)em.getDelegate()).createNamedQuery("CachedTimeOfDayAllEmployees");
readQuery = (ReadQuery)jpaQuery.getDatabaseQuery();
if (readQuery.getQueryResultsCachePolicy() == null) {
fail("Query cache not set.");
}
if (readQuery.getQueryResultsCachePolicy().getMaximumCachedResults() != 200) {
fail("Query cache size not set.");
}
if (!(readQuery.getQueryResultsCachePolicy().getCacheInvalidationPolicy() instanceof DailyCacheInvalidationPolicy)) {
fail("Query cache invalidation not set.");
}
Calendar calendar = ((DailyCacheInvalidationPolicy)readQuery.getQueryResultsCachePolicy().getCacheInvalidationPolicy()).getExpiryTime();
if ((calendar.get(Calendar.HOUR_OF_DAY) != 23 )
&& (calendar.get(Calendar.MINUTE) != 59)
&& (calendar.get(Calendar.SECOND) != 59)) {
fail("Query cache invalidation time not set.");
}
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
Query query = em.createNamedQuery("CachedAllEmployees");
if (result.size() != query.getResultList().size()) {
fail("List result size is not correct on 2nd cached query.");
}
if (counter.getSqlStatements().size() > 0) {
fail("Query cache was not used: " + counter.getSqlStatements());
}
clearCache();
// Preload uow to test query cache in uow.
em.createQuery("Select e from Employee e").getResultList();
query.getResultList();
if (result.size() != query.getResultList().size()) {
fail("List result size is not correct on cached query in unit of work.");
}
clearCache();
// Also test query cache in early transaction.
em.persist(new Address());
em.flush();
query.getResultList();
if (result.size() != query.getResultList().size()) {
fail("List result size is not correct on cached query in transaction.");
}
// Query by primary key.
query = em.createNamedQuery("CachedNoEmployees");
if (!query.getResultList().isEmpty()) {
fail("List result size is not correct.");
}
// Also test empty query cache.
counter.remove();
counter = new QuerySQLTracker(getServerSession());
if (!query.getResultList().isEmpty()) {
fail("List result size is not correct.");
}
if (counter.getSqlStatements().size() > 0) {
fail("Query cache was not used: " + counter.getSqlStatements());
}
rollbackTransaction(em);
beginTransaction(em);
query = em.createNamedQuery("CachedEmployeeJoinAddress");
result = query.getResultList();
// Test that an insert triggers the query cache to invalidate.
Employee employee = new Employee();
Address address = new Address();
address.setCity("Ottawa");
employee.setAddress(address);
em.persist(employee);
commitTransaction(em);
beginTransaction(em);
query = em.createNamedQuery("CachedEmployeeJoinAddress");
if ((result.size() + 1) != query.getResultList().size()) {
fail("Query result cache not invalidated.");
}
address = em.merge(address);
address.setCity("nowhere");
commitTransaction(em);
beginTransaction(em);
query = em.createNamedQuery("CachedEmployeeJoinAddress");
if (result.size() != query.getResultList().size()) {
fail("Query result cache not invalidated.");
}
em.remove(em.find(Employee.class, employee.getId()));
commitTransaction(em);
query = em.createNamedQuery("CachedEmployeeJoinAddress");
if (result.size() != query.getResultList().size()) {
fail("Query result cache not invalidated on delete.");
}
} finally {
closeEntityManagerAndTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
public void testQueryREADLock(){
// Cannot create parallel entity managers in the server.
if (isOnServer()) {
return;
}
// Load an employee into the cache.
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception optimisticLockException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.READ);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
Employee employee2 = em2.find(Employee.class, employee.getId());
employee2.setFirstName("Read");
commitTransaction(em2);
} catch (RuntimeException ex) {
rollbackTransaction(em2);
throw ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
try {
em.flush();
} catch (PersistenceException exception) {
if (exception instanceof OptimisticLockException) {
optimisticLockException = exception;
} else {
throw exception;
}
}
rollbackTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.READ is used.", optimisticLockException == null);
}
public void testQueryWRITELock(){
// Cannot create parallel transactions.
if (isOnServer()) {
return;
}
// Load an employee into the cache.
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception optimisticLockException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.WRITE);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
Employee employee2 = em2.find(Employee.class, queryResult.getId());
employee2.setFirstName("Write");
commitTransaction(em2);
} catch (RuntimeException ex) {
rollbackTransaction(em2);
closeEntityManager(em2);
throw ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RollbackException exception) {
if (exception.getCause() instanceof OptimisticLockException){
optimisticLockException = exception;
}
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
} finally {
closeEntityManagerAndTransaction(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.WRITE is used.", optimisticLockException == null);
}
public void testQueryOPTIMISTICLock(){
// Cannot create parallel entity managers in the server.
if (! isOnServer()) {
// Load an employee into the cache.
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception optimisticLockException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.OPTIMISTIC);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
Employee employee2 = em2.find(Employee.class, employee.getId());
employee2.setFirstName("Optimistic");
commitTransaction(em2);
} catch (RuntimeException ex) {
rollbackTransaction(em2);
throw ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
try {
em.flush();
} catch (PersistenceException exception) {
if (exception instanceof OptimisticLockException) {
optimisticLockException = exception;
} else {
throw exception;
}
}
rollbackTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)){
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.READ is used.", optimisticLockException == null);
}
}
public void testQueryOPTIMISTIC_FORCE_INCREMENTLock(){
// Cannot create parallel transactions.
if (! isOnServer()) {
// Load an employee into the cache.
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception optimisticLockException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.OPTIMISTIC_FORCE_INCREMENT);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
Employee employee2 = em2.find(Employee.class, queryResult.getId());
employee2.setFirstName("OptimisticForceIncrement");
commitTransaction(em2);
} catch (RuntimeException ex) {
rollbackTransaction(em2);
throw ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RollbackException exception) {
if (exception.getCause() instanceof OptimisticLockException){
optimisticLockException = exception;
}
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
} finally {
closeEntityManagerAndTransaction(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.WRITE is used.", optimisticLockException == null);
}
}
public void testQueryPESSIMISTIC_READLock() {
if ((JUnitTestCase.getServerSession()).getPlatform().isHANA()) {
// HANA currently doesn't support pessimistic locking with queries on multiple tables
// feature is under development (see bug 384129), but test should be skipped for the time being
return;
}
// Cannot create parallel entity managers in the server.
if (! isOnServer() && isSelectForUpateSupported()) {
EntityManager em = createEntityManager();
PessimisticLockException pessimisticLockException = null;
try {
beginTransaction(em);
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
List employees2 = em2.createQuery("Select employee from Employee employee").getResultList(); //
Employee employee2 = (Employee) employees2.get(0);
// Find all the departments and lock them.
List employees = em.createQuery("Select employee from Employee employee").setLockMode(LockModeType.PESSIMISTIC_READ).getResultList();
Employee employee = (Employee) employees.get(0);
employee.setFirstName("New Pessimistic Employee");
HashMap properties = new HashMap();
properties.put(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 0);
em2.lock(employee2, LockModeType.PESSIMISTIC_READ, properties);
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (jakarta.persistence.PessimisticLockException ex) {
pessimisticLockException = ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.PESSIMISTIC is used.", pessimisticLockException == null);
}
}
public void testQueryPESSIMISTIC_WRITELock() {
if ((JUnitTestCase.getServerSession()).getPlatform().isHANA()) {
// HANA currently doesn't support pessimistic locking with queries on multiple tables
// feature is under development (see bug 384129), but test should be skipped for the time being
return;
}
// Cannot create parallel entity managers in the server.
if (! isOnServer() && isSelectForUpateSupported()) {
EntityManager em = createEntityManager();
Exception pessimisticLockException = null;
try {
beginTransaction(em);
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
List employees2 = em2.createQuery("Select employee from Employee employee").getResultList(); //
Employee employee2 = (Employee) employees2.get(0);
// Find all the departments and lock them.
List employees = em.createQuery("Select employee from Employee employee").setLockMode(LockModeType.PESSIMISTIC_READ).getResultList();
Employee employee = (Employee) employees.get(0);
employee.setFirstName("New Pessimistic Employee");
HashMap properties = new HashMap();
properties.put(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 0);
em2.lock(employee2, LockModeType.PESSIMISTIC_READ, properties);
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (jakarta.persistence.PessimisticLockException ex) {
pessimisticLockException = ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.PESSIMISTIC is used.", pessimisticLockException == null);
}
}
public void testQueryPESSIMISTIC_FORCE_INCREMENTLock() {
if ((JUnitTestCase.getServerSession()).getPlatform().isHANA()) {
// HANA currently doesn't support pessimistic locking with queries on multiple tables
// feature is under development (see bug 384129), but test should be skipped for the time being
return;
}
if (isSelectForUpateSupported()) {
Employee employee = null;
Integer version1;
EntityManager em = createEntityManager();
beginTransaction(em);
try {
employee = new Employee();
employee.setFirstName("Guillaume");
employee.setLastName("Aujet");
em.persist(employee);
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
}
version1 = employee.getVersion();
try {
beginTransaction(em);
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName").setLockMode(LockModeType.PESSIMISTIC_FORCE_INCREMENT);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.setLastName("Auger");
commitTransaction(em);
employee = em.find(Employee.class, employee.getId());
assertTrue("The version was not updated on the pessimistic lock.", version1 < employee.getVersion());
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
}
//Verify if the entity has been updated correctly by using PESSIMISTIC_FORCE_INCREMENT as PESSIMISTIC_WRITE
try {
beginTransaction(em);
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName").setLockMode(LockModeType.PESSIMISTIC_FORCE_INCREMENT);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
rollbackTransaction(em);
assertTrue("The last name is not updated by using PESSIMISTIC_FORCE_INCREMENT.", queryResult.getLastName().equals("Auger"));
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
}
}
public void testQueryPESSIMISTIC_READ_TIMEOUTLock() {
ServerSession session = JUnitTestCase.getServerSession();
// Cannot create parallel entity managers in the server.
// Lock timeout only supported on Oracle.
if (! isOnServer() && session.getPlatform().supportsWaitForUpdate()) {
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception lockTimeOutException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.PESSIMISTIC_READ);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
// Query by primary key.
Query query2 = em2.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query2.setLockMode(LockModeType.PESSIMISTIC_READ);
query2.setHint(QueryHints.REFRESH, true);
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 5000);
query2.setParameter("id", employee.getId());
query2.setParameter("firstName", employee.getFirstName());
Employee employee2 = (Employee) query2.getSingleResult();
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (PersistenceException ex) {
if (ex instanceof jakarta.persistence.LockTimeoutException) {
lockTimeOutException = ex;
} else {
throw ex;
}
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.PESSIMISTIC is used.", lockTimeOutException == null);
}
}
public void testQueryPESSIMISTIC_WRITE_TIMEOUTLock() {
ServerSession session = JUnitTestCase.getServerSession();
// Cannot create parallel entity managers in the server.
// Lock timeout only supported on Oracle.
if (! isOnServer() && session.getPlatform().supportsWaitForUpdate()) {
EntityManager em = createEntityManager();
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception lockTimeOutException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
// Query by primary key.
Query query2 = em2.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query2.setLockMode(LockModeType.PESSIMISTIC_WRITE);
query2.setHint(QueryHints.REFRESH, true);
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 5000);
query2.setParameter("id", employee.getId());
query2.setParameter("firstName", employee.getFirstName());
Employee employee2 = (Employee) query2.getSingleResult();
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (PersistenceException ex) {
if (ex instanceof jakarta.persistence.LockTimeoutException) {
lockTimeOutException = ex;
} else {
throw ex;
}
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.PESSIMISTIC is used.", lockTimeOutException == null);
}
}
public void testPESSIMISTIC_LockWithDefaultTimeOutUnit() {
ServerSession session = JUnitTestCase.getServerSession();
// Cannot create parallel entity managers in the server.
// Lock timeout only supported on Oracle.
if (! isOnServer() && session.getPlatform().supportsWaitForUpdate()) {
EntityManager em = createEntityManager();
// sleep for 2 seconds (2000 milliseconds)
// this timeout value must be smaller, than PESSIMISTIC_LOCK_TIMEOUT property for the second query
// to execute this test without LockTimeoutException
Thread pesimisticLockRunnerThread = new Thread(new PesimisticLockRunner(em, 2000));
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception lockTimeOutException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.PESSIMISTIC_READ);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
// Query by primary key.
Query query2 = em2.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query2.setLockMode(LockModeType.PESSIMISTIC_READ);
query2.setHint(QueryHints.REFRESH, true);
query2.setParameter("id", employee.getId());
query2.setParameter("firstName", employee.getFirstName());
// Set timeout for 4000 milliseconds (4 seconds)
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 4000);
// Release (rollback) locked rows by first query in second thread after timeout
pesimisticLockRunnerThread.start();
Employee employee2 = (Employee) query2.getSingleResult();
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (PersistenceException ex) {
if (ex instanceof jakarta.persistence.LockTimeoutException) {
lockTimeOutException = ex;
} else {
throw ex;
}
} finally {
closeEntityManagerAndTransaction(em2);
}
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
try {
pesimisticLockRunnerThread.join(10000);
} catch (InterruptedException ex) {
ex.printStackTrace();
fail("PesimisticLockRunnerThread failed with:" + ex);
}
Assert.assertNull("A jakarta.persistence.LockTimeoutException was unexpectedly thrown", lockTimeOutException);
}
}
public void testPESSIMISTIC_LockWithSecondsTimeOutUnit() {
ServerSession session = JUnitTestCase.getServerSession();
// Cannot create parallel entity managers in the server.
if (! isOnServer() && session.getPlatform().supportsWaitForUpdate()) {
EntityManager em = createEntityManager();
// sleep for 4 seconds (4000 milliseconds)
// this timeout value must be higher, than PESSIMISTIC_LOCK_TIMEOUT property for the second query
// a LockTimeoutException is expected to be thrown since the lock timeout should still be in effect
Thread pesimisticLockRunnerThread = new Thread(new PesimisticLockRunner(em, 4000));
List result = em.createQuery("Select employee from Employee employee").getResultList();
Employee employee = (Employee) result.get(0);
Exception lockTimeOutException = null;
try {
beginTransaction(em);
// Query by primary key.
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query.setLockMode(LockModeType.PESSIMISTIC_READ);
query.setHint(QueryHints.REFRESH, true);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.toString();
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
// Query by primary key.
Query query2 = em2.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName");
query2.setLockMode(LockModeType.PESSIMISTIC_READ);
query2.setHint(QueryHints.REFRESH, true);
// Set timeout for 2 seconds
// It's smaller, than lock of the first query => throws LockTimeoutException
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 2);
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT_UNIT, "SECONDS");
query2.setParameter("id", employee.getId());
query2.setParameter("firstName", employee.getFirstName());
// Release (rollback) locked rows by first query in second thread after timeout
pesimisticLockRunnerThread.start();
Employee employee2 = (Employee) query2.getSingleResult();
employee2.setFirstName("Invalid Lock Employee");
commitTransaction(em2);
} catch (PersistenceException ex) {
if (ex instanceof jakarta.persistence.LockTimeoutException) {
lockTimeOutException = ex;
} else {
throw ex;
}
} finally {
closeEntityManagerAndTransaction(em2);
}
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
try {
pesimisticLockRunnerThread.join(10000);
} catch (InterruptedException ex) {
ex.printStackTrace();
fail("PesimisticLockRunnerThread failed with:" + ex);
}
Assert.assertNotNull("A jakarta.persistence.LockTimeoutException was expected to be thrown", lockTimeOutException);
}
}
private class PesimisticLockRunner implements Runnable {
private EntityManager em;
private long timeout;
public PesimisticLockRunner(EntityManager em, long timeout) {
this.em = em;
this.timeout = timeout;
}
@Override
public void run() {
// sleep for "timeout" milliseconds) to allow the first lock to timeout
try {
Thread.sleep(timeout);
} catch (Exception e) {
}
rollbackTransaction(em);
}
}
public void testLockWithSecondaryTable() {
if ((JUnitTestCase.getServerSession()).getPlatform().isHANA()) {
// HANA currently doesn't support pessimistic locking with queries on multiple tables
// feature is under development (see bug 384129), but test should be skipped for the time being
return;
}
// Cannot create parallel entity managers in the server.
if (! isOnServer() && isSelectForUpateSupported()) {
EntityManager em = createEntityManager();
Exception pessimisticLockException = null;
try {
beginTransaction(em);
EntityManager em2 = createEntityManager();
try {
beginTransaction(em2);
List employees2 = em2.createQuery("Select employee from Employee employee").getResultList();
Employee employee2 = (Employee) employees2.get(0);
// Find all the employees and lock them.
List employees = em.createQuery("Select employee from Employee employee").setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();
Employee employee = (Employee) employees.get(0);
employee.setSalary(90000);
HashMap properties = new HashMap();
properties.put(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 0);
em2.lock(employee2, LockModeType.PESSIMISTIC_WRITE, properties);
employee2.setSalary(100000);
commitTransaction(em2);
} catch (PessimisticLockException ex) {
pessimisticLockException = ex;
} finally {
closeEntityManagerAndTransaction(em2);
}
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
closeEntityManager(em);
}
assertFalse("Proper exception not thrown when Query with LockModeType.PESSIMISTIC is used.", pessimisticLockException == null);
}
}
public void testVersionChangeWithReadLock() {
if (isSelectForUpateNoWaitSupported()){
Employee employee = null;
Integer version1;
EntityManager em = createEntityManager();
beginTransaction(em);
try {
employee = new Employee();
employee.setFirstName("Version Change");
employee.setLastName("Readlock");
em.persist(employee);
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
}
version1 = employee.getVersion();
try {
beginTransaction(em);
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName").setLockMode(LockModeType.PESSIMISTIC_READ);
query.setHint(QueryHints.REFRESH, true);
query.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 0);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.setLastName("Burger");
commitTransaction(em);
employee = em.find(Employee.class, employee.getId());
assertTrue("The version was not updated on the pessimistic read lock.", version1 < employee.getVersion());
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
}
public void testNamedQueryAnnotationOverwritePersistenceXML() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
Query query = em.createNamedQuery("findAllEmployeesByIdAndFirstName");
Map<String, Object> hints = query.getHints();
assertTrue("query hint", hints.get(QueryHints.PESSIMISTIC_LOCK_TIMEOUT).equals("15000"));
rollbackTransaction(em);
} catch(Exception ex){
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
throw ex;
} finally{
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
public void testVersionChangeWithWriteLock() {
if (isSelectForUpateNoWaitSupported()) {
Employee employee = null;
Integer version1;
EntityManager em = createEntityManager();
beginTransaction(em);
try {
employee = new Employee();
employee.setFirstName("Version Change");
employee.setLastName("Writelock");
em.persist(employee);
commitTransaction(em);
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
}
version1 = employee.getVersion();
try {
beginTransaction(em);
Query query = em.createQuery("Select employee from Employee employee where employee.id = :id and employee.firstName = :firstName").setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.setHint(QueryHints.REFRESH, true);
query.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 0);
query.setParameter("id", employee.getId());
query.setParameter("firstName", employee.getFirstName());
Employee queryResult = (Employee) query.getSingleResult();
queryResult.setLastName("Burger");
commitTransaction(em);
employee = em.find(Employee.class, employee.getId());
assertTrue("The version was not updated on the pessimistic write lock.", version1 < employee.getVersion());
} catch (RuntimeException ex) {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
throw ex;
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
}
/**
* Test batch fetching.
*/
public void testBatchFetchingIN() {
testBatchFetching(BatchFetchType.IN, 1000);
}
/**
* Test batch fetching.
*/
public void testBatchFetchingIN5() {
testBatchFetching(BatchFetchType.IN, 5);
}
/**
* Test batch fetching.
*/
public void testBatchFetchingIN2() {
testBatchFetching(BatchFetchType.IN, 2);
}
/**
* Test batch fetching.
*/
public void testBatchFetchingJOIN() {
testBatchFetching(BatchFetchType.JOIN, 0);
}
/**
* Test batch fetching.
*/
public void testBatchFetchingEXISTS() {
testBatchFetching(BatchFetchType.EXISTS, 0);
}
/**
* Test batch fetching.
*/
public void testBatchFetching(BatchFetchType type, int size) {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e where e.gender = :g1 or e.gender = :g2");
query.setHint(QueryHints.BATCH_SIZE, size);
query.setHint(QueryHints.BATCH_TYPE, type);
query.setHint(QueryHints.BATCH, "e.address");
query.setHint(QueryHints.BATCH, "e.manager");
query.setHint(QueryHints.BATCH, "e.projects");
query.setHint(QueryHints.BATCH, "e.managedEmployees");
query.setHint(QueryHints.BATCH, "e.responsibilities");
query.setHint(QueryHints.BATCH, "e.dealers");
query.setHint(QueryHints.BATCH, "e.phoneNumbers");
//query.setHint(QueryHints.BATCH, "e.department"); is join fetched already.
query.setHint(QueryHints.BATCH, "e.workWeek");
query.setParameter("g1", Gender.Male);
query.setParameter("g2", Gender.Female);
List<Employee> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
for (Employee employee : results) {
employee.getAddress();
employee.getManager();
employee.getProjects().size();
employee.getManagedEmployees().size();
employee.getResponsibilities().size();
employee.getDealers().size();
employee.getPhoneNumbers().size();
employee.getWorkWeek().size();
}
int queries = 11;
if (size == 2) {
queries = 55;
} else if (size == 5) {
queries = 30;
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
if (type != BatchFetchType.JOIN) {
for (String sql : counter.getSqlStatements()) {
if ((sql.indexOf("DISTINCT") != -1) && (sql.indexOf("PROJ_TYPE") == -1)) {
fail("SQL should not contain DISTINCT: " + sql);
}
}
}
clearCache();
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching with IN and a partial cache.
*/
public void testBatchFetchingINCache() {
testBatchFetchingINCache(1);
testBatchFetchingINCache(2);
testBatchFetchingINCache(3);
testBatchFetchingINCache(4);
testBatchFetchingINCache(5);
}
/**
* Test batch fetching with IN and a partial cache.
* This tests that the paging is working correctly for different page sizes and empty and disjoint pages.
*/
public void testBatchFetchingINCache(int batchSize) {
clearCache();
EntityManager em = createEntityManager();
try {
Query query = em.createQuery("Select a from Address a where exists (Select e from Employee e where e.address = a and e.gender = :g1)");
query.setParameter("g1", Gender.Female);
query.getResultList();
query = em.createQuery("Select e from Employee e where e.gender = :g1 or e.gender = :g2");
query.setHint(QueryHints.BATCH_SIZE, batchSize);
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
query.setHint(QueryHints.BATCH, "e.address");
query.setHint(QueryHints.BATCH, "e.manager");
query.setParameter("g1", Gender.Male);
query.setParameter("g2", Gender.Female);
List<Employee> results = query.getResultList();
for (Employee employee : results) {
employee.getAddress();
employee.getManager();
}
clearCache();
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
closeEntityManagerAndTransaction(em);
}
}
/**
* Test join fetching.
*/
public void testJoinFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e where e.gender = :g1 or e.gender = :g2");
query.setHint(QueryHints.LEFT_FETCH, "e.address");
//query.setHint(QueryHints.LEFT_FETCH, "e.manager"); - has eagers
//query.setHint(QueryHints.LEFT_FETCH, "e.projects"); - has eagers
//query.setHint(QueryHints.LEFT_FETCH, "e.managedEmployees"); - has eagers
query.setHint(QueryHints.LEFT_FETCH, "e.responsibilities");
query.setHint(QueryHints.LEFT_FETCH, "e.dealers");
query.setHint(QueryHints.LEFT_FETCH, "e.phoneNumbers");
//query.setHint(QueryHints.BATCH, "e.department"); is join fetched already.
query.setHint(QueryHints.LEFT_FETCH, "e.workWeek");
query.setParameter("g1", Gender.Male);
query.setParameter("g2", Gender.Female);
List<Employee> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
for (Employee employee : results) {
employee.getAddress();
employee.getResponsibilities().size();
employee.getDealers().size();
employee.getPhoneNumbers().size();
employee.getWorkWeek().size();
}
int queries = 1;
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching of maps.
*/
public void testBasicMapBatchFetchingJOIN() {
testBasicMapBatchFetching(BatchFetchType.JOIN, 0);
}
/**
* Test batch fetching of maps.
*/
public void testBasicMapBatchFetchingIN() {
testBasicMapBatchFetching(BatchFetchType.IN, 100);
}
/**
* Test batch fetching of maps.
*/
public void testBasicMapBatchFetchingEXISTS() {
testBasicMapBatchFetching(BatchFetchType.EXISTS, 0);
}
/**
* Test batch fetching of maps.
*/
public void testMapBatchFetchingJOIN() {
testMapBatchFetching(BatchFetchType.JOIN, 0);
}
/**
* Test batch fetching of maps.
*/
public void testMapBatchFetchingIN() {
testMapBatchFetching(BatchFetchType.IN, 100);
}
/**
* Test batch fetching of maps.
*/
public void testMapBatchFetchingEXISTS() {
testMapBatchFetching(BatchFetchType.EXISTS, 0);
}
/**
* Test batch fetching of maps.
*/
public void testBasicMapBatchFetching(BatchFetchType type, int size) {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select b from Buyer b where b.name like :name");
query.setHint(QueryHints.BATCH_SIZE, size);
query.setHint(QueryHints.BATCH_TYPE, type);
query.setHint(QueryHints.BATCH, "e.creditCards");
query.setHint(QueryHints.BATCH, "e.creditLines");
query.setParameter("name", "%Gold%");
List<Buyer> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 3) {
fail("Should have been 3 query but was: " + counter.getSqlStatements().size());
}
for (Buyer buyer : results) {
buyer.getCreditCards().size();
buyer.getCreditLines().size();
}
int queries = 4;
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Buyer buyer : results) {
verifyObject(buyer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching of maps.
*/
public void testBasicMapJoinFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select b from Buyer b where b.name like :name");
query.setHint(QueryHints.FETCH, "e.creditCards");
query.setHint(QueryHints.FETCH, "e.creditLines");
query.setParameter("name", "%Gold%");
List<Buyer> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 2) {
fail("Should have been 2 query but was: " + counter.getSqlStatements().size());
}
for (Buyer buyer : results) {
buyer.getCreditCards().size();
buyer.getCreditLines().size();
}
int queries = 2;
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Buyer buyer : results) {
verifyObject(buyer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching of maps.
*/
public void testBasicMapLeftJoinFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select b from Buyer b where b.name like :name");
query.setHint(QueryHints.LEFT_FETCH, "e.creditCards");
query.setHint(QueryHints.LEFT_FETCH, "e.creditLines");
query.setParameter("name", "%Gold%");
List<Buyer> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 2) {
fail("Should have been 2 query but was: " + counter.getSqlStatements().size());
}
boolean found = false;
for (Buyer buyer : results) {
found = found || buyer.getCreditCards().size() > 0;
found = found || buyer.getCreditLines().size() > 0;
}
assertTrue("No data to join.", found);
int queries = 2;
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Buyer buyer : results) {
verifyObject(buyer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching of maps.
*/
public void testMapBatchFetching(BatchFetchType type, int size) {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select c from Customer c");
query.setHint(QueryHints.BATCH_SIZE, size);
query.setHint(QueryHints.BATCH_TYPE, type);
query.setHint(QueryHints.BATCH, "e.CSInteractions");
query.setHint(QueryHints.BATCH, "e.CCustomers");
List<Customer> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() > 3) {
fail("Should have been 3 queries but was: " + counter.getSqlStatements().size());
}
int queries = 5;
for (Customer customer : results) {
queries = queries + customer.getCSInteractions().size();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Customer customer : results) {
verifyObject(customer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test load groups.
*/
public void testLoadGroup() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select c from Customer c");
query.setHint(QueryHints.LOAD_GROUP_ATTRIBUTE, "CSInteractions");
query.setHint(QueryHints.LOAD_GROUP_ATTRIBUTE, "CCustomers");
List<Customer> results = query.getResultList();
counter.getSqlStatements().clear();
for (Customer customer : results) {
customer.getCSInteractions().size();
}
if (counter.getSqlStatements().size() > 0) {
fail("Load group should have loaded attributes.");
}
clearCache();
for (Customer customer : results) {
verifyObject(customer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test concurrent load groups.
*/
public void testConcurrentLoadGroup() {
clearCache();
boolean concurrent = getDatabaseSession().isConcurrent();
getDatabaseSession().setIsConcurrent(true);
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select c from Customer c");
query.setHint(QueryHints.LOAD_GROUP_ATTRIBUTE, "CSInteractions");
query.setHint(QueryHints.LOAD_GROUP_ATTRIBUTE, "CCustomers");
List<Customer> results = query.getResultList();
counter.getSqlStatements().clear();
for (Customer customer : results) {
customer.getCSInteractions().size();
}
if (counter.getSqlStatements().size() > 0) {
fail("Load group should have loaded attributes.");
}
clearCache();
for (Customer customer : results) {
verifyObject(customer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
getDatabaseSession().setIsConcurrent(concurrent);
}
}
/**
* Test join fetching of maps.
*/
public void testMapJoinFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select c from Customer c");
query.setHint(QueryHints.LEFT_FETCH, "e.CSInteractions");
query.setHint(QueryHints.LEFT_FETCH, "e.CCustomers");
List<Customer> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() > 3) {
fail("Should have been 3 queries but was: " + counter.getSqlStatements().size());
}
int queries = 1;
for (Customer customer : results) {
queries = queries + customer.getCSInteractions().size();
}
assertTrue("No data to join.", queries > 1);
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Customer customer : results) {
verifyObject(customer);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching of maps.
*/
public void testMapKeyJoinFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select d from ADV_DEPT d");
query.setHint(QueryHints.LEFT_FETCH, "d.equipment");
query.setHint(QueryHints.LEFT_FETCH, "d.employees");
query.setHint(QueryHints.LEFT_FETCH, "d.managers");
List<Department> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() > 1) {
fail("Should have been 13 queries but was: " + counter.getSqlStatements().size());
}
int queries = 1;
for (Department department : results) {
queries = queries + department.getEquipment().size();
department.getEmployees().size();
department.getManagers().size();
}
assertTrue("No data to join.", queries > 1);
if (isWeavingEnabled() && counter.getSqlStatements().size() > 1) {
fail("Should have been " + 1 + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Department department : results) {
verifyObject(department);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching of maps.
*/
public void testMapKeyBatchFetching() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select d from ADV_DEPT d");
query.setHint(QueryHints.BATCH, "d.equipment");
query.setHint(QueryHints.BATCH, "d.employees");
query.setHint(QueryHints.BATCH, "d.managers");
List<Department> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() > 1) {
fail("Should have been 1 queries but was: " + counter.getSqlStatements().size());
}
int queries = 1;
for (Department department : results) {
queries = queries + department.getEquipment().size();
department.getEmployees().size();
department.getManagers().size();
}
assertTrue("No data to join.", queries > 1);
if (isWeavingEnabled() && counter.getSqlStatements().size() > 4) {
fail("Should have been " + 4 + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Department department : results) {
verifyObject(department);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching using first/max results.
*/
public void testBatchFetchingPagination() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e");
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
query.setHint(QueryHints.BATCH_SIZE, 5);
query.setHint(QueryHints.BATCH, "e.address");
query.setHint(QueryHints.BATCH, "e.manager");
query.setFirstResult(5);
query.setMaxResults(5);
List<Employee> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
if (results.size() > 5) {
fail("Should have only returned 5 objects but was: " + results.size());
}
for (Employee employee : results) {
employee.getAddress();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > 2) {
fail("Should have been 2 queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching using first/max results.
*/
public void testJoinFetchingPagination() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e");
query.setHint(QueryHints.LEFT_FETCH, "e.address");
query.setHint(QueryHints.LEFT_FETCH, "e.phoneNumbers");
query.setFirstResult(5);
query.setMaxResults(5);
List<Employee> results = query.getResultList();
int nExpectedStatements = 3;
if (usesSOP()) {
// In SOP case there are no sql to read PhoneNumbers - they are read from sopObject instead.
nExpectedStatements = 1;
}
if (isWeavingEnabled() && counter.getSqlStatements().size() != nExpectedStatements) {
fail("Should have been " + nExpectedStatements + " query but was: " + counter.getSqlStatements().size());
}
if (results.size() > 5) {
fail("Should have only returned 5 objects but was: " + results.size());
}
for (Employee employee : results) {
employee.getAddress();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > nExpectedStatements) {
fail("Should have been " + nExpectedStatements + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
counter.remove();
counter = null;
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching using read object query.
*/
public void testBatchFetchingReadObject() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e");
query.setHint(QueryHints.BATCH, "e.managedEmployees");
query.setHint(QueryHints.BATCH, "e.managedEmployees.address");
query.setHint(QueryHints.QUERY_TYPE, QueryType.ReadObject);
Employee result = (Employee)query.getSingleResult();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
for (Employee employee : result.getManagedEmployees()) {
employee.getAddress();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > 3) {
fail("Should have been 3 queries but was: " + counter.getSqlStatements().size());
}
clearCache();
verifyObject(result);
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching with outer joins.
*/
public void testBatchFetchOuterJoin() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select p from Person p left join p.bestFriend f order by f.title");
query.setHint(QueryHints.BATCH, "p.bestFriend");
List<Person> result = query.getResultList();
if (result.size() != 8) {
fail("Should have been 8 results but was: " + result.size());
}
if (isWeavingEnabled() && counter.getSqlStatements().size() != 2) {
fail("Should have been 2 query but was: " + counter.getSqlStatements().size());
}
for (Person person : result) {
person.getBestFriend();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > 2) {
fail("Should have been 2 queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Person person : result) {
verifyObject(person);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching on inheritance.
*/
public void testBatchFetchingInheritance() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select p from Person p");
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
query.setHint(QueryHints.BATCH, "p.company");
List<Person> result = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 5) {
fail("Should have been 5 query but was: " + counter.getSqlStatements().size());
}
for (Person person : result) {
if (person instanceof Engineer) {
((Engineer)person).getCompany();
}
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > 5) {
fail("Should have been 5 queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Person person : result) {
verifyObject(person);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching using first/max results.
*/
public void testBatchFetchingPagination2() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e");
query.setHint(QueryHints.BATCH, "e.address");
query.setHint(QueryHints.BATCH, "e.manager");
query.setFirstResult(5);
query.setMaxResults(5);
List<Employee> results = query.getResultList();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
if (results.size() > 5) {
fail("Should have only returned 5 objects but was: " + results.size());
}
for (Employee employee : results) {
employee.getAddress();
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > 2) {
fail("Should have been 2 queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Employee employee : results) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test batch fetching using a cursor.
*/
public void testBatchFetchingCursor() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e");
query.setHint(QueryHints.BATCH_TYPE, String.valueOf(BatchFetchType.IN)); // Test as String as well.
query.setHint(QueryHints.BATCH, "e.address");
query.setHint(QueryHints.BATCH, "e.manager");
query.setHint(QueryHints.CURSOR_PAGE_SIZE, 5);
query.setHint(QueryHints.CURSOR_INITIAL_SIZE, 2);
Iterator<Employee> results = (Iterator<Employee>)query.getSingleResult();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
int count = 0;
List<Employee> employees = new ArrayList<Employee>();
while (results.hasNext()) {
Employee employee = results.next();
employee.getAddress();
count++;
}
if (isWeavingEnabled() && counter.getSqlStatements().size() > (count/5 + 2)) {
fail("Should have been " + (count/5 + 2) + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Employee employee : employees) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test join fetching using a cursor.
*/
public void testJoinFetchingCursor() {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
// Count SQL.
QuerySQLTracker counter = new QuerySQLTracker(getServerSession());
try {
Query query = em.createQuery("Select e from Employee e order by e.id"); // Currently need to order for multiple 1-m joins.
query.setHint(QueryHints.LEFT_FETCH, "e.address");
//query.setHint(QueryHints.LEFT_FETCH, "e.manager"); - has eagers
//query.setHint(QueryHints.LEFT_FETCH, "e.projects"); - has eagers
//query.setHint(QueryHints.LEFT_FETCH, "e.managedEmployees"); - has eagers
query.setHint(QueryHints.LEFT_FETCH, "e.responsibilities");
query.setHint(QueryHints.LEFT_FETCH, "e.dealers");
query.setHint(QueryHints.LEFT_FETCH, "e.phoneNumbers");
//query.setHint(QueryHints.BATCH, "e.department"); is join fetched already.
query.setHint(QueryHints.LEFT_FETCH, "e.workWeek");
query.setHint(QueryHints.CURSOR_PAGE_SIZE, 5);
query.setHint(QueryHints.CURSOR_INITIAL_SIZE, 2);
Iterator<Employee> results = (Iterator<Employee>)query.getSingleResult();
if (isWeavingEnabled() && counter.getSqlStatements().size() != 1) {
fail("Should have been 1 query but was: " + counter.getSqlStatements().size());
}
int count = 0;
List<Employee> employees = new ArrayList<Employee>();
while (results.hasNext()) {
Employee employee = results.next();
employees.add(employee);
employee.getAddress();
employee.getResponsibilities().size();
employee.getDealers().size();
employee.getPhoneNumbers().size();
employee.getWorkWeek().size();
count++;
}
int queries = 1;
if (isWeavingEnabled() && counter.getSqlStatements().size() > queries) {
fail("Should have been " + queries + " queries but was: " + counter.getSqlStatements().size());
}
clearCache();
for (Employee employee : employees) {
verifyObject(employee);
}
} finally {
rollbackTransaction(em);
closeEntityManager(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test cache hits on pk JPQL queries.
*/
public void testJPQLCacheHits() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee");
List result = query.getResultList();
Employee employee = (Employee)result.get(result.size() - 1);
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.id = :id");
query.setParameter("id", employee.getId());
Employee queryResult = (Employee)query.getSingleResult();
if (!queryResult.getId().equals(employee.getId())) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit did not occur: " + counter.getSqlStatements());
}
} finally {
rollbackTransaction(em);
if (counter != null) {
counter.remove();
}
}
}
/**
* Test cache indexes.
*/
public void testCacheIndexes() {
EntityManager em = createEntityManager();
beginTransaction(em);
QuerySQLTracker counter = null;
Buyer buyer = null;
Employee employee = null;
String lastName = null;
try {
// Load an employee into the cache.
Query query = em.createQuery("Select employee from Employee employee where employee.lastName = 'Chanley'");
List result = query.getResultList();
employee = (Employee)result.get(0);
lastName = employee.getLastName();
// Count SQL.
counter = new QuerySQLTracker(getServerSession());
// Query by primary key.
query = em.createQuery("Select employee from Employee employee where employee.firstName = :firstName and employee.lastName = :lastName");
query.setParameter("firstName", employee.getFirstName());
query.setParameter("lastName", employee.getLastName());
counter.getSqlStatements().clear();
Employee queryResult = (Employee)query.getSingleResult();
if (queryResult != employee) {
fail("Employees are not equal: " + employee + ", " + queryResult);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
employee.setLastName("fail");
commitTransaction(em);
query = em.createQuery("Select employee from Employee employee where employee.firstName = :firstName and employee.lastName = :lastName");
query.setParameter("firstName", employee.getFirstName());
query.setParameter("lastName", lastName);
counter.getSqlStatements().clear();
try {
queryResult = null;
queryResult = (Employee)query.getSingleResult();
} catch (NoResultException ignore) {}
if (queryResult != null) {
fail("Employees should not be found, " + queryResult);
}
if (counter.getSqlStatements().size() == 0) {
fail("Cache hit should not occur: " + counter.getSqlStatements());
}
closeEntityManager(em);
em = createEntityManager();
beginTransaction(em);
buyer = new Buyer();
buyer.setName("index");
buyer.setDescription("description");
em.persist(buyer);
commitTransaction(em);
query = em.createQuery("Select b from Buyer b where b.name = :name");
query.setParameter("name", buyer.getName());
counter.getSqlStatements().clear();
Buyer queryResult2 = (Buyer)query.getSingleResult();
if (!queryResult2.getName().equals(buyer.getName())) {
fail("Buyers are not equal: " + buyer + ", " + queryResult2);
}
if (counter.getSqlStatements().size() > 0) {
fail("Cache hit do not occur: " + counter.getSqlStatements());
}
} finally {
if (counter != null) {
counter.remove();
}
closeEntityManagerAndTransaction(em);
em = createEntityManager();
beginTransaction(em);
if (buyer != null) {
buyer = em.find(Buyer.class, buyer.getId());
em.remove(buyer);
}
if (employee != null) {
Employee reset = em.find(Employee.class, employee.getId());
reset.setLastName(lastName);
}
commitTransaction(em);
closeEntityManagerAndTransaction(em);
}
}
public void testQueryPESSIMISTICLockWithLimit() throws InterruptedException {
clearCache();
EntityManager em = createEntityManager();
beginTransaction(em);
try {
TypedQuery<Employee> query = em.createQuery("Select e from Employee e where e.lastName != :lastName", Employee.class);
query.setParameter("lastName", "Chanley");
query.setHint(QueryHints.PESSIMISTIC_LOCK, PessimisticLock.Lock);
query.setFirstResult(5);
query.setMaxResults(2);
List<Employee> results = query.getResultList();
final Employee e = results.get(0);
final String name = e.getFirstName();
if (results.size() > 2) {
fail("Should have only returned 2 objects but was: " + results.size());
}
clearCache();
final EntityManager em2 = createEntityManager();
try {
// P2 (Non-repeatable read)
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
beginTransaction(em2);
TypedQuery<Employee> query2 = em2.createQuery("select e from Employee e where e.id = :id", Employee.class);
query2.setParameter("id", e.getId());
query2.setHint(QueryHints.PESSIMISTIC_LOCK_TIMEOUT, 5000);
Employee emp = query2.getSingleResult(); // might wait for lock to be released
emp.setFirstName("Trouba");
commitTransaction(em2); // might wait for lock to be released
} catch (jakarta.persistence.RollbackException ex) {
if (!ex.getMessage().contains("org.eclipse.persistence.exceptions.DatabaseException")) {
ex.printStackTrace();
fail("it's not the right exception:" + ex);
}
}
}
};
Thread t2 = new Thread(runnable);
t2.start();
Thread.sleep(1000); // allow t2 to attempt update
em.refresh(e);
assertTrue("pessimistic lock failed: parallel transaction modified locked entity (non-repeatable read)", name.equals(e.getFirstName()));
rollbackTransaction(em); // release lock
t2.join(); // wait until t2 finished
} finally {
if (isTransactionActive(em2)) {
rollbackTransaction(em2);
}
closeEntityManager(em2);
}
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
}