HHH-16197 Circular references of the same entity result in different Java objects when caching is enabled and using a query
This commit is contained in:
parent
c5769ad06e
commit
6e4bee8c57
|
@ -329,6 +329,16 @@ public interface SharedSessionContractImplementor
|
|||
*/
|
||||
String bestGuessEntityName(Object object);
|
||||
|
||||
/**
|
||||
* Obtain the best estimate of the entity name of the given entity
|
||||
* instance, which is not involved in an association, by also
|
||||
* considering information held in the proxy, and whether the object
|
||||
* is already associated with this session.
|
||||
*/
|
||||
default String bestGuessEntityName(Object object, EntityEntry entry) {
|
||||
return bestGuessEntityName( object );
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an estimate of the entity name of the given entity instance,
|
||||
* which is not involved in an association, using only the
|
||||
|
|
|
@ -81,7 +81,7 @@ public class DefaultPersistEventListener
|
|||
private void persist(PersistEvent event, PersistContext createCache, Object entity) {
|
||||
final EventSource source = event.getSession();
|
||||
final EntityEntry entityEntry = source.getPersistenceContextInternal().getEntry( entity );
|
||||
final String entityName = entityName( event, entity );
|
||||
final String entityName = entityName( event, entity, entityEntry );
|
||||
switch ( entityState( event, entity, entityName, entityEntry ) ) {
|
||||
case DETACHED:
|
||||
throw new PersistentObjectException( "detached entity passed to persist: "
|
||||
|
@ -133,13 +133,13 @@ public class DefaultPersistEventListener
|
|||
return entityState;
|
||||
}
|
||||
|
||||
private static String entityName(PersistEvent event, Object entity) {
|
||||
private static String entityName(PersistEvent event, Object entity, EntityEntry entityEntry) {
|
||||
if ( event.getEntityName() != null ) {
|
||||
return event.getEntityName();
|
||||
}
|
||||
else {
|
||||
// changes event.entityName by side effect!
|
||||
final String entityName = event.getSession().bestGuessEntityName( entity );
|
||||
final String entityName = event.getSession().bestGuessEntityName( entity, entityEntry );
|
||||
event.setEntityName( entityName );
|
||||
return entityName;
|
||||
}
|
||||
|
|
|
@ -1718,6 +1718,25 @@ public class SessionImpl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String bestGuessEntityName(Object object, EntityEntry entry) {
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( object );
|
||||
if ( lazyInitializer != null ) {
|
||||
// it is possible for this method to be called during flush processing,
|
||||
// so make certain that we do not accidentally initialize an uninitialized proxy
|
||||
if ( lazyInitializer.isUninitialized() ) {
|
||||
return lazyInitializer.getEntityName();
|
||||
}
|
||||
object = lazyInitializer.getImplementation();
|
||||
}
|
||||
if ( entry == null ) {
|
||||
return guessEntityName( object );
|
||||
}
|
||||
else {
|
||||
return entry.getPersister().getEntityName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntityName(Object object) {
|
||||
checkOpen();
|
||||
|
|
|
@ -699,9 +699,9 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
preLoad( rowProcessingState );
|
||||
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( entityInstance );
|
||||
if ( lazyInitializer != null ) {
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
if ( lazyInitializer != null ) {
|
||||
Object instance = persistenceContext.getEntity( entityKey );
|
||||
if ( instance == null ) {
|
||||
instance = resolveInstance(
|
||||
|
@ -714,10 +714,44 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
lazyInitializer.setImplementation( instance );
|
||||
entityInstanceForNotify = instance;
|
||||
}
|
||||
else {
|
||||
if ( entityDescriptor.canReadFromCache() ) {
|
||||
/*
|
||||
@Cache
|
||||
class Child {
|
||||
|
||||
@ManyToOne
|
||||
private Parent parent;
|
||||
}
|
||||
|
||||
@Cache
|
||||
class Parent {
|
||||
@OneToOne
|
||||
private Parent parent;
|
||||
|
||||
}
|
||||
|
||||
when the query "select c from Child c" is executed and the second level cache (2LC) contains
|
||||
an instance of Child and Parent
|
||||
then when the EntitySelectFetchInitializer#initializeInstance() is executed before the EntityResultInitializer one
|
||||
the persistence context will contain the instances retrieved form the 2LC
|
||||
*/
|
||||
final Object entity = persistenceContext.getEntity( entityKey );
|
||||
if ( entity != null ) {
|
||||
entityInstance = entity;
|
||||
registerLoadingEntity( rowProcessingState, entityInstance );
|
||||
initializeEntityInstance( entityInstance, rowProcessingState );
|
||||
}
|
||||
else {
|
||||
initializeEntity( entityInstance, rowProcessingState );
|
||||
}
|
||||
entityInstanceForNotify = entityInstance;
|
||||
}
|
||||
else {
|
||||
initializeEntity( entityInstance, rowProcessingState );
|
||||
entityInstanceForNotify = entityInstance;
|
||||
}
|
||||
}
|
||||
|
||||
notifyResolutionListeners( entityInstanceForNotify );
|
||||
|
||||
|
|
Loading…
Reference in New Issue