Skip to content

Commit 0fe6273

Browse files
committed
HHH-19605 Fix entity dirtiness logic when dealing with proxies
1 parent c50b13d commit 0fe6273

File tree

2 files changed

+24
-41
lines changed

2 files changed

+24
-41
lines changed

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

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.hibernate.engine.spi.Status;
2727
import org.hibernate.internal.util.ImmutableBitSet;
2828
import org.hibernate.persister.entity.EntityPersister;
29+
import org.hibernate.proxy.HibernateProxy;
2930
import org.hibernate.type.TypeHelper;
3031

3132
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -37,10 +38,8 @@
3738
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS;
3839
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS;
3940
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
40-
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
41+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
4142
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
42-
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
43-
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
4443
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
4544
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfManagedEntity;
4645
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker;
@@ -52,7 +51,6 @@
5251
import static org.hibernate.engine.spi.Status.SAVING;
5352
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
5453
import static org.hibernate.pretty.MessageHelper.infoString;
55-
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
5654

5755
/**
5856
* A base implementation of {@link EntityEntry}.
@@ -385,43 +383,31 @@ private boolean isUnequivocallyNonDirty(Object entity) {
385383
}
386384

387385
private boolean isNonDirtyViaCustomStrategy(Object entity) {
388-
if ( isPersistentAttributeInterceptable( entity ) ) {
389-
if ( asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor()
390-
instanceof EnhancementAsProxyLazinessInterceptor ) {
386+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
387+
if ( interceptable != null ) {
388+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor
389+
&& !interceptor.isInitialized() ) {
391390
// we never have to check an uninitialized proxy
392391
return true;
393392
}
394393
}
395-
396394
final var session = (SessionImplementor) getPersistenceContext().getSession();
397395
final var customEntityDirtinessStrategy = session.getFactory().getCustomEntityDirtinessStrategy();
398396
return customEntityDirtinessStrategy.canDirtyCheck( entity, persister, session )
399397
&& !customEntityDirtinessStrategy.isDirty( entity, persister, session );
400398
}
401399

402400
private boolean isNonDirtyViaTracker(Object entity) {
403-
final boolean uninitializedProxy;
404-
if ( isPersistentAttributeInterceptable( entity ) ) {
405-
if ( asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor()
406-
instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) {
407-
return !lazinessInterceptor.hasWrittenFieldNames();
408-
}
409-
else {
410-
uninitializedProxy = false;
401+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
402+
if ( interceptable != null ) {
403+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor ) {
404+
return !interceptor.hasWrittenFieldNames();
411405
}
412406
}
413-
else if ( isHibernateProxy( entity ) ) {
414-
uninitializedProxy = extractLazyInitializer( entity ).isUninitialized();
415-
}
416-
else {
417-
uninitializedProxy = false;
418-
}
419-
// we never have to check an uninitialized proxy
420-
return uninitializedProxy
421-
|| !persister.hasCollections()
422-
&& !persister.hasMutableProperties()
423-
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes()
424-
&& asManagedEntity( entity ).$$_hibernate_useTracker();
407+
return !persister.hasCollections()
408+
&& !persister.hasMutableProperties()
409+
&& asManagedEntity( entity ).$$_hibernate_useTracker()
410+
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
425411
}
426412

427413
@Override

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.hibernate.HibernateException;
99
import org.hibernate.collection.spi.PersistentCollection;
1010
import org.hibernate.engine.spi.EntityEntry;
11-
import org.hibernate.engine.spi.EntityHolder;
1211
import org.hibernate.engine.spi.Status;
1312
import org.hibernate.event.spi.DirtyCheckEvent;
1413
import org.hibernate.event.spi.DirtyCheckEventListener;
@@ -17,6 +16,7 @@
1716
import org.hibernate.persister.entity.EntityPersister;
1817

1918

19+
2020
/**
2121
* Determines if the current session holds modified state which
2222
* would be synchronized with the database if the session were
@@ -39,13 +39,11 @@ public class DefaultDirtyCheckEventListener implements DirtyCheckEventListener {
3939
public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
4040
final var session = event.getSession();
4141
final var persistenceContext = session.getPersistenceContext();
42-
final var holdersByKey = persistenceContext.getEntityHoldersByKey();
43-
if ( holdersByKey != null ) {
44-
for ( var entry : holdersByKey.entrySet() ) {
45-
if ( isEntityDirty( entry.getValue(), session ) ) {
46-
event.setDirty( true );
47-
return;
48-
}
42+
final var entityEntries = persistenceContext.reentrantSafeEntityEntries();
43+
for ( var me : entityEntries ) {
44+
if ( isEntityDirty( me.getKey(), me.getValue(), session ) ) {
45+
event.setDirty( true );
46+
return;
4947
}
5048
}
5149
final var entriesByCollection = persistenceContext.getCollectionEntries();
@@ -59,20 +57,19 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
5957
}
6058
}
6159

62-
private static boolean isEntityDirty(EntityHolder holder, EventSource session) {
63-
final var entityEntry = holder.getEntityEntry();
60+
private static boolean isEntityDirty(Object entity, EntityEntry entityEntry, EventSource session) {
6461
final Status status = entityEntry.getStatus();
6562
return switch ( status ) {
6663
case GONE, READ_ONLY -> false;
6764
case DELETED -> true;
68-
case MANAGED -> isManagedEntityDirty( holder.getManagedObject(), holder.getDescriptor(), entityEntry, session );
65+
case MANAGED -> isManagedEntityDirty( entity, entityEntry, session );
6966
case SAVING, LOADING -> throw new AssertionFailure( "Unexpected status: " + status );
7067
};
7168
}
7269

73-
private static boolean isManagedEntityDirty(
74-
Object entity, EntityPersister descriptor, EntityEntry entityEntry, EventSource session) {
70+
private static boolean isManagedEntityDirty(Object entity, EntityEntry entityEntry, EventSource session) {
7571
if ( entityEntry.requiresDirtyCheck( entity ) ) { // takes into account CustomEntityDirtinessStrategy
72+
final EntityPersister descriptor = entityEntry.getPersister();
7673
final Object[] propertyValues =
7774
entityEntry.getStatus() == Status.DELETED
7875
? entityEntry.getDeletedState()

0 commit comments

Comments
 (0)