Author: smarlow(a)redhat.com
Date: 2010-02-02 12:03:05 -0500 (Tue, 02 Feb 2010)
New Revision: 18681
Modified:
core/trunk/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/TestCase.java
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
Log:
HHH-4765 Enhance Dialect support for JPA-2 locking
Modified: core/trunk/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java 2010-02-01
21:02:12 UTC (rev 18680)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java 2010-02-02
17:03:05 UTC (rev 18681)
@@ -30,6 +30,7 @@
import java.sql.Types;
import org.hibernate.Hibernate;
+import org.hibernate.LockOptions;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.PositionSubstringFunction;
@@ -359,16 +360,23 @@
return false;
}
+ // locking support
public String getForUpdateString() {
return " for update";
}
public String getWriteLockString(int timeout) {
- return " for update";
+ if ( timeout == LockOptions.NO_WAIT )
+ return " for update nowait";
+ else
+ return " for update";
}
public String getReadLockString(int timeout) {
- return " for share";
+ if ( timeout == LockOptions.NO_WAIT )
+ return " for share nowait";
+ else
+ return " for share";
}
}
Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/TestCase.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/TestCase.java 2010-02-01
21:02:12 UTC (rev 18680)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/TestCase.java 2010-02-02
17:03:05 UTC (rev 18681)
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManager;
@@ -55,6 +56,7 @@
protected static EntityManagerFactory factory;
private EntityManager em;
+ private ArrayList isolatedEms = new ArrayList();
public TestCase() {
@@ -80,7 +82,7 @@
factory = ejbconfig.createEntityManagerFactory( getConfig() );
}
- protected void handleUnclosedResources(){
+ private void cleanUnclosed(EntityManager em){
if(em == null) {
return;
}
@@ -94,6 +96,13 @@
em.close();
log.warn( "The EntityManager is not closed. Closing it." );
}
+ }
+ protected void handleUnclosedResources(){
+ cleanUnclosed( this.em );
+ for ( Iterator iter = isolatedEms.iterator(); iter.hasNext();) {
+ cleanUnclosed( (EntityManager)iter.next() );
+ }
+
cfg = null;
}
@@ -110,6 +119,12 @@
return em;
}
+ protected EntityManager createIsolatedEntityManager() {
+ EntityManager isolatedEm = factory.createEntityManager( );
+ isolatedEms.add( isolatedEm );
+ return isolatedEm;
+ }
+
/**
* always reopen a new EM and clse the existing one
*/
Modified:
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java
===================================================================
---
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java 2010-02-01
21:02:12 UTC (rev 18680)
+++
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/lock/LockTest.java 2010-02-02
17:03:05 UTC (rev 18681)
@@ -5,13 +5,20 @@
import javax.persistence.LockModeType;
import javax.persistence.OptimisticLockException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.dialect.HSQLDialect;
import org.hibernate.ejb.test.TestCase;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* @author Emmanuel Bernard
*/
public class LockTest extends TestCase {
+ private static final Log log = LogFactory.getLog( LockTest.class );
public void testLockRead() throws Exception {
Lock lock = new Lock();
lock.setName( "name" );
@@ -167,7 +174,77 @@
}
em.close();
}
-
+
+ public void testContendedPessimisticLock() throws Exception {
+
+ EntityManager em = getOrCreateEntityManager();
+ final EntityManager em2 = createIsolatedEntityManager();
+ // TODO: replace dialect instanceof test with a Dialect.hasCapability (e.g.
supportsPessimisticWriteLock)
+ if ( getDialect() instanceof HSQLDialect) {
+ log.info("skipping testContendedPessimisticLock");
+ return;
+ }
+ Lock lock = new Lock();
+ Thread t = null;
+ try {
+ lock.setName( "contendedLock" );
+
+ 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("testContendedPessimisticLock: got write lock");
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ t = new Thread( new Runnable() {
+ public void run() {
+
+ em2.getTransaction().begin();
+ log.info("testContendedPessimisticLock: (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("testContendedPessimisticLock: (BG) read write-locked entity");
+ em2.lock( lock2, LockModeType.PESSIMISTIC_READ);
+ log.info("testContendedPessimisticLock: (BG) got read lock on entity");
+ em2.getTransaction().commit();
+ latch.countDown(); // signal that we got the read lock
+ }
+ } );
+
+ // t.setDaemon( true );
+ t.setName("LockTest read lock");
+ t.start();
+ log.info("testContendedPessimisticLock: wait on BG thread");
+ boolean latchSet = latch.await( 10, TimeUnit.SECONDS );
+ // latchSet should be false (timeout) because the background thread
+ // shouldn't be able to get a read lock on write locked entity.
+ log.info("testContendedPessimisticLock: BG thread completed transaction");
+ assertFalse( "shouldn't be able to get read lock while another transaction
has write lock",latchSet );
+ 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,
Show replies by thread