Skip to content

Commit 094a3c6

Browse files
committed
HHH-19910 Check if existing instance is not managed during initialization
1 parent a505603 commit 094a3c6

12 files changed

+1499
-80
lines changed

hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/NonAggregatedIdentifierMappingInitializer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ public void resolveInstance(@Nullable Object instance, NonAggregatedIdentifierMa
303303
data.setState( State.MISSING );
304304
data.setInstance( null );
305305
}
306+
else if ( hasIdClass ) {
307+
resolveKey( data );
308+
resolveInstance( data );
309+
}
306310
else {
307311
data.setState( State.INITIALIZED );
308312
data.setInstance( instance );

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ public void resolveInstance(Object instance, Data data) {
141141

142142
private void resolve(Object instance, Data data) {
143143
final var rowProcessingState = data.getRowProcessingState();
144+
final var session = rowProcessingState.getSession();
145+
final var persistenceContext = session.getPersistenceContextInternal();
144146
// Only need to extract the identifier if the identifier has a many to one
145147
final var lazyInitializer = extractLazyInitializer( instance );
146-
data.entityKey = null;
147148
data.entityIdentifier = null;
148149
if ( lazyInitializer == null ) {
149150
// Entity is most probably initialized
@@ -159,19 +160,15 @@ && getAttributeInterceptor( instance )
159160
data.setState( State.RESOLVED );
160161
data.entityIdentifier = enhancementInterceptor.getIdentifier();
161162
}
162-
if ( keyIsEager && data.entityIdentifier == null ) {
163-
data.entityIdentifier =
164-
concreteDescriptor.getIdentifier( instance,
165-
rowProcessingState.getSession() );
163+
if ( data.entityIdentifier == null ) {
164+
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
166165
}
167166
}
168167
else {
169168
// If the entity initializer is null, we know the entity is fully initialized;
170169
// otherwise, it will be initialized by some other initializer
171170
data.setState( State.RESOLVED );
172-
data.entityIdentifier =
173-
concreteDescriptor.getIdentifier( instance,
174-
rowProcessingState.getSession() );
171+
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
175172
}
176173
}
177174
else if ( lazyInitializer.isUninitialized() ) {
@@ -181,15 +178,47 @@ else if ( lazyInitializer.isUninitialized() ) {
181178
else {
182179
// Entity is initialized
183180
data.setState( State.INITIALIZED );
184-
if ( keyIsEager ) {
185-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
186-
}
181+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
187182
data.setInstance( lazyInitializer.getImplementation() );
188183
}
189184

185+
data.entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
186+
final var entityHolder = persistenceContext.getEntityHolder(
187+
data.entityKey
188+
);
189+
190+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
191+
// the existing entity instance is detached or transient
192+
if ( entityHolder != null ) {
193+
final var managed = entityHolder.getManagedObject();
194+
data.setInstance( managed );
195+
data.entityKey = entityHolder.getEntityKey();
196+
data.entityIdentifier = data.entityKey.getIdentifier();
197+
if ( entityHolder.isInitialized() ) {
198+
data.setState( State.INITIALIZED );
199+
}
200+
else {
201+
data.setState( State.RESOLVED );
202+
}
203+
}
204+
else {
205+
data.setState( State.RESOLVED );
206+
}
207+
}
208+
190209
if ( data.getState() == State.RESOLVED ) {
191-
resolveInstanceFromIdentifier( data );
210+
// similar to resolveInstanceFromIdentifier, but we already have the holder here
211+
if ( data.batchDisabled ) {
212+
initialize( data, entityHolder, session, persistenceContext );
213+
}
214+
else if ( entityHolder == null || !entityHolder.isEventuallyInitialized() ) {
215+
// need to add the key to the batch queue only when the entity has not been already loaded or
216+
// there isn't another initializer that is loading it
217+
registerResolutionListener( data );
218+
registerToBatchFetchQueue( data );
219+
}
192220
}
221+
193222
if ( keyIsEager ) {
194223
final var initializer = keyAssembler.getInitializer();
195224
assert initializer != null;
@@ -242,6 +271,7 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) {
242271
: parentInstance;
243272
// No need to initialize these fields
244273
data.entityKey = null;
274+
data.entityIdentifier = null;
245275
data.setInstance( null );
246276
if ( instance == null ) {
247277
data.setState( State.MISSING );

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -234,32 +234,43 @@ private void resolve(
234234
final var lazyInitializer = extractLazyInitializer( instance );
235235
if ( lazyInitializer == null ) {
236236
data.setState( State.INITIALIZED );
237-
if ( keyIsEager ) {
238-
data.concreteDescriptor = session.getEntityPersister( null, instance );
239-
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
240-
}
237+
data.concreteDescriptor = session.getEntityPersister( null, instance );
238+
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
241239
}
242240
else if ( lazyInitializer.isUninitialized() ) {
243241
data.setState( eager ? State.RESOLVED : State.INITIALIZED );
244-
if ( keyIsEager ) {
245-
// Read the discriminator from the result set if necessary
246-
final Object discriminatorValue =
247-
discriminatorValueAssembler.assemble( rowProcessingState );
248-
data.concreteDescriptor =
249-
fetchedPart.resolveDiscriminatorValue( discriminatorValue )
250-
.getEntityPersister();
251-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
252-
}
242+
// Read the discriminator from the result set if necessary
243+
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
244+
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
245+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
253246
}
254247
else {
255248
data.setState( State.INITIALIZED );
256-
if ( keyIsEager ) {
257-
data.concreteDescriptor =
258-
session.getEntityPersister( null, lazyInitializer.getImplementation() );
259-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
249+
data.concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
250+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
251+
}
252+
253+
final var entityKey = new EntityKey( data.entityIdentifier, data.concreteDescriptor );
254+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
255+
entityKey
256+
);
257+
258+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
259+
// the existing entity instance is detached or transient
260+
if ( entityHolder != null ) {
261+
final var managed = entityHolder.getManagedObject();
262+
data.setInstance( managed );
263+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
264+
data.setState( !eager || entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
265+
}
266+
else {
267+
data.setState( State.RESOLVED );
268+
initializeInstance( data );
260269
}
261270
}
262-
data.setInstance( instance );
271+
else {
272+
data.setInstance( instance );
273+
}
263274
}
264275

265276
@Override

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,25 +169,35 @@ public void resolveInstance(EntityDelayedFetchInitializerData data) {
169169
concreteDescriptor = entityPersister;
170170
}
171171

172-
if ( selectByUniqueKey ) {
173-
data.setInstance( instanceWithUniqueKey( data, concreteDescriptor ) );
174-
}
175-
else {
176-
data.setInstance( instanceWithId( data, concreteDescriptor ) );
177-
}
172+
initialize( data, null, concreteDescriptor );
178173
}
179174
}
180175
}
181176

182-
private Object instanceWithId(
177+
protected void initialize(
183178
EntityDelayedFetchInitializerData data,
179+
@Nullable EntityKey entityKey,
184180
EntityPersister concreteDescriptor) {
181+
if ( selectByUniqueKey ) {
182+
data.setInstance( instanceWithUniqueKey( data, concreteDescriptor ) );
183+
}
184+
else {
185+
data.setInstance( instanceWithId( data, concreteDescriptor, entityKey) );
186+
}
187+
}
188+
189+
private Object instanceWithId(
190+
EntityDelayedFetchInitializerData data,
191+
EntityPersister concreteDescriptor,
192+
EntityKey entityKey) {
185193
final var rowProcessingState = data.getRowProcessingState();
186194
final var session = rowProcessingState.getSession();
187195
final var persistenceContext = session.getPersistenceContextInternal();
188196

189-
final var entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
190-
final var holder = persistenceContext.getEntityHolder( entityKey );
197+
final var ek = entityKey == null ?
198+
new EntityKey( data.entityIdentifier, concreteDescriptor ) :
199+
entityKey;
200+
final var holder = persistenceContext.getEntityHolder( ek );
191201
if ( holder != null && holder.getEntity() != null ) {
192202
return persistenceContext.proxyFor( holder, concreteDescriptor );
193203
}
@@ -313,8 +323,28 @@ public void resolveInstance(Object instance, EntityDelayedFetchInitializerData d
313323
data.setState( State.INITIALIZED );
314324
data.setInstance( instance );
315325
final var rowProcessingState = data.getRowProcessingState();
326+
final var session = rowProcessingState.getSession();
327+
final var entityDescriptor = getEntityDescriptor();
328+
data.entityIdentifier = entityDescriptor.getIdentifier( instance, session );
329+
330+
final var entityKey = new EntityKey( data.entityIdentifier, entityDescriptor );
331+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
332+
entityKey
333+
);
334+
335+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
336+
// the existing entity instance is detached or transient
337+
if ( entityHolder != null ) {
338+
final var managed = entityHolder.getManagedObject();
339+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
340+
data.setInstance( managed );
341+
}
342+
else {
343+
initialize( data, entityKey, entityDescriptor );
344+
}
345+
}
346+
316347
if ( keyIsEager ) {
317-
data.entityIdentifier = getEntityDescriptor().getIdentifier( instance, rowProcessingState.getSession() );
318348
final var initializer = identifierAssembler.getInitializer();
319349
assert initializer != null;
320350
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -987,29 +987,62 @@ public void resolveInstance(Object instance, EntityInitializerData data) {
987987
setMissing( data );
988988
}
989989
else {
990-
data.setInstance( instance );
991990
final var lazyInitializer = extractLazyInitializer( instance );
992991
final var rowProcessingState = data.getRowProcessingState();
993992
final var session = rowProcessingState.getSession();
994993
final var persistenceContext = session.getPersistenceContextInternal();
995994
if ( lazyInitializer == null ) {
996995
// Entity is most probably initialized
997-
data.entityInstanceForNotify = instance;
998996
data.concreteDescriptor = session.getEntityPersister( null, instance );
999-
resolveEntityKey( data, data.concreteDescriptor.getIdentifier( instance, session ) );
1000-
data.entityHolder = persistenceContext.getEntityHolder( data.entityKey );
1001-
if ( data.entityHolder == null ) {
1002-
// Entity was most probably removed in the same session without setting this association to null.
1003-
// Since this load request can happen through `find()` which doesn't auto-flush on association joins,
1004-
// the entity must be fully initialized, even if it is removed already
1005-
data.entityHolder =
1006-
persistenceContext.claimEntityHolderIfPossible(
1007-
data.entityKey,
1008-
data.entityInstanceForNotify,
1009-
rowProcessingState.getJdbcValuesSourceProcessingState(),
1010-
this
1011-
);
997+
resolveEntityKey(
998+
data,
999+
data.concreteDescriptor.getIdentifier( instance, session )
1000+
);
1001+
data.entityHolder = persistenceContext.claimEntityHolderIfPossible(
1002+
data.entityKey,
1003+
null,
1004+
rowProcessingState.getJdbcValuesSourceProcessingState(),
1005+
this
1006+
);
1007+
if ( data.entityHolder.getManagedObject() == null ) {
1008+
final EntityEntry entry = persistenceContext.getEntry(
1009+
instance ); // make sure an EntityEntry exists
1010+
if ( entry == null ) {
1011+
// We cannot reuse an entity instance that has no entry in the PC,
1012+
// this can happen if the parent entity contained a detached instance.
1013+
// We need to create a new instance in this case (see resolveEntityInstance1)
1014+
instance = resolveEntityInstance( data );
1015+
}
1016+
else {
1017+
// Entity was most probably removed in the same session without setting this association to null.
1018+
// Since this load request can happen through `find()` which doesn't auto-flush on association joins,
1019+
// the entity must be fully initialized, even if it is removed already
1020+
data.entityHolder = persistenceContext.claimEntityHolderIfPossible(
1021+
data.entityKey,
1022+
instance,
1023+
rowProcessingState.getJdbcValuesSourceProcessingState(),
1024+
this
1025+
);
1026+
}
1027+
}
1028+
else if ( data.entityHolder.getEntity() == null ) {
1029+
assert data.entityHolder.getProxy() != instance;
1030+
instance = resolveEntityInstance( data );
1031+
data.entityKey = data.entityHolder.getEntityKey();
1032+
if ( data.entityHolder.getProxy() != null ) {
1033+
castNonNull( extractLazyInitializer( data.entityHolder.getProxy() ) )
1034+
.setImplementation( instance );
1035+
}
1036+
}
1037+
else if ( data.entityHolder.getEntity() != instance ) {
1038+
// The instance contained in the parent entity is different from the managed persistent instance
1039+
// currently in the persistence context. We should always initialize the managed one in this case.
1040+
instance = data.entityHolder.getEntity();
1041+
data.entityKey = data.entityHolder.getEntityKey();
10121042
}
1043+
1044+
data.entityInstanceForNotify = instance;
1045+
10131046
if ( data.concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
10141047
&& isPersistentAttributeInterceptable( data.entityInstanceForNotify )
10151048
&& getAttributeInterceptor( data.entityInstanceForNotify )
@@ -1049,19 +1082,51 @@ else if ( lazyInitializer.isUninitialized() ) {
10491082
this
10501083
);
10511084
// Resolve and potentially create the entity instance
1052-
data.entityInstanceForNotify = resolveEntityInstance( data );
1053-
lazyInitializer.setImplementation( data.entityInstanceForNotify );
1054-
registerLoadingEntity( data, data.entityInstanceForNotify );
1085+
if ( data.entityHolder.getProxy() == instance ) {
1086+
data.entityInstanceForNotify = resolveEntityInstance( data );
1087+
lazyInitializer.setImplementation( data.entityInstanceForNotify );
1088+
}
1089+
else if ( data.entityHolder.getEntity() == null ) {
1090+
data.entityInstanceForNotify = resolveEntityInstance( data );
1091+
if ( data.entityHolder.getProxy() != null ) {
1092+
castNonNull( extractLazyInitializer( data.entityHolder.getProxy() ) ).setImplementation(
1093+
data.entityInstanceForNotify );
1094+
}
1095+
}
1096+
else {
1097+
data.entityInstanceForNotify = data.entityHolder.getEntity();
1098+
data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
1099+
}
10551100
}
10561101
else {
1057-
data.entityInstanceForNotify = lazyInitializer.getImplementation();
1058-
data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify );
1102+
final var implementation = lazyInitializer.getImplementation();
1103+
data.concreteDescriptor = session.getEntityPersister( null, implementation );
10591104
resolveEntityKey( data, lazyInitializer.getInternalIdentifier() );
10601105
data.entityHolder = persistenceContext.getEntityHolder( data.entityKey );
1061-
// Even though the lazyInitializer reports it is initialized, check if the entity holder reports initialized,
1062-
// because in a nested initialization scenario, this nested initializer must initialize the entity
1063-
data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
1106+
if ( data.entityHolder.getProxy() == instance ) {
1107+
data.entityInstanceForNotify = implementation;
1108+
// Even though the lazyInitializer reports it is initialized, check if the entity holder reports initialized,
1109+
// because in a nested initialization scenario, this nested initializer must initialize the entity
1110+
data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
1111+
}
1112+
else if ( data.entityHolder.getEntity() == null ) {
1113+
data.entityInstanceForNotify = resolveEntityInstance( data );
1114+
data.entityKey = data.entityHolder.getEntityKey();
1115+
if ( data.entityHolder.getProxy() != null ) {
1116+
castNonNull( extractLazyInitializer( data.entityHolder.getProxy() ) ).setImplementation(
1117+
data.entityInstanceForNotify );
1118+
}
1119+
data.setState( State.RESOLVED );
1120+
}
1121+
else {
1122+
data.entityInstanceForNotify = data.entityHolder.getEntity();
1123+
data.entityKey = data.entityHolder.getEntityKey();
1124+
data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
1125+
}
10641126
}
1127+
1128+
data.setInstance( data.entityHolder.getManagedObject() );
1129+
10651130
if ( identifierAssembler != null ) {
10661131
final Initializer<?> initializer = identifierAssembler.getInitializer();
10671132
if ( initializer != null ) {
@@ -1478,7 +1543,7 @@ protected void initializeEntityInstance(EntityInitializerData data) {
14781543
final var entityKey = data.entityKey;
14791544
assert entityKey != null;
14801545

1481-
final Object entityIdentifier = entityKey.getIdentifier();
1546+
final var entityIdentifier = entityKey.getIdentifier();
14821547
final var resolvedEntityState = extractConcreteTypeStateValues( data );
14831548

14841549
rowProcessingState.getJdbcValuesSourceProcessingState().registerLoadingEntityHolder( data.entityHolder );

0 commit comments

Comments
 (0)