HHH-4662 Implement javax.persistence.query.timeout
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18852 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
f3aa0c63bb
commit
02451f12fb
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
* Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
|
||||||
* indicated by the @author tags or express copyright attribution
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* statements applied by the authors. All third-party contributions are
|
||||||
* distributed under license by Red Hat Middleware LLC.
|
* distributed under license by Red Hat Middleware LLC.
|
||||||
|
@ -69,7 +69,7 @@ public abstract class QueryBinder {
|
||||||
queryName,
|
queryName,
|
||||||
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
||||||
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
||||||
getInteger( queryName, "org.hibernate.timeout", hints ),
|
getTimeout( queryName, hints ),
|
||||||
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
||||||
getFlushMode( queryName, hints ),
|
getFlushMode( queryName, hints ),
|
||||||
getCacheMode( queryName, hints ),
|
getCacheMode( queryName, hints ),
|
||||||
|
@ -105,7 +105,7 @@ public abstract class QueryBinder {
|
||||||
null,
|
null,
|
||||||
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
||||||
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
||||||
getInteger( queryName, "org.hibernate.timeout", hints ),
|
getTimeout( queryName, hints ),
|
||||||
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
||||||
getFlushMode( queryName, hints ),
|
getFlushMode( queryName, hints ),
|
||||||
getCacheMode( queryName, hints ),
|
getCacheMode( queryName, hints ),
|
||||||
|
@ -126,7 +126,7 @@ public abstract class QueryBinder {
|
||||||
null,
|
null,
|
||||||
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
getBoolean( queryName, "org.hibernate.cacheable", hints ),
|
||||||
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
getString( queryName, "org.hibernate.cacheRegion", hints ),
|
||||||
getInteger( queryName, "org.hibernate.timeout", hints ),
|
getTimeout( queryName, hints ),
|
||||||
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
getInteger( queryName, "org.hibernate.fetchSize", hints ),
|
||||||
getFlushMode( queryName, hints ),
|
getFlushMode( queryName, hints ),
|
||||||
getCacheMode( queryName, hints ),
|
getCacheMode( queryName, hints ),
|
||||||
|
@ -407,4 +407,18 @@ public abstract class QueryBinder {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Integer getTimeout(String queryName, QueryHint[] hints) {
|
||||||
|
Integer timeout = getInteger( queryName, "javax.persistence.query.timeout", hints );
|
||||||
|
|
||||||
|
if ( timeout != null ) {
|
||||||
|
// convert milliseconds to seconds
|
||||||
|
timeout = new Integer ((int)Math.round(timeout.doubleValue() / 1000.0 ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// timeout is already in seconds
|
||||||
|
timeout = getInteger( queryName, "org.hibernate.timeout", hints );
|
||||||
|
}
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
* Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
|
||||||
* indicated by the @author tags or express copyright attribution
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* statements applied by the authors. All third-party contributions are
|
||||||
* distributed under license by Red Hat Middleware LLC.
|
* distributed under license by Red Hat Middleware LLC.
|
||||||
|
@ -26,6 +26,7 @@ package org.hibernate.exception;
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
|
import org.hibernate.QueryTimeoutException;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -115,6 +116,13 @@ public class SQLStateConverter implements SQLExceptionConverter {
|
||||||
// Derby "A lock could not be obtained within the time requested."
|
// Derby "A lock could not be obtained within the time requested."
|
||||||
return new PessimisticLockException( message, sqlException, sql );
|
return new PessimisticLockException( message, sqlException, sql );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MySQL Query execution was interrupted
|
||||||
|
if ( "70100".equals( sqlState ) ||
|
||||||
|
// Oracle user requested cancel of current operation
|
||||||
|
"72000".equals( sqlState ) ) {
|
||||||
|
throw new QueryTimeoutException( message, sqlException, sql );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handledNonSpecificException( sqlException, message, sql );
|
return handledNonSpecificException( sqlException, message, sql );
|
||||||
|
|
|
@ -1130,6 +1130,11 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
|
||||||
handlePersistenceException( converted );
|
handlePersistenceException( converted );
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
else if ( e instanceof org.hibernate.QueryTimeoutException ) {
|
||||||
|
QueryTimeoutException converted = new QueryTimeoutException(e.getMessage(), e);
|
||||||
|
handlePersistenceException( converted );
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
else if ( e instanceof ObjectNotFoundException ) {
|
else if ( e instanceof ObjectNotFoundException ) {
|
||||||
EntityNotFoundException converted = new EntityNotFoundException( e.getMessage() );
|
EntityNotFoundException converted = new EntityNotFoundException( e.getMessage() );
|
||||||
handlePersistenceException( converted );
|
handlePersistenceException( converted );
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
|
* Copyright (c) 2010 by Red Hat Inc and/or its affiliates or by
|
||||||
* third-party contributors as indicated by either @author tags or express
|
* third-party contributors as indicated by either @author tags or express
|
||||||
* copyright attribution statements applied by the authors. All
|
* copyright attribution statements applied by the authors. All
|
||||||
* third-party contributions are distributed under license by Red Hat Inc.
|
* third-party contributions are distributed under license by Red Hat Inc.
|
||||||
|
@ -50,6 +50,7 @@ import static org.hibernate.ejb.QueryHints.HINT_FETCH_SIZE;
|
||||||
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
|
import static org.hibernate.ejb.QueryHints.HINT_FLUSH_MODE;
|
||||||
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
|
import static org.hibernate.ejb.QueryHints.HINT_READONLY;
|
||||||
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
|
import static org.hibernate.ejb.QueryHints.HINT_TIMEOUT;
|
||||||
|
import static org.hibernate.ejb.QueryHints.SPEC_HINT_TIMEOUT;
|
||||||
|
|
||||||
import org.hibernate.ejb.util.CacheModeHelper;
|
import org.hibernate.ejb.util.CacheModeHelper;
|
||||||
import org.hibernate.ejb.util.ConfigurationHelper;
|
import org.hibernate.ejb.util.ConfigurationHelper;
|
||||||
|
@ -204,6 +205,11 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
|
||||||
if ( HINT_TIMEOUT.equals( hintName ) ) {
|
if ( HINT_TIMEOUT.equals( hintName ) ) {
|
||||||
applyTimeout( ConfigurationHelper.getInteger( value ) );
|
applyTimeout( ConfigurationHelper.getInteger( value ) );
|
||||||
}
|
}
|
||||||
|
else if ( SPEC_HINT_TIMEOUT.equals( hintName ) ) {
|
||||||
|
// convert milliseconds to seconds
|
||||||
|
int timeout = (int)Math.round(ConfigurationHelper.getInteger( value ).doubleValue() / 1000.0 );
|
||||||
|
applyTimeout( new Integer(timeout) );
|
||||||
|
}
|
||||||
else if ( HINT_COMMENT.equals( hintName ) ) {
|
else if ( HINT_COMMENT.equals( hintName ) ) {
|
||||||
applyComment( (String) value );
|
applyComment( (String) value );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
|
* Copyright (c) 2010 by Red Hat Inc and/or its affiliates or by
|
||||||
* third-party contributors as indicated by either @author tags or express
|
* third-party contributors as indicated by either @author tags or express
|
||||||
* copyright attribution statements applied by the authors. All
|
* copyright attribution statements applied by the authors. All
|
||||||
* third-party contributions are distributed under license by Red Hat Inc.
|
* third-party contributions are distributed under license by Red Hat Inc.
|
||||||
|
@ -32,7 +32,12 @@ import java.util.HashSet;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class QueryHints {
|
public class QueryHints {
|
||||||
public static final String HINT_TIMEOUT = "org.hibernate.timeout";
|
/**
|
||||||
|
* @deprecated HINT_TIMEOUT (org.hibernate.timeout),
|
||||||
|
* instead use SPEC_HINT_TIMEOUT (javax.persistence.query.timeout)
|
||||||
|
*/
|
||||||
|
public static final String HINT_TIMEOUT = "org.hibernate.timeout"; // Query timeout in seconds
|
||||||
|
public static final String SPEC_HINT_TIMEOUT = "javax.persistence.query.timeout"; // timeout in milliseconds
|
||||||
public static final String HINT_COMMENT = "org.hibernate.comment";
|
public static final String HINT_COMMENT = "org.hibernate.comment";
|
||||||
public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize";
|
public static final String HINT_FETCH_SIZE = "org.hibernate.fetchSize";
|
||||||
public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion";
|
public static final String HINT_CACHE_REGION = "org.hibernate.cacheRegion";
|
||||||
|
@ -46,6 +51,7 @@ public class QueryHints {
|
||||||
private static Set<String> buildHintsSet() {
|
private static Set<String> buildHintsSet() {
|
||||||
HashSet<String> hints = new HashSet<String>();
|
HashSet<String> hints = new HashSet<String>();
|
||||||
hints.add( HINT_TIMEOUT );
|
hints.add( HINT_TIMEOUT );
|
||||||
|
hints.add( SPEC_HINT_TIMEOUT );
|
||||||
hints.add( HINT_COMMENT );
|
hints.add( HINT_COMMENT );
|
||||||
hints.add( HINT_FETCH_SIZE );
|
hints.add( HINT_FETCH_SIZE );
|
||||||
hints.add( HINT_CACHE_REGION );
|
hints.add( HINT_CACHE_REGION );
|
||||||
|
|
|
@ -6,6 +6,7 @@ import javax.persistence.LockModeType;
|
||||||
import javax.persistence.LockTimeoutException;
|
import javax.persistence.LockTimeoutException;
|
||||||
import javax.persistence.OptimisticLockException;
|
import javax.persistence.OptimisticLockException;
|
||||||
import javax.persistence.Query;
|
import javax.persistence.Query;
|
||||||
|
import javax.persistence.QueryTimeoutException;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -511,6 +512,94 @@ public class LockTest extends TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testQueryTimeout() throws Exception {
|
||||||
|
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
final EntityManager em2 = createIsolatedEntityManager();
|
||||||
|
// TODO: replace dialect instanceof test with a Dialect.hasCapability
|
||||||
|
if ( ! (getDialect() instanceof Oracle10gDialect)) {
|
||||||
|
log.info("skipping testQueryTimeout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Lock lock = new Lock();
|
||||||
|
Thread t = null;
|
||||||
|
FutureTask<Boolean> bgTask = null;
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
try {
|
||||||
|
lock.setName( "testQueryTimeout" );
|
||||||
|
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist( lock );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.clear();
|
||||||
|
|
||||||
|
em.getTransaction().begin();
|
||||||
|
lock = em.getReference( Lock.class, lock.getId() );
|
||||||
|
em.lock( lock, LockModeType.PESSIMISTIC_WRITE );
|
||||||
|
final Integer id = lock.getId();
|
||||||
|
lock.getName(); // force entity to be read
|
||||||
|
log.info("testQueryTimeout: got write lock");
|
||||||
|
|
||||||
|
bgTask = new FutureTask<Boolean>( new Callable() {
|
||||||
|
public Boolean call() {
|
||||||
|
try {
|
||||||
|
boolean timedOut = false; // true (success) if LockTimeoutException occurred
|
||||||
|
em2.getTransaction().begin();
|
||||||
|
log.info( "testQueryTimeout: (BG) about to read write-locked entity" );
|
||||||
|
// we should block on the following read
|
||||||
|
Lock lock2 = em2.getReference( Lock.class, id );
|
||||||
|
lock2.getName(); // force entity to be read
|
||||||
|
log.info( "testQueryTimeout: (BG) read write-locked entity" );
|
||||||
|
try {
|
||||||
|
// we should block on the following read
|
||||||
|
Query query = em2.createQuery(
|
||||||
|
"select L from Lock_ L where L.id < 10000 ");
|
||||||
|
query.setLockMode( LockModeType.PESSIMISTIC_READ );
|
||||||
|
query.setHint( "javax.persistence.query.timeout", new Integer(500) ); // 1 sec timeout
|
||||||
|
List<Lock> resultList = query.getResultList();
|
||||||
|
String name = resultList.get(0).getName(); // force entity to be read
|
||||||
|
log.info( "testQueryTimeout: name read =" + name );
|
||||||
|
}
|
||||||
|
catch( QueryTimeoutException e) {
|
||||||
|
// success
|
||||||
|
log.info( "testQueryTimeout: (BG) got expected timeout exception" );
|
||||||
|
timedOut = true;
|
||||||
|
}
|
||||||
|
catch ( Throwable e) {
|
||||||
|
log.info( "testQueryTimeout: Expected LockTimeoutException but got unexpected exception", e );
|
||||||
|
}
|
||||||
|
em2.getTransaction().commit();
|
||||||
|
return new Boolean( timedOut );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
latch.countDown(); // signal that we finished
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
t = new Thread(bgTask);
|
||||||
|
t.setDaemon( true );
|
||||||
|
t.setName( "testQueryTimeout (bg)" );
|
||||||
|
t.start();
|
||||||
|
boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success
|
||||||
|
assertTrue( "background test thread finished (lock timeout is broken)", latchSet);
|
||||||
|
assertTrue( "background test thread timed out on lock attempt", bgTask.get().booleanValue() );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ( em.getTransaction().isActive() ) {
|
||||||
|
em.getTransaction().rollback();
|
||||||
|
}
|
||||||
|
if ( t != null) { // wait for background thread to finish before deleting entity
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
em.getTransaction().begin();
|
||||||
|
lock = em.getReference( Lock.class, lock.getId() );
|
||||||
|
em.remove( lock );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
em2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Class[] getAnnotatedClasses() {
|
public Class[] getAnnotatedClasses() {
|
||||||
|
|
Loading…
Reference in New Issue