Skip to content

Commit 0ff8f46

Browse files
beikovsoul2zimate
authored andcommitted
HHH-18229 Handle null owner key for collections
(cherry picked from commit 1f08501)
1 parent f6c6827 commit 0ff8f46

13 files changed

+394
-66
lines changed

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

+8-11
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,16 @@ private void addCollectionKey(
201201
PersistenceContext persistenceContext) {
202202
if ( o instanceof PersistentCollection ) {
203203
final CollectionPersister collectionPersister = pluralAttributeMapping.getCollectionDescriptor();
204-
final CollectionKey collectionKey = new CollectionKey(
204+
final Object key = ( (AbstractEntityPersister) getPersister() ).getCollectionKey(
205205
collectionPersister,
206-
( (AbstractEntityPersister) getPersister() ).getCollectionKey(
207-
collectionPersister,
208-
getInstance(),
209-
persistenceContext.getEntry( getInstance() ),
210-
getSession()
211-
)
212-
);
213-
persistenceContext.addCollectionByKey(
214-
collectionKey,
215-
(PersistentCollection<?>) o
206+
getInstance(),
207+
persistenceContext.getEntry( getInstance() ),
208+
getSession()
216209
);
210+
if ( key != null ) {
211+
final CollectionKey collectionKey = new CollectionKey( collectionPersister, key );
212+
persistenceContext.addCollectionByKey( collectionKey, (PersistentCollection<?>) o );
213+
}
217214
}
218215
}
219216

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

+12-8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
import org.hibernate.type.CompositeType;
4343
import org.hibernate.type.Type;
4444

45+
import org.checkerframework.checker.nullness.qual.Nullable;
46+
47+
import static org.hibernate.pretty.MessageHelper.collectionInfoString;
48+
4549
/**
4650
* Base class implementing {@link PersistentCollection}
4751
*
@@ -58,16 +62,16 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
5862

5963
private transient List<DelayedOperation<E>> operationQueue;
6064
private transient boolean directlyAccessible;
61-
private Object owner;
65+
private @Nullable Object owner;
6266
private int cachedSize = -1;
6367

64-
private String role;
65-
private Object key;
68+
private @Nullable String role;
69+
private @Nullable Object key;
6670
// collections detect changes made via their public interface and mark
6771
// themselves as dirty as a performance optimization
6872
private boolean dirty;
6973
protected boolean elementRemoved;
70-
private Serializable storedSnapshot;
74+
private @Nullable Serializable storedSnapshot;
7175

7276
private String sessionFactoryUuid;
7377
private boolean allowLoadOutsideTransaction;
@@ -84,12 +88,12 @@ protected AbstractPersistentCollection(SharedSessionContractImplementor session)
8488
}
8589

8690
@Override
87-
public final String getRole() {
91+
public final @Nullable String getRole() {
8892
return role;
8993
}
9094

9195
@Override
92-
public final Object getKey() {
96+
public final @Nullable Object getKey() {
9397
return key;
9498
}
9599

@@ -120,7 +124,7 @@ public final void dirty() {
120124
}
121125

122126
@Override
123-
public final Serializable getStoredSnapshot() {
127+
public final @Nullable Serializable getStoredSnapshot() {
124128
return storedSnapshot;
125129
}
126130

@@ -1351,7 +1355,7 @@ public Object getIdentifier(Object entry, int i) {
13511355
}
13521356

13531357
@Override
1354-
public Object getOwner() {
1358+
public @Nullable Object getOwner() {
13551359
return owner;
13561360
}
13571361

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public interface PersistentCollection<E> extends LazyInitializable {
6060
*
6161
* @return The owner
6262
*/
63-
Object getOwner();
63+
@Nullable Object getOwner();
6464

6565
/**
6666
* Set the reference to the owning entity
@@ -403,14 +403,14 @@ default boolean needsUpdating(
403403
*
404404
* @return the current collection key value
405405
*/
406-
Object getKey();
406+
@Nullable Object getKey();
407407

408408
/**
409409
* Get the current role name
410410
*
411411
* @return the collection role name
412412
*/
413-
String getRole();
413+
@Nullable String getRole();
414414

415415
/**
416416
* Is the collection unreferenced?
@@ -459,7 +459,7 @@ default boolean isDirectlyProvidedCollection(Object collection) {
459459
*
460460
* @return The internally stored snapshot state
461461
*/
462-
Serializable getStoredSnapshot();
462+
@Nullable Serializable getStoredSnapshot();
463463

464464
/**
465465
* Mark the collection as dirty

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,10 @@ public void addUninitializedCollection(CollectionPersister persister, Persistent
875875

876876
@Override
877877
public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection<?> collection) {
878-
final CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
879-
addCollection( collection, ce, collection.getKey() );
878+
final Object key = collection.getKey();
879+
assert key != null;
880+
final CollectionEntry ce = new CollectionEntry( persister, key );
881+
addCollection( collection, ce, key );
880882
if ( persister.getBatchSize() > 1 ) {
881883
getBatchFetchQueue().addBatchLoadableCollection( collection, ce );
882884
}
@@ -934,7 +936,7 @@ private void addCollection(PersistentCollection<?> collection, CollectionPersist
934936
@Override
935937
public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection<?> collection)
936938
throws HibernateException {
937-
if ( collection.isUnreferenced() ) {
939+
if ( collection.isUnreferenced() || collection.getKey() == null ) {
938940
//treat it just like a new collection
939941
addCollection( collection, collectionPersister );
940942
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.collection.spi.PersistentCollection;
1919
import org.hibernate.internal.CoreLogging;
2020
import org.hibernate.internal.CoreMessageLogger;
21+
import org.hibernate.internal.util.NullnessUtil;
2122
import org.hibernate.persister.collection.CollectionPersister;
2223
import org.hibernate.pretty.MessageHelper;
2324

@@ -118,8 +119,9 @@ public CollectionEntry(PersistentCollection<?> collection, SessionFactoryImpleme
118119
ignore = false;
119120

120121
loadedKey = collection.getKey();
122+
role = collection.getRole();
121123
setLoadedPersister(
122-
factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( collection.getRole() )
124+
factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( NullnessUtil.castNonNull( role ) )
123125
);
124126

125127
snapshot = collection.getStoredSnapshot();

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,8 @@ protected void postFlush(SessionImplementor session) throws HibernateException {
407407
persistenceContext.forEachCollectionEntry(
408408
(persistentCollection, collectionEntry) -> {
409409
collectionEntry.postFlush( persistentCollection );
410-
if ( collectionEntry.getLoadedPersister() == null ) {
410+
final Object key;
411+
if ( collectionEntry.getLoadedPersister() == null || ( key = collectionEntry.getLoadedKey() ) == null ) {
411412
//if the collection is dereferenced, unset its session reference and remove from the session cache
412413
//iter.remove(); //does not work, since the entrySet is not backed by the set
413414
persistentCollection.unsetSession( session );
@@ -417,7 +418,7 @@ protected void postFlush(SessionImplementor session) throws HibernateException {
417418
//otherwise recreate the mapping between the collection and its key
418419
CollectionKey collectionKey = new CollectionKey(
419420
collectionEntry.getLoadedPersister(),
420-
collectionEntry.getLoadedKey()
421+
key
421422
);
422423
persistenceContext.addCollectionByKey( collectionKey, persistentCollection );
423424
}

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

+3-11
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,9 @@ 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
);
138130
persistenceContext.addUninitializedCollection( persister, collectionInstance, key );
139131
final CollectionEntry collectionEntry = persistenceContext.getCollectionEntry(

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,7 @@ void cannotResolveNonNullableTransientDependencies(
16901690
+ " This is likely due to unsafe use of the session (e.g. used in multiple threads concurrently, updates during entity lifecycle hooks).",
16911691
id = 479
16921692
)
1693-
String collectionNotProcessedByFlush(String role);
1693+
String collectionNotProcessedByFlush(@Nullable String role);
16941694

16951695
@LogMessage(level = WARN)
16961696
@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

+2-1
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;
@@ -147,7 +148,7 @@ public PersistentCollection<?> load(Object triggerKey, SharedSessionContractImpl
147148
persistenceContext,
148149
getLoadable().getCollectionDescriptor(),
149150
c,
150-
c.getKey(),
151+
NullnessUtil.castNonNull( c.getKey() ),
151152
true
152153
);
153154
}

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@
292292
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
293293
import org.hibernate.type.spi.TypeConfiguration;
294294

295+
import org.checkerframework.checker.nullness.qual.Nullable;
296+
295297
import static java.util.Collections.emptyList;
296298
import static java.util.Collections.emptyMap;
297299
import static java.util.Collections.emptySet;
@@ -1463,6 +1465,7 @@ public Object initializeLazyProperty(String fieldName, Object entity, SharedSess
14631465
// see if there is already a collection instance associated with the session
14641466
// NOTE : can this ever happen?
14651467
final Object key = getCollectionKey( persister, entity, entry, session );
1468+
assert key != null;
14661469
PersistentCollection<?> collection = persistenceContext.getCollection( new CollectionKey( persister, key ) );
14671470
if ( collection == null ) {
14681471
collection = collectionType.instantiate( session, persister, key );
@@ -1531,7 +1534,7 @@ public Object initializeLazyProperty(String fieldName, Object entity, SharedSess
15311534

15321535
}
15331536

1534-
public Object getCollectionKey(
1537+
public @Nullable Object getCollectionKey(
15351538
CollectionPersister persister,
15361539
Object owner,
15371540
EntityEntry ownerEntry,

hibernate-core/src/main/java/org/hibernate/type/CollectionType.java

+7-23
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848

4949
import org.jboss.logging.Logger;
5050

51+
import org.checkerframework.checker.nullness.qual.Nullable;
52+
5153
/**
5254
* A type that handles Hibernate {@code PersistentCollection}s (including arrays).
5355
*
@@ -366,7 +368,7 @@ public ForeignKeyDirection getForeignKeyDirection() {
366368
* @param session The session from which the request is originating.
367369
* @return The collection owner's key
368370
*/
369-
public Object getKeyOfOwner(Object owner, SharedSessionContractImplementor session) {
371+
public @Nullable getKeyOfOwner(Object owner, SharedSessionContractImplementor session) {
370372
final PersistenceContext pc = session.getPersistenceContextInternal();
371373

372374
EntityEntry entityEntry = pc.getEntry( owner );
@@ -380,28 +382,10 @@ public Object getKeyOfOwner(Object owner, SharedSessionContractImplementor sessi
380382
return entityEntry.getId();
381383
}
382384
else {
383-
// TODO: at the point where we are resolving collection references, we don't
384-
// know if the uk value has been resolved (depends if it was earlier or
385-
// later in the mapping document) - now, we could try and use e.getStatus()
386-
// to decide to semiResolve(), trouble is that initializeEntity() reuses
387-
// the same array for resolved and hydrated values
388-
Object id = entityEntry.getLoadedState() != null
389-
? entityEntry.getLoadedValue( foreignKeyPropertyName )
390-
: entityEntry.getPersister().getPropertyValue( owner, foreignKeyPropertyName );
391-
392-
// NOTE VERY HACKISH WORKAROUND!!
393-
// TODO: Fix this so it will work for non-POJO entity mode
394-
Type keyType = getPersister( session ).getKeyType();
395-
if ( !keyType.getReturnedClass().isInstance( id ) ) {
396-
throw new UnsupportedOperationException( "Re-work support for semi-resolve" );
397-
// id = keyType.semiResolve(
398-
// entityEntry.getLoadedValue( foreignKeyPropertyName ),
399-
// session,
400-
// owner
401-
// );
402-
}
403-
404-
return id;
385+
final Object loadedValue = entityEntry.getLoadedValue( foreignKeyPropertyName );
386+
return loadedValue == null
387+
? entityEntry.getPersister().getPropertyValue( owner, foreignKeyPropertyName )
388+
: loadedValue;
405389
}
406390
}
407391

0 commit comments

Comments
 (0)