mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-16 16:15:06 +00:00
HHH-10654 - LockOptions.SKIP_LOCKED semantics implementation on MSSQL
This commit is contained in:
parent
6bfe281589
commit
2a9c0fe0dd
@ -258,4 +258,7 @@ test.dependsOn ":hibernate-orm-modules:prepareWildFlyForTests"
|
||||
|
||||
test {
|
||||
systemProperty "file.encoding", "utf-8"
|
||||
beforeTest { descriptor ->
|
||||
//println "Starting test: " + descriptor
|
||||
}
|
||||
}
|
@ -2882,6 +2882,15 @@ public boolean supportsValuesList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect/database support SKIP_LOCKED timeout.
|
||||
*
|
||||
* @return {@code true} if SKIP_LOCKED is supported
|
||||
*/
|
||||
public boolean supportsSkipLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLegacyLimitHandlerBehaviorEnabled() {
|
||||
return legacyLimitHandlerBehavior;
|
||||
}
|
||||
|
@ -85,4 +85,9 @@ public int registerResultSetOutParameter(CallableStatement statement, String nam
|
||||
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
|
||||
return (ResultSet) statement.getObject( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -62,4 +62,9 @@ public String getForUpdateSkipLockedString() {
|
||||
public String getForUpdateSkipLockedString(String aliases) {
|
||||
return getForUpdateString() + " of " + aliases + " skip locked";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -64,24 +64,31 @@ protected LimitHandler getDefaultLimitHandler() {
|
||||
|
||||
@Override
|
||||
public String appendLockHint(LockOptions lockOptions, String tableName) {
|
||||
// NOTE : since SQLServer2005 the nowait hint is supported
|
||||
if ( lockOptions.getLockMode() == LockMode.UPGRADE_NOWAIT ) {
|
||||
return tableName + " with (updlock, rowlock, nowait)";
|
||||
|
||||
LockMode lockMode = lockOptions.getAliasSpecificLockMode( tableName );
|
||||
if(lockMode == null) {
|
||||
lockMode = lockOptions.getLockMode();
|
||||
}
|
||||
|
||||
final LockMode mode = lockOptions.getLockMode();
|
||||
final boolean isNoWait = lockOptions.getTimeOut() == LockOptions.NO_WAIT;
|
||||
final String noWaitStr = isNoWait ? ", nowait" : "";
|
||||
switch ( mode ) {
|
||||
final String writeLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? "updlock" : "updlock, holdlock";
|
||||
final String readLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? "updlock" : "holdlock";
|
||||
|
||||
final String noWaitStr = lockOptions.getTimeOut() == LockOptions.NO_WAIT ? ", nowait" : "";
|
||||
final String skipLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? ", readpast" : "";
|
||||
|
||||
switch ( lockMode ) {
|
||||
case UPGRADE:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE: {
|
||||
return tableName + " with (updlock, rowlock" + noWaitStr + ")";
|
||||
return tableName + " with (" + writeLockStr + ", rowlock" + noWaitStr + skipLockStr + ")";
|
||||
}
|
||||
case PESSIMISTIC_READ: {
|
||||
return tableName + " with (holdlock, rowlock" + noWaitStr + ")";
|
||||
}case UPGRADE_SKIPLOCKED:
|
||||
return tableName + " with (" + readLockStr + ", rowlock" + noWaitStr + skipLockStr + ")";
|
||||
}
|
||||
case UPGRADE_SKIPLOCKED:
|
||||
return tableName + " with (updlock, rowlock, readpast" + noWaitStr + ")";
|
||||
case UPGRADE_NOWAIT:
|
||||
return tableName + " with (updlock, holdlock, rowlock, nowait)";
|
||||
default: {
|
||||
return tableName;
|
||||
}
|
||||
@ -110,4 +117,9 @@ public JDBCException convert(SQLException sqlException, String message, String s
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -446,7 +446,7 @@ public void testAppendLockHintPessimisticReadNoTimeOut() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintWrite() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.WRITE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
@ -457,7 +457,7 @@ public void testAppendLockHintWrite() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintWriteWithNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock, nowait)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock, nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.WRITE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
@ -470,7 +470,7 @@ public void testAppendLockHintWriteWithNoTimeOut() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoWait() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock, nowait)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock, nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_NOWAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
@ -481,7 +481,7 @@ public void testAppendLockHintUpgradeNoWait() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoWaitNoTimeout() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock, nowait)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock, nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_NOWAIT );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
@ -493,7 +493,7 @@ public void testAppendLockHintUpgradeNoWaitNoTimeout() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgrade() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
@ -504,7 +504,7 @@ public void testAppendLockHintUpgrade() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoTimeout() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock, nowait)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock, nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
@ -516,7 +516,7 @@ public void testAppendLockHintUpgradeNoTimeout() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticWrite() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
@ -527,7 +527,7 @@ public void testAppendLockHintPessimisticWrite() {
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticWriteNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (updlock, rowlock, nowait)";
|
||||
final String expectedLockHint = "tab1 with (updlock, holdlock, rowlock, nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
|
@ -13,6 +13,7 @@
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.LockTimeoutException;
|
||||
@ -22,9 +23,12 @@
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.QueryTimeoutException;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.dialect.Oracle10gDialect;
|
||||
import org.hibernate.dialect.PostgreSQL81Dialect;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.dialect.SybaseASE15Dialect;
|
||||
import org.hibernate.jpa.AvailableSettings;
|
||||
import org.hibernate.jpa.QueryHints;
|
||||
@ -40,6 +44,7 @@
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@ -108,6 +113,7 @@ public void testFindWithPessimisticWriteLockTimeoutException() {
|
||||
}
|
||||
catch (LockTimeoutException lte) {
|
||||
// Proper exception thrown for dialect supporting lock timeouts when an immediate timeout is set.
|
||||
lte.getCause();
|
||||
}
|
||||
catch (PessimisticLockException pe) {
|
||||
fail( "Find with immediate timeout should have thrown LockTimeoutException." );
|
||||
@ -134,6 +140,100 @@ public void testFindWithPessimisticWriteLockTimeoutException() {
|
||||
em2.getTransaction().commit();
|
||||
em2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature( value = DialectChecks.SupportSkipLocked.class )
|
||||
public void testUpdateWithPessimisticReadLockSkipLocked() {
|
||||
Lock lock = new Lock();
|
||||
lock.setName( "name" );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( lock );
|
||||
} );
|
||||
|
||||
EntityManager em2 = createIsolatedEntityManager();
|
||||
em2.getTransaction().begin();
|
||||
|
||||
try {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, -2L );
|
||||
em2.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ, properties );
|
||||
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.createNativeQuery( updateStatement() )
|
||||
.setParameter( "name", "changed" )
|
||||
.setParameter( "id", lock.getId() )
|
||||
.executeUpdate();
|
||||
} );
|
||||
fail("Should throw LockTimeoutException");
|
||||
}
|
||||
catch (LockTimeoutException expected) {
|
||||
}
|
||||
}
|
||||
finally {
|
||||
em2.getTransaction().commit();
|
||||
}
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Lock _lock = entityManager.merge( lock );
|
||||
entityManager.remove( _lock );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialectFeature(value = DialectChecks.SupportsLockTimeouts.class)
|
||||
public void testUpdateWithPessimisticReadLockWithoutNoWait() {
|
||||
Lock lock = new Lock();
|
||||
lock.setName( "name" );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( lock );
|
||||
} );
|
||||
|
||||
EntityManager em2 = createIsolatedEntityManager();
|
||||
em2.getTransaction().begin();
|
||||
|
||||
try {
|
||||
em2.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ );
|
||||
|
||||
AtomicBoolean failureExpected = new AtomicBoolean();
|
||||
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
try {
|
||||
entityManager.createNativeQuery( updateStatement() )
|
||||
.setParameter( "name", "changed" )
|
||||
.setParameter( "id", lock.getId() )
|
||||
.executeUpdate();
|
||||
}
|
||||
catch (LockTimeoutException | PessimisticLockException expected) {
|
||||
failureExpected.set( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ( !failureExpected.get() ) {
|
||||
fail( "Should throw LockTimeoutException or PessimisticLockException" );
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
em2.getTransaction().commit();
|
||||
}
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Lock _lock = entityManager.merge( lock );
|
||||
entityManager.remove( _lock );
|
||||
} );
|
||||
}
|
||||
|
||||
protected String updateStatement() {
|
||||
if( SQLServerDialect.class.isAssignableFrom( Dialect.getDialect().getClass() ) ) {
|
||||
return "UPDATE Lock_ WITH(NOWAIT) SET name = :name where id = :id";
|
||||
}
|
||||
return "UPDATE Lock_ SET name = :name where id = :id";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockRead() throws Exception {
|
||||
|
@ -18,7 +18,7 @@ public class SQLServer2005LockHintsTest extends AbstractLockHintTest {
|
||||
public static final Dialect DIALECT = new SQLServer2005Dialect();
|
||||
|
||||
protected String getLockHintUsed() {
|
||||
return "with (updlock, rowlock, nowait)";
|
||||
return "with (updlock, holdlock, rowlock, nowait)";
|
||||
}
|
||||
|
||||
protected Dialect getDialectUnderTest() {
|
||||
|
@ -41,7 +41,7 @@ public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Column(name="`value`")
|
||||
@Column(name="a_value")
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
@ -0,0 +1,193 @@
|
||||
package org.hibernate.test.locking;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.dialect.PostgreSQL95Dialect;
|
||||
import org.hibernate.dialect.SQLServer2005Dialect;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public abstract class AbstractSkipLockedTest
|
||||
extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { A.class, BatchJob.class };
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@RequiresDialect({ SQLServer2005Dialect.class })
|
||||
public void testSQLServerSkipLocked() {
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( long i = 1; i <= 10; i++ ) {
|
||||
BatchJob batchJob = new BatchJob();
|
||||
batchJob.setId( i );
|
||||
session.persist( batchJob );
|
||||
}
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
List<BatchJob> firstFive = nextFiveBatchJobs( session );
|
||||
|
||||
assertEquals( 5, firstFive.size() );
|
||||
assertTrue( firstFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
|
||||
|
||||
executeSync( () -> {
|
||||
doInHibernate( this::sessionFactory, _session -> {
|
||||
List<BatchJob> nextFive = nextFiveBatchJobs( _session );
|
||||
|
||||
assertEquals( 5, nextFive.size() );
|
||||
|
||||
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 6L, 7L, 8L, 9L, 10L ) ) );
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialect({ PostgreSQL95Dialect.class })
|
||||
public void testPostgreSQLSkipLocked() {
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( long i = 1; i <= 10; i++ ) {
|
||||
BatchJob batchJob = new BatchJob();
|
||||
batchJob.setId( i );
|
||||
session.persist( batchJob );
|
||||
}
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
List<BatchJob> firstFive = nextFiveBatchJobs( session );
|
||||
|
||||
assertEquals( 5, firstFive.size() );
|
||||
assertTrue( firstFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
|
||||
|
||||
executeSync( () -> {
|
||||
doInHibernate( this::sessionFactory, _session -> {
|
||||
List<BatchJob> nextFive = nextFiveBatchJobs( _session );
|
||||
|
||||
assertEquals( 5, nextFive.size() );
|
||||
|
||||
if ( lockMode() == LockMode.PESSIMISTIC_READ ) {
|
||||
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
|
||||
}
|
||||
else {
|
||||
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 6L, 7L, 8L, 9L, 10L ) ) );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialect({ Oracle8iDialect.class })
|
||||
public void testOracleSkipLocked() {
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
for ( long i = 1; i <= 10; i++ ) {
|
||||
BatchJob batchJob = new BatchJob();
|
||||
batchJob.setId( i );
|
||||
session.persist( batchJob );
|
||||
}
|
||||
} );
|
||||
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
List<BatchJob> firstFive = nextFiveBatchJobs( session );
|
||||
|
||||
assertEquals( 5, firstFive.size() );
|
||||
assertTrue( firstFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 1L, 2L, 3L, 4L, 5L ) ) );
|
||||
|
||||
executeSync( () -> {
|
||||
doInHibernate( this::sessionFactory, _session -> {
|
||||
List<BatchJob> nextFive = nextFiveBatchJobs( _session );
|
||||
|
||||
assertEquals( 0, nextFive.size() );
|
||||
|
||||
nextFive = nextFiveBatchJobs( _session, 10 );
|
||||
|
||||
assertTrue( nextFive.stream().map( BatchJob::getId ).collect( Collectors.toList() )
|
||||
.containsAll( Arrays.asList( 6L, 7L, 8L, 9L, 10L ) ) );
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
private List<BatchJob> nextFiveBatchJobs(Session session) {
|
||||
return nextFiveBatchJobs( session, 5 );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<BatchJob> nextFiveBatchJobs(Session session, Integer maxResult) {
|
||||
Query query = session.createQuery(
|
||||
"select j from BatchJob j", BatchJob.class )
|
||||
.setMaxResults( maxResult )
|
||||
.unwrap( Query.class );
|
||||
|
||||
applySkipLocked(query);
|
||||
|
||||
return query.list();
|
||||
}
|
||||
|
||||
protected void applySkipLocked(Query query) {
|
||||
query.setLockOptions(
|
||||
new LockOptions( lockMode() )
|
||||
.setTimeOut( LockOptions.SKIP_LOCKED )
|
||||
);
|
||||
}
|
||||
|
||||
protected abstract LockMode lockMode();
|
||||
|
||||
@Entity(name = "BatchJob")
|
||||
public static class BatchJob {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private boolean processed;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isProcessed() {
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void setProcessed(boolean processed) {
|
||||
this.processed = processed;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,13 +8,10 @@
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.PessimisticLockException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.dialect.SybaseASE15Dialect;
|
||||
import org.hibernate.exception.GenericJDBCException;
|
||||
@ -22,8 +19,6 @@
|
||||
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.async.Executable;
|
||||
import org.hibernate.testing.async.TimedExecutor;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -174,7 +169,9 @@ private void nowAttemptToUpdateRow() {
|
||||
_session.doWork( connection -> {
|
||||
try {
|
||||
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000);
|
||||
} catch (Throwable ignore) {}
|
||||
} catch (Throwable ignore) {
|
||||
ignore.fillInStackTrace();
|
||||
}
|
||||
} );
|
||||
try {
|
||||
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
|
||||
@ -184,8 +181,10 @@ private void nowAttemptToUpdateRow() {
|
||||
id,
|
||||
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT )
|
||||
);
|
||||
it.setValue( "changed" );
|
||||
_session.flush();
|
||||
_session.createNativeQuery( updateStatement() )
|
||||
.setParameter( "value", "changed" )
|
||||
.setParameter( "id", it.getId() )
|
||||
.executeUpdate();
|
||||
fail( "Pessimistic lock not obtained/held" );
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
@ -203,4 +202,11 @@ private void nowAttemptToUpdateRow() {
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
protected String updateStatement() {
|
||||
if( SQLServerDialect.class.isAssignableFrom( DIALECT.getClass() ) ) {
|
||||
return "UPDATE T_LOCK_A WITH(NOWAIT) SET a_value = :value where id = :id";
|
||||
}
|
||||
return "UPDATE T_LOCK_A SET a_value = :value where id = :id";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package org.hibernate.test.locking;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class PessimisticReadSkipLockedTest
|
||||
extends AbstractSkipLockedTest {
|
||||
|
||||
@Override
|
||||
protected LockMode lockMode() {
|
||||
return LockMode.PESSIMISTIC_READ;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.hibernate.test.locking;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class PessimisticWriteSkipLockedTest
|
||||
extends AbstractSkipLockedTest {
|
||||
|
||||
@Override
|
||||
protected LockMode lockMode() {
|
||||
return LockMode.PESSIMISTIC_WRITE;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.hibernate.test.locking;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class UpgradeSkipLockedTest
|
||||
extends AbstractSkipLockedTest {
|
||||
|
||||
|
||||
@Override
|
||||
protected void applySkipLocked(Query query) {
|
||||
query.setLockOptions(
|
||||
new LockOptions( lockMode() ).setFollowOnLocking( false )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LockMode lockMode() {
|
||||
return LockMode.UPGRADE_SKIPLOCKED;
|
||||
}
|
||||
}
|
@ -223,4 +223,10 @@ public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsRowValueConstructorSyntaxInInList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportSkipLocked implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsSkipLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ protected void executeSync(Runnable callable) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user