HHH-5275 - Criteria.setLockMode does not work correctly

This commit is contained in:
Steve Ebersole 2012-01-05 16:02:48 -06:00
parent 343269b00d
commit f8b5190a19
1 changed files with 131 additions and 25 deletions

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.test.locking;
import java.util.concurrent.TimeoutException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
@ -178,35 +180,139 @@ public class LockModeTest extends BaseCoreFunctionalTestCase {
}
private void nowAttemptToUpdateRow() {
Session s = sessionFactory().openSession();
s.beginTransaction();
// here we just need to open a new connection (database session and transaction) and make sure that
// we are not allowed to acquire exclusive locks to that row and/or write to that row. That may take
// one of two forms:
// 1) either the get-with-lock or the update fails immediately with a sql error
// 2) either the get-with-lock or the update blocks indef (in real world, it would block
// until the txn in the calling method completed.
// To be able to cater to the second type, we run this block in a separate thread to be able to "time it out"
try {
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
// to write a locked row
A it = (A) s.get(
A.class,
1,
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT )
new TimedExecutor( 10*1000, 1*1000 ).execute(
new Executable() {
Session s;
@Override
public void execute() {
s = sessionFactory().openSession();
s.beginTransaction();
try {
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
// to write a locked row
A it = (A) s.get(
A.class,
1,
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT )
);
it.setValue( "changed" );
s.flush();
fail( "Pessimistic lock not obtained/held" );
}
catch ( Exception e ) {
// grr, exception can be any number of types based on database
// see HHH-6887
if ( LockAcquisitionException.class.isInstance( e )
|| GenericJDBCException.class.isInstance( e )
|| PessimisticLockException.class.isInstance( e ) ) {
// "ok"
}
else {
fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() );
}
}
finally {
shutDown();
}
}
private void shutDown() {
try {
s.getTransaction().rollback();
s.close();
}
catch (Exception ignore) {
}
s = null;
}
@Override
public void forceStop() {
s.cancelQuery();
shutDown();
}
}
);
it.setValue( "changed" );
s.flush();
fail( "Pessimistic lock not obtained/held" );
}
catch ( Exception e ) {
// grr, exception can be any number of types based on database
// see HHH-6887
if ( LockAcquisitionException.class.isInstance( e )
|| GenericJDBCException.class.isInstance( e )
|| PessimisticLockException.class.isInstance( e ) ) {
// "ok"
}
else {
fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() );
}
catch (TimeoutException e) {
// timeout is ok, see rule #2 above
}
finally {
s.getTransaction().rollback();
s.close();
}
interface Executable {
public void execute();
public void forceStop();
}
class TimedExecutor {
private final long timeOut;
private final int checkMilliSeconds;
TimedExecutor(long timeOut) {
this( timeOut, 1000 );
}
TimedExecutor(long timeOut, int checkMilliSeconds) {
this.timeOut = timeOut;
this.checkMilliSeconds = checkMilliSeconds;
}
public void execute(Executable executable) throws TimeoutException {
final ExecutableAdapter adapter = new ExecutableAdapter( executable );
final Thread separateThread = new Thread( adapter );
separateThread.start();
int runningTime = 0;
do {
if ( runningTime > timeOut ) {
try {
executable.forceStop();
}
catch (Exception ignore) {
}
throw new TimeoutException();
}
try {
Thread.sleep( checkMilliSeconds );
runningTime += checkMilliSeconds;
}
catch (InterruptedException ignore) {
}
} while ( !adapter.isDone() );
}
}
class ExecutableAdapter implements Runnable {
private final Executable executable;
private boolean isDone;
ExecutableAdapter(Executable executable) {
this.executable = executable;
}
public boolean isDone() {
return isDone;
}
@Override
public void run() {
isDone = false;
try {
executable.execute();
}
finally {
isDone = true;
}
}
}
}