Skip to content

Commit 818df6d

Browse files
committed
HHH-19551 - Address deficiencies in pessimistic locking
1 parent 5bf8dbb commit 818df6d

File tree

1 file changed

+28
-23
lines changed

1 file changed

+28
-23
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/SqlAstBasedLockingStrategy.java

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import org.hibernate.engine.spi.LoadQueryInfluencers;
1616
import org.hibernate.engine.spi.SessionFactoryImplementor;
1717
import org.hibernate.engine.spi.SharedSessionContractImplementor;
18-
import org.hibernate.event.spi.EventSource;
1918
import org.hibernate.exception.LockTimeoutException;
2019
import org.hibernate.loader.ast.internal.LoaderSqlAstCreationState;
2120
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
@@ -27,6 +26,7 @@
2726
import org.hibernate.spi.NavigablePath;
2827
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
2928
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
29+
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
3030
import org.hibernate.sql.ast.tree.from.TableGroup;
3131
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
3232
import org.hibernate.sql.ast.tree.select.QuerySpec;
@@ -37,7 +37,7 @@
3737
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
3838
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
3939
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
40-
import org.hibernate.sql.results.graph.DomainResult;
40+
import org.hibernate.sql.results.graph.basic.BasicResult;
4141
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
4242
import org.hibernate.sql.results.spi.NoRowException;
4343
import org.hibernate.sql.results.spi.SingleResultConsumer;
@@ -67,30 +67,33 @@ public void lock(
6767
Object object,
6868
int timeout,
6969
SharedSessionContractImplementor session) throws StaleObjectStateException, LockingStrategyException {
70-
if ( session instanceof EventSource eventSource ) {
71-
doLock( id, version, object, timeout, eventSource );
72-
}
73-
else {
74-
throw new UnsupportedOperationException( "Optimistic locking strategies not supported in stateless session" );
75-
}
76-
}
77-
78-
private void doLock(Object id, Object version, Object object, int timeout, EventSource eventSource) {
79-
final SessionFactoryImplementor factory = eventSource.getFactory();
70+
final SessionFactoryImplementor factory = session.getFactory();
8071

8172
final LockOptions lockOptions = new LockOptions( lockMode );
8273
lockOptions.setScope( lockScope );
8374
lockOptions.setTimeOut( timeout );
8475

8576
final QuerySpec rootQuerySpec = new QuerySpec( true );
8677
final NavigablePath entityPath = new NavigablePath( entityToLock.getRootPathName() );
78+
final EntityIdentifierMapping idMapping = entityToLock.getIdentifierMapping();
8779

80+
// NOTE: there are 2 possible ways to handle the select list for the query...
81+
// 1) use normal `idMapping.createDomainResult`. for simple ids, this is fine; however,
82+
// for composite ids, this would require a proper implementation of `FetchProcessor`
83+
// (the parts of the composition are considered `Fetch`es). `FetchProcessor` is not
84+
// a trivial thing to implement though. this would be the "best" approach though.
85+
// look at simplifying LoaderSelectBuilder.visitFetches for reusability
86+
// 2) for now, we'll just manually build the selection list using "one of" the id columns
87+
// and manually build a simple `BasicResult`
8888
final LoaderSqlAstCreationState sqlAstCreationState = new LoaderSqlAstCreationState(
8989
rootQuerySpec,
9090
new SqlAliasBaseManager(),
9191
new SimpleFromClauseAccessImpl(),
9292
lockOptions,
93-
(fetchParent, creationState) -> ImmutableFetchList.EMPTY,
93+
(fetchParent, creationState) -> {
94+
// todo (db-locking) : look to simplify LoaderSelectBuilder.visitFetches for reusability
95+
return ImmutableFetchList.EMPTY;
96+
},
9497
true,
9598
new LoadQueryInfluencers( factory ),
9699
factory.getSqlTranslationEngine()
@@ -108,13 +111,15 @@ private void doLock(Object id, Object version, Object object, int timeout, Event
108111
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
109112
sqlAstCreationState.getFromClauseAccess().registerTableGroup( entityPath, rootTableGroup );
110113

111-
final EntityIdentifierMapping idMapping = entityToLock.getIdentifierMapping();
112-
final DomainResult<?> idResult = idMapping.createDomainResult(
113-
entityPath.append( idMapping.getPartName() ),
114-
rootTableGroup,
114+
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
115+
final SelectableMapping firstIdColumn = idMapping.getSelectable( 0 );
116+
sqlExpressionResolver.resolveSqlSelection(
117+
sqlExpressionResolver.resolveSqlExpression( rootTableGroup.getPrimaryTableReference(), firstIdColumn ),
118+
firstIdColumn.getJdbcMapping().getJdbcJavaType(),
115119
null,
116-
sqlAstCreationState
120+
session.getTypeConfiguration()
117121
);
122+
final BasicResult<Object> idResult = new BasicResult<>( 0, null, idMapping.getJdbcMapping( 0 ) );
118123

119124
final int jdbcParamCount = entityToLock.getVersionMapping() != null
120125
? idMapping.getJdbcTypeCount() + 1
@@ -130,7 +135,7 @@ private void doLock(Object id, Object version, Object object, int timeout, Event
130135
rootTableGroup,
131136
jdbcParameterBindings
132137
),
133-
eventSource
138+
session
134139
);
135140

136141
if ( entityToLock.getVersionMapping() != null ) {
@@ -144,19 +149,19 @@ private void doLock(Object id, Object version, Object object, int timeout, Event
144149
rootTableGroup,
145150
jdbcParameterBindings
146151
),
147-
eventSource
152+
session
148153
);
149154
}
150155

151156
final SelectStatement selectStatement = new SelectStatement( rootQuerySpec, List.of( idResult ) );
152-
final JdbcOperationQuerySelect selectOperation = eventSource
157+
final JdbcOperationQuerySelect selectOperation = session
153158
.getDialect()
154159
.getSqlAstTranslatorFactory()
155160
.buildSelectTranslator( factory, selectStatement )
156161
.translate( jdbcParameterBindings, sqlAstCreationState );
157162

158163
final JdbcSelectExecutor jdbcSelectExecutor = factory.getJdbcServices().getJdbcSelectExecutor();
159-
final LockingExecutionContext lockingExecutionContext = new LockingExecutionContext( eventSource );
164+
final LockingExecutionContext lockingExecutionContext = new LockingExecutionContext( session );
160165

161166
try {
162167
jdbcSelectExecutor.executeQuery(
@@ -178,7 +183,7 @@ private void doLock(Object id, Object version, Object object, int timeout, Event
178183
}
179184
catch (NoRowException e) {
180185
if ( entityToLock.optimisticLockStyle() != OptimisticLockStyle.NONE ) {
181-
final StatisticsImplementor statistics = eventSource.getFactory().getStatistics();
186+
final StatisticsImplementor statistics = session.getFactory().getStatistics();
182187
if ( statistics.isStatisticsEnabled() ) {
183188
statistics.optimisticFailure( entityToLock.getEntityName() );
184189
}

0 commit comments

Comments
 (0)