mirror of https://github.com/apache/openjpa.git
OPENJPA-963: commit patch provided by Donald Woods
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@756193 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1d92b9a5c0
commit
9e683c7612
|
@ -4184,7 +4184,7 @@ public class DBDictionary
|
|||
* implementation of setting query and lock timeouts.
|
||||
*
|
||||
* @param stmnt
|
||||
* @param fetch
|
||||
* @param fetch - optional lock and query timeouts in milliseconds
|
||||
* @param forUpdate - true if we should also try setting a lock timeout
|
||||
* @throws SQLException
|
||||
*/
|
||||
|
@ -4210,7 +4210,7 @@ public class DBDictionary
|
|||
* implementation of setting query and lock timeouts.
|
||||
*
|
||||
* @param stmnt
|
||||
* @param fetch
|
||||
* @param conf - optional lock and query timeouts in milliseconds
|
||||
* @param forUpdate - true if we should also try setting a lock timeout
|
||||
* @throws SQLException
|
||||
*/
|
||||
|
@ -4229,22 +4229,44 @@ public class DBDictionary
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is to provide override for non-JDBC or JDBC-like
|
||||
* implementation of setting query timeout.
|
||||
* Provides the default validation handling of setting a query timeout.
|
||||
* @param stmnt
|
||||
* @param timeout in milliseconds
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void setQueryTimeout(PreparedStatement stmnt, int timeout)
|
||||
throws SQLException {
|
||||
if (supportsQueryTimeout && timeout >= 0) {
|
||||
if (timeout > 0 && timeout < 1000) {
|
||||
if (supportsQueryTimeout) {
|
||||
if (timeout == -1) {
|
||||
// special OpenJPA allowed case denoting no timeout
|
||||
timeout = 0;
|
||||
} else if (timeout < 0) {
|
||||
if (log.isWarnEnabled())
|
||||
log.warn(_loc.get("invalid-timeout", new Integer(timeout)));
|
||||
return;
|
||||
} else if (timeout > 0 && timeout < 1000) {
|
||||
// round up to 1 sec
|
||||
timeout = 1000;
|
||||
Log log = conf.getLog(JDBCConfiguration.LOG_JDBC);
|
||||
if (log.isWarnEnabled())
|
||||
log.warn(_loc.get("millis-query-timeout"));
|
||||
}
|
||||
stmnt.setQueryTimeout(timeout / 1000);
|
||||
setStatementQueryTimeout(stmnt, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow subclasses to provide DB unique override implementations of
|
||||
* setting query timeouts, while preserving the default timeout logic
|
||||
* in the public setQueryTimeout method.
|
||||
* @param stmnt
|
||||
* @param timeout in milliseconds
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected void setStatementQueryTimeout(PreparedStatement stmnt,
|
||||
int timeout) throws SQLException {
|
||||
// JDBC uses seconds, so we'll do a simple round-down conversion here
|
||||
stmnt.setQueryTimeout(timeout / 1000);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// ConnectionDecorator implementation
|
||||
|
|
|
@ -75,3 +75,5 @@ objectid-abstract: Cannot create new application identity instance for \
|
|||
abstract class "{0}".
|
||||
query-failed: A query statement timeout has occurred.
|
||||
query-timeout: A query statement timeout (set to {0} milliseconds) has occurred.
|
||||
invalid-timeout: An invalid timeout of {0} milliseconds was ignored. \
|
||||
Expected a value that is greater than or equal to zero.
|
||||
|
|
|
@ -16,18 +16,24 @@ package org.apache.openjpa.persistence.query;
|
|||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||
import org.apache.openjpa.jdbc.sql.DerbyDictionary;
|
||||
import org.apache.openjpa.kernel.Broker;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.persistence.JPAFacadeHelper;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
import org.apache.openjpa.persistence.OpenJPAQuery;
|
||||
import org.apache.openjpa.persistence.PersistenceException;
|
||||
import org.apache.openjpa.persistence.QueryTimeoutException;
|
||||
import org.apache.openjpa.persistence.query.common.apps.QTimeout;
|
||||
|
@ -36,14 +42,38 @@ import org.apache.openjpa.persistence.test.SQLListenerTestCase;
|
|||
|
||||
/**
|
||||
* Tests the new query timeout hint support in the JPA 2.0 spec.
|
||||
* Query timeout scenarios being tested:
|
||||
* 1) By default, there is no timeout
|
||||
* 2) Setting timeout to 0 is same as no timeout (JDBC defined)
|
||||
* 2.1) using Map properties on createEMF (or PU properties)
|
||||
* 2.2) using the QueryHint annotation
|
||||
* 2.3) using setHint()
|
||||
* 3) Setting timeout to msecs < DELAY value causes new
|
||||
* javax.persistence.QueryTimeoutException for databases that do not
|
||||
* cause a rollback or a PersistenceException if they do, when set by:
|
||||
* 3.1) using persistence.xml PU properties (or createEMF Map properties)
|
||||
* 3.2) using the QueryHint annotation
|
||||
* 3.3) calling setHint()
|
||||
* Query operations to validate through cross coverage of items #1-#3:
|
||||
* a) getResultList()
|
||||
* b) getSingleResult()
|
||||
* c) executeUpdate()
|
||||
* Other behaviors to test for:
|
||||
* 4) Setting timeout to < 0 should be treated as no timeout supplied
|
||||
* 5) Updates after EM.find()/findAll() are not affected by query timeout
|
||||
* Exception generation to test for:
|
||||
* If the DB query timeout does not cause a transaction rollback, then a
|
||||
* QueryTimeoutException should be thrown.
|
||||
* Applicable to: unknown
|
||||
* Else if the DB query timeout causes a transaction rollback, then a
|
||||
* PersistenceException should be thrown instead of a QTE.
|
||||
* Applicable to: Derby
|
||||
*
|
||||
* @version $Rev$ $Date$
|
||||
*/
|
||||
public class TestQueryTimeout extends SQLListenerTestCase {
|
||||
|
||||
private boolean skipTests = false;
|
||||
// does the DB platform allow retry instead of forcing transaction rollback
|
||||
private boolean supportsQueryTimeoutException = false;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
|
@ -57,20 +87,24 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
// exception type to catch
|
||||
DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration())
|
||||
.getDBDictionaryInstance();
|
||||
if ((dict.supportsQueryTimeout) && (dict instanceof DerbyDictionary)) {
|
||||
// set whether we expect to see QueryTimeoutException or
|
||||
// PersistenceException
|
||||
supportsQueryTimeoutException = false;
|
||||
} else {
|
||||
// FIXME drwoods - OPENJPA-964 - haven't determined what the other
|
||||
// DBs support
|
||||
// setQueryTimeout is not working with DB2 v9.5.3a on Windows...
|
||||
getLog().info("TestQueryTimeout tests are being skipped, due to " +
|
||||
"DB not supporting Query Timeouts.");
|
||||
if (dict.supportsQueryTimeout) {
|
||||
if (!(dict instanceof DerbyDictionary)) {
|
||||
// FIXME drwoods - OPENJPA-964 - haven't determined what the
|
||||
// other DBs support
|
||||
// setQueryTimeout is not working with DB2 v9.5.3a on Windows
|
||||
getLog().info("FIXME - TestQueryTimeout tests are being " +
|
||||
"skipped, as tests are not being run against a Derby DB.");
|
||||
skipTests = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
getLog().info("TestQueryTimeout tests are being skipped, " +
|
||||
"due to DB not supporting Query Timeouts.");
|
||||
skipTests = true;
|
||||
}
|
||||
if (skipTests)
|
||||
return;
|
||||
|
||||
// create some initial entities
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
|
@ -89,53 +123,50 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
// execute some native SQL with no timeouts
|
||||
// create delay function only on Derby, other DBs require manual setup
|
||||
if (dict instanceof DerbyDictionary) {
|
||||
getLog().trace("setUp() - creating DELAY function only for Derby." +
|
||||
" Other DBs require manual setup.");
|
||||
// remove existing function if it exists and ignore any errors
|
||||
getLog().trace("setUp() - creating DELAY function only for " +
|
||||
"Derby. Other DBs require manual setup.");
|
||||
// remove existing function if it exists and recreate
|
||||
try {
|
||||
exec(true, 0, "DROP FUNCTION DELAY");
|
||||
exec(false, 0, "CREATE FUNCTION DELAY(SECONDS INTEGER, " +
|
||||
"VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA NO SQL " +
|
||||
"LANGUAGE JAVA EXTERNAL NAME 'org.apache.openjpa.persistence." +
|
||||
"VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA " +
|
||||
"NO SQL LANGUAGE JAVA EXTERNAL NAME " +
|
||||
"'org.apache.openjpa.persistence." +
|
||||
"query.TestQueryTimeout.delay'");
|
||||
} catch (SQLException sqe) {
|
||||
fail(sqe.toString());
|
||||
}
|
||||
getLog().trace("setUp() - creating BEFORE UPDATE/INSERT TRIGGERs for " +
|
||||
"all DBs");
|
||||
}
|
||||
|
||||
// create triggers on all DBs
|
||||
try {
|
||||
getLog().trace("setUp() - creating BEFORE UPDATE/INSERT " +
|
||||
"TRIGGERs for all DBs");
|
||||
exec(false, 0, "CREATE TRIGGER t1 NO CASCADE BEFORE UPDATE ON " +
|
||||
"qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-1)");
|
||||
exec(false, 0, "CREATE TRIGGER t2 NO CASCADE BEFORE INSERT ON " +
|
||||
"qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-2)");
|
||||
// Don't include a DELETE trigger, as it slows down the DROP_TABLES
|
||||
// cleanup between tests
|
||||
// exec(0, "CREATE TRIGGER t3 NO CASCADE BEFORE DELETE ON qtimeout " +
|
||||
// "FOR EACH ROW MODE DB2SQL values DELAY(2,-3)");
|
||||
// exec(0, "CREATE TRIGGER t3 NO CASCADE BEFORE DELETE ON " +
|
||||
// "qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-3)");
|
||||
} catch (SQLException sqe) {
|
||||
if (dict instanceof DerbyDictionary) {
|
||||
// Always fail if we couldn't create triggers in Derby
|
||||
fail(sqe.toString());
|
||||
} else {
|
||||
// just disable tests for other DBs
|
||||
getLog().info("TestQueryTimeout tests are being skipped, " +
|
||||
"due to DB delay() function missing and/or problems " +
|
||||
"creating the required triggers. DBs other than " +
|
||||
"Derby require manual setup steps for these tests.");
|
||||
skipTests = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Query timeout scenarios to test for:
|
||||
* 1) By default, there is no timeout
|
||||
* 2) Setting timeout to 0 is same as no timeout (JDBC defined)
|
||||
* 2.1) using the QueryHint annotation
|
||||
* 2.2) calling setHint()
|
||||
* 3) Setting timeout to msecs < DELAY value causes new
|
||||
* javax.persistence.QueryTimeoutException when set by:
|
||||
* 3.1) using the QueryHint annotation
|
||||
* 3.2) calling setHint()
|
||||
* Operations to validate through cross coverage of items #1-#3:
|
||||
* a) getResultList()
|
||||
* b) getSingleResult()
|
||||
* c) executeUpdate()
|
||||
* Other behaviors to test for:
|
||||
* 4) Setting timeout to < 0 should be treated as no timeout supplied
|
||||
* Exception generation to test for:
|
||||
* If the DB query timeout does not cause a transaction rollback, then a
|
||||
* QueryTimeoutException should be thrown.
|
||||
* Applicable to: unknown
|
||||
* Else if the DB query timeout causes a transaction rollback, then a
|
||||
* PersistenceException should be thrown instead of a QTE.
|
||||
* Applicable to: Derby
|
||||
*/
|
||||
|
||||
/**
|
||||
* Scenario being tested: 1a) By default, there is no timeout for queries.
|
||||
|
@ -186,29 +217,29 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout1c() - No Update timeout");
|
||||
getLog().trace("testQueryTimeout1c() - No executeUpdate timeout");
|
||||
EntityManager em = null;
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
Query q = em.createQuery("UPDATE QTimeout q SET q.stringField = :strVal WHERE q.id = 1");
|
||||
q.setParameter("strVal", new String("updated"));
|
||||
// verify no default javax.persistence.query.timeout is supplied
|
||||
Map<String, Object> hints = q.getHints();
|
||||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
QTimeout qt = em.find(QTimeout.class, new Integer(1));
|
||||
em.getTransaction().begin();
|
||||
qt.setStringField("updated");
|
||||
em.flush();
|
||||
int count = q.executeUpdate();
|
||||
em.getTransaction().commit();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout1c() - EM find/update runTime" +
|
||||
" msecs=" + runTime);
|
||||
getLog().trace("testQueryTimeout1c() - executeUpdate runTime " +
|
||||
"msecs=" + runTime);
|
||||
assertTrue("Verify we received one result.", (count == 1));
|
||||
// Hack - Windows sometimes returns 1999 instead of 2000+
|
||||
assertTrue("Should have taken 2+ secs, but was msecs=" +
|
||||
runTime, runTime > 1900);
|
||||
em.clear();
|
||||
qt = em.find(QTimeout.class, new Integer(1));
|
||||
assertEquals("Verify the entity was updated.",
|
||||
qt.getStringField(), "updated");
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected testQueryTimeout1c() exception = " + e);
|
||||
}
|
||||
|
@ -220,16 +251,76 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 2.1.a) Explicit annotated QueryHint of timeout=0
|
||||
* Scenario being tested: 2.1.b) Explicit Map of properties to createEMF
|
||||
* with timeout=0 is treated the same as the default no query timeout.
|
||||
* Expected Results: The DELAY function is being called and the query
|
||||
* takes 2000+ msecs to complete.
|
||||
*/
|
||||
public void testQueryTimeout21b() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout21b() - Map(timeout=0)");
|
||||
OpenJPAEntityManagerFactory emf = null;
|
||||
OpenJPAEntityManager em = null;
|
||||
Integer setTime = new Integer(0);
|
||||
// create the Map to test overrides
|
||||
Map<String,String >props = new HashMap<String,String>();
|
||||
props.put("javax.persistence.query.timeout", "0");
|
||||
|
||||
try {
|
||||
// create our EMF with our timeout property
|
||||
emf = OpenJPAPersistence.createEntityManagerFactory(
|
||||
"qtimeout-no-properties", "persistence3.xml", props);
|
||||
assertNotNull(emf);
|
||||
// verify Map properties updated the config
|
||||
OpenJPAConfiguration conf = emf.getConfiguration();
|
||||
assertNotNull(conf);
|
||||
assertEquals("Map provided query timeout", setTime.intValue(),
|
||||
conf.getQueryTimeout());
|
||||
// verify no default javax.persistence.query.timeout is supplied
|
||||
// as the Map properties are not passed through as hints
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
OpenJPAQuery q = em.createNamedQuery("NoHintSingle");
|
||||
Map<String, Object> hints = q.getHints();
|
||||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
// verify internal config values were updated
|
||||
assertEquals("Map provided query timeout", setTime.intValue(),
|
||||
q.getFetchPlan().getQueryTimeout());
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
Object result = q.getSingleResult();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout21b() - NoHintSingle runTime " +
|
||||
"msecs=" + runTime);
|
||||
// Hack - Windows sometimes returns 1999 instead of 2000+
|
||||
assertTrue("Should have taken 2+ secs, but was msecs=" +
|
||||
runTime, runTime > 1900);
|
||||
assertNotNull("Verify we received a result.", result);
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected testQueryTimeout21b() exception = " + e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 2.2.a) Explicit annotated QueryHint of timeout=0
|
||||
* is treated the same as the default no timeout for queries.
|
||||
* Expected Results: The DELAY function is being called and the query
|
||||
* takes 6000+ msecs to complete.
|
||||
*/
|
||||
public void testQueryTimeout2a() {
|
||||
public void testQueryTimeout22a() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout2a() - QueryHint=0");
|
||||
getLog().trace("testQueryTimeout22a() - QueryHint=0");
|
||||
EntityManager em = null;
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
|
@ -240,7 +331,7 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
assertTrue(hints.containsKey("javax.persistence.query.timeout"));
|
||||
Integer timeout = new Integer(
|
||||
(String) hints.get("javax.persistence.query.timeout"));
|
||||
getLog().trace("testQueryTimeout2a() - Retrieved hint " +
|
||||
getLog().trace("testQueryTimeout22a() - Retrieved hint " +
|
||||
"javax.persistence.query.timeout=" + timeout);
|
||||
assertEquals(timeout, new Integer(0));
|
||||
|
||||
|
@ -250,14 +341,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
List results = q.getResultList();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout2a() - Hint0msec runTime msecs="
|
||||
getLog().trace("testQueryTimeout22a() - Hint0msec runTime msecs="
|
||||
+ runTime);
|
||||
// Hack - Windows sometimes returns 5999 instead of 6000+
|
||||
assertTrue("Should have taken 6+ secs, but was msecs=" +
|
||||
runTime, runTime > 5900);
|
||||
assertEquals("Verify we found 2 results.", 2, results.size());
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected testQueryTimeout2a() exception = " + e);
|
||||
fail("Unexpected testQueryTimeout22a() exception = " + e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
|
@ -267,17 +358,17 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 2.1.b) Explicit setHint of timeout=0 is treated
|
||||
* Scenario being tested: 2.3.b) Explicit setHint of timeout=0 is treated
|
||||
* the same as the default no timeout for queries.
|
||||
* Expected Results: The DELAY function is being called and the query
|
||||
* takes 2000+ msecs to complete.
|
||||
*/
|
||||
public void testQueryTimeout2b() {
|
||||
public void testQueryTimeout23b() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
Integer setTime = new Integer(0);
|
||||
getLog().trace("testQueryTimeout2b() - setHint(" + setTime + ")");
|
||||
getLog().trace("testQueryTimeout23b() - setHint(" + setTime + ")");
|
||||
EntityManager em = null;
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
|
@ -289,14 +380,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
|
||||
// update the timeout value to 0 and verify it was set
|
||||
getLog().trace("testQueryTimeout2b() - Setting hint " +
|
||||
getLog().trace("testQueryTimeout23b() - Setting hint " +
|
||||
"javax.persistence.query.timeout=" + setTime);
|
||||
q.setHint("javax.persistence.query.timeout", setTime);
|
||||
hints = q.getHints();
|
||||
assertTrue(hints.containsKey("javax.persistence.query.timeout"));
|
||||
Integer timeout = (Integer) hints.get(
|
||||
"javax.persistence.query.timeout");
|
||||
getLog().trace("testQueryTimeout2b() - Retrieved hint " +
|
||||
getLog().trace("testQueryTimeout23b() - Retrieved hint " +
|
||||
"javax.persistence.query.timeout=" + timeout);
|
||||
assertEquals(timeout, setTime);
|
||||
|
||||
|
@ -305,14 +396,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
Object result = q.getSingleResult();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout2b() - NoHintSingle runTime " +
|
||||
getLog().trace("testQueryTimeout23b() - NoHintSingle runTime " +
|
||||
"msecs=" + runTime);
|
||||
// Hack - Windows sometimes returns 1999 instead of 2000+
|
||||
assertTrue("Should have taken 2+ secs, but was msecs=" +
|
||||
runTime, runTime > 1900);
|
||||
assertNotNull("Verify we received a result.", result);
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected testQueryTimeout2b() exception = " + e);
|
||||
fail("Unexpected testQueryTimeout23b() exception = " + e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
|
@ -322,32 +413,114 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 3.1.a) Explicit annotated QueryHint of
|
||||
* timeout=1000 msecs will cause the query to timeout.
|
||||
* Expected Results: QueryTimeoutException or PersistenceException
|
||||
* Scenario being tested: 3.1.c) Explicit persistence.xml provided PU
|
||||
* property of timeout=1000 msecs will cause the query to timeout.
|
||||
* Expected Results: The DELAY function is being called and the query
|
||||
* takes 2000+ msecs to complete.
|
||||
*/
|
||||
public void testQueryTimeout3a() {
|
||||
public void testQueryTimeout31c() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout31c() - PU(timeout=1000), executeUpdate timeout");
|
||||
OpenJPAEntityManagerFactory emf = null;
|
||||
OpenJPAEntityManager em = null;
|
||||
Integer setTime = new Integer(1000);
|
||||
getLog().trace("testQueryTimeout3a() - QueryHint(" + setTime + ")");
|
||||
EntityManager em = null;
|
||||
|
||||
try {
|
||||
// create our EMF with our PU set timeout property
|
||||
emf = OpenJPAPersistence.createEntityManagerFactory(
|
||||
"qtimeout-1000msecs", "persistence3.xml");
|
||||
assertNotNull(emf);
|
||||
// verify PU properties updated the config
|
||||
OpenJPAConfiguration conf = emf.getConfiguration();
|
||||
assertNotNull(conf);
|
||||
assertEquals("PU provided query timeout", setTime.intValue(),
|
||||
conf.getQueryTimeout());
|
||||
// create EM and Query
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
Query q = em.createNamedQuery("Hint1000msec");
|
||||
OpenJPAQuery q = em.createNativeQuery("UPDATE QTimeout SET stringField = ? WHERE mod(DELAY(2,id),2)=0");
|
||||
q.setParameter(1, new String("updated"));
|
||||
// verify no default javax.persistence.query.timeout is supplied
|
||||
Map<String, Object> hints = q.getHints();
|
||||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
// verify internal config values were updated
|
||||
assertEquals("PU provided query timeout", setTime.intValue(),
|
||||
q.getFetchPlan().getQueryTimeout());
|
||||
|
||||
// verify queryTimeout on EM find operations
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
em.getTransaction().begin();
|
||||
int count = q.executeUpdate();
|
||||
em.getTransaction().commit();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout31c() - executeUpdate runTime " +
|
||||
"msecs=" + runTime);
|
||||
fail("QueryTimeout for executeUpdate failed to cause an " +
|
||||
"Exception in testQueryTimeout31c(" + setTime +
|
||||
" mscs), runTime msecs=" + runTime);
|
||||
} catch (Exception e) {
|
||||
// expected - Should cause a QueryTimeoutException for Derby
|
||||
checkException("testQueryTimeout31c()", e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
if (em.getTransaction().isActive())
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 3.2.a) Explicit annotated QueryHint of
|
||||
* timeout=1000 msecs will override the PU and Map provided timeouts
|
||||
* and cause the query to timeout.
|
||||
* Expected Results: QueryTimeoutException or PersistenceException
|
||||
*/
|
||||
public void testQueryTimeout32a() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout32a() - PU(1000), Map(0), QueryHint(1000)");
|
||||
OpenJPAEntityManagerFactory emf = null;
|
||||
OpenJPAEntityManager em = null;
|
||||
Integer setTime = new Integer(0);
|
||||
// create the Map to test overrides
|
||||
Map<String,String> props = new HashMap<String,String>();
|
||||
props.put("javax.persistence.query.timeout", "0");
|
||||
|
||||
try {
|
||||
// create our EMF with our PU set timeout property
|
||||
emf = OpenJPAPersistence.createEntityManagerFactory(
|
||||
"qtimeout-1000msecs", "persistence3.xml", props);
|
||||
assertNotNull(emf);
|
||||
// verify Map properties overrode the PU properties in config
|
||||
OpenJPAConfiguration conf = emf.getConfiguration();
|
||||
assertNotNull(conf);
|
||||
assertEquals("Map provided query timeout", setTime.intValue(),
|
||||
conf.getQueryTimeout());
|
||||
// create EM and named query
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
OpenJPAQuery q = em.createNamedQuery("Hint1000msec");
|
||||
setTime = 1000;
|
||||
// verify javax.persistence.query.timeout hint via annotation set
|
||||
Map<String, Object> hints = q.getHints();
|
||||
assertTrue(hints.containsKey("javax.persistence.query.timeout"));
|
||||
Integer timeout = new Integer((String) hints.get(
|
||||
"javax.persistence.query.timeout"));
|
||||
getLog().trace(
|
||||
"testQueryTimeout3a() - Found javax.persistence.query.timeout="
|
||||
"testQueryTimeout32a() - Found javax.persistence.query.timeout="
|
||||
+ timeout);
|
||||
assertTrue("Expected to find a javax.persistence.query.timeout="
|
||||
+ setTime, (timeout.intValue() == setTime.intValue()));
|
||||
// verify internal config values were updated
|
||||
assertEquals("QueryHint provided query timeout", setTime.intValue(),
|
||||
q.getFetchPlan().getQueryTimeout());
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
@ -356,16 +529,16 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace(
|
||||
"testQueryTimeout3a() - Hint1000msec runTime msecs="
|
||||
"testQueryTimeout32a() - Hint1000msec runTime msecs="
|
||||
+ runTime);
|
||||
//assertEquals("Should never get valid results due to the " +
|
||||
// "timeout.", 2, results.size());
|
||||
fail("QueryTimeout annotation failed to cause an Exception " +
|
||||
"in testQueryTimeout3a(" + setTime +
|
||||
"in testQueryTimeout32a(" + setTime +
|
||||
" msecs), runTime msecs=" + runTime);
|
||||
} catch (Exception e) {
|
||||
// expected
|
||||
checkException("testQueryTimeout3a()", e);
|
||||
checkException("testQueryTimeout32a()", e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
|
@ -375,16 +548,16 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 3.2.b) Explicit setHint of timeout to 1000 msecs
|
||||
* Scenario being tested: 3.3.b) Explicit setHint of timeout to 1000 msecs
|
||||
* will cause the query to timeout.
|
||||
* Expected Results: QueryTimeoutException or PersistenceException
|
||||
*/
|
||||
public void testQueryTimeout3b() {
|
||||
public void testQueryTimeout33b() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
Integer setTime = new Integer(1000);
|
||||
getLog().trace("testQueryTimeout3b() - setHint(" + setTime + ")");
|
||||
getLog().trace("testQueryTimeout33b() - setHint(" + setTime + ")");
|
||||
EntityManager em = null;
|
||||
try {
|
||||
em = emf.createEntityManager();
|
||||
|
@ -396,7 +569,7 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
|
||||
// update the timeout value and verify it was set
|
||||
getLog().trace("testQueryTimeout3b() - Setting hint " +
|
||||
getLog().trace("testQueryTimeout33b() - Setting hint " +
|
||||
"javax.persistence.query.timeout=" + setTime);
|
||||
q.setHint("javax.persistence.query.timeout", setTime);
|
||||
hints = q.getHints();
|
||||
|
@ -412,15 +585,15 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace(
|
||||
"testQueryTimeout3b() - NoHintSingle runTime msecs="
|
||||
"testQueryTimeout33b() - NoHintSingle runTime msecs="
|
||||
+ runTime);
|
||||
//assertNull("Should never get valid result due to the timeout.", result);
|
||||
fail("QueryTimeout annotation failed to cause an Exception " +
|
||||
"in testQueryTimeout3b(" + setTime +
|
||||
"in testQueryTimeout33b(" + setTime +
|
||||
" mscs), runTime msecs=" + runTime);
|
||||
} catch (Exception e) {
|
||||
// expected
|
||||
checkException("testQueryTimeout3b()", e);
|
||||
checkException("testQueryTimeout33b()", e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
|
@ -429,6 +602,85 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 3.3.c) Explicit setHint of timeout to 1000 msecs
|
||||
* will cause the PU provided timeout=0 value to be overridden and the
|
||||
* executeUpdate to timeout.
|
||||
* Expected Results: QueryTimeoutException (Derby) or PersistenceException
|
||||
*/
|
||||
public void testQueryTimeout33c() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout33c() - PU(timeout=0), setHint(1000), executeUpdate timeout");
|
||||
OpenJPAEntityManagerFactory emf = null;
|
||||
OpenJPAEntityManager em = null;
|
||||
Integer setTime = new Integer(0);
|
||||
|
||||
try {
|
||||
// create our EMF with our PU set timeout property
|
||||
emf = OpenJPAPersistence.createEntityManagerFactory(
|
||||
"qtimeout-0msecs", "persistence3.xml");
|
||||
assertNotNull(emf);
|
||||
// verify PU properties updated the config
|
||||
OpenJPAConfiguration conf = emf.getConfiguration();
|
||||
assertNotNull(conf);
|
||||
assertEquals("PU provided no query timeout", setTime.intValue(),
|
||||
conf.getQueryTimeout());
|
||||
// create EM and Query
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
// Following fails to cause a SQLException, but takes 2+ secs
|
||||
// Query q = em.createQuery("UPDATE QTimeout q SET q.stringField = :strVal WHERE q.id > 0");
|
||||
// q.setParameter("strVal", new String("updated"));
|
||||
// Following fails to cause a SQLException, but takes 2+ secs
|
||||
// Query q = em.createNativeQuery("INSERT INTO QTimeout (id, stringField) VALUES (?,?)");
|
||||
// q.setParameter(1, 99);
|
||||
// q.setParameter(2, new String("inserted"));
|
||||
OpenJPAQuery q = em.createNativeQuery("UPDATE QTimeout SET stringField = ? WHERE mod(DELAY(2,id),2)=0");
|
||||
q.setParameter(1, new String("updated"));
|
||||
// verify no default javax.persistence.query.timeout is supplied
|
||||
Map<String, Object> hints = q.getHints();
|
||||
assertFalse(hints.containsKey("javax.persistence.query.timeout"));
|
||||
// update the query timeout value and verify it was set
|
||||
setTime = 1000;
|
||||
getLog().trace("testQueryTimeout33c() - Setting hint " +
|
||||
"javax.persistence.query.timeout=" + setTime);
|
||||
q.setHint("javax.persistence.query.timeout", setTime);
|
||||
hints = q.getHints();
|
||||
assertTrue(hints.containsKey("javax.persistence.query.timeout"));
|
||||
Integer timeout = (Integer) hints.get(
|
||||
"javax.persistence.query.timeout");
|
||||
assertEquals(timeout, setTime);
|
||||
// verify internal config values were updated
|
||||
assertEquals("PU provided query timeout", setTime.intValue(),
|
||||
q.getFetchPlan().getQueryTimeout());
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
em.getTransaction().begin();
|
||||
int count = q.executeUpdate();
|
||||
em.getTransaction().commit();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout33c() - executeUpdate runTime " +
|
||||
"msecs=" + runTime);
|
||||
fail("QueryTimeout for executeUpdate failed to cause an " +
|
||||
"Exception in testQueryTimeout33c(" + setTime +
|
||||
" mscs), runTime msecs=" + runTime);
|
||||
} catch (Exception e) {
|
||||
// expected - Should cause a QueryTimeoutException for Derby
|
||||
checkException("testQueryTimeout33c()", e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
if (em.getTransaction().isActive())
|
||||
em.getTransaction().rollback();
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 4) Timeouts < 0 are ignored and treated as the
|
||||
* default no timeout scenario.
|
||||
|
@ -487,6 +739,65 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario being tested: 5) PU Query timeout hints do not affect EM
|
||||
* operations like updating Entities returned by EM.find()/findAll()
|
||||
* Expected Results: The DELAY function is being called and the update
|
||||
* takes 2000+ msecs to complete.
|
||||
*/
|
||||
public void testQueryTimeout5() {
|
||||
if (skipTests) {
|
||||
return;
|
||||
}
|
||||
getLog().trace("testQueryTimeout5() - No EM.find() update timeout");
|
||||
OpenJPAEntityManagerFactory emf = null;
|
||||
OpenJPAEntityManager em = null;
|
||||
Integer setTime = new Integer(1000);
|
||||
|
||||
try {
|
||||
// create our EMF with our PU set timeout property
|
||||
emf = OpenJPAPersistence.createEntityManagerFactory(
|
||||
"qtimeout-1000msecs", "persistence3.xml");
|
||||
assertNotNull(emf);
|
||||
// verify PU properties updated the config
|
||||
OpenJPAConfiguration conf = emf.getConfiguration();
|
||||
assertNotNull(conf);
|
||||
assertEquals("PU provided timeout", setTime.intValue(),
|
||||
conf.getQueryTimeout());
|
||||
// create EM
|
||||
em = emf.createEntityManager();
|
||||
assertNotNull(em);
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
QTimeout qt = em.find(QTimeout.class, new Integer(1));
|
||||
em.getTransaction().begin();
|
||||
qt.setStringField("updated");
|
||||
em.flush();
|
||||
em.getTransaction().commit();
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runTime = endTime - startTime;
|
||||
getLog().trace("testQueryTimeout1d() - EM find/update runTime" +
|
||||
" msecs=" + runTime);
|
||||
// Hack - Windows sometimes returns 1999 instead of 2000+
|
||||
assertTrue("Should have taken 2+ secs, but was msecs=" +
|
||||
runTime, runTime > 1900);
|
||||
em.clear();
|
||||
qt = em.find(QTimeout.class, new Integer(1));
|
||||
assertEquals("Verify the entity was updated.",
|
||||
qt.getStringField(), "updated");
|
||||
} catch (Exception e) {
|
||||
// setting a timeout property via PU or Map shouldn't cause a
|
||||
// timeout exception
|
||||
fail("Unexpected testQueryTimeout5() exception = " + e);
|
||||
}
|
||||
} finally {
|
||||
if ((em != null) && em.isOpen()) {
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal convenience method to execute SQL statements
|
||||
*
|
||||
|
@ -495,7 +806,8 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
* @param timeoutSecs
|
||||
* @param fail
|
||||
*/
|
||||
private void exec(boolean ignoreExceptions, int timeoutSecs, String sql) {
|
||||
private void exec(boolean ignoreExceptions, int timeoutSecs, String sql)
|
||||
throws SQLException {
|
||||
EntityManager em = null;
|
||||
Statement s = null;
|
||||
try {
|
||||
|
@ -511,7 +823,8 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
s.execute(sql);
|
||||
} catch (SQLException sqe) {
|
||||
if (!ignoreExceptions) {
|
||||
fail(sqe.toString());
|
||||
// fail(sqe.toString());
|
||||
throw sqe;
|
||||
}
|
||||
} finally {
|
||||
if (s != null) {
|
||||
|
@ -544,13 +857,10 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
* @param e
|
||||
*/
|
||||
private void checkException(String test, Exception e) {
|
||||
if (supportsQueryTimeoutException) {
|
||||
assertTrue("Expected QueryTimeoutException instead of " + e,
|
||||
matchesExpectedException(QueryTimeoutException.class, e));
|
||||
} else {
|
||||
assertTrue("Expected PersistenceException instead of " + e,
|
||||
// no easy way to determine exact Exception type for all DBs
|
||||
assertTrue(test + " - UNEXPECTED Exception = " + e,
|
||||
matchesExpectedException(QueryTimeoutException.class, e) ||
|
||||
matchesExpectedException(PersistenceException.class, e));
|
||||
}
|
||||
getLog().trace(test + " - Caught expected Exception = " + e);
|
||||
}
|
||||
|
||||
|
@ -569,6 +879,11 @@ public class TestQueryTimeout extends SQLListenerTestCase {
|
|||
if (tested != null) {
|
||||
Class<?> testExClass = tested.getClass();
|
||||
exMatched = expected.isAssignableFrom(testExClass);
|
||||
if (exMatched) {
|
||||
// make sure it is our expected exception text from
|
||||
// localizer.properties
|
||||
exMatched = (tested.getMessage().indexOf("query statement timeout") != -1);
|
||||
}
|
||||
}
|
||||
return exMatched;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
version="1.0">
|
||||
|
||||
<!-- Following 2 PUs are used by TestQueryProperties -->
|
||||
<!-- Following PUs are used by TestQueryProperties and TestQueryTimeout -->
|
||||
<persistence-unit name="qtimeout-no-properties">
|
||||
<class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
|
||||
</persistence-unit>
|
||||
|
@ -34,7 +34,13 @@
|
|||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<!-- Following PU is used by TestQueryTimeout -->
|
||||
<persistence-unit name="qtimeout-0msecs">
|
||||
<class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
|
||||
<properties>
|
||||
<property name="javax.persistence.query.timeout" value="0"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
|
||||
<persistence-unit name="qtimeout-1000msecs">
|
||||
<class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
|
||||
<properties>
|
||||
|
|
Loading…
Reference in New Issue