HHH-13135 Use FOR NO KEY UPDATE when locking in PostgreSQL
This commit is contained in:
parent
f048ea8205
commit
de21820f84
|
@ -65,8 +65,14 @@ public class PostgreSQLSqlAstTranslator<T extends JdbcOperation> extends Abstrac
|
|||
return getDialect().getVersion().isSameOrAfter( 9, 4 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getForUpdate() {
|
||||
return getDialect().getVersion().isSameOrAfter( 9, 3 ) ? " for no key update" : " for update";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getForShare(int timeoutMillis) {
|
||||
// Note that `for key share` is inappropriate as that only means "prevent PK changes"
|
||||
return " for share";
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.orm.test.jpa.lock;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.NamedQuery;
|
||||
import jakarta.persistence.QueryHint;
|
||||
import jakarta.persistence.Version;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
public class LockReference {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Lock lock;
|
||||
|
||||
public LockReference() {
|
||||
}
|
||||
|
||||
public LockReference(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
public Lock getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
public void setLock(Lock lock) {
|
||||
this.lock = lock;
|
||||
}
|
||||
}
|
|
@ -19,9 +19,12 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.TransactionException;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.AbstractHANADialect;
|
||||
import org.hibernate.dialect.CockroachDialect;
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.dialect.MariaDBDialect;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.OracleDialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
|
@ -50,6 +53,7 @@ import static org.hibernate.jpa.SpecHints.HINT_SPEC_QUERY_TIMEOUT;
|
|||
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.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -1156,11 +1160,91 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 5 * 1000) //5 seconds
|
||||
@TestForIssue( jiraKey = "HHH-13135" )
|
||||
@SkipForDialect(value = {
|
||||
MySQLDialect.class,
|
||||
MariaDBDialect.class
|
||||
}, strictMatching = true, comment = "With InnoDB, a FK constraint check acquires a shared lock that isn't compatible with an exclusive lock")
|
||||
@SkipForDialect(value = HSQLDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks")
|
||||
@SkipForDialect(value = AbstractHANADialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks")
|
||||
public void testLockInsertFkTarget() {
|
||||
Lock lock = new Lock();
|
||||
lock.setName( "name" );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( lock );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, _entityManager -> {
|
||||
|
||||
Lock lock2 = _entityManager.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_WRITE );
|
||||
assertEquals( "lock mode should be PESSIMISTIC_WRITE ", LockModeType.PESSIMISTIC_WRITE, _entityManager.getLockMode( lock2 ) );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) );
|
||||
LockReference ref = new LockReference( 1, "name" );
|
||||
ref.setLock( entityManager.getReference( Lock.class, lock.getId() ) );
|
||||
|
||||
// Check that we can insert a LockReference, referring to a Lock that is PESSIMISTIC_WRITE locked
|
||||
entityManager.persist( ref );
|
||||
} );
|
||||
} );
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
LockReference lockReference = entityManager.find( LockReference.class, 1 );
|
||||
assertNotNull( lockReference );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test(timeout = 5 * 1000) //5 seconds
|
||||
@TestForIssue( jiraKey = "HHH-13135" )
|
||||
@SkipForDialect(value = {
|
||||
MySQLDialect.class,
|
||||
MariaDBDialect.class
|
||||
}, strictMatching = true, comment = "With InnoDB, a FK constraint check acquires a shared lock that isn't compatible with an exclusive lock")
|
||||
@SkipForDialect(value = HSQLDialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks")
|
||||
@SkipForDialect(value = AbstractHANADialect.class, comment = "Seems like FK constraint checks are not compatible with exclusive locks")
|
||||
public void testLockUpdateFkTarget() {
|
||||
Lock lock1 = new Lock();
|
||||
lock1.setName( "l1" );
|
||||
Lock lock2 = new Lock();
|
||||
lock2.setName( "l2" );
|
||||
LockReference ref = new LockReference( 1, "name" );
|
||||
ref.setLock( lock1 );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( lock1 );
|
||||
entityManager.persist( lock2 );
|
||||
entityManager.persist( ref );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, _entityManager -> {
|
||||
|
||||
Lock l1 = _entityManager.find( Lock.class, lock1.getId(), LockModeType.PESSIMISTIC_WRITE );
|
||||
assertEquals( "lock mode should be PESSIMISTIC_WRITE ", LockModeType.PESSIMISTIC_WRITE, _entityManager.getLockMode( l1 ) );
|
||||
Lock l2 = _entityManager.find( Lock.class, lock2.getId(), LockModeType.PESSIMISTIC_WRITE );
|
||||
assertEquals( "lock mode should be PESSIMISTIC_WRITE ", LockModeType.PESSIMISTIC_WRITE, _entityManager.getLockMode( l2 ) );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) );
|
||||
LockReference lockReference = entityManager.find( LockReference.class, ref.getId() );
|
||||
|
||||
// Check that we can update a LockReference, referring to a Lock that is PESSIMISTIC_WRITE locked
|
||||
lockReference.setLock( entityManager.getReference( Lock.class, lock2.getId() ) );
|
||||
} );
|
||||
} );
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
LockReference lockReference = entityManager.find( LockReference.class, 1 );
|
||||
assertEquals( lock2.getId(), lockReference.getLock().getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Lock.class,
|
||||
UnversionedLock.class
|
||||
UnversionedLock.class,
|
||||
LockReference.class
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue