HHH-4765 Enhance Dialect support for JPA-2 locking. pessimistic no wait/timed locking and additional pessimistic locking tests. Oracle support for pessimistic locking
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18688 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
46bc41c226
commit
80dbc69529
|
@ -26,6 +26,7 @@ package org.hibernate.dialect;
|
|||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.sql.CaseFragment;
|
||||
import org.hibernate.sql.ANSICaseFragment;
|
||||
|
||||
|
@ -58,9 +59,13 @@ public class Oracle9iDialect extends Oracle8iDialect {
|
|||
|
||||
public String getLimitString(String sql, boolean hasOffset) {
|
||||
sql = sql.trim();
|
||||
String forUpdateClause = null;
|
||||
boolean isForUpdate = false;
|
||||
if ( sql.toLowerCase().endsWith(" for update") ) {
|
||||
sql = sql.substring( 0, sql.length()-11 );
|
||||
final int forUpdateIndex = sql.toLowerCase().lastIndexOf( "for update") ;
|
||||
if ( forUpdateIndex > -1 ) {
|
||||
// save 'for update ...' and then remove it
|
||||
forUpdateClause = sql.substring( forUpdateIndex );
|
||||
sql = sql.substring( 0, forUpdateIndex-1 );
|
||||
isForUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -80,7 +85,8 @@ public class Oracle9iDialect extends Oracle8iDialect {
|
|||
}
|
||||
|
||||
if ( isForUpdate ) {
|
||||
pagingSelect.append( " for update" );
|
||||
pagingSelect.append( " " );
|
||||
pagingSelect.append( forUpdateClause );
|
||||
}
|
||||
|
||||
return pagingSelect.toString();
|
||||
|
@ -98,4 +104,28 @@ public class Oracle9iDialect extends Oracle8iDialect {
|
|||
// the standard SQL function name is current_timestamp...
|
||||
return "current_timestamp";
|
||||
}
|
||||
|
||||
// locking support
|
||||
public String getForUpdateString() {
|
||||
return " for update";
|
||||
}
|
||||
|
||||
public String getWriteLockString(int timeout) {
|
||||
if ( timeout == LockOptions.NO_WAIT ) {
|
||||
return " for update nowait";
|
||||
}
|
||||
else if ( timeout > 0 ) {
|
||||
// convert from milliseconds to seconds
|
||||
float seconds = timeout / 1000.0f;
|
||||
timeout = Math.round(seconds);
|
||||
return " for update wait " + timeout;
|
||||
}
|
||||
else
|
||||
return " for update";
|
||||
}
|
||||
|
||||
public String getReadLockString(int timeout) {
|
||||
return getWriteLockString( timeout );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
package org.hibernate.dialect.lock;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.persister.entity.Lockable;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
|
@ -72,7 +73,7 @@ public class PessimisticReadSelectLockingStrategy implements LockingStrategy {
|
|||
public PessimisticReadSelectLockingStrategy(Lockable lockable, LockMode lockMode) {
|
||||
this.lockable = lockable;
|
||||
this.lockMode = lockMode;
|
||||
this.sql = generateLockString();
|
||||
this.sql = generateLockString(LockOptions.WAIT_FOREVER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,6 +84,13 @@ public class PessimisticReadSelectLockingStrategy implements LockingStrategy {
|
|||
Object version,
|
||||
Object object,
|
||||
int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
|
||||
String sql = this.sql;
|
||||
if ( timeout == LockOptions.NO_WAIT ) {
|
||||
sql = generateLockString( LockOptions.NO_WAIT );
|
||||
}
|
||||
else if ( timeout > 0) {
|
||||
sql = generateLockString( timeout );
|
||||
}
|
||||
|
||||
SessionFactoryImplementor factory = session.getFactory();
|
||||
try {
|
||||
|
@ -132,10 +140,12 @@ public class PessimisticReadSelectLockingStrategy implements LockingStrategy {
|
|||
return lockMode;
|
||||
}
|
||||
|
||||
protected String generateLockString() {
|
||||
protected String generateLockString(int lockTimeout) {
|
||||
SessionFactoryImplementor factory = lockable.getFactory();
|
||||
LockOptions lockOptions = new LockOptions(this.lockMode);
|
||||
lockOptions.setTimeOut( lockTimeout );
|
||||
SimpleSelect select = new SimpleSelect( factory.getDialect() )
|
||||
.setLockMode( lockMode )
|
||||
.setLockOptions( lockOptions )
|
||||
.setTableName( lockable.getRootTableName() )
|
||||
.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
|
||||
.addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
package org.hibernate.dialect.lock;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.persister.entity.Lockable;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
|
@ -72,7 +73,7 @@ public class PessimisticWriteSelectLockingStrategy implements LockingStrategy {
|
|||
public PessimisticWriteSelectLockingStrategy(Lockable lockable, LockMode lockMode) {
|
||||
this.lockable = lockable;
|
||||
this.lockMode = lockMode;
|
||||
this.sql = generateLockString();
|
||||
this.sql = generateLockString(LockOptions.WAIT_FOREVER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,6 +84,13 @@ public class PessimisticWriteSelectLockingStrategy implements LockingStrategy {
|
|||
Object version,
|
||||
Object object,
|
||||
int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
|
||||
String sql = this.sql;
|
||||
if ( timeout == LockOptions.NO_WAIT ) {
|
||||
sql = generateLockString( LockOptions.NO_WAIT );
|
||||
}
|
||||
else if ( timeout > 0) {
|
||||
sql = generateLockString( timeout );
|
||||
}
|
||||
|
||||
SessionFactoryImplementor factory = session.getFactory();
|
||||
try {
|
||||
|
@ -132,10 +140,12 @@ public class PessimisticWriteSelectLockingStrategy implements LockingStrategy {
|
|||
return lockMode;
|
||||
}
|
||||
|
||||
protected String generateLockString() {
|
||||
protected String generateLockString(int lockTimeout) {
|
||||
SessionFactoryImplementor factory = lockable.getFactory();
|
||||
LockOptions lockOptions = new LockOptions(this.lockMode);
|
||||
lockOptions.setTimeOut( lockTimeout );
|
||||
SimpleSelect select = new SimpleSelect( factory.getDialect() )
|
||||
.setLockMode( lockMode )
|
||||
.setLockOptions( lockOptions )
|
||||
.setTableName( lockable.getRootTableName() )
|
||||
.addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
|
||||
.addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
|
||||
|
|
|
@ -107,7 +107,7 @@ public class AbstractLockUpgradeEventListener extends AbstractReassociateEventLi
|
|||
entry.forceLocked( object, nextVersion );
|
||||
}
|
||||
else {
|
||||
persister.lock( entry.getId(), entry.getVersion(), object, requestedLockMode, source );
|
||||
persister.lock( entry.getId(), entry.getVersion(), object, lockOptions, source );
|
||||
}
|
||||
entry.setLockMode(requestedLockMode);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
/**
|
||||
|
@ -51,7 +52,7 @@ public class SimpleSelect {
|
|||
private String tableName;
|
||||
private String orderBy;
|
||||
private Dialect dialect;
|
||||
private LockMode lockMode = LockMode.READ;
|
||||
private LockOptions lockOptions = new LockOptions( LockMode.READ);
|
||||
private String comment;
|
||||
|
||||
private List columns = new ArrayList();
|
||||
|
@ -99,8 +100,13 @@ public class SimpleSelect {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SimpleSelect setLockOptions( LockOptions lockOptions ) {
|
||||
LockOptions.copy(lockOptions, this.lockOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleSelect setLockMode(LockMode lockMode) {
|
||||
this.lockMode = lockMode;
|
||||
this.lockOptions.setLockMode( lockMode );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -172,7 +178,7 @@ public class SimpleSelect {
|
|||
}
|
||||
|
||||
buf.append(" from ")
|
||||
.append( dialect.appendLockHint(lockMode, tableName) );
|
||||
.append( dialect.appendLockHint(lockOptions.getLockMode(), tableName) );
|
||||
|
||||
if ( whereTokens.size() > 0 ) {
|
||||
buf.append(" where ")
|
||||
|
@ -181,8 +187,8 @@ public class SimpleSelect {
|
|||
|
||||
if (orderBy!=null) buf.append(orderBy);
|
||||
|
||||
if (lockMode!=null) {
|
||||
buf.append( dialect.getForUpdateString(lockMode) );
|
||||
if (lockOptions!=null) {
|
||||
buf.append( dialect.getForUpdateString(lockOptions) );
|
||||
}
|
||||
|
||||
return dialect.transformSelectString( buf.toString() );
|
||||
|
|
|
@ -3,14 +3,22 @@ package org.hibernate.ejb.test.lock;
|
|||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.LockTimeoutException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.dialect.Oracle10gDialect;
|
||||
import org.hibernate.ejb.test.TestCase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
@ -187,7 +195,7 @@ public class LockTest extends TestCase {
|
|||
Lock lock = new Lock();
|
||||
Thread t = null;
|
||||
try {
|
||||
lock.setName( "contendedLock" );
|
||||
lock.setName( "testContendedPessimisticLock" );
|
||||
|
||||
em.getTransaction().begin();
|
||||
em.persist( lock );
|
||||
|
@ -204,21 +212,24 @@ public class LockTest extends TestCase {
|
|||
|
||||
t = new Thread( new Runnable() {
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
em2.getTransaction().begin();
|
||||
log.info("testContendedPessimisticLock: (BG) about to read write-locked entity");
|
||||
log.info("testContendedPessimisticLock: (BG) about to issue (PESSIMISTIC_READ) query against 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("testContendedPessimisticLock: (BG) read write-locked entity");
|
||||
em2.lock( lock2, LockModeType.PESSIMISTIC_READ);
|
||||
log.info("testContendedPessimisticLock: (BG) got read lock on entity");
|
||||
Query query = em2.createQuery(
|
||||
"select L from Lock_ L where L.id < 10000 ");
|
||||
query.setLockMode(LockModeType.PESSIMISTIC_READ);
|
||||
List<Lock> resultList = query.getResultList();
|
||||
resultList.get(0).getName(); // force entity to be read
|
||||
}
|
||||
finally {
|
||||
em2.getTransaction().commit();
|
||||
latch.countDown(); // signal that we got the read lock
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// t.setDaemon( true );
|
||||
t.setDaemon( true );
|
||||
t.setName("LockTest read lock");
|
||||
t.start();
|
||||
log.info("testContendedPessimisticLock: wait on BG thread");
|
||||
|
@ -245,6 +256,263 @@ public class LockTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testContendedPessimisticReadLockTimeout() throws Exception {
|
||||
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
final EntityManager em2 = createIsolatedEntityManager();
|
||||
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
|
||||
if ( ! (getDialect() instanceof Oracle10gDialect)) {
|
||||
log.info("skipping testContendedPessimisticReadLockTimeout");
|
||||
return;
|
||||
}
|
||||
Lock lock = new Lock();
|
||||
Thread t = null;
|
||||
FutureTask<Boolean> bgTask = null;
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
try {
|
||||
lock.setName( "testContendedPessimisticReadLockTimeout" );
|
||||
|
||||
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("testContendedPessimisticReadLockTimeout: 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("testContendedPessimisticReadLockTimeout: (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("testContendedPessimisticReadLockTimeout: (BG) read write-locked entity");
|
||||
Map<String,Object> props = new HashMap<String, Object>();
|
||||
// timeout is in milliseconds
|
||||
props.put("javax.persistence.lock.timeout", new Integer(1000));
|
||||
try {
|
||||
em2.lock( lock2, LockModeType.PESSIMISTIC_READ, props);
|
||||
}
|
||||
catch( LockTimeoutException e) {
|
||||
// success
|
||||
log.info("testContendedPessimisticReadLockTimeout: (BG) got expected timeout exception");
|
||||
timedOut = true;
|
||||
}
|
||||
catch ( Throwable e) {
|
||||
log.info("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("Lock timeout Test (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 void testContendedPessimisticWriteLockTimeout() throws Exception {
|
||||
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
final EntityManager em2 = createIsolatedEntityManager();
|
||||
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
|
||||
if ( ! (getDialect() instanceof Oracle10gDialect)) {
|
||||
log.info("skipping testContendedPessimisticWriteLockTimeout");
|
||||
return;
|
||||
}
|
||||
Lock lock = new Lock();
|
||||
Thread t = null;
|
||||
FutureTask<Boolean> bgTask = null;
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
try {
|
||||
lock.setName( "testContendedPessimisticWriteLockTimeout" );
|
||||
|
||||
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("testContendedPessimisticWriteLockTimeout: 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("testContendedPessimisticWriteLockTimeout: (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("testContendedPessimisticWriteLockTimeout: (BG) read write-locked entity");
|
||||
Map<String,Object> props = new HashMap<String, Object>();
|
||||
// timeout is in milliseconds
|
||||
props.put("javax.persistence.lock.timeout", new Integer(1000));
|
||||
try {
|
||||
em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props);
|
||||
}
|
||||
catch( LockTimeoutException e) {
|
||||
// success
|
||||
log.info("testContendedPessimisticWriteLockTimeout: (BG) got expected timeout exception");
|
||||
timedOut = true;
|
||||
}
|
||||
catch ( Throwable e) {
|
||||
log.info("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("Lock timeout Test (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 void testContendedPessimisticWriteLockNoWait() throws Exception {
|
||||
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
final EntityManager em2 = createIsolatedEntityManager();
|
||||
// TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g. supportsPessimisticLockTimeout)
|
||||
if ( ! (getDialect() instanceof Oracle10gDialect)) {
|
||||
log.info("skipping testContendedPessimisticWriteLockNoWait");
|
||||
return;
|
||||
}
|
||||
Lock lock = new Lock();
|
||||
Thread t = null;
|
||||
FutureTask<Boolean> bgTask = null;
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
try {
|
||||
lock.setName( "testContendedPessimisticWriteLockNoWait" );
|
||||
|
||||
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("testContendedPessimisticWriteLockNoWait: 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("testContendedPessimisticWriteLockNoWait: (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("testContendedPessimisticWriteLockNoWait: (BG) read write-locked entity");
|
||||
Map<String,Object> props = new HashMap<String, Object>();
|
||||
// timeout of zero means no wait (for lock)
|
||||
props.put("javax.persistence.lock.timeout", new Integer(0));
|
||||
try {
|
||||
em2.lock( lock2, LockModeType.PESSIMISTIC_WRITE, props);
|
||||
}
|
||||
catch( LockTimeoutException e) {
|
||||
// success
|
||||
log.info("testContendedPessimisticWriteLockNoWait: (BG) got expected timeout exception");
|
||||
timedOut = true;
|
||||
}
|
||||
catch ( Throwable e) {
|
||||
log.info("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("Lock timeout Test (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() {
|
||||
return new Class[]{
|
||||
Lock.class,
|
||||
|
|
Loading…
Reference in New Issue