Skip to content

Commit cfbb66c

Browse files
committed
HHH-19336 - Proper implementation for JPA extended locking scope
HHH-19459 - LockScope, FollowOnLocking HHH-19501 - Session#lock w/ pessimistic locks for scopes HHH-19502 - Disallow SKIP_LOCKED with Session#lock
1 parent f326433 commit cfbb66c

File tree

25 files changed

+926
-519
lines changed

25 files changed

+926
-519
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import jakarta.persistence.TemporalType;
99
import org.hibernate.LockMode;
1010
import org.hibernate.LockOptions;
11+
import org.hibernate.Locking;
1112
import org.hibernate.boot.model.FunctionContributions;
1213
import org.hibernate.cfg.Environment;
1314
import org.hibernate.community.dialect.identity.CacheIdentityColumnSupport;
@@ -18,13 +19,8 @@
1819
import org.hibernate.dialect.function.CommonFunctionFactory;
1920
import org.hibernate.dialect.identity.IdentityColumnSupport;
2021
import org.hibernate.dialect.lock.LockingStrategy;
21-
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
22-
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
23-
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
2422
import org.hibernate.dialect.lock.PessimisticReadUpdateLockingStrategy;
2523
import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy;
26-
import org.hibernate.dialect.lock.SelectLockingStrategy;
27-
import org.hibernate.dialect.lock.UpdateLockingStrategy;
2824
import org.hibernate.dialect.pagination.LimitHandler;
2925
import org.hibernate.dialect.pagination.TopLimitHandler;
3026
import org.hibernate.dialect.sequence.SequenceSupport;
@@ -323,27 +319,16 @@ public boolean supportsOuterJoinForUpdate() {
323319
}
324320

325321
@Override
326-
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
322+
protected LockingStrategy buildPessimisticWriteStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
327323
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
328324
// Set your transaction mode to READ_COMMITTED before using
329-
switch (lockMode) {
330-
case PESSIMISTIC_FORCE_INCREMENT:
331-
return new PessimisticForceIncrementLockingStrategy(lockable, lockMode);
332-
case PESSIMISTIC_WRITE:
333-
return new PessimisticWriteUpdateLockingStrategy(lockable, lockMode);
334-
case PESSIMISTIC_READ:
335-
return new PessimisticReadUpdateLockingStrategy(lockable, lockMode);
336-
case OPTIMISTIC:
337-
return new OptimisticLockingStrategy(lockable, lockMode);
338-
case OPTIMISTIC_FORCE_INCREMENT:
339-
return new OptimisticForceIncrementLockingStrategy(lockable, lockMode);
340-
}
341-
if ( lockMode.greaterThan( LockMode.READ ) ) {
342-
return new UpdateLockingStrategy( lockable, lockMode );
343-
}
344-
else {
345-
return new SelectLockingStrategy( lockable, lockMode );
346-
}
325+
return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode );
326+
}
327+
328+
protected LockingStrategy buildPessimisticReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
329+
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
330+
// Set your transaction mode to READ_COMMITTED before using
331+
return new PessimisticReadUpdateLockingStrategy( lockable, lockMode );
347332
}
348333

349334
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.checkerframework.checker.nullness.qual.Nullable;
1313
import org.hibernate.JDBCException;
1414
import org.hibernate.LockMode;
15+
import org.hibernate.Locking;
1516
import org.hibernate.StaleObjectStateException;
1617
import org.hibernate.boot.model.FunctionContributions;
1718
import org.hibernate.dialect.*;
@@ -20,11 +21,6 @@
2021
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
2122
import org.hibernate.dialect.identity.IdentityColumnSupport;
2223
import org.hibernate.dialect.lock.LockingStrategy;
23-
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
24-
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
25-
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
26-
import org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy;
27-
import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
2824
import org.hibernate.dialect.lock.SelectLockingStrategy;
2925
import org.hibernate.community.dialect.pagination.LegacyHSQLLimitHandler;
3026
import org.hibernate.dialect.pagination.LimitHandler;
@@ -763,32 +759,10 @@ public boolean doesRoundTemporalOnOverflow() {
763759
return false;
764760
}
765761

766-
/**
767-
* For HSQLDB 2.0, this is a copy of the base class implementation.
768-
* For HSQLDB 1.8, only READ_UNCOMMITTED is supported.
769-
* <p>
770-
* {@inheritDoc}
771-
*/
772-
@Override
773-
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
774-
switch (lockMode) {
775-
case PESSIMISTIC_FORCE_INCREMENT:
776-
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode);
777-
case PESSIMISTIC_WRITE:
778-
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode);
779-
case PESSIMISTIC_READ:
780-
return new PessimisticReadSelectLockingStrategy( lockable, lockMode);
781-
case OPTIMISTIC:
782-
return new OptimisticLockingStrategy( lockable, lockMode);
783-
case OPTIMISTIC_FORCE_INCREMENT:
784-
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode);
785-
}
786-
if ( getVersion().isBefore( 2 ) ) {
787-
return new ReadUncommittedLockingStrategy( lockable, lockMode );
788-
}
789-
else {
790-
return new SelectLockingStrategy( lockable, lockMode );
791-
}
762+
protected LockingStrategy buildReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
763+
return getVersion().isBefore( 2 )
764+
? new ReadUncommittedLockingStrategy( lockable, lockMode )
765+
: new SelectLockingStrategy( lockable, lockMode );
792766
}
793767

794768
private static class ReadUncommittedLockingStrategy extends SelectLockingStrategy {

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/RDMSOS2200Dialect.java

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import jakarta.persistence.TemporalType;
88
import org.hibernate.LockMode;
99
import org.hibernate.LockOptions;
10+
import org.hibernate.Locking;
1011
import org.hibernate.boot.model.FunctionContributions;
1112
import org.hibernate.community.dialect.sequence.RDMSSequenceSupport;
1213
import org.hibernate.dialect.AbstractTransactSQLDialect;
@@ -16,13 +17,8 @@
1617
import org.hibernate.dialect.SimpleDatabaseVersion;
1718
import org.hibernate.dialect.function.CommonFunctionFactory;
1819
import org.hibernate.dialect.lock.LockingStrategy;
19-
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
20-
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
21-
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
2220
import org.hibernate.dialect.lock.PessimisticReadUpdateLockingStrategy;
2321
import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy;
24-
import org.hibernate.dialect.lock.SelectLockingStrategy;
25-
import org.hibernate.dialect.lock.UpdateLockingStrategy;
2622
import org.hibernate.dialect.pagination.FetchLimitHandler;
2723
import org.hibernate.dialect.pagination.LimitHandler;
2824
import org.hibernate.dialect.sequence.SequenceSupport;
@@ -394,26 +390,14 @@ public boolean supportsOrderByInSubquery() {
394390
}
395391

396392
@Override
397-
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
398-
// RDMS has no known variation of a "SELECT ... FOR UPDATE" syntax...
399-
switch (lockMode) {
400-
case PESSIMISTIC_FORCE_INCREMENT:
401-
return new PessimisticForceIncrementLockingStrategy(lockable, lockMode);
402-
case PESSIMISTIC_WRITE:
403-
return new PessimisticWriteUpdateLockingStrategy(lockable, lockMode);
404-
case PESSIMISTIC_READ:
405-
return new PessimisticReadUpdateLockingStrategy(lockable, lockMode);
406-
case OPTIMISTIC:
407-
return new OptimisticLockingStrategy(lockable, lockMode);
408-
case OPTIMISTIC_FORCE_INCREMENT:
409-
return new OptimisticForceIncrementLockingStrategy(lockable, lockMode);
410-
}
411-
if ( lockMode.greaterThan( LockMode.READ ) ) {
412-
return new UpdateLockingStrategy( lockable, lockMode );
413-
}
414-
else {
415-
return new SelectLockingStrategy( lockable, lockMode );
416-
}
393+
protected LockingStrategy buildPessimisticWriteStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
394+
// RDMS has no known variation of "SELECT ... FOR UPDATE" syntax...
395+
return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode );
396+
}
397+
398+
protected LockingStrategy buildPessimisticReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
399+
// RDMS has no known variation of "SELECT ... FOR UPDATE" syntax...
400+
return new PessimisticReadUpdateLockingStrategy( lockable, lockMode );
417401
}
418402

419403
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import jakarta.persistence.Timeout;
1010
import org.hibernate.LockMode;
11+
import org.hibernate.Locking;
1112
import org.hibernate.Timeouts;
1213
import org.hibernate.boot.model.FunctionContributions;
1314
import org.hibernate.community.dialect.pagination.TimesTenLimitHandler;
@@ -17,13 +18,8 @@
1718
import org.hibernate.dialect.RowLockStrategy;
1819
import org.hibernate.dialect.function.CommonFunctionFactory;
1920
import org.hibernate.dialect.lock.LockingStrategy;
20-
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
21-
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
22-
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
2321
import org.hibernate.dialect.lock.PessimisticReadUpdateLockingStrategy;
2422
import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy;
25-
import org.hibernate.dialect.lock.SelectLockingStrategy;
26-
import org.hibernate.dialect.lock.UpdateLockingStrategy;
2723
import org.hibernate.dialect.pagination.LimitHandler;
2824
import org.hibernate.dialect.sequence.SequenceSupport;
2925
import org.hibernate.dialect.temptable.TemporaryTable;
@@ -394,26 +390,14 @@ public String getTemporaryTableCreateOptions() {
394390
}
395391

396392
@Override
397-
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
393+
protected LockingStrategy buildPessimisticWriteStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
398394
// TimesTen has no known variation of a "SELECT ... FOR UPDATE" syntax...
399-
switch ( lockMode ) {
400-
case OPTIMISTIC:
401-
return new OptimisticLockingStrategy( lockable, lockMode );
402-
case OPTIMISTIC_FORCE_INCREMENT:
403-
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
404-
case PESSIMISTIC_READ:
405-
return new PessimisticReadUpdateLockingStrategy( lockable, lockMode );
406-
case PESSIMISTIC_WRITE:
407-
return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode );
408-
case PESSIMISTIC_FORCE_INCREMENT:
409-
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
410-
}
411-
if ( lockMode.greaterThan( LockMode.READ ) ) {
412-
return new UpdateLockingStrategy( lockable, lockMode );
413-
}
414-
else {
415-
return new SelectLockingStrategy( lockable, lockMode );
416-
}
395+
return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode );
396+
}
397+
398+
protected LockingStrategy buildPessimisticReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
399+
// TimesTen has no known variation of a "SELECT ... FOR UPDATE" syntax...
400+
return new PessimisticReadUpdateLockingStrategy( lockable, lockMode );
417401
}
418402

419403
@Override

hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy;
4949
import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
5050
import org.hibernate.dialect.lock.SelectLockingStrategy;
51+
import org.hibernate.dialect.lock.internal.SqlAstBasedLockingStrategy;
5152
import org.hibernate.dialect.pagination.LimitHandler;
5253
import org.hibernate.dialect.sequence.NoSequenceSupport;
5354
import org.hibernate.dialect.sequence.SequenceSupport;
@@ -2236,28 +2237,71 @@ public boolean supportsLockTimeouts() {
22362237
* @param lockMode The type of lock to be acquired.
22372238
* @return The appropriate locking strategy.
22382239
*
2239-
* @since 3.2
2240+
* @since 7
22402241
*/
2241-
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
2242+
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
22422243
return switch (lockMode) {
2243-
case PESSIMISTIC_FORCE_INCREMENT ->
2244-
new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
2245-
case UPGRADE_NOWAIT, UPGRADE_SKIPLOCKED, PESSIMISTIC_WRITE ->
2246-
new PessimisticWriteSelectLockingStrategy( lockable, lockMode );
2247-
case PESSIMISTIC_READ ->
2248-
new PessimisticReadSelectLockingStrategy( lockable, lockMode );
2249-
case OPTIMISTIC_FORCE_INCREMENT ->
2250-
new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
2251-
case OPTIMISTIC ->
2252-
new OptimisticLockingStrategy( lockable, lockMode );
2253-
case READ ->
2254-
new SelectLockingStrategy( lockable, lockMode );
2255-
default ->
2256-
// WRITE, NONE are not allowed here
2257-
throw new IllegalArgumentException( "Unsupported lock mode" );
2244+
case PESSIMISTIC_FORCE_INCREMENT -> buildPessimisticForceIncrementStrategy( lockable, lockMode, lockScope );
2245+
case UPGRADE_NOWAIT, UPGRADE_SKIPLOCKED, PESSIMISTIC_WRITE -> buildPessimisticWriteStrategy( lockable, lockMode, lockScope );
2246+
case PESSIMISTIC_READ -> buildPessimisticReadStrategy( lockable, lockMode, lockScope );
2247+
case OPTIMISTIC_FORCE_INCREMENT -> buildOptimisticForceIncrementStrategy( lockable, lockMode );
2248+
case OPTIMISTIC -> buildOptimisticStrategy( lockable, lockMode );
2249+
case READ -> buildReadStrategy( lockable, lockMode, lockScope );
2250+
default -> throw new IllegalArgumentException( "Unsupported lock mode : " + lockMode );
22582251
};
22592252
}
22602253

2254+
/**
2255+
* A {@link LockingStrategy} which is able to acquire a database-level
2256+
* lock with the specified {@linkplain LockMode level}.
2257+
*
2258+
* @param lockable The persister for the entity to be locked.
2259+
* @param lockMode The type of lock to be acquired.
2260+
* @return The appropriate locking strategy.
2261+
*
2262+
* @since 3.2
2263+
*
2264+
* @deprecated Use {@linkplain #getLockingStrategy(EntityPersister, LockMode, Locking.Scope)} instead.
2265+
*/
2266+
@Deprecated(since = "7", forRemoval = true)
2267+
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
2268+
return getLockingStrategy( lockable, lockMode, Locking.Scope.ROOT_ONLY );
2269+
}
2270+
2271+
protected LockingStrategy buildPessimisticForceIncrementStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
2272+
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
2273+
}
2274+
2275+
protected LockingStrategy buildPessimisticWriteStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
2276+
if ( lockScope == Locking.Scope.ROOT_ONLY && !lockable.hasMultipleTables() ) {
2277+
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode );
2278+
}
2279+
else {
2280+
return new SqlAstBasedLockingStrategy( lockable, lockMode, lockScope );
2281+
}
2282+
}
2283+
2284+
protected LockingStrategy buildPessimisticReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
2285+
if ( lockScope == Locking.Scope.ROOT_ONLY && !lockable.hasMultipleTables() ) {
2286+
return new PessimisticReadSelectLockingStrategy( lockable, lockMode );
2287+
}
2288+
else {
2289+
return new SqlAstBasedLockingStrategy( lockable, lockMode, lockScope );
2290+
}
2291+
}
2292+
2293+
protected LockingStrategy buildOptimisticForceIncrementStrategy(EntityPersister lockable, LockMode lockMode) {
2294+
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
2295+
}
2296+
2297+
protected LockingStrategy buildOptimisticStrategy(EntityPersister lockable, LockMode lockMode) {
2298+
return new OptimisticLockingStrategy( lockable, lockMode );
2299+
}
2300+
2301+
protected LockingStrategy buildReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
2302+
return new SelectLockingStrategy( lockable, lockMode );
2303+
}
2304+
22612305
/**
22622306
* Given a set of {@link LockOptions} (lock level, timeout),
22632307
* determine the appropriate {@code for update} fragment to

hibernate-core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@ default void lock(Object id, Object version, Object object, int timeout, SharedS
7575

7676
default void lock(Object id, Object version, Object object, Timeout timeout, SharedSessionContractImplementor session)
7777
throws StaleObjectStateException, LockingStrategyException {
78-
if ( session instanceof EventSource eventSource ) {
79-
lock( id, version, object, timeout.milliseconds(), eventSource );
80-
}
81-
else {
82-
throw new UnsupportedOperationException( "Optimistic locking strategies not supported in stateless session" );
83-
}
78+
lock( id, version, object, timeout.milliseconds(), session );
8479
}
8580
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.dialect.lock.internal;
6+
7+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
8+
import org.hibernate.sql.exec.internal.BaseExecutionContext;
9+
10+
/**
11+
* @author Steve Ebersole
12+
*/
13+
public class LockingExecutionContext extends BaseExecutionContext {
14+
public LockingExecutionContext(SharedSessionContractImplementor session) {
15+
super( session );
16+
}
17+
}

0 commit comments

Comments
 (0)