Skip to content

Commit 1f08501

Browse files
committed
HHH-18229 Handle null owner key for collections
1 parent 2f3a01f commit 1f08501

File tree

13 files changed

+402
-68
lines changed

13 files changed

+402
-68
lines changed

hibernate-core/src/main/java/org/hibernate/action/internal/AbstractEntityInsertAction.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -215,19 +215,16 @@ private void addCollectionKey(
215215
PersistenceContext persistenceContext) {
216216
if ( o instanceof PersistentCollection ) {
217217
final CollectionPersister collectionPersister = pluralAttributeMapping.getCollectionDescriptor();
218-
final CollectionKey collectionKey = new CollectionKey(
218+
final Object key = ( (AbstractEntityPersister) getPersister() ).getCollectionKey(
219219
collectionPersister,
220-
( (AbstractEntityPersister) getPersister() ).getCollectionKey(
221-
collectionPersister,
222-
getInstance(),
223-
persistenceContext.getEntry( getInstance() ),
224-
getSession()
225-
)
226-
);
227-
persistenceContext.addCollectionByKey(
228-
collectionKey,
229-
(PersistentCollection<?>) o
220+
getInstance(),
221+
persistenceContext.getEntry( getInstance() ),
222+
getSession()
230223
);
224+
if ( key != null ) {
225+
final CollectionKey collectionKey = new CollectionKey( collectionPersister, key );
226+
persistenceContext.addCollectionByKey( collectionKey, (PersistentCollection<?>) o );
227+
}
231228
}
232229
}
233230

hibernate-core/src/main/java/org/hibernate/collection/spi/AbstractPersistentCollection.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.hibernate.type.CompositeType;
4141
import org.hibernate.type.Type;
4242

43+
import org.checkerframework.checker.nullness.qual.Nullable;
44+
4345
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
4446

4547
/**
@@ -58,16 +60,16 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
5860

5961
private transient List<DelayedOperation<E>> operationQueue;
6062
private transient boolean directlyAccessible;
61-
private Object owner;
63+
private @Nullable Object owner;
6264
private int cachedSize = -1;
6365

64-
private String role;
65-
private Object key;
66+
private @Nullable String role;
67+
private @Nullable Object key;
6668
// collections detect changes made via their public interface and mark
6769
// themselves as dirty as a performance optimization
6870
private boolean dirty;
6971
protected boolean elementRemoved;
70-
private Serializable storedSnapshot;
72+
private @Nullable Serializable storedSnapshot;
7173

7274
private String sessionFactoryUuid;
7375
private boolean allowLoadOutsideTransaction;
@@ -84,12 +86,12 @@ protected AbstractPersistentCollection(SharedSessionContractImplementor session)
8486
}
8587

8688
@Override
87-
public final String getRole() {
89+
public final @Nullable String getRole() {
8890
return role;
8991
}
9092

9193
@Override
92-
public final Object getKey() {
94+
public final @Nullable Object getKey() {
9395
return key;
9496
}
9597

@@ -120,7 +122,7 @@ public final void dirty() {
120122
}
121123

122124
@Override
123-
public final Serializable getStoredSnapshot() {
125+
public final @Nullable Serializable getStoredSnapshot() {
124126
return storedSnapshot;
125127
}
126128

@@ -1354,7 +1356,7 @@ public Object getIdentifier(Object entry, int i) {
13541356
}
13551357

13561358
@Override
1357-
public Object getOwner() {
1359+
public @Nullable Object getOwner() {
13581360
return owner;
13591361
}
13601362

hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public interface PersistentCollection<E> extends LazyInitializable {
6262
*
6363
* @return The owner
6464
*/
65-
Object getOwner();
65+
@Nullable Object getOwner();
6666

6767
/**
6868
* Set the reference to the owning entity
@@ -405,14 +405,14 @@ default boolean needsUpdating(
405405
*
406406
* @return the current collection key value
407407
*/
408-
Object getKey();
408+
@Nullable Object getKey();
409409

410410
/**
411411
* Get the current role name
412412
*
413413
* @return the collection role name
414414
*/
415-
String getRole();
415+
@Nullable String getRole();
416416

417417
/**
418418
* Is the collection unreferenced?
@@ -461,7 +461,7 @@ default boolean isDirectlyProvidedCollection(Object collection) {
461461
*
462462
* @return The internally stored snapshot state
463463
*/
464-
Serializable getStoredSnapshot();
464+
@Nullable Serializable getStoredSnapshot();
465465

466466
/**
467467
* Mark the collection as dirty

hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,8 +1050,10 @@ public void addUninitializedCollection(CollectionPersister persister, Persistent
10501050

10511051
@Override
10521052
public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection<?> collection) {
1053-
final CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
1054-
addCollection( collection, ce, collection.getKey() );
1053+
final Object key = collection.getKey();
1054+
assert key != null;
1055+
final CollectionEntry ce = new CollectionEntry( persister, key );
1056+
addCollection( collection, ce, key );
10551057
if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( persister ) ) {
10561058
getBatchFetchQueue().addBatchLoadableCollection( collection, ce );
10571059
}
@@ -1109,7 +1111,7 @@ private void addCollection(PersistentCollection<?> collection, CollectionPersist
11091111
@Override
11101112
public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection<?> collection)
11111113
throws HibernateException {
1112-
if ( collection.isUnreferenced() ) {
1114+
if ( collection.isUnreferenced() || collection.getKey() == null ) {
11131115
//treat it just like a new collection
11141116
addCollection( collection, collectionPersister );
11151117
}

hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ public CollectionEntry(PersistentCollection<?> collection, SessionFactoryImpleme
125125
ignore = false;
126126

127127
loadedKey = collection.getKey();
128-
loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( collection.getRole() );
129-
this.role = ( loadedPersister == null ? null : loadedPersister.getRole() );
128+
role = collection.getRole();
129+
loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( NullnessUtil.castNonNull( role ) );
130130

131131
snapshot = collection.getStoredSnapshot();
132132
}

hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,8 @@ protected void postFlush(SessionImplementor session) throws HibernateException {
443443
persistenceContext.forEachCollectionEntry(
444444
(persistentCollection, collectionEntry) -> {
445445
collectionEntry.postFlush( persistentCollection );
446-
if ( collectionEntry.getLoadedPersister() == null ) {
446+
final Object key;
447+
if ( collectionEntry.getLoadedPersister() == null || ( key = collectionEntry.getLoadedKey() ) == null ) {
447448
//if the collection is dereferenced, unset its session reference and remove from the session cache
448449
//iter.remove(); //does not work, since the entrySet is not backed by the set
449450
persistentCollection.unsetSession( session );
@@ -453,7 +454,7 @@ protected void postFlush(SessionImplementor session) throws HibernateException {
453454
//otherwise recreate the mapping between the collection and its key
454455
final CollectionKey collectionKey = new CollectionKey(
455456
collectionEntry.getLoadedPersister(),
456-
collectionEntry.getLoadedKey()
457+
key
457458
);
458459
persistenceContext.addCollectionByKey( collectionKey, persistentCollection );
459460
}

hibernate-core/src/main/java/org/hibernate/event/internal/WrapVisitor.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,24 @@ else if ( attributeInterceptor != null
123123
entry,
124124
session
125125
);
126-
PersistentCollection<?> collectionInstance = persistenceContext.getCollection(
127-
new CollectionKey( persister, key )
128-
);
129-
130-
if ( collectionInstance == null ) {
131-
// the collection has not been initialized and new collection values have been assigned,
132-
// we need to be sure to delete all the collection elements before inserting the new ones
133-
collectionInstance = persister.getCollectionSemantics().instantiateWrapper(
134-
key,
135-
persister,
136-
session
126+
if ( key != null ) {
127+
PersistentCollection<?> collectionInstance = persistenceContext.getCollection(
128+
new CollectionKey( persister, key )
137129
);
138-
persistenceContext.addUninitializedCollection( persister, collectionInstance, key );
139-
final CollectionEntry collectionEntry =
140-
persistenceContext.getCollectionEntry( collectionInstance );
141-
collectionEntry.setDoremove( true );
130+
131+
if ( collectionInstance == null ) {
132+
// the collection has not been initialized and new collection values have been assigned,
133+
// we need to be sure to delete all the collection elements before inserting the new ones
134+
collectionInstance = persister.getCollectionSemantics().instantiateWrapper(
135+
key,
136+
persister,
137+
session
138+
);
139+
persistenceContext.addUninitializedCollection( persister, collectionInstance, key );
140+
final CollectionEntry collectionEntry =
141+
persistenceContext.getCollectionEntry( collectionInstance );
142+
collectionEntry.setDoremove( true );
143+
}
142144
}
143145
}
144146
}

hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1651,7 +1651,7 @@ void cannotResolveNonNullableTransientDependencies(
16511651
+ " This is likely due to unsafe use of the session (e.g. used in multiple threads concurrently, updates during entity lifecycle hooks).",
16521652
id = 479
16531653
)
1654-
String collectionNotProcessedByFlush(String role);
1654+
String collectionNotProcessedByFlush(@Nullable String role);
16551655

16561656
@LogMessage(level = WARN)
16571657
@Message(value = "A ManagedEntity was associated with a stale PersistenceContext. A ManagedEntity may only be associated with one PersistenceContext at a time; %s", id = 480)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.hibernate.engine.spi.SessionFactoryImplementor;
2222
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2323
import org.hibernate.engine.spi.SubselectFetch;
24+
import org.hibernate.internal.util.NullnessUtil;
2425
import org.hibernate.internal.util.collections.CollectionHelper;
2526
import org.hibernate.loader.ast.spi.CollectionLoader;
2627
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
@@ -153,7 +154,7 @@ public PersistentCollection<?> load(Object triggerKey, SharedSessionContractImpl
153154
persistenceContext,
154155
getLoadable().getCollectionDescriptor(),
155156
c,
156-
c.getKey(),
157+
NullnessUtil.castNonNull( c.getKey() ),
157158
true
158159
);
159160
}

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@
301301
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
302302
import org.hibernate.type.spi.TypeConfiguration;
303303

304+
import org.checkerframework.checker.nullness.qual.Nullable;
305+
304306
import static java.util.Collections.emptyList;
305307
import static java.util.Collections.emptyMap;
306308
import static java.util.Collections.emptySet;
@@ -1555,6 +1557,7 @@ public Object initializeLazyProperty(String fieldName, Object entity, SharedSess
15551557
// see if there is already a collection instance associated with the session
15561558
// NOTE : can this ever happen?
15571559
final Object key = getCollectionKey( persister, entity, entry, session );
1560+
assert key != null;
15581561
PersistentCollection<?> collection = persistenceContext.getCollection( new CollectionKey( persister, key ) );
15591562
if ( collection == null ) {
15601563
collection = collectionType.instantiate( session, persister, key );
@@ -1623,7 +1626,7 @@ public Object initializeLazyProperty(String fieldName, Object entity, SharedSess
16231626

16241627
}
16251628

1626-
public Object getCollectionKey(
1629+
public @Nullable Object getCollectionKey(
16271630
CollectionPersister persister,
16281631
Object owner,
16291632
EntityEntry ownerEntry,

0 commit comments

Comments
 (0)