1515import org .hibernate .engine .spi .LoadQueryInfluencers ;
1616import org .hibernate .engine .spi .SessionFactoryImplementor ;
1717import org .hibernate .engine .spi .SharedSessionContractImplementor ;
18- import org .hibernate .event .spi .EventSource ;
1918import org .hibernate .exception .LockTimeoutException ;
2019import org .hibernate .loader .ast .internal .LoaderSqlAstCreationState ;
2120import org .hibernate .metamodel .mapping .EntityIdentifierMapping ;
2726import org .hibernate .spi .NavigablePath ;
2827import org .hibernate .sql .ast .spi .SimpleFromClauseAccessImpl ;
2928import org .hibernate .sql .ast .spi .SqlAliasBaseManager ;
29+ import org .hibernate .sql .ast .spi .SqlExpressionResolver ;
3030import org .hibernate .sql .ast .tree .from .TableGroup ;
3131import org .hibernate .sql .ast .tree .predicate .ComparisonPredicate ;
3232import org .hibernate .sql .ast .tree .select .QuerySpec ;
3737import org .hibernate .sql .exec .spi .JdbcOperationQuerySelect ;
3838import org .hibernate .sql .exec .spi .JdbcParameterBindings ;
3939import org .hibernate .sql .exec .spi .JdbcSelectExecutor ;
40- import org .hibernate .sql .results .graph .DomainResult ;
40+ import org .hibernate .sql .results .graph .basic . BasicResult ;
4141import org .hibernate .sql .results .graph .internal .ImmutableFetchList ;
4242import org .hibernate .sql .results .spi .NoRowException ;
4343import 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