Skip to content

Commit 2cfe7da

Browse files
committed
HHH-19605 Fix entity dirtiness logic when dealing with proxies
1 parent ee6e7b3 commit 2cfe7da

File tree

2 files changed

+27
-40
lines changed

2 files changed

+27
-40
lines changed

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

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,8 @@
3737
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS;
3838
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS;
3939
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
40-
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
40+
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
4141
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;
4442
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
4543
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfManagedEntity;
4644
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker;
@@ -52,7 +50,6 @@
5250
import static org.hibernate.engine.spi.Status.SAVING;
5351
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
5452
import static org.hibernate.pretty.MessageHelper.infoString;
55-
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
5653

5754
/**
5855
* A base implementation of {@link EntityEntry}.
@@ -385,43 +382,31 @@ private boolean isUnequivocallyNonDirty(Object entity) {
385382
}
386383

387384
private boolean isNonDirtyViaCustomStrategy(Object entity) {
388-
if ( isPersistentAttributeInterceptable( entity ) ) {
389-
if ( asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor()
390-
instanceof EnhancementAsProxyLazinessInterceptor ) {
385+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
386+
if ( interceptable != null ) {
387+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor
388+
&& !interceptor.isInitialized() ) {
391389
// we never have to check an uninitialized proxy
392390
return true;
393391
}
394392
}
395-
396393
final var session = (SessionImplementor) getPersistenceContext().getSession();
397394
final var customEntityDirtinessStrategy = session.getFactory().getCustomEntityDirtinessStrategy();
398395
return customEntityDirtinessStrategy.canDirtyCheck( entity, persister, session )
399396
&& !customEntityDirtinessStrategy.isDirty( entity, persister, session );
400397
}
401398

402399
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;
400+
final var interceptable = asPersistentAttributeInterceptableOrNull( entity );
401+
if ( interceptable != null ) {
402+
if ( interceptable.$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor interceptor ) {
403+
return !interceptor.hasWrittenFieldNames();
411404
}
412405
}
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();
406+
return !persister.hasCollections()
407+
&& !persister.hasMutableProperties()
408+
&& asManagedEntity( entity ).$$_hibernate_useTracker()
409+
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
425410
}
426411

427412
@Override

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
import org.hibernate.event.spi.DirtyCheckEventListener;
1515
import org.hibernate.event.spi.EventSource;
1616
import org.hibernate.persister.collection.CollectionPersister;
17-
import org.hibernate.persister.entity.EntityPersister;
18-
1917

2018
/**
2119
* Determines if the current session holds modified state which
@@ -38,11 +36,11 @@ public class DefaultDirtyCheckEventListener implements DirtyCheckEventListener {
3836
@Override
3937
public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
4038
final var session = event.getSession();
41-
final var persistenceContext = session.getPersistenceContext();
39+
final var persistenceContext = session.getPersistenceContextInternal();
4240
final var holdersByKey = persistenceContext.getEntityHoldersByKey();
4341
if ( holdersByKey != null ) {
44-
for ( var entry : holdersByKey.entrySet() ) {
45-
if ( isEntityDirty( entry.getValue(), session ) ) {
42+
for ( var holder : holdersByKey.values() ) {
43+
if ( isEntityDirty( holder, session ) ) {
4644
event.setDirty( true );
4745
return;
4846
}
@@ -61,24 +59,28 @@ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
6159

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

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

0 commit comments

Comments
 (0)