Skip to content

Commit 53beb52

Browse files
committed
HHH-19910 Check if existing instance is not managed during initialization
1 parent 59e8657 commit 53beb52

File tree

6 files changed

+306
-141
lines changed

6 files changed

+306
-141
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
@@ -297,6 +297,10 @@ public void resolveInstance(@Nullable Object instance, NonAggregatedIdentifierMa
297297
data.setState( State.MISSING );
298298
data.setInstance( null );
299299
}
300+
else if ( hasIdClass ) {
301+
resolveKey( data );
302+
resolveInstance( data );
303+
}
300304
else {
301305
data.setState( State.INITIALIZED );
302306
data.setInstance( instance );

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

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,11 @@ public void resolveInstance(Object instance, Data data) {
138138
data.setInstance( null );
139139
return;
140140
}
141-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
141+
final var rowProcessingState = data.getRowProcessingState();
142+
final var session = rowProcessingState.getSession();
143+
final var persistenceContext = session.getPersistenceContextInternal();
142144
// Only need to extract the identifier if the identifier has a many to one
143145
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
144-
data.entityKey = null;
145146
data.entityIdentifier = null;
146147
if ( lazyInitializer == null ) {
147148
// Entity is most probably initialized
@@ -162,10 +163,10 @@ && getAttributeInterceptor( instance )
162163
// If the entity initializer is null, we know the entity is fully initialized,
163164
// otherwise it will be initialized by some other initializer
164165
data.setState( State.RESOLVED );
165-
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() );
166+
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
166167
}
167-
if ( keyIsEager && data.entityIdentifier == null ) {
168-
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() );
168+
if ( data.entityIdentifier == null ) {
169+
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
169170
}
170171
}
171172
else if ( lazyInitializer.isUninitialized() ) {
@@ -175,15 +176,47 @@ else if ( lazyInitializer.isUninitialized() ) {
175176
else {
176177
// Entity is initialized
177178
data.setState( State.INITIALIZED );
178-
if ( keyIsEager ) {
179-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
180-
}
179+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
181180
data.setInstance( lazyInitializer.getImplementation() );
182181
}
183182

183+
data.entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
184+
final var entityHolder = persistenceContext.getEntityHolder(
185+
data.entityKey
186+
);
187+
188+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
189+
// the existing entity instance is detached or transient
190+
if ( entityHolder != null ) {
191+
final var managed = entityHolder.getManagedObject();
192+
data.setInstance( managed );
193+
data.entityKey = entityHolder.getEntityKey();
194+
data.entityIdentifier = data.entityKey.getIdentifier();
195+
if ( entityHolder.isInitialized() ) {
196+
data.setState( State.INITIALIZED );
197+
}
198+
else {
199+
data.setState( State.RESOLVED );
200+
}
201+
}
202+
else {
203+
data.setState( State.RESOLVED );
204+
}
205+
}
206+
184207
if ( data.getState() == State.RESOLVED ) {
185-
resolveInstanceFromIdentifier( data );
208+
// similar to resolveInstanceFromIdentifier, but we already have the holder here
209+
if ( data.batchDisabled ) {
210+
initialize( data, entityHolder, session, persistenceContext );
211+
}
212+
else if ( entityHolder == null || !entityHolder.isEventuallyInitialized() ) {
213+
// need to add the key to the batch queue only when the entity has not been already loaded or
214+
// there isn't another initializer that is loading it
215+
registerResolutionListener( data );
216+
registerToBatchFetchQueue( data );
217+
}
186218
}
219+
187220
if ( keyIsEager ) {
188221
final Initializer<?> initializer = keyAssembler.getInitializer();
189222
assert initializer != null;
@@ -233,6 +266,7 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) {
233266
: parentInstance;
234267
// No need to initialize these fields
235268
data.entityKey = null;
269+
data.entityIdentifier = null;
236270
data.setInstance( null );
237271
if ( instance == null ) {
238272
data.setState( State.MISSING );

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

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,33 +207,50 @@ public void resolveInstance(Object instance, DiscriminatedEntityInitializerData
207207
data.setInstance( null );
208208
}
209209
else {
210-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
210+
final var rowProcessingState = data.getRowProcessingState();
211+
final var session = rowProcessingState.getSession();
211212
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
212213
if ( lazyInitializer == null ) {
213214
data.setState( State.INITIALIZED );
214-
if ( keyIsEager ) {
215-
final SharedSessionContractImplementor session = rowProcessingState.getSession();
216-
data.concreteDescriptor = session.getEntityPersister( null, instance );
217-
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
218-
}
215+
data.concreteDescriptor = session.getEntityPersister( null, instance );
216+
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
219217
}
220218
else if ( lazyInitializer.isUninitialized() ) {
221219
data.setState( eager ? State.RESOLVED : State.INITIALIZED );
222-
if ( keyIsEager ) {
223-
// Read the discriminator from the result set if necessary
224-
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
225-
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
226-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
227-
}
220+
// Read the discriminator from the result set if necessary
221+
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
222+
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
223+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
228224
}
229225
else {
230226
data.setState( State.INITIALIZED );
231-
if ( keyIsEager ) {
232-
data.concreteDescriptor = rowProcessingState.getSession().getEntityPersister( null, lazyInitializer.getImplementation() );
233-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
227+
data.concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
228+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
229+
}
230+
231+
232+
final var entityKey = new EntityKey( data.entityIdentifier, data.concreteDescriptor );
233+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
234+
entityKey
235+
);
236+
237+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
238+
// the existing entity instance is detached or transient
239+
if ( entityHolder != null ) {
240+
final var managed = entityHolder.getManagedObject();
241+
data.setInstance( managed );
242+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
243+
data.setState( !eager || entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
244+
}
245+
else {
246+
data.setState( State.RESOLVED );
247+
initializeInstance( data );
234248
}
235249
}
236-
data.setInstance( instance );
250+
else {
251+
data.setInstance( instance );
252+
}
253+
237254
if ( keyIsEager ) {
238255
final Initializer<?> initializer = keyValueAssembler.getInitializer();
239256
assert initializer != null;

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

Lines changed: 107 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -174,94 +174,102 @@ public void resolveInstance(EntityDelayedFetchInitializerData data) {
174174
concreteDescriptor = entityPersister;
175175
}
176176

177-
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
178-
if ( selectByUniqueKey ) {
179-
final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName();
180-
final Type uniqueKeyPropertyType = ( referencedModelPart.getReferencedPropertyName() == null ) ?
181-
concreteDescriptor.getIdentifierType() :
182-
session.getFactory().getRuntimeMetamodels()
183-
.getReferencedPropertyType(
184-
concreteDescriptor.getEntityName(),
185-
uniqueKeyPropertyName
186-
);
187-
188-
final EntityUniqueKey euk = new EntityUniqueKey(
189-
concreteDescriptor.getEntityName(),
190-
uniqueKeyPropertyName,
191-
data.entityIdentifier,
192-
uniqueKeyPropertyType,
193-
session.getFactory()
194-
);
195-
Object instance = persistenceContext.getEntity( euk );
196-
if ( instance == null ) {
197-
// For unique-key mappings, we always use bytecode-laziness if possible,
198-
// because we can't generate a proxy based on the unique key yet
199-
if ( referencedModelPart.isLazy() ) {
200-
instance = UNFETCHED_PROPERTY;
201-
}
202-
else {
203-
// Try to load a PersistentAttributeInterceptable. If we get one, we can add the lazy
204-
// field to the interceptor. If we don't get one, we load the entity by unique key.
205-
PersistentAttributeInterceptable persistentAttributeInterceptable = null;
206-
if ( getParent().isEntityInitializer() && isLazyByGraph( rowProcessingState ) ) {
207-
final Object resolvedInstance =
208-
getParent().asEntityInitializer().getResolvedInstance( rowProcessingState );
209-
persistentAttributeInterceptable =
210-
ManagedTypeHelper.asPersistentAttributeInterceptableOrNull( resolvedInstance );
211-
}
177+
initialize( data, null, concreteDescriptor );
178+
}
179+
}
212180

213-
if ( persistentAttributeInterceptable != null ) {
214-
final LazyAttributeLoadingInterceptor persistentAttributeInterceptor = (LazyAttributeLoadingInterceptor) persistentAttributeInterceptable.$$_hibernate_getInterceptor();
215-
persistentAttributeInterceptor.addLazyFieldByGraph( navigablePath.getLocalName() );
216-
instance = UNFETCHED_PROPERTY;
217-
}
218-
else {
219-
instance = concreteDescriptor.loadByUniqueKey(
220-
uniqueKeyPropertyName,
221-
data.entityIdentifier,
222-
session
181+
protected void initialize(EntityDelayedFetchInitializerData data, @Nullable EntityKey entityKey, EntityPersister concreteDescriptor) {
182+
final RowProcessingState rowProcessingState = data.getRowProcessingState();
183+
final SharedSessionContractImplementor session = rowProcessingState.getSession();
184+
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
185+
if ( selectByUniqueKey ) {
186+
final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName();
187+
final Type uniqueKeyPropertyType = ( referencedModelPart.getReferencedPropertyName() == null ) ?
188+
concreteDescriptor.getIdentifierType() :
189+
session.getFactory().getRuntimeMetamodels()
190+
.getReferencedPropertyType(
191+
concreteDescriptor.getEntityName(),
192+
uniqueKeyPropertyName
223193
);
224194

225-
// If the entity was not in the Persistence Context, but was found now,
226-
// add it to the Persistence Context
227-
if ( instance != null ) {
228-
persistenceContext.addEntity( euk, instance );
229-
}
195+
final EntityUniqueKey euk = new EntityUniqueKey(
196+
concreteDescriptor.getEntityName(),
197+
uniqueKeyPropertyName,
198+
data.entityIdentifier,
199+
uniqueKeyPropertyType,
200+
session.getFactory()
201+
);
202+
Object instance = persistenceContext.getEntity( euk );
203+
if ( instance == null ) {
204+
// For unique-key mappings, we always use bytecode-laziness if possible,
205+
// because we can't generate a proxy based on the unique key yet
206+
if ( referencedModelPart.isLazy() ) {
207+
instance = UNFETCHED_PROPERTY;
208+
}
209+
else {
210+
// Try to load a PersistentAttributeInterceptable. If we get one, we can add the lazy
211+
// field to the interceptor. If we don't get one, we load the entity by unique key.
212+
PersistentAttributeInterceptable persistentAttributeInterceptable = null;
213+
if ( getParent().isEntityInitializer() && isLazyByGraph( rowProcessingState ) ) {
214+
final Object resolvedInstance =
215+
getParent().asEntityInitializer().getResolvedInstance( rowProcessingState );
216+
persistentAttributeInterceptable =
217+
ManagedTypeHelper.asPersistentAttributeInterceptableOrNull( resolvedInstance );
218+
}
219+
220+
if ( persistentAttributeInterceptable != null ) {
221+
final LazyAttributeLoadingInterceptor persistentAttributeInterceptor = (LazyAttributeLoadingInterceptor) persistentAttributeInterceptable.$$_hibernate_getInterceptor();
222+
persistentAttributeInterceptor.addLazyFieldByGraph( navigablePath.getLocalName() );
223+
instance = UNFETCHED_PROPERTY;
224+
}
225+
else {
226+
instance = concreteDescriptor.loadByUniqueKey(
227+
uniqueKeyPropertyName,
228+
data.entityIdentifier,
229+
session
230+
);
231+
232+
// If the entity was not in the Persistence Context, but was found now,
233+
// add it to the Persistence Context
234+
if ( instance != null ) {
235+
persistenceContext.addEntity( euk, instance );
230236
}
231237
}
232238
}
233-
if ( instance != null ) {
234-
instance = persistenceContext.proxyFor( instance );
235-
}
236-
data.setInstance( instance );
239+
}
240+
if ( instance != null ) {
241+
instance = persistenceContext.proxyFor( instance );
242+
}
243+
data.setInstance( instance );
244+
}
245+
else {
246+
final EntityKey ek = entityKey == null ?
247+
new EntityKey( data.entityIdentifier, concreteDescriptor ) :
248+
entityKey;
249+
final EntityHolder holder = persistenceContext.getEntityHolder( ek );
250+
final Object instance;
251+
if ( holder != null && holder.getEntity() != null ) {
252+
instance = persistenceContext.proxyFor( holder, concreteDescriptor );
253+
}
254+
// For primary key based mappings we only use bytecode-laziness if the attribute is optional,
255+
// because the non-optionality implies that it is safe to have a proxy
256+
else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) {
257+
instance = UNFETCHED_PROPERTY;
237258
}
238259
else {
239-
final EntityKey entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
240-
final EntityHolder holder = persistenceContext.getEntityHolder( entityKey );
241-
final Object instance;
242-
if ( holder != null && holder.getEntity() != null ) {
243-
instance = persistenceContext.proxyFor( holder, concreteDescriptor );
244-
}
245-
// For primary key based mappings we only use bytecode-laziness if the attribute is optional,
246-
// because the non-optionality implies that it is safe to have a proxy
247-
else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) {
248-
instance = UNFETCHED_PROPERTY;
249-
}
250-
else {
251-
instance = session.internalLoad(
252-
concreteDescriptor.getEntityName(),
253-
data.entityIdentifier,
254-
false,
255-
false
256-
);
257-
258-
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance );
259-
if ( lazyInitializer != null ) {
260-
lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() );
261-
}
260+
instance = session.internalLoad(
261+
concreteDescriptor.getEntityName(),
262+
data.entityIdentifier,
263+
false,
264+
false
265+
);
266+
267+
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance );
268+
if ( lazyInitializer != null ) {
269+
lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() );
262270
}
263-
data.setInstance( instance );
264271
}
272+
data.setInstance( instance );
265273
}
266274
}
267275

@@ -287,9 +295,28 @@ public void resolveInstance(Object instance, EntityDelayedFetchInitializerData d
287295
// This initializer is done initializing, since this is only invoked for delayed or select initializers
288296
data.setState( State.INITIALIZED );
289297
data.setInstance( instance );
290-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
298+
final var rowProcessingState = data.getRowProcessingState();
299+
final var session = rowProcessingState.getSession();
300+
final var entityDescriptor = getEntityDescriptor();
301+
data.entityIdentifier = entityDescriptor.getIdentifier( instance, session );
302+
303+
final var entityKey = new EntityKey( data.entityIdentifier, entityDescriptor );
304+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
305+
entityKey
306+
);
307+
308+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
309+
// the existing entity instance is detached or transient
310+
if ( entityHolder != null ) {
311+
final var managed = entityHolder.getManagedObject();
312+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
313+
data.setInstance( managed );
314+
}
315+
else {
316+
initialize( data, entityKey, entityDescriptor );
317+
}
318+
}
291319
if ( keyIsEager ) {
292-
data.entityIdentifier = getEntityDescriptor().getIdentifier( instance, rowProcessingState.getSession() );
293320
final Initializer<?> initializer = identifierAssembler.getInitializer();
294321
assert initializer != null;
295322
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );

0 commit comments

Comments
 (0)