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);
|
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,
|
* Obtain an estimate of the entity name of the given entity instance,
|
||||||
* which is not involved in an association, using only the
|
* 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) {
|
private void persist(PersistEvent event, PersistContext createCache, Object entity) {
|
||||||
final EventSource source = event.getSession();
|
final EventSource source = event.getSession();
|
||||||
final EntityEntry entityEntry = source.getPersistenceContextInternal().getEntry( entity );
|
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 ) ) {
|
switch ( entityState( event, entity, entityName, entityEntry ) ) {
|
||||||
case DETACHED:
|
case DETACHED:
|
||||||
throw new PersistentObjectException( "detached entity passed to persist: "
|
throw new PersistentObjectException( "detached entity passed to persist: "
|
||||||
|
@ -133,13 +133,13 @@ public class DefaultPersistEventListener
|
||||||
return entityState;
|
return entityState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String entityName(PersistEvent event, Object entity) {
|
private static String entityName(PersistEvent event, Object entity, EntityEntry entityEntry) {
|
||||||
if ( event.getEntityName() != null ) {
|
if ( event.getEntityName() != null ) {
|
||||||
return event.getEntityName();
|
return event.getEntityName();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// changes event.entityName by side effect!
|
// changes event.entityName by side effect!
|
||||||
final String entityName = event.getSession().bestGuessEntityName( entity );
|
final String entityName = event.getSession().bestGuessEntityName( entity, entityEntry );
|
||||||
event.setEntityName( entityName );
|
event.setEntityName( entityName );
|
||||||
return 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
|
@Override
|
||||||
public String getEntityName(Object object) {
|
public String getEntityName(Object object) {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
|
@ -699,9 +699,9 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
preLoad( rowProcessingState );
|
preLoad( rowProcessingState );
|
||||||
|
|
||||||
final LazyInitializer lazyInitializer = extractLazyInitializer( entityInstance );
|
final LazyInitializer lazyInitializer = extractLazyInitializer( entityInstance );
|
||||||
|
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
||||||
|
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||||
if ( lazyInitializer != null ) {
|
if ( lazyInitializer != null ) {
|
||||||
final SharedSessionContractImplementor session = rowProcessingState.getSession();
|
|
||||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
|
||||||
Object instance = persistenceContext.getEntity( entityKey );
|
Object instance = persistenceContext.getEntity( entityKey );
|
||||||
if ( instance == null ) {
|
if ( instance == null ) {
|
||||||
instance = resolveInstance(
|
instance = resolveInstance(
|
||||||
|
@ -715,8 +715,42 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
||||||
entityInstanceForNotify = instance;
|
entityInstanceForNotify = instance;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
initializeEntity( entityInstance, rowProcessingState );
|
if ( entityDescriptor.canReadFromCache() ) {
|
||||||
entityInstanceForNotify = entityInstance;
|
/*
|
||||||
|
@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 );
|
notifyResolutionListeners( entityInstanceForNotify );
|
||||||
|
|
Loading…
Reference in New Issue