4
4
*/
5
5
package org .hibernate .loader .ast .internal ;
6
6
7
+ import org .checkerframework .checker .nullness .qual .NonNull ;
7
8
import org .hibernate .LockMode ;
8
9
import org .hibernate .LockOptions ;
9
10
import org .hibernate .engine .jdbc .spi .JdbcServices ;
20
21
import org .hibernate .sql .exec .spi .JdbcSelectExecutor ;
21
22
import org .hibernate .type .descriptor .java .JavaType ;
22
23
24
+ import java .lang .reflect .Array ;
23
25
import java .util .ArrayList ;
24
26
import java .util .List ;
25
27
26
28
import static org .hibernate .internal .util .collections .CollectionHelper .arrayList ;
29
+ import static org .hibernate .internal .util .collections .CollectionHelper .isEmpty ;
27
30
import static org .hibernate .loader .ast .internal .CacheEntityLoaderHelper .loadFromSessionCacheStatic ;
28
31
import static org .hibernate .loader .ast .internal .LoaderHelper .getReadOnlyFromLoadQueryInfluencers ;
29
32
import static org .hibernate .loader .ast .internal .MultiKeyLoadLogging .MULTI_KEY_LOAD_LOGGER ;
@@ -37,11 +40,13 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
37
40
private final EntityMappingType entityDescriptor ;
38
41
private final SessionFactoryImplementor sessionFactory ;
39
42
private final EntityIdentifierMapping identifierMapping ;
43
+ protected final Object [] idArray ;
40
44
41
45
public AbstractMultiIdEntityLoader (EntityMappingType entityDescriptor , SessionFactoryImplementor sessionFactory ) {
42
46
this .entityDescriptor = entityDescriptor ;
43
47
this .sessionFactory = sessionFactory ;
44
48
identifierMapping = getLoadable ().getIdentifierMapping ();
49
+ idArray = (Object []) Array .newInstance ( identifierMapping .getJavaType ().getJavaTypeClass (), 0 );
45
50
}
46
51
47
52
protected EntityMappingType getEntityDescriptor () {
@@ -76,31 +81,43 @@ public EntityMappingType getLoadable() {
76
81
@ Override
77
82
public final <K > List <T > load (K [] ids , MultiIdLoadOptions loadOptions , EventSource session ) {
78
83
assert ids != null ;
79
- if ( loadOptions .isOrderReturnEnabled () ) {
80
- return performOrderedMultiLoad ( ids , loadOptions , session );
81
- }
82
- else {
83
- return performUnorderedMultiLoad ( ids , loadOptions , session );
84
+ return loadOptions .isOrderReturnEnabled ()
85
+ ? performOrderedMultiLoad ( ids , loadOptions , session )
86
+ : performUnorderedMultiLoad ( ids , loadOptions , session );
87
+ }
88
+
89
+ private List <T > performUnorderedMultiLoad (
90
+ Object [] ids ,
91
+ MultiIdLoadOptions loadOptions ,
92
+ EventSource session ) {
93
+ assert !loadOptions .isOrderReturnEnabled ();
94
+ assert ids != null ;
95
+ if ( MULTI_KEY_LOAD_LOGGER .isTraceEnabled () ) {
96
+ MULTI_KEY_LOAD_LOGGER .tracef ( "#performUnorderedMultiLoad(`%s`, ..)" , getLoadable ().getEntityName () );
84
97
}
98
+ return unorderedMultiLoad ( ids , loadOptions , lockOptions ( loadOptions ), session );
85
99
}
86
100
87
101
protected List <T > performOrderedMultiLoad (
88
102
Object [] ids ,
89
103
MultiIdLoadOptions loadOptions ,
90
104
EventSource session ) {
105
+ assert loadOptions .isOrderReturnEnabled ();
106
+ assert ids != null ;
91
107
if ( MULTI_KEY_LOAD_LOGGER .isTraceEnabled () ) {
92
108
MULTI_KEY_LOAD_LOGGER .tracef ( "#performOrderedMultiLoad(`%s`, ..)" , getLoadable ().getEntityName () );
93
109
}
110
+ return orderedMultiLoad ( ids , loadOptions , lockOptions ( loadOptions ), session );
111
+ }
94
112
95
- assert loadOptions .isOrderReturnEnabled ();
96
-
97
- final boolean coerce = !getSessionFactory ().getJpaMetamodel ().getJpaCompliance ().isLoadByIdComplianceEnabled ();
113
+ private List <T > orderedMultiLoad (
114
+ Object [] ids ,
115
+ MultiIdLoadOptions loadOptions ,
116
+ LockOptions lockOptions ,
117
+ EventSource session ) {
118
+ final boolean idCoercionEnabled = isIdCoercionEnabled ();
98
119
final JavaType <?> idType = getLoadable ().getIdentifierMapping ().getJavaType ();
99
120
100
- final LockOptions lockOptions = loadOptions .getLockOptions () == null
101
- ? new LockOptions ( LockMode .NONE )
102
- : loadOptions .getLockOptions ();
103
-
104
121
final int maxBatchSize = maxBatchSize ( ids , loadOptions );
105
122
106
123
final List <Object > result = arrayList ( ids .length );
@@ -109,10 +126,10 @@ protected List<T> performOrderedMultiLoad(
109
126
final List <Integer > elementPositionsLoadedByBatch = new ArrayList <>();
110
127
111
128
for ( int i = 0 ; i < ids .length ; i ++ ) {
112
- final Object id = coerce ? idType .coerce ( ids [i ], session ) : ids [i ];
129
+ final Object id = idCoercionEnabled ? idType .coerce ( ids [i ], session ) : ids [i ];
113
130
final EntityKey entityKey = new EntityKey ( id , getLoadable ().getEntityPersister () );
114
131
115
- if ( !loadFromCaches ( loadOptions , session , id , lockOptions , entityKey , result , i ) ) {
132
+ if ( !loadFromEnabledCaches ( loadOptions , session , id , lockOptions , entityKey , result , i ) ) {
116
133
// if we did not hit any of the continues above,
117
134
// then we need to batch load the entity state.
118
135
idsInBatch .add ( id );
@@ -142,13 +159,27 @@ protected List<T> performOrderedMultiLoad(
142
159
return (List <T >) result ;
143
160
}
144
161
162
+ protected static LockOptions lockOptions (MultiIdLoadOptions loadOptions ) {
163
+ return loadOptions .getLockOptions () == null
164
+ ? new LockOptions ( LockMode .NONE )
165
+ : loadOptions .getLockOptions ();
166
+ }
167
+
145
168
protected abstract int maxBatchSize (Object [] ids , MultiIdLoadOptions loadOptions );
146
169
147
- protected abstract void handleResults (MultiIdLoadOptions loadOptions , EventSource session , List <Integer > elementPositionsLoadedByBatch , List <Object > result );
170
+ protected abstract void handleResults (
171
+ MultiIdLoadOptions loadOptions ,
172
+ EventSource session ,
173
+ List <Integer > elementPositionsLoadedByBatch ,
174
+ List <Object > result );
148
175
149
- protected abstract void loadEntitiesById (List <Object > idsInBatch , LockOptions lockOptions , MultiIdLoadOptions loadOptions , EventSource session );
176
+ protected abstract void loadEntitiesById (
177
+ List <Object > idsInBatch ,
178
+ LockOptions lockOptions ,
179
+ MultiIdLoadOptions loadOptions ,
180
+ EventSource session );
150
181
151
- protected boolean loadFromCaches (
182
+ protected boolean loadFromEnabledCaches (
152
183
MultiIdLoadOptions loadOptions ,
153
184
EventSource session ,
154
185
Object id ,
@@ -157,48 +188,189 @@ protected boolean loadFromCaches(
157
188
List <Object > result ,
158
189
int i ) {
159
190
if ( loadOptions .isSessionCheckingEnabled () || loadOptions .isSecondLevelCacheCheckingEnabled () ) {
160
- final LoadEvent loadEvent = new LoadEvent (
161
- id ,
162
- getLoadable ().getJavaType ().getJavaTypeClass ().getName (),
163
- lockOptions ,
164
- session ,
165
- getReadOnlyFromLoadQueryInfluencers ( session )
191
+ return loadFromCaches ( loadOptions , entityKey , result , i ,
192
+ new LoadEvent (
193
+ id ,
194
+ getLoadable ().getJavaType ().getJavaTypeClass ().getName (),
195
+ lockOptions ,
196
+ session ,
197
+ getReadOnlyFromLoadQueryInfluencers ( session )
198
+ )
166
199
);
200
+ }
201
+ else {
202
+ return false ;
203
+ }
204
+ }
167
205
168
- Object managedEntity = null ;
206
+ private boolean loadFromCaches (
207
+ MultiIdLoadOptions loadOptions ,
208
+ EntityKey entityKey ,
209
+ List <Object > result ,
210
+ int i ,
211
+ LoadEvent loadEvent ) {
212
+ Object managedEntity = null ;
213
+
214
+ if ( loadOptions .isSessionCheckingEnabled () ) {
215
+ // look for it in the Session first
216
+ final CacheEntityLoaderHelper .PersistenceContextEntry persistenceContextEntry =
217
+ loadFromSessionCacheStatic ( loadEvent , entityKey , LoadEventListener .GET );
218
+ managedEntity = persistenceContextEntry .getEntity ();
219
+
220
+ if ( managedEntity != null
221
+ && !loadOptions .isReturnOfDeletedEntitiesEnabled ()
222
+ && !persistenceContextEntry .isManaged () ) {
223
+ // put a null in the result
224
+ result .add ( i , null );
225
+ return true ;
226
+ }
227
+ }
169
228
170
- if ( loadOptions .isSessionCheckingEnabled () ) {
171
- // look for it in the Session first
172
- final CacheEntityLoaderHelper .PersistenceContextEntry persistenceContextEntry =
173
- loadFromSessionCacheStatic ( loadEvent , entityKey , LoadEventListener .GET );
174
- managedEntity = persistenceContextEntry .getEntity ();
229
+ if ( managedEntity == null
230
+ && loadOptions .isSecondLevelCacheCheckingEnabled () ) {
231
+ // look for it in the SessionFactory
232
+ managedEntity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (
233
+ loadEvent ,
234
+ getLoadable ().getEntityPersister (),
235
+ entityKey
236
+ );
237
+ }
175
238
176
- if ( managedEntity != null
177
- && !loadOptions .isReturnOfDeletedEntitiesEnabled ()
178
- && !persistenceContextEntry .isManaged () ) {
179
- // put a null in the result
180
- result .add ( i , null );
181
- return true ;
182
- }
183
- }
239
+ if ( managedEntity != null ) {
240
+ result .add ( i , managedEntity );
241
+ return true ;
242
+ }
243
+ else {
244
+ return false ;
245
+ }
246
+ }
247
+
248
+ protected List <T > unorderedMultiLoad (
249
+ Object [] ids ,
250
+ MultiIdLoadOptions loadOptions ,
251
+ LockOptions lockOptions ,
252
+ EventSource session ) {
253
+ final List <T > result = arrayList ( ids .length );
254
+ final Object [] unresolvableIds =
255
+ resolveInCachesIfEnabled ( ids , loadOptions , lockOptions , session ,
256
+ (position , entityKey , resolvedRef ) -> result .add ( (T ) resolvedRef ) );
257
+ if ( !isEmpty ( unresolvableIds ) ) {
258
+ loadEntitiesWithUnresolvedIds ( loadOptions , lockOptions , session , unresolvableIds , result );
259
+ }
260
+ return result ;
261
+ }
262
+
263
+ protected abstract void loadEntitiesWithUnresolvedIds (
264
+ MultiIdLoadOptions loadOptions ,
265
+ LockOptions lockOptions ,
266
+ EventSource session ,
267
+ Object [] unresolvableIds ,
268
+ List <T > result );
269
+
270
+ protected final <R > Object [] resolveInCachesIfEnabled (
271
+ Object [] ids ,
272
+ @ NonNull MultiIdLoadOptions loadOptions ,
273
+ @ NonNull LockOptions lockOptions ,
274
+ EventSource session ,
275
+ ResolutionConsumer <R > resolutionConsumer ) {
276
+ return loadOptions .isSessionCheckingEnabled () || loadOptions .isSecondLevelCacheCheckingEnabled ()
277
+ // the user requested that we exclude ids corresponding to already managed
278
+ // entities from the generated load SQL. So here we will iterate all
279
+ // incoming id values and see whether it corresponds to an existing
280
+ // entity associated with the PC - if it does we add it to the result
281
+ // list immediately and remove its id from the group of ids to load.
282
+ // we'll load all of them from the database
283
+ ? resolveInCaches ( ids , loadOptions , lockOptions , session , resolutionConsumer )
284
+ : ids ;
285
+ }
286
+
287
+ protected final <R > Object [] resolveInCaches (
288
+ Object [] ids ,
289
+ MultiIdLoadOptions loadOptions ,
290
+ LockOptions lockOptions ,
291
+ EventSource session ,
292
+ ResolutionConsumer <R > resolutionConsumer ) {
293
+
294
+ final boolean idCoercionEnabled = isIdCoercionEnabled ();
295
+ final JavaType <?> idType = getLoadable ().getIdentifierMapping ().getJavaType ();
184
296
185
- if ( managedEntity == null && loadOptions .isSecondLevelCacheCheckingEnabled () ) {
186
- // look for it in the SessionFactory
187
- managedEntity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (
188
- loadEvent ,
189
- getLoadable ().getEntityPersister (),
190
- entityKey
191
- );
297
+ List <Object > unresolvedIds = null ;
298
+ for ( int i = 0 ; i < ids .length ; i ++ ) {
299
+ final Object id = idCoercionEnabled ? idType .coerce ( ids [i ], session ) : ids [i ];
300
+ final EntityKey entityKey = new EntityKey ( id , getLoadable ().getEntityPersister () );
301
+ unresolvedIds = loadFromCaches ( id , entityKey , i , unresolvedIds , loadOptions , resolutionConsumer ,
302
+ new LoadEvent (
303
+ id ,
304
+ getLoadable ().getJavaType ().getJavaTypeClass ().getName (),
305
+ lockOptions ,
306
+ session ,
307
+ getReadOnlyFromLoadQueryInfluencers ( session )
308
+ )
309
+ );
310
+ }
311
+
312
+ if ( isEmpty ( unresolvedIds ) ) {
313
+ // all the given ids were already associated with the Session
314
+ return null ;
315
+ }
316
+ else if ( unresolvedIds .size () == ids .length ) {
317
+ // we need to load all the ids
318
+ return ids ;
319
+ }
320
+ else {
321
+ // we need to load only some the ids
322
+ return unresolvedIds .toArray ( idArray );
323
+ }
324
+ }
325
+
326
+ private boolean isIdCoercionEnabled () {
327
+ return !getSessionFactory ().getJpaMetamodel ().getJpaCompliance ().isLoadByIdComplianceEnabled ();
328
+ }
329
+
330
+ public interface ResolutionConsumer <T > {
331
+ void consume (int position , EntityKey entityKey , T resolvedRef );
332
+ }
333
+ private <R , K > List <K > loadFromCaches (
334
+ K id , EntityKey entityKey , int i ,
335
+ List <K > unresolvedIds ,
336
+ MultiIdLoadOptions loadOptions ,
337
+ ResolutionConsumer <R > resolutionConsumer ,
338
+ LoadEvent loadEvent ) {
339
+ Object cachedEntity = null ;
340
+
341
+ // look for it in the Session first
342
+ final CacheEntityLoaderHelper .PersistenceContextEntry persistenceContextEntry =
343
+ loadFromSessionCacheStatic ( loadEvent , entityKey , LoadEventListener .GET );
344
+ if ( loadOptions .isSessionCheckingEnabled () ) {
345
+ cachedEntity = persistenceContextEntry .getEntity ();
346
+
347
+ if ( cachedEntity != null
348
+ && !loadOptions .isReturnOfDeletedEntitiesEnabled ()
349
+ && !persistenceContextEntry .isManaged () ) {
350
+ resolutionConsumer .consume ( i , entityKey , null );
351
+ return unresolvedIds ;
192
352
}
353
+ }
193
354
194
- if ( managedEntity != null ) {
195
- result .add ( i , managedEntity );
196
- return true ;
355
+ if ( cachedEntity == null && loadOptions .isSecondLevelCacheCheckingEnabled () ) {
356
+ cachedEntity = CacheEntityLoaderHelper .INSTANCE .loadFromSecondLevelCache (
357
+ loadEvent ,
358
+ getLoadable ().getEntityPersister (),
359
+ entityKey
360
+ );
361
+ }
362
+
363
+ if ( cachedEntity != null ) {
364
+ //noinspection unchecked
365
+ resolutionConsumer .consume ( i , entityKey , (R ) cachedEntity );
366
+ }
367
+ else {
368
+ if ( unresolvedIds == null ) {
369
+ unresolvedIds = new ArrayList <>();
197
370
}
371
+ unresolvedIds .add ( id );
198
372
}
199
- return false ;
373
+ return unresolvedIds ;
200
374
}
201
375
202
- protected abstract <K > List <T > performUnorderedMultiLoad (K [] ids , MultiIdLoadOptions loadOptions , EventSource session );
203
-
204
376
}
0 commit comments