Skip to content

Commit 5b4c064

Browse files
committed
HHH-19605 Fix entity dirtiness logic when dealing with proxies
1 parent 02feed0 commit 5b4c064

File tree

2 files changed

+31
-47
lines changed

2 files changed

+31
-47
lines changed

hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@
4141
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS;
4242
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS;
4343
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
44-
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
44+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
4545
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
46-
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
47-
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
4846
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
4947
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfManagedEntity;
5048
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker;
@@ -56,7 +54,6 @@
5654
import static org.hibernate.engine.spi.Status.SAVING;
5755
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
5856
import static org.hibernate.pretty.MessageHelper.infoString;
59-
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
6057

6158
/**
6259
* A base implementation of {@link EntityEntry}.
@@ -390,46 +387,31 @@ private boolean isUnequivocallyNonDirty(Object entity) {
390387
}
391388

392389
private boolean isNonDirtyViaCustomStrategy(Object entity) {
393-
if ( isPersistentAttributeInterceptable( entity ) ) {
394-
final PersistentAttributeInterceptor interceptor =
395-
asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
396-
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
390+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
391+
if ( interceptable != null ) {
392+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor
393+
&& !interceptor.isInitialized() ) {
397394
// we never have to check an uninitialized proxy
398395
return true;
399396
}
400397
}
401-
402-
final SessionImplementor session = (SessionImplementor) getPersistenceContext().getSession();
403-
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
404-
session.getFactory().getCustomEntityDirtinessStrategy();
398+
final var session = (SessionImplementor) getPersistenceContext().getSession();
399+
final var customEntityDirtinessStrategy = session.getFactory().getCustomEntityDirtinessStrategy();
405400
return customEntityDirtinessStrategy.canDirtyCheck( entity, persister, session )
406401
&& !customEntityDirtinessStrategy.isDirty( entity, persister, session );
407402
}
408403

409404
private boolean isNonDirtyViaTracker(Object entity) {
410-
final boolean uninitializedProxy;
411-
if ( isPersistentAttributeInterceptable( entity ) ) {
412-
final PersistentAttributeInterceptor interceptor =
413-
asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
414-
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) {
415-
return !lazinessInterceptor.hasWrittenFieldNames();
416-
}
417-
else {
418-
uninitializedProxy = false;
405+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
406+
if ( interceptable != null ) {
407+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor ) {
408+
return !interceptor.hasWrittenFieldNames();
419409
}
420410
}
421-
else if ( isHibernateProxy( entity ) ) {
422-
uninitializedProxy = extractLazyInitializer( entity ).isUninitialized();
423-
}
424-
else {
425-
uninitializedProxy = false;
426-
}
427-
// we never have to check an uninitialized proxy
428-
return uninitializedProxy
429-
|| !persister.hasCollections()
430-
&& !persister.hasMutableProperties()
431-
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes()
432-
&& asManagedEntity( entity ).$$_hibernate_useTracker();
411+
return !persister.hasCollections()
412+
&& !persister.hasMutableProperties()
413+
&& asManagedEntity( entity ).$$_hibernate_useTracker()
414+
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
433415
}
434416

435417
@Override

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDirtyCheckEventListener.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
import org.hibernate.event.spi.DirtyCheckEventListener;
1616
import org.hibernate.event.spi.EventSource;
1717
import org.hibernate.persister.collection.CollectionPersister;
18-
import org.hibernate.persister.entity.EntityPersister;
19-
2018

2119
/**
2220
* Determines if the current session holds modified state which
@@ -38,12 +36,12 @@ public class DefaultDirtyCheckEventListener implements DirtyCheckEventListener {
3836

3937
@Override
4038
public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
41-
final EventSource session = event.getSession();
42-
final PersistenceContext persistenceContext = session.getPersistenceContext();
39+
final var session = event.getSession();
40+
final var persistenceContext = session.getPersistenceContextInternal();
4341
final var holdersByKey = persistenceContext.getEntityHoldersByKey();
4442
if ( holdersByKey != null ) {
45-
for ( var entry : holdersByKey.entrySet() ) {
46-
if ( isEntityDirty( entry.getValue(), session ) ) {
43+
for ( var holder : holdersByKey.values() ) {
44+
if ( isEntityDirty( holder, session ) ) {
4745
event.setDirty( true );
4846
return;
4947
}
@@ -61,25 +59,29 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
6159
}
6260

6361
private static boolean isEntityDirty(EntityHolder holder, EventSource session) {
64-
final EntityEntry entityEntry = holder.getEntityEntry();
62+
final var entityEntry = holder.getEntityEntry();
63+
if ( entityEntry == null ) {
64+
// holders with no entity entry yet cannot contain dirty entities
65+
return false;
66+
}
6567
final Status status = entityEntry.getStatus();
6668
return switch ( status ) {
6769
case GONE, READ_ONLY -> false;
6870
case DELETED -> true;
69-
case MANAGED -> isManagedEntityDirty( holder.getManagedObject(), holder.getDescriptor(), entityEntry, session );
71+
case MANAGED -> isManagedEntityDirty( holder.getEntity(), entityEntry, session );
7072
case SAVING, LOADING -> throw new AssertionFailure( "Unexpected status: " + status );
7173
};
7274
}
7375

74-
private static boolean isManagedEntityDirty(
75-
Object entity, EntityPersister descriptor, EntityEntry entityEntry, EventSource session) {
76+
private static boolean isManagedEntityDirty(Object entity, EntityEntry entityEntry, EventSource session) {
7677
if ( entityEntry.requiresDirtyCheck( entity ) ) { // takes into account CustomEntityDirtinessStrategy
77-
final Object[] propertyValues =
78+
final var persister = entityEntry.getPersister();
79+
final var propertyValues =
7880
entityEntry.getStatus() == Status.DELETED
7981
? entityEntry.getDeletedState()
80-
: descriptor.getValues( entity );
81-
final int[] dirty =
82-
descriptor.findDirty( propertyValues, entityEntry.getLoadedState(), entity, session );
82+
: persister.getValues( entity );
83+
final var dirty =
84+
persister.findDirty( propertyValues, entityEntry.getLoadedState(), entity, session );
8385
return dirty != null;
8486
}
8587
else {

0 commit comments

Comments
 (0)