77import jakarta .persistence .Timeout ;
88import org .hibernate .AssertionFailure ;
99import org .hibernate .LockOptions ;
10- import org .hibernate .Locking ;
1110import org .hibernate .dialect .Dialect ;
1211import org .hibernate .dialect .RowLockStrategy ;
1312import org .hibernate .internal .util .collections .CollectionHelper ;
1716import org .hibernate .metamodel .mapping .PluralAttributeMapping ;
1817import org .hibernate .metamodel .mapping .SelectableMappings ;
1918import org .hibernate .metamodel .mapping .TableDetails ;
20- import org .hibernate .metamodel .mapping .internal .BasicValuedCollectionPart ;
2119import org .hibernate .persister .entity .EntityPersister ;
2220import org .hibernate .persister .entity .mutation .EntityTableMapping ;
2321import org .hibernate .spi .NavigablePath ;
2422import org .hibernate .sql .ast .SqlAstJoinType ;
25- import org .hibernate .sql .ast .spi .LockingClauseStrategy ;
2623import org .hibernate .sql .ast .spi .SqlAppender ;
2724import org .hibernate .sql .ast .tree .from .NamedTableReference ;
2825import org .hibernate .sql .ast .tree .from .TableGroup ;
4441 *
4542 * @author Steve Ebersole
4643 */
47- public class StandardLockingClauseStrategy implements LockingClauseStrategy {
44+ public class StandardLockingClauseStrategy extends AbstractLockingClauseStrategy {
4845 private final Dialect dialect ;
4946 private final RowLockStrategy rowLockStrategy ;
5047 private final PessimisticLockKind lockKind ;
51- private final Locking .Scope lockingScope ;
5248 private final Timeout timeout ;
5349
54- /**
55- * @implNote Tracked separately from {@linkplain #rootsToLock} and
56- * {@linkplain #joinsToLock} to help answer {@linkplain #containsOuterJoins()}
57- * for {@linkplain RowLockStrategy#NONE cases} where we otherwise don't need to
58- * track the tables, allowing to avoid the overhead of the Sets. There is a
59- * slight trade-off in that we need to inspect the from-elements to make that
60- * determination when we might otherwise not need to - memory versus cpu.
61- */
6250 private boolean queryHasOuterJoins = false ;
6351
64- private final Set <NavigablePath > rootsForLocking ;
65-
6652 private Set <TableGroup > rootsToLock ;
6753 private Set <TableGroupJoin > joinsToLock ;
68- private Set <NavigablePath > pathsToLock ;
6954
7055 public StandardLockingClauseStrategy (
7156 Dialect dialect ,
7257 PessimisticLockKind lockKind ,
7358 RowLockStrategy rowLockStrategy ,
7459 LockOptions lockOptions ,
7560 Set <NavigablePath > rootsForLocking ) {
76- // NOTE: previous versions would limit collection based on RowLockStrategy.
77- // however, this causes problems with the new follow-on locking approach
61+ super ( lockOptions .getScope (), rootsForLocking );
7862
7963 assert lockKind != PessimisticLockKind .NONE ;
8064
8165 this .dialect = dialect ;
8266 this .rowLockStrategy = rowLockStrategy ;
8367 this .lockKind = lockKind ;
84- this .lockingScope = lockOptions .getScope ();
8568 this .timeout = lockOptions .getTimeout ();
69+ }
70+
71+ @ Override
72+ public Collection <TableGroup > getRootsToLock () {
73+ return rootsToLock ;
74+ }
8675
87- this .rootsForLocking = rootsForLocking == null ? Set .of () : rootsForLocking ;
76+ @ Override
77+ public Collection <TableGroupJoin > getJoinsToLock () {
78+ return joinsToLock ;
8879 }
8980
9081 @ Override
@@ -96,62 +87,31 @@ public void registerRoot(TableGroup root) {
9687 }
9788 }
9889
99- if ( rootsForLocking .contains ( root .getNavigablePath () ) ) {
100- if ( rootsToLock == null ) {
101- rootsToLock = new HashSet <>();
102- }
103- if ( pathsToLock == null ) {
104- pathsToLock = new HashSet <>();
105- }
106- rootsToLock .add ( root );
107- pathsToLock .add ( root .getNavigablePath () );
108- }
90+ super .registerRoot ( root );
10991 }
11092
11193 @ Override
112- public void registerJoin (TableGroupJoin join ) {
113- checkForOuterJoins ( join );
114-
115- // we only want to consider applying locks to joins in 2 cases:
116- // 1) It is a root path for locking (aka occurs in the domain select-clause)
117- // 2) It's left-hand side is to be locked
118- if ( isRootForLocking ( join ) ) {
119- trackJoin ( join );
120- }
121- else if ( isLhsLocked ( join ) ) {
122- if ( lockingScope == Locking .Scope .INCLUDE_COLLECTIONS ) {
123- // if the TableGroup is an owned (aka, non-inverse) collection,
124- // and we are to lock collections, track it
125- if ( join .getJoinedGroup ().getModelPart () instanceof PluralAttributeMapping attrMapping ) {
126- if ( !attrMapping .getCollectionDescriptor ().isInverse () ) {
127- // owned collection
128- if ( attrMapping .getElementDescriptor () instanceof BasicValuedCollectionPart ) {
129- // an element-collection
130- trackJoin ( join );
131- }
132- }
133- }
134- }
135- else if ( lockingScope == Locking .Scope .INCLUDE_FETCHES ) {
136- if ( join .getJoinedGroup ().isFetched () ) {
137- trackJoin ( join );
138- }
139- }
94+ protected void trackRoot (TableGroup root ) {
95+ super .trackRoot ( root );
96+ if ( rootsToLock == null ) {
97+ rootsToLock = new HashSet <>();
14098 }
99+ rootsToLock .add ( root );
141100 }
142101
143- private boolean isRootForLocking (TableGroupJoin join ) {
144- return rootsForLocking .contains ( join .getNavigablePath () );
102+ @ Override
103+ public void registerJoin (TableGroupJoin join ) {
104+ checkForOuterJoins ( join );
105+ super .registerJoin ( join );
145106 }
146107
147- private boolean isLhsLocked (TableGroupJoin join ) {
148- // TODO (pessimistic-locking) : The use of NavigablePath#parent for LHS here is not ideal.
149- // However, the only alternative is to change the method signature to pass the
150- // join's LHS which would have a broad impact on Dialects and translators.
151- // I'm sure this will miss some cases, but let's start here fow now and deal with
152- // these other cases as they come up.
153- return pathsToLock != null
154- && pathsToLock .contains ( join .getNavigablePath ().getParent () );
108+ @ Override
109+ protected void trackJoin (TableGroupJoin join ) {
110+ super .trackJoin ( join );
111+ if ( joinsToLock == null ) {
112+ joinsToLock = new LinkedHashSet <>();
113+ }
114+ joinsToLock .add ( join );
155115 }
156116
157117 private void checkForOuterJoins (TableGroupJoin join ) {
@@ -179,17 +139,6 @@ private boolean hasOuterJoin(TableGroupJoin join) {
179139 return false ;
180140 }
181141
182- private void trackJoin (TableGroupJoin join ) {
183- if ( joinsToLock == null ) {
184- joinsToLock = new LinkedHashSet <>();
185- }
186- if ( pathsToLock == null ) {
187- pathsToLock = new HashSet <>();
188- }
189- joinsToLock .add ( join );
190- pathsToLock .add ( join .getNavigablePath () );
191- }
192-
193142 @ Override
194143 public boolean containsOuterJoins () {
195144 return queryHasOuterJoins ;
@@ -201,16 +150,6 @@ public void render(SqlAppender sqlAppender) {
201150 renderResultSetOptions ( sqlAppender );
202151 }
203152
204- @ Override
205- public Collection <TableGroup > getRootsToLock () {
206- return rootsToLock ;
207- }
208-
209- @ Override
210- public Collection <TableGroupJoin > getJoinsToLock () {
211- return joinsToLock ;
212- }
213-
214153 protected void renderLockFragment (SqlAppender sqlAppender ) {
215154 final String fragment ;
216155 if ( rowLockStrategy == RowLockStrategy .NONE ) {
@@ -233,13 +172,13 @@ private String collectLockItems() {
233172 }
234173
235174 final List <String > lockItems = new ArrayList <>();
236- if ( rootsToLock != null ) {
237- for ( TableGroup root : rootsToLock ) {
175+ if ( getRootsToLock () != null ) {
176+ for ( TableGroup root : getRootsToLock () ) {
238177 collectLockItems ( root , lockItems );
239178 }
240179 }
241- if ( joinsToLock != null ) {
242- for ( TableGroupJoin join : joinsToLock ) {
180+ if ( getJoinsToLock () != null ) {
181+ for ( TableGroupJoin join : getJoinsToLock () ) {
243182 collectLockItems ( join .getJoinedGroup (), lockItems );
244183 }
245184 }
0 commit comments