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:
Fay Wang 2009-03-19 20:54:28 +00:00
parent 1d92b9a5c0
commit 9e683c7612
4 changed files with 462 additions and 117 deletions

View File

@ -4184,7 +4184,7 @@ public class DBDictionary
* implementation of setting query and lock timeouts. * implementation of setting query and lock timeouts.
* *
* @param stmnt * @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 * @param forUpdate - true if we should also try setting a lock timeout
* @throws SQLException * @throws SQLException
*/ */
@ -4210,7 +4210,7 @@ public class DBDictionary
* implementation of setting query and lock timeouts. * implementation of setting query and lock timeouts.
* *
* @param stmnt * @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 * @param forUpdate - true if we should also try setting a lock timeout
* @throws SQLException * @throws SQLException
*/ */
@ -4229,22 +4229,44 @@ public class DBDictionary
} }
/** /**
* This method is to provide override for non-JDBC or JDBC-like * Provides the default validation handling of setting a query timeout.
* implementation of setting query timeout. * @param stmnt
* @param timeout in milliseconds
* @throws SQLException
*/ */
public void setQueryTimeout(PreparedStatement stmnt, int timeout) public void setQueryTimeout(PreparedStatement stmnt, int timeout)
throws SQLException { throws SQLException {
if (supportsQueryTimeout && timeout >= 0) { if (supportsQueryTimeout) {
if (timeout > 0 && timeout < 1000) { 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; timeout = 1000;
Log log = conf.getLog(JDBCConfiguration.LOG_JDBC);
if (log.isWarnEnabled()) if (log.isWarnEnabled())
log.warn(_loc.get("millis-query-timeout")); 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 // ConnectionDecorator implementation

View File

@ -75,3 +75,5 @@ objectid-abstract: Cannot create new application identity instance for \
abstract class "{0}". abstract class "{0}".
query-failed: A query statement timeout has occurred. query-failed: A query statement timeout has occurred.
query-timeout: A query statement timeout (set to {0} milliseconds) 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.

View File

@ -16,18 +16,24 @@ package org.apache.openjpa.persistence.query;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.Query; import javax.persistence.Query;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.DerbyDictionary; import org.apache.openjpa.jdbc.sql.DerbyDictionary;
import org.apache.openjpa.kernel.Broker; import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.persistence.JPAFacadeHelper; 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.PersistenceException;
import org.apache.openjpa.persistence.QueryTimeoutException; import org.apache.openjpa.persistence.QueryTimeoutException;
import org.apache.openjpa.persistence.query.common.apps.QTimeout; 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. * 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$ * @version $Rev$ $Date$
*/ */
public class TestQueryTimeout extends SQLListenerTestCase { public class TestQueryTimeout extends SQLListenerTestCase {
private boolean skipTests = false; private boolean skipTests = false;
// does the DB platform allow retry instead of forcing transaction rollback
private boolean supportsQueryTimeoutException = false;
@Override @Override
public void setUp() { public void setUp() {
@ -57,20 +87,24 @@ public class TestQueryTimeout extends SQLListenerTestCase {
// exception type to catch // exception type to catch
DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration()) DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration())
.getDBDictionaryInstance(); .getDBDictionaryInstance();
if ((dict.supportsQueryTimeout) && (dict instanceof DerbyDictionary)) { if (dict.supportsQueryTimeout) {
// set whether we expect to see QueryTimeoutException or if (!(dict instanceof DerbyDictionary)) {
// PersistenceException // FIXME drwoods - OPENJPA-964 - haven't determined what the
supportsQueryTimeoutException = false; // other DBs support
} else { // setQueryTimeout is not working with DB2 v9.5.3a on Windows
// FIXME drwoods - OPENJPA-964 - haven't determined what the other getLog().info("FIXME - TestQueryTimeout tests are being " +
// DBs support "skipped, as tests are not being run against a Derby DB.");
// 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.");
skipTests = true; 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 { try {
em = emf.createEntityManager(); em = emf.createEntityManager();
assertNotNull(em); 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) { if (dict instanceof DerbyDictionary) {
getLog().trace("setUp() - creating DELAY function only for Derby." + getLog().trace("setUp() - creating DELAY function only for " +
" Other DBs require manual setup."); "Derby. Other DBs require manual setup.");
// remove existing function if it exists and ignore any errors // remove existing function if it exists and recreate
try {
exec(true, 0, "DROP FUNCTION DELAY"); exec(true, 0, "DROP FUNCTION DELAY");
exec(false, 0, "CREATE FUNCTION DELAY(SECONDS INTEGER, " + exec(false, 0, "CREATE FUNCTION DELAY(SECONDS INTEGER, " +
"VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA NO SQL " + "VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA " +
"LANGUAGE JAVA EXTERNAL NAME 'org.apache.openjpa.persistence." + "NO SQL LANGUAGE JAVA EXTERNAL NAME " +
"'org.apache.openjpa.persistence." +
"query.TestQueryTimeout.delay'"); "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 " + exec(false, 0, "CREATE TRIGGER t1 NO CASCADE BEFORE UPDATE ON " +
"qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-1)"); "qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-1)");
exec(false, 0, "CREATE TRIGGER t2 NO CASCADE BEFORE INSERT ON " + exec(false, 0, "CREATE TRIGGER t2 NO CASCADE BEFORE INSERT ON " +
"qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-2)"); "qtimeout FOR EACH ROW MODE DB2SQL values DELAY(2,-2)");
// Don't include a DELETE trigger, as it slows down the DROP_TABLES // Don't include a DELETE trigger, as it slows down the DROP_TABLES
// cleanup between tests // cleanup between tests
// exec(0, "CREATE TRIGGER t3 NO CASCADE BEFORE DELETE ON qtimeout " + // exec(0, "CREATE TRIGGER t3 NO CASCADE BEFORE DELETE ON " +
// "FOR EACH ROW MODE DB2SQL values DELAY(2,-3)"); // "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. * Scenario being tested: 1a) By default, there is no timeout for queries.
@ -186,29 +217,29 @@ public class TestQueryTimeout extends SQLListenerTestCase {
if (skipTests) { if (skipTests) {
return; return;
} }
getLog().trace("testQueryTimeout1c() - No Update timeout"); getLog().trace("testQueryTimeout1c() - No executeUpdate timeout");
EntityManager em = null; EntityManager em = null;
try { try {
em = emf.createEntityManager(); em = emf.createEntityManager();
assertNotNull(em); 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 { try {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
QTimeout qt = em.find(QTimeout.class, new Integer(1));
em.getTransaction().begin(); em.getTransaction().begin();
qt.setStringField("updated"); int count = q.executeUpdate();
em.flush();
em.getTransaction().commit(); em.getTransaction().commit();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long runTime = endTime - startTime; long runTime = endTime - startTime;
getLog().trace("testQueryTimeout1c() - EM find/update runTime" + getLog().trace("testQueryTimeout1c() - executeUpdate runTime " +
"msecs=" + runTime); "msecs=" + runTime);
assertTrue("Verify we received one result.", (count == 1));
// Hack - Windows sometimes returns 1999 instead of 2000+ // Hack - Windows sometimes returns 1999 instead of 2000+
assertTrue("Should have taken 2+ secs, but was msecs=" + assertTrue("Should have taken 2+ secs, but was msecs=" +
runTime, runTime > 1900); 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) { } catch (Exception e) {
fail("Unexpected testQueryTimeout1c() 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. * is treated the same as the default no timeout for queries.
* Expected Results: The DELAY function is being called and the query * Expected Results: The DELAY function is being called and the query
* takes 6000+ msecs to complete. * takes 6000+ msecs to complete.
*/ */
public void testQueryTimeout2a() { public void testQueryTimeout22a() {
if (skipTests) { if (skipTests) {
return; return;
} }
getLog().trace("testQueryTimeout2a() - QueryHint=0"); getLog().trace("testQueryTimeout22a() - QueryHint=0");
EntityManager em = null; EntityManager em = null;
try { try {
em = emf.createEntityManager(); em = emf.createEntityManager();
@ -240,7 +331,7 @@ public class TestQueryTimeout extends SQLListenerTestCase {
assertTrue(hints.containsKey("javax.persistence.query.timeout")); assertTrue(hints.containsKey("javax.persistence.query.timeout"));
Integer timeout = new Integer( Integer timeout = new Integer(
(String) hints.get("javax.persistence.query.timeout")); (String) hints.get("javax.persistence.query.timeout"));
getLog().trace("testQueryTimeout2a() - Retrieved hint " + getLog().trace("testQueryTimeout22a() - Retrieved hint " +
"javax.persistence.query.timeout=" + timeout); "javax.persistence.query.timeout=" + timeout);
assertEquals(timeout, new Integer(0)); assertEquals(timeout, new Integer(0));
@ -250,14 +341,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
List results = q.getResultList(); List results = q.getResultList();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long runTime = endTime - startTime; long runTime = endTime - startTime;
getLog().trace("testQueryTimeout2a() - Hint0msec runTime msecs=" getLog().trace("testQueryTimeout22a() - Hint0msec runTime msecs="
+ runTime); + runTime);
// Hack - Windows sometimes returns 5999 instead of 6000+ // Hack - Windows sometimes returns 5999 instead of 6000+
assertTrue("Should have taken 6+ secs, but was msecs=" + assertTrue("Should have taken 6+ secs, but was msecs=" +
runTime, runTime > 5900); runTime, runTime > 5900);
assertEquals("Verify we found 2 results.", 2, results.size()); assertEquals("Verify we found 2 results.", 2, results.size());
} catch (Exception e) { } catch (Exception e) {
fail("Unexpected testQueryTimeout2a() exception = " + e); fail("Unexpected testQueryTimeout22a() exception = " + e);
} }
} finally { } finally {
if ((em != null) && em.isOpen()) { 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. * the same as the default no timeout for queries.
* Expected Results: The DELAY function is being called and the query * Expected Results: The DELAY function is being called and the query
* takes 2000+ msecs to complete. * takes 2000+ msecs to complete.
*/ */
public void testQueryTimeout2b() { public void testQueryTimeout23b() {
if (skipTests) { if (skipTests) {
return; return;
} }
Integer setTime = new Integer(0); Integer setTime = new Integer(0);
getLog().trace("testQueryTimeout2b() - setHint(" + setTime + ")"); getLog().trace("testQueryTimeout23b() - setHint(" + setTime + ")");
EntityManager em = null; EntityManager em = null;
try { try {
em = emf.createEntityManager(); em = emf.createEntityManager();
@ -289,14 +380,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
assertFalse(hints.containsKey("javax.persistence.query.timeout")); assertFalse(hints.containsKey("javax.persistence.query.timeout"));
// update the timeout value to 0 and verify it was set // 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); "javax.persistence.query.timeout=" + setTime);
q.setHint("javax.persistence.query.timeout", setTime); q.setHint("javax.persistence.query.timeout", setTime);
hints = q.getHints(); hints = q.getHints();
assertTrue(hints.containsKey("javax.persistence.query.timeout")); assertTrue(hints.containsKey("javax.persistence.query.timeout"));
Integer timeout = (Integer) hints.get( Integer timeout = (Integer) hints.get(
"javax.persistence.query.timeout"); "javax.persistence.query.timeout");
getLog().trace("testQueryTimeout2b() - Retrieved hint " + getLog().trace("testQueryTimeout23b() - Retrieved hint " +
"javax.persistence.query.timeout=" + timeout); "javax.persistence.query.timeout=" + timeout);
assertEquals(timeout, setTime); assertEquals(timeout, setTime);
@ -305,14 +396,14 @@ public class TestQueryTimeout extends SQLListenerTestCase {
Object result = q.getSingleResult(); Object result = q.getSingleResult();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long runTime = endTime - startTime; long runTime = endTime - startTime;
getLog().trace("testQueryTimeout2b() - NoHintSingle runTime " + getLog().trace("testQueryTimeout23b() - NoHintSingle runTime " +
"msecs=" + runTime); "msecs=" + runTime);
// Hack - Windows sometimes returns 1999 instead of 2000+ // Hack - Windows sometimes returns 1999 instead of 2000+
assertTrue("Should have taken 2+ secs, but was msecs=" + assertTrue("Should have taken 2+ secs, but was msecs=" +
runTime, runTime > 1900); runTime, runTime > 1900);
assertNotNull("Verify we received a result.", result); assertNotNull("Verify we received a result.", result);
} catch (Exception e) { } catch (Exception e) {
fail("Unexpected testQueryTimeout2b() exception = " + e); fail("Unexpected testQueryTimeout23b() exception = " + e);
} }
} finally { } finally {
if ((em != null) && em.isOpen()) { if ((em != null) && em.isOpen()) {
@ -322,32 +413,114 @@ public class TestQueryTimeout extends SQLListenerTestCase {
} }
/** /**
* Scenario being tested: 3.1.a) Explicit annotated QueryHint of * Scenario being tested: 3.1.c) Explicit persistence.xml provided PU
* timeout=1000 msecs will cause the query to timeout. * property of timeout=1000 msecs will cause the query to timeout.
* Expected Results: QueryTimeoutException or PersistenceException * Expected Results: The DELAY function is being called and the query
* takes 2000+ msecs to complete.
*/ */
public void testQueryTimeout3a() { public void testQueryTimeout31c() {
if (skipTests) { if (skipTests) {
return; return;
} }
getLog().trace("testQueryTimeout31c() - PU(timeout=1000), executeUpdate timeout");
OpenJPAEntityManagerFactory emf = null;
OpenJPAEntityManager em = null;
Integer setTime = new Integer(1000); Integer setTime = new Integer(1000);
getLog().trace("testQueryTimeout3a() - QueryHint(" + setTime + ")");
EntityManager em = null;
try { 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(); em = emf.createEntityManager();
assertNotNull(em); 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 // verify javax.persistence.query.timeout hint via annotation set
Map<String, Object> hints = q.getHints(); Map<String, Object> hints = q.getHints();
assertTrue(hints.containsKey("javax.persistence.query.timeout")); assertTrue(hints.containsKey("javax.persistence.query.timeout"));
Integer timeout = new Integer((String) hints.get( Integer timeout = new Integer((String) hints.get(
"javax.persistence.query.timeout")); "javax.persistence.query.timeout"));
getLog().trace( getLog().trace(
"testQueryTimeout3a() - Found javax.persistence.query.timeout=" "testQueryTimeout32a() - Found javax.persistence.query.timeout="
+ timeout); + timeout);
assertTrue("Expected to find a javax.persistence.query.timeout=" assertTrue("Expected to find a javax.persistence.query.timeout="
+ setTime, (timeout.intValue() == setTime.intValue())); + setTime, (timeout.intValue() == setTime.intValue()));
// verify internal config values were updated
assertEquals("QueryHint provided query timeout", setTime.intValue(),
q.getFetchPlan().getQueryTimeout());
try { try {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
@ -356,16 +529,16 @@ public class TestQueryTimeout extends SQLListenerTestCase {
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long runTime = endTime - startTime; long runTime = endTime - startTime;
getLog().trace( getLog().trace(
"testQueryTimeout3a() - Hint1000msec runTime msecs=" "testQueryTimeout32a() - Hint1000msec runTime msecs="
+ runTime); + runTime);
//assertEquals("Should never get valid results due to the " + //assertEquals("Should never get valid results due to the " +
// "timeout.", 2, results.size()); // "timeout.", 2, results.size());
fail("QueryTimeout annotation failed to cause an Exception " + fail("QueryTimeout annotation failed to cause an Exception " +
"in testQueryTimeout3a(" + setTime + "in testQueryTimeout32a(" + setTime +
" msecs), runTime msecs=" + runTime); " msecs), runTime msecs=" + runTime);
} catch (Exception e) { } catch (Exception e) {
// expected // expected
checkException("testQueryTimeout3a()", e); checkException("testQueryTimeout32a()", e);
} }
} finally { } finally {
if ((em != null) && em.isOpen()) { 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. * will cause the query to timeout.
* Expected Results: QueryTimeoutException or PersistenceException * Expected Results: QueryTimeoutException or PersistenceException
*/ */
public void testQueryTimeout3b() { public void testQueryTimeout33b() {
if (skipTests) { if (skipTests) {
return; return;
} }
Integer setTime = new Integer(1000); Integer setTime = new Integer(1000);
getLog().trace("testQueryTimeout3b() - setHint(" + setTime + ")"); getLog().trace("testQueryTimeout33b() - setHint(" + setTime + ")");
EntityManager em = null; EntityManager em = null;
try { try {
em = emf.createEntityManager(); em = emf.createEntityManager();
@ -396,7 +569,7 @@ public class TestQueryTimeout extends SQLListenerTestCase {
assertFalse(hints.containsKey("javax.persistence.query.timeout")); assertFalse(hints.containsKey("javax.persistence.query.timeout"));
// update the timeout value and verify it was set // update the timeout value and verify it was set
getLog().trace("testQueryTimeout3b() - Setting hint " + getLog().trace("testQueryTimeout33b() - Setting hint " +
"javax.persistence.query.timeout=" + setTime); "javax.persistence.query.timeout=" + setTime);
q.setHint("javax.persistence.query.timeout", setTime); q.setHint("javax.persistence.query.timeout", setTime);
hints = q.getHints(); hints = q.getHints();
@ -412,15 +585,15 @@ public class TestQueryTimeout extends SQLListenerTestCase {
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
long runTime = endTime - startTime; long runTime = endTime - startTime;
getLog().trace( getLog().trace(
"testQueryTimeout3b() - NoHintSingle runTime msecs=" "testQueryTimeout33b() - NoHintSingle runTime msecs="
+ runTime); + runTime);
//assertNull("Should never get valid result due to the timeout.", result); //assertNull("Should never get valid result due to the timeout.", result);
fail("QueryTimeout annotation failed to cause an Exception " + fail("QueryTimeout annotation failed to cause an Exception " +
"in testQueryTimeout3b(" + setTime + "in testQueryTimeout33b(" + setTime +
" mscs), runTime msecs=" + runTime); " mscs), runTime msecs=" + runTime);
} catch (Exception e) { } catch (Exception e) {
// expected // expected
checkException("testQueryTimeout3b()", e); checkException("testQueryTimeout33b()", e);
} }
} finally { } finally {
if ((em != null) && em.isOpen()) { 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 * Scenario being tested: 4) Timeouts < 0 are ignored and treated as the
* default no timeout scenario. * 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 * Internal convenience method to execute SQL statements
* *
@ -495,7 +806,8 @@ public class TestQueryTimeout extends SQLListenerTestCase {
* @param timeoutSecs * @param timeoutSecs
* @param fail * @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; EntityManager em = null;
Statement s = null; Statement s = null;
try { try {
@ -511,7 +823,8 @@ public class TestQueryTimeout extends SQLListenerTestCase {
s.execute(sql); s.execute(sql);
} catch (SQLException sqe) { } catch (SQLException sqe) {
if (!ignoreExceptions) { if (!ignoreExceptions) {
fail(sqe.toString()); // fail(sqe.toString());
throw sqe;
} }
} finally { } finally {
if (s != null) { if (s != null) {
@ -544,13 +857,10 @@ public class TestQueryTimeout extends SQLListenerTestCase {
* @param e * @param e
*/ */
private void checkException(String test, Exception e) { private void checkException(String test, Exception e) {
if (supportsQueryTimeoutException) { // no easy way to determine exact Exception type for all DBs
assertTrue("Expected QueryTimeoutException instead of " + e, assertTrue(test + " - UNEXPECTED Exception = " + e,
matchesExpectedException(QueryTimeoutException.class, e)); matchesExpectedException(QueryTimeoutException.class, e) ||
} else {
assertTrue("Expected PersistenceException instead of " + e,
matchesExpectedException(PersistenceException.class, e)); matchesExpectedException(PersistenceException.class, e));
}
getLog().trace(test + " - Caught expected Exception = " + e); getLog().trace(test + " - Caught expected Exception = " + e);
} }
@ -569,6 +879,11 @@ public class TestQueryTimeout extends SQLListenerTestCase {
if (tested != null) { if (tested != null) {
Class<?> testExClass = tested.getClass(); Class<?> testExClass = tested.getClass();
exMatched = expected.isAssignableFrom(testExClass); 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; return exMatched;
} }

View File

@ -21,7 +21,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0"> version="1.0">
<!-- Following 2 PUs are used by TestQueryProperties --> <!-- Following PUs are used by TestQueryProperties and TestQueryTimeout -->
<persistence-unit name="qtimeout-no-properties"> <persistence-unit name="qtimeout-no-properties">
<class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class> <class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
</persistence-unit> </persistence-unit>
@ -34,7 +34,13 @@
</properties> </properties>
</persistence-unit> </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"> <persistence-unit name="qtimeout-1000msecs">
<class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class> <class>org.apache.openjpa.persistence.query.common.apps.QTimeout</class>
<properties> <properties>