Skip to content

Commit 55255e9

Browse files
committed
finish big refactor of AbstractMultiIdEntityLoader and children
Signed-off-by: Gavin King <[email protected]>
1 parent ae53810 commit 55255e9

File tree

4 files changed

+250
-322
lines changed

4 files changed

+250
-322
lines changed

hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public static <T> Set<T> makeCopy(Set<T> source) {
262262
return copy;
263263
}
264264

265-
public static boolean isEmpty(Collection collection) {
265+
public static boolean isEmpty(Collection<?> collection) {
266266
return collection == null || collection.isEmpty();
267267
}
268268

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java

Lines changed: 222 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.loader.ast.internal;
66

7+
import org.checkerframework.checker.nullness.qual.NonNull;
78
import org.hibernate.LockMode;
89
import org.hibernate.LockOptions;
910
import org.hibernate.engine.jdbc.spi.JdbcServices;
@@ -20,10 +21,12 @@
2021
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
2122
import org.hibernate.type.descriptor.java.JavaType;
2223

24+
import java.lang.reflect.Array;
2325
import java.util.ArrayList;
2426
import java.util.List;
2527

2628
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
29+
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
2730
import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic;
2831
import static org.hibernate.loader.ast.internal.LoaderHelper.getReadOnlyFromLoadQueryInfluencers;
2932
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
@@ -37,11 +40,13 @@ public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoa
3740
private final EntityMappingType entityDescriptor;
3841
private final SessionFactoryImplementor sessionFactory;
3942
private final EntityIdentifierMapping identifierMapping;
43+
protected final Object[] idArray;
4044

4145
public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) {
4246
this.entityDescriptor = entityDescriptor;
4347
this.sessionFactory = sessionFactory;
4448
identifierMapping = getLoadable().getIdentifierMapping();
49+
idArray = (Object[]) Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 );
4550
}
4651

4752
protected EntityMappingType getEntityDescriptor() {
@@ -76,31 +81,43 @@ public EntityMappingType getLoadable() {
7681
@Override
7782
public final <K> List<T> load(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) {
7883
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() );
8497
}
98+
return unorderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
8599
}
86100

87101
protected List<T> performOrderedMultiLoad(
88102
Object[] ids,
89103
MultiIdLoadOptions loadOptions,
90104
EventSource session) {
105+
assert loadOptions.isOrderReturnEnabled();
106+
assert ids != null;
91107
if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
92108
MULTI_KEY_LOAD_LOGGER.tracef( "#performOrderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
93109
}
110+
return orderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
111+
}
94112

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();
98119
final JavaType<?> idType = getLoadable().getIdentifierMapping().getJavaType();
99120

100-
final LockOptions lockOptions = loadOptions.getLockOptions() == null
101-
? new LockOptions( LockMode.NONE )
102-
: loadOptions.getLockOptions();
103-
104121
final int maxBatchSize = maxBatchSize( ids, loadOptions );
105122

106123
final List<Object> result = arrayList( ids.length );
@@ -109,10 +126,10 @@ protected List<T> performOrderedMultiLoad(
109126
final List<Integer> elementPositionsLoadedByBatch = new ArrayList<>();
110127

111128
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];
113130
final EntityKey entityKey = new EntityKey( id, getLoadable().getEntityPersister() );
114131

115-
if ( !loadFromCaches( loadOptions, session, id, lockOptions, entityKey, result, i ) ) {
132+
if ( !loadFromEnabledCaches( loadOptions, session, id, lockOptions, entityKey, result, i ) ) {
116133
// if we did not hit any of the continues above,
117134
// then we need to batch load the entity state.
118135
idsInBatch.add( id );
@@ -142,13 +159,27 @@ protected List<T> performOrderedMultiLoad(
142159
return (List<T>) result;
143160
}
144161

162+
protected static LockOptions lockOptions(MultiIdLoadOptions loadOptions) {
163+
return loadOptions.getLockOptions() == null
164+
? new LockOptions( LockMode.NONE )
165+
: loadOptions.getLockOptions();
166+
}
167+
145168
protected abstract int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions);
146169

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);
148175

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);
150181

151-
protected boolean loadFromCaches(
182+
protected boolean loadFromEnabledCaches(
152183
MultiIdLoadOptions loadOptions,
153184
EventSource session,
154185
Object id,
@@ -157,48 +188,189 @@ protected boolean loadFromCaches(
157188
List<Object> result,
158189
int i) {
159190
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+
)
166199
);
200+
}
201+
else {
202+
return false;
203+
}
204+
}
167205

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+
}
169228

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+
}
175238

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();
184296

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;
192352
}
353+
}
193354

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<>();
197370
}
371+
unresolvedIds.add( id );
198372
}
199-
return false;
373+
return unresolvedIds;
200374
}
201375

202-
protected abstract <K> List<T> performUnorderedMultiLoad(K[] ids, MultiIdLoadOptions loadOptions, EventSource session);
203-
204376
}

0 commit comments

Comments
 (0)