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;
|
package org.hibernate.test.locking;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.PessimisticLockException;
|
import org.hibernate.PessimisticLockException;
|
||||||
|
@ -178,7 +180,22 @@ public class LockModeTest extends BaseCoreFunctionalTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void nowAttemptToUpdateRow() {
|
private void nowAttemptToUpdateRow() {
|
||||||
Session s = sessionFactory().openSession();
|
// 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 {
|
||||||
|
new TimedExecutor( 10*1000, 1*1000 ).execute(
|
||||||
|
new Executable() {
|
||||||
|
Session s;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
s = sessionFactory().openSession();
|
||||||
s.beginTransaction();
|
s.beginTransaction();
|
||||||
try {
|
try {
|
||||||
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
|
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
|
||||||
|
@ -205,8 +222,97 @@ public class LockModeTest extends BaseCoreFunctionalTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
shutDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutDown() {
|
||||||
|
try {
|
||||||
s.getTransaction().rollback();
|
s.getTransaction().rollback();
|
||||||
s.close();
|
s.close();
|
||||||
}
|
}
|
||||||
|
catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
s = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forceStop() {
|
||||||
|
s.cancelQuery();
|
||||||
|
shutDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (TimeoutException e) {
|
||||||
|
// timeout is ok, see rule #2 above
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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