HHH-5275 - Criteria.setLockMode does not work correctly
This commit is contained in:
parent
343269b00d
commit
f8b5190a19
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue