Skip to content

Commit 2ee7d1e

Browse files
committed
HHH-19605 Fix entity dirtiness logic when dealing with proxies
1 parent 5452f9e commit 2ee7d1e

File tree

2 files changed

+26
-51
lines changed

2 files changed

+26
-51
lines changed

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

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.io.Serializable;
1111

1212
import org.hibernate.AssertionFailure;
13-
import org.hibernate.CustomEntityDirtinessStrategy;
1413
import org.hibernate.HibernateException;
1514
import org.hibernate.LockMode;
1615
import org.hibernate.UnsupportedLockAttemptException;
@@ -21,7 +20,6 @@
2120
import org.hibernate.engine.spi.EntityKey;
2221
import org.hibernate.engine.spi.ManagedEntity;
2322
import org.hibernate.engine.spi.PersistenceContext;
24-
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
2523
import org.hibernate.engine.spi.SelfDirtinessTracker;
2624
import org.hibernate.engine.spi.SessionFactoryImplementor;
2725
import org.hibernate.engine.spi.SessionImplementor;
@@ -41,10 +39,8 @@
4139
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS;
4240
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS;
4341
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
44-
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
42+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
4543
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;
4844
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
4945
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfManagedEntity;
5046
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker;
@@ -56,7 +52,6 @@
5652
import static org.hibernate.engine.spi.Status.SAVING;
5753
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
5854
import static org.hibernate.pretty.MessageHelper.infoString;
59-
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
6055

6156
/**
6257
* A base implementation of {@link EntityEntry}.
@@ -390,46 +385,31 @@ private boolean isUnequivocallyNonDirty(Object entity) {
390385
}
391386

392387
private boolean isNonDirtyViaCustomStrategy(Object entity) {
393-
if ( isPersistentAttributeInterceptable( entity ) ) {
394-
final PersistentAttributeInterceptor interceptor =
395-
asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
396-
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
388+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
389+
if ( interceptable != null ) {
390+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor
391+
&& !interceptor.isInitialized() ) {
397392
// we never have to check an uninitialized proxy
398393
return true;
399394
}
400395
}
401-
402-
final SessionImplementor session = (SessionImplementor) getPersistenceContext().getSession();
403-
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
404-
session.getFactory().getCustomEntityDirtinessStrategy();
396+
final var session = (SessionImplementor) getPersistenceContext().getSession();
397+
final var customEntityDirtinessStrategy = session.getFactory().getCustomEntityDirtinessStrategy();
405398
return customEntityDirtinessStrategy.canDirtyCheck( entity, persister, session )
406399
&& !customEntityDirtinessStrategy.isDirty( entity, persister, session );
407400
}
408401

409402
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;
403+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
404+
if ( interceptable != null ) {
405+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor ) {
406+
return !interceptor.hasWrittenFieldNames();
419407
}
420408
}
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();
409+
return !persister.hasCollections()
410+
&& !persister.hasMutableProperties()
411+
&& asManagedEntity( entity ).$$_hibernate_useTracker()
412+
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
433413
}
434414

435415
@Override

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

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +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;
12-
import org.hibernate.engine.spi.PersistenceContext;
1311
import org.hibernate.engine.spi.Status;
1412
import org.hibernate.event.spi.DirtyCheckEvent;
1513
import org.hibernate.event.spi.DirtyCheckEventListener;
@@ -38,15 +36,13 @@ 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();
43-
final var holdersByKey = persistenceContext.getEntityHoldersByKey();
44-
if ( holdersByKey != null ) {
45-
for ( var entry : holdersByKey.entrySet() ) {
46-
if ( isEntityDirty( entry.getValue(), session ) ) {
47-
event.setDirty( true );
48-
return;
49-
}
39+
final var session = event.getSession();
40+
final var persistenceContext = session.getPersistenceContext();
41+
final var entityEntries = persistenceContext.reentrantSafeEntityEntries();
42+
for ( var me : entityEntries ) {
43+
if ( isEntityDirty( me.getKey(), me.getValue(), session ) ) {
44+
event.setDirty( true );
45+
return;
5046
}
5147
}
5248
final var entriesByCollection = persistenceContext.getCollectionEntries();
@@ -60,20 +56,19 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
6056
}
6157
}
6258

63-
private static boolean isEntityDirty(EntityHolder holder, EventSource session) {
64-
final EntityEntry entityEntry = holder.getEntityEntry();
59+
private static boolean isEntityDirty(Object entity, EntityEntry entityEntry, EventSource session) {
6560
final Status status = entityEntry.getStatus();
6661
return switch ( status ) {
6762
case GONE, READ_ONLY -> false;
6863
case DELETED -> true;
69-
case MANAGED -> isManagedEntityDirty( holder.getManagedObject(), holder.getDescriptor(), entityEntry, session );
64+
case MANAGED -> isManagedEntityDirty( entity, entityEntry, session );
7065
case SAVING, LOADING -> throw new AssertionFailure( "Unexpected status: " + status );
7166
};
7267
}
7368

74-
private static boolean isManagedEntityDirty(
75-
Object entity, EntityPersister descriptor, EntityEntry entityEntry, EventSource session) {
69+
private static boolean isManagedEntityDirty(Object entity, EntityEntry entityEntry, EventSource session) {
7670
if ( entityEntry.requiresDirtyCheck( entity ) ) { // takes into account CustomEntityDirtinessStrategy
71+
final EntityPersister descriptor = entityEntry.getPersister();
7772
final Object[] propertyValues =
7873
entityEntry.getStatus() == Status.DELETED
7974
? entityEntry.getDeletedState()

0 commit comments

Comments
 (0)