HHH-1645 HHH-17395 Refresh with LockMode on an unitialized proxy does not work

This commit is contained in:
Andrea Boriero 2023-12-18 13:07:00 +01:00 committed by Christian Beikov
parent 839c695c8e
commit d085936fcb
3 changed files with 61 additions and 17 deletions

View File

@ -33,6 +33,8 @@ import org.hibernate.loader.ast.spi.CascadingFetchProfile;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
@ -62,7 +64,22 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final Object object = event.getObject();
if ( persistenceContext.reassociateIfUninitializedProxy( object ) ) {
if ( isTransient( event, source, object ) ) {
final boolean isTransient = isTransient( event, source, object );
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object );
final EntityPersister persister = source.getEntityPersister( lazyInitializer.getEntityName(), object );
refresh(
event,
null,
source,
persister,
lazyInitializer,
null,
persister.getIdentifier( object, event.getSession() ),
persistenceContext
);
if ( isTransient ) {
source.setReadOnly( object, source.isDefaultReadOnly() );
}
}
@ -144,9 +161,22 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
evictEntity( object, persister, id, source );
evictCachedCollections( persister, id, source );
refresh( event, object, source, persister, null, entry, id, persistenceContext );
}
private static void refresh(
RefreshEvent event,
Object object,
EventSource source,
EntityPersister persister,
LazyInitializer lazyInitializer,
EntityEntry entry,
Object id,
PersistenceContext persistenceContext) {
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
CascadingFetchProfile.REFRESH,
() -> doRefresh( event, source, object, entry, persister, id, persistenceContext )
() -> doRefresh( event, source, object, entry, persister, lazyInitializer, id, persistenceContext )
);
UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() );
}
@ -179,6 +209,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
Object object,
EntityEntry entry,
EntityPersister persister,
LazyInitializer lazyInitializer,
Object id,
PersistenceContext persistenceContext) {
// Handle the requested lock-mode (if one) in relation to the entry's (if one) current lock-mode
@ -228,19 +259,32 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
persistenceContext.getEntry( result ).setLockMode( postRefreshLockMode );
}
// Keep the same read-only/modifiable setting for the entity that it had before refreshing;
// If it was transient, then set it to the default for the source.
if ( !persister.isMutable() ) {
// this is probably redundant; it should already be read-only
source.setReadOnly( result, true );
}
else {
source.setReadOnly( result, entry == null ? source.isDefaultReadOnly() : entry.isReadOnly() );
}
source.setReadOnly( result, isReadOnly( entry, persister, lazyInitializer, source ) );
}
return result;
}
private static boolean isReadOnly(
EntityEntry entry,
EntityPersister persister,
LazyInitializer lazyInitializer,
EventSource source) {
// Keep the same read-only/modifiable setting for the entity that it had before refreshing;
// If it was transient, then set it to the default for the source.
if ( !persister.isMutable() ) {
return true;
}
else if ( entry != null ) {
return entry.isReadOnly();
}
else if ( lazyInitializer != null ) {
return lazyInitializer.isReadOnly();
}
else {
return source.isDefaultReadOnly();
}
}
private static void evictCachedCollections(EntityPersister persister, Object id, EventSource source) {
evictCachedCollections( persister.getPropertyTypes(), id, source );
}

View File

@ -1102,7 +1102,7 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
s.setReadOnly( dp, true );
assertFalse( Hibernate.isInitialized( dp ) );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
assertEquals( "original", dp.getDescription() );
assertTrue( Hibernate.isInitialized( dp ) );
dp.setDescription( "changed" );
@ -1232,7 +1232,7 @@ public class ReadOnlyProxyTest extends AbstractReadOnlyTest {
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
assertFalse( s.isReadOnly( dp ) );
dp.setDescription( "changed" );
assertEquals( "changed", dp.getDescription() );

View File

@ -516,11 +516,11 @@ public class ReadOnlySessionTest extends AbstractReadOnlyTest {
assertTrue( s.isReadOnly( dp ) );
assertFalse( Hibernate.isInitialized( dp ) );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
assertTrue( s.isReadOnly( dp ) );
s.setDefaultReadOnly( false );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
assertTrue( s.isReadOnly( dp ) );
assertEquals( "original", dp.getDescription() );
assertTrue( Hibernate.isInitialized( dp ) );
@ -574,12 +574,12 @@ public class ReadOnlySessionTest extends AbstractReadOnlyTest {
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
s.setDefaultReadOnly( false );
assertTrue( s.isReadOnly( dp ) );
s.evict( dp );
s.refresh( dp );
assertFalse( Hibernate.isInitialized( dp ) );
assertTrue( Hibernate.isInitialized( dp ) );
assertFalse( s.isReadOnly( dp ) );
assertFalse( s.isReadOnly( ( (HibernateProxy) dp ).getHibernateLazyInitializer().getImplementation() ) );
dp.setDescription( "changed" );