Skip to content

HHH-19336 - Proper implementation for JPA extended locking scope #10194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c6336f7
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 21, 2025
6610ff4
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 21, 2025
9e547ac
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 21, 2025
db9e55c
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 21, 2025
f54769a
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 21, 2025
009be86
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 22, 2025
8e0ec71
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 23, 2025
81d4711
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 23, 2025
c6071b7
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 23, 2025
99ad70f
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 23, 2025
a0089c1
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 24, 2025
3247576
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 24, 2025
ec485e8
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 25, 2025
f2a0c94
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 27, 2025
5cc9cec
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 29, 2025
f5b9db8
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 31, 2025
f721527
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 31, 2025
a29b2d3
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 31, 2025
9f1ebb5
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 31, 2025
60dea38
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole May 31, 2025
84c2413
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 1, 2025
a79af6e
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 2, 2025
7a4b07c
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 3, 2025
3b7f2a8
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 3, 2025
75bacaa
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 3, 2025
d110c72
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 4, 2025
cb0e666
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 4, 2025
6e87c48
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 4, 2025
8374c32
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 13, 2025
1ebe9de
HHH-19336 - Proper implementation for JPA extended locking scope
sebersole Jun 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.OracleTruncFunction;
import org.hibernate.dialect.lock.spi.OuterJoinLockingLevel;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
Expand Down Expand Up @@ -585,9 +586,9 @@ public boolean supportsFromClauseInUpdate() {
}

@Override
public boolean supportsOuterJoinForUpdate() {
public OuterJoinLockingLevel getOuterJoinLockingLevel() {
// "SELECT FOR UPDATE can only be used with a single-table SELECT statement"
return false;
return OuterJoinLockingLevel.UNSUPPORTED;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGro
predicate = tableGroupJoin.getPredicate();
}
if ( predicate != null && !predicate.isEmpty() ) {
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
renderJoinedTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
}
else {
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
renderJoinedTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
*/
package org.hibernate.community.dialect;

import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Locking;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.cfg.Environment;
import org.hibernate.community.dialect.identity.CacheIdentityColumnSupport;
Expand All @@ -20,13 +19,9 @@
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.PessimisticReadUpdateLockingStrategy;
import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy;
import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.dialect.lock.UpdateLockingStrategy;
import org.hibernate.dialect.lock.spi.OuterJoinLockingLevel;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.dialect.sequence.SequenceSupport;
Expand All @@ -39,24 +34,29 @@
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.LockingClauseStrategy;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;

import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
import static org.hibernate.sql.ast.internal.NonLockingClauseStrategy.NON_CLAUSE_STRATEGY;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.CLOB;
Expand Down Expand Up @@ -310,32 +310,27 @@
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@Override
public boolean supportsOuterJoinForUpdate() {
return false;
public LockingClauseStrategy getLockingClauseStrategy(QuerySpec querySpec, LockOptions lockOptions) {
return NON_CLAUSE_STRATEGY;
}

@Override
public LockingStrategy getLockingStrategy(EntityPersister lockable, LockMode lockMode) {
public OuterJoinLockingLevel getOuterJoinLockingLevel() {
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax at all
return OuterJoinLockingLevel.UNSUPPORTED;
}

@Override
protected LockingStrategy buildPessimisticWriteStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
// Set your transaction mode to READ_COMMITTED before using
switch (lockMode) {
case PESSIMISTIC_FORCE_INCREMENT:
return new PessimisticForceIncrementLockingStrategy(lockable, lockMode);
case PESSIMISTIC_WRITE:
return new PessimisticWriteUpdateLockingStrategy(lockable, lockMode);
case PESSIMISTIC_READ:
return new PessimisticReadUpdateLockingStrategy(lockable, lockMode);
case OPTIMISTIC:
return new OptimisticLockingStrategy(lockable, lockMode);
case OPTIMISTIC_FORCE_INCREMENT:
return new OptimisticForceIncrementLockingStrategy(lockable, lockMode);
}
if ( lockMode.greaterThan( LockMode.READ ) ) {
return new UpdateLockingStrategy( lockable, lockMode );
}
else {
return new SelectLockingStrategy( lockable, lockMode );
}
return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode );
}

protected LockingStrategy buildPessimisticReadStrategy(EntityPersister lockable, LockMode lockMode, Locking.Scope lockScope) {

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
Dialect.buildPessimisticReadStrategy
; it is advisable to add an Override annotation.
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
// Set your transaction mode to READ_COMMITTED before using
return new PessimisticReadUpdateLockingStrategy( lockable, lockMode );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.util.List;

import org.hibernate.Locking;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
Expand Down Expand Up @@ -34,16 +35,10 @@ public CacheSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement
@Override
protected LockStrategy determineLockingStrategy(
QuerySpec querySpec,
ForUpdateClause forUpdateClause,
Boolean followOnLocking) {
Locking.FollowOn followOnLocking) {
return LockStrategy.NONE;
}

@Override
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
// Cache does not support the FOR UPDATE clause
}

@Override
protected boolean needsRowsToSkip() {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.hibernate.dialect.function.PostgreSQLTruncFunction;
import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.lock.spi.OuterJoinLockingLevel;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport;
Expand Down Expand Up @@ -92,7 +93,6 @@
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -994,25 +994,10 @@ public String getForUpdateString(String aliases, LockOptions lockOptions) {
if ( getVersion().isBefore( 20, 1 ) ) {
return "";
}
/*
* Parent's implementation for (aliases, lockOptions) ignores aliases.
*/
if ( aliases.isEmpty() ) {
LockMode lockMode = lockOptions.getLockMode();
for ( Map.Entry<String, LockMode> entry : lockOptions.getAliasSpecificLocks() ) {
// seek the highest lock mode
if ( entry.getValue().greaterThan(lockMode) ) {
aliases = entry.getKey();
}
}
}
LockMode lockMode = lockOptions.getAliasSpecificLockMode( aliases );
if (lockMode == null ) {
lockMode = lockOptions.getLockMode();
}
final LockMode lockMode = lockOptions.getLockMode();
return switch ( lockMode ) {
case PESSIMISTIC_READ -> getReadLockString( aliases, lockOptions.getTimeOut() );
case PESSIMISTIC_WRITE -> getWriteLockString( aliases, lockOptions.getTimeOut() );
case PESSIMISTIC_READ -> getReadLockString( aliases, lockOptions.getTimeout() );
case PESSIMISTIC_WRITE -> getWriteLockString( aliases, lockOptions.getTimeout() );
case UPGRADE_NOWAIT, PESSIMISTIC_FORCE_INCREMENT -> getForUpdateNowaitString( aliases );
case UPGRADE_SKIPLOCKED -> getForUpdateSkipLockedString( aliases );
default -> "";
Expand Down Expand Up @@ -1100,8 +1085,8 @@ public String getForUpdateSkipLockedString(String aliases) {
}

@Override
public boolean supportsOuterJoinForUpdate() {
return false;
public OuterJoinLockingLevel getOuterJoinLockingLevel() {
return OuterJoinLockingLevel.UNSUPPORTED;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
package org.hibernate.community.dialect;

import org.hibernate.Locking;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
Expand Down Expand Up @@ -129,35 +130,15 @@ protected void renderMaterializationHint(CteMaterialization materialization) {
}
}

@Override
protected String getForShare(int timeoutMillis) {
return " for share";
}

@Override
protected String getForUpdate() {
return getDialect().getVersion().isBefore( 20, 1 ) ? "" : " for update";
}

@Override
protected LockStrategy determineLockingStrategy(
QuerySpec querySpec,
ForUpdateClause forUpdateClause,
Boolean followOnLocking) {
Locking.FollowOn followOnLocking) {
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
return LockStrategy.NONE;
}
return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking );
}

@Override
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
return;
}
super.renderForUpdateClause( querySpec, forUpdateClause );
return super.determineLockingStrategy( querySpec, followOnLocking );
}

protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.hibernate.dialect.function.TrimFunction;
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.lock.spi.OuterJoinLockingLevel;
import org.hibernate.dialect.pagination.DB2LimitHandler;
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
Expand Down Expand Up @@ -810,8 +811,8 @@ public String getReadLockString(int timeout) {
}

@Override
public boolean supportsOuterJoinForUpdate() {
return false;
public OuterJoinLockingLevel getOuterJoinLockingLevel() {
return OuterJoinLockingLevel.UNSUPPORTED;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected boolean needsRecursiveKeywordInWithClause() {
}

@Override
protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinIndex, boolean forceLeftJoin) {
protected void renderTableReferenceJoins(TableGroup tableGroup, LockMode lockMode, int swappedJoinIndex, boolean forceLeftJoin) {
// When we are in a recursive CTE, we can't render joins on DB2...
// See https://modern-sql.com/feature/with-recursive/db2/error-345-state-42836
if ( isInRecursiveQueryPart() ) {
Expand All @@ -102,7 +102,7 @@ protected void renderTableReferenceJoins(TableGroup tableGroup, int swappedJoinI
}
}
else {
super.renderTableReferenceJoins( tableGroup, swappedJoinIndex, forceLeftJoin );
super.renderTableReferenceJoins( tableGroup, lockMode, swappedJoinIndex, forceLeftJoin );
}
}

Expand All @@ -118,7 +118,7 @@ protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGro
}
appendSql( COMMA_SEPARATOR_CHAR );

renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
renderJoinedTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
addAdditionalWherePredicate( tableGroupJoin.getPredicate() );
}
Expand Down Expand Up @@ -212,21 +212,6 @@ protected void visitAnsiCaseSimpleExpression(
}
}

@Override
protected String getForUpdate() {
return " for read only with rs use and keep update locks";
}

@Override
protected String getForShare(int timeoutMillis) {
return " for read only with rs use and keep share locks";
}

@Override
protected String getSkipLocked() {
return " skip locked data";
}

protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
// Check if current query part is already row numbering to avoid infinite recursion
if ( getQueryPartForRowNumbering() == queryPart ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Locale;

import jakarta.persistence.Timeout;
import org.hibernate.Locking;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.DB2Dialect;
Expand All @@ -29,6 +30,7 @@
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.community.dialect.pagination.DerbyLimitHandler;
import org.hibernate.dialect.lock.spi.OuterJoinLockingLevel;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.community.dialect.sequence.DerbySequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
Expand Down Expand Up @@ -62,6 +64,8 @@
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.internal.PessimisticLockKind;
import org.hibernate.sql.ast.spi.LockingClauseStrategy;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
Expand Down Expand Up @@ -568,6 +572,11 @@ public boolean supportsCommentOn() {
return false;
}

@Override
protected LockingClauseStrategy buildLockingClauseStrategy(PessimisticLockKind lockKind, RowLockStrategy rowLockStrategy, Locking.Scope lockScope, int timeout) {
return new DerbyLockingClauseStrategy( this, lockKind, rowLockStrategy, lockScope, timeout );
}

@Override
public RowLockStrategy getReadRowLockStrategy() {
return RowLockStrategy.NONE;
Expand Down Expand Up @@ -599,9 +608,9 @@ public String getReadLockString(int timeout) {
}

@Override
public boolean supportsOuterJoinForUpdate() {
public OuterJoinLockingLevel getOuterJoinLockingLevel() {
//TODO: check this!
return false;
return OuterJoinLockingLevel.UNSUPPORTED;
}

@Override
Expand Down
Loading
Loading