Skip to content

Commit 95ddb93

Browse files
committed
HHH-19910 Check if existing instance is not managed during initialization
1 parent 534eab7 commit 95ddb93

File tree

6 files changed

+305
-141
lines changed

6 files changed

+305
-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
@@ -307,6 +307,10 @@ public void resolveInstance(@Nullable Object instance, NonAggregatedIdentifierMa
307307
data.setState( State.MISSING );
308308
data.setInstance( null );
309309
}
310+
else if ( hasIdClass ) {
311+
resolveKey( data );
312+
resolveInstance( data );
313+
}
310314
else {
311315
data.setState( State.INITIALIZED );
312316
data.setInstance( instance );

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

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ public void resolveInstance(Object instance, Data data) {
137137
data.setInstance( null );
138138
return;
139139
}
140-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
140+
final var rowProcessingState = data.getRowProcessingState();
141+
final var session = rowProcessingState.getSession();
142+
final var persistenceContext = session.getPersistenceContextInternal();
141143
// Only need to extract the identifier if the identifier has a many to one
142144
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
143-
data.entityKey = null;
144145
data.entityIdentifier = null;
145146
if ( lazyInitializer == null ) {
146147
// Entity is most probably initialized
@@ -162,10 +163,10 @@ && isPersistentAttributeInterceptable( 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;

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
@@ -210,33 +210,50 @@ public void resolveInstance(Object instance, DiscriminatedEntityInitializerData
210210
data.setInstance( null );
211211
}
212212
else {
213-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
213+
final var rowProcessingState = data.getRowProcessingState();
214+
final var session = rowProcessingState.getSession();
214215
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
215216
if ( lazyInitializer == null ) {
216217
data.setState( State.INITIALIZED );
217-
if ( keyIsEager ) {
218-
final SharedSessionContractImplementor session = rowProcessingState.getSession();
219-
data.concreteDescriptor = session.getEntityPersister( null, instance );
220-
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
221-
}
218+
data.concreteDescriptor = session.getEntityPersister( null, instance );
219+
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
222220
}
223221
else if ( lazyInitializer.isUninitialized() ) {
224222
data.setState( eager ? State.RESOLVED : State.INITIALIZED );
225-
if ( keyIsEager ) {
226-
// Read the discriminator from the result set if necessary
227-
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
228-
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
229-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
230-
}
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();
231227
}
232228
else {
233229
data.setState( State.INITIALIZED );
234-
if ( keyIsEager ) {
235-
data.concreteDescriptor = rowProcessingState.getSession().getEntityPersister( null, lazyInitializer.getImplementation() );
236-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
230+
data.concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
231+
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
232+
}
233+
234+
235+
final var entityKey = new EntityKey( data.entityIdentifier, data.concreteDescriptor );
236+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
237+
entityKey
238+
);
239+
240+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
241+
// the existing entity instance is detached or transient
242+
if ( entityHolder != null ) {
243+
final var managed = entityHolder.getManagedObject();
244+
data.setInstance( managed );
245+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
246+
data.setState( !eager || entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
247+
}
248+
else {
249+
data.setState( State.RESOLVED );
250+
initializeInstance( data );
237251
}
238252
}
239-
data.setInstance( instance );
253+
else {
254+
data.setInstance( instance );
255+
}
256+
240257
if ( keyIsEager ) {
241258
final Initializer<?> initializer = keyValueAssembler.getInitializer();
242259
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
@@ -176,94 +176,102 @@ public void resolveInstance(EntityDelayedFetchInitializerData data) {
176176
concreteDescriptor = entityPersister;
177177
}
178178

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

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

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

@@ -291,9 +299,28 @@ public void resolveInstance(Object instance, EntityDelayedFetchInitializerData d
291299
// This initializer is done initializing, since this is only invoked for delayed or select initializers
292300
data.setState( State.INITIALIZED );
293301
data.setInstance( instance );
294-
final RowProcessingState rowProcessingState = data.getRowProcessingState();
302+
final var rowProcessingState = data.getRowProcessingState();
303+
final var session = rowProcessingState.getSession();
304+
final var entityDescriptor = getEntityDescriptor();
305+
data.entityIdentifier = entityDescriptor.getIdentifier( instance, session );
306+
307+
final var entityKey = new EntityKey( data.entityIdentifier, entityDescriptor );
308+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
309+
entityKey
310+
);
311+
312+
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
313+
// the existing entity instance is detached or transient
314+
if ( entityHolder != null ) {
315+
final var managed = entityHolder.getManagedObject();
316+
data.entityIdentifier = entityHolder.getEntityKey().getIdentifier();
317+
data.setInstance( managed );
318+
}
319+
else {
320+
initialize( data, entityKey, entityDescriptor );
321+
}
322+
}
295323
if ( keyIsEager ) {
296-
data.entityIdentifier = getEntityDescriptor().getIdentifier( instance, rowProcessingState.getSession() );
297324
final Initializer<?> initializer = identifierAssembler.getInitializer();
298325
assert initializer != null;
299326
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );

0 commit comments

Comments
 (0)