HHH-14424 When enhanced as a proxy is enabled with dirty checking, on flush uninitialized entities containing collections are updated and all the fields are set to null

This commit is contained in:
Andrea Boriero 2021-01-29 14:25:46 +01:00 committed by Sanne Grinovero
parent fa004010bb
commit c37911049e
3 changed files with 26 additions and 4 deletions

View File

@ -35,6 +35,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
private final boolean inLineDirtyChecking;
private Set<String> writtenFieldNames;
private Set<String> collectionAttributeNames;
private Status status;
@ -57,11 +58,22 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
this.entityKey = entityKey;
final EntityPersister entityPersister = session.getFactory().getMetamodel().entityPersister( entityName );
if ( entityPersister.hasCollections() ) {
Type[] propertyTypes = entityPersister.getPropertyTypes();
collectionAttributeNames = new HashSet<>();
for ( int i = 0; i < propertyTypes.length; i++ ) {
Type propertyType = propertyTypes[i];
if ( propertyType.isCollectionType() ) {
collectionAttributeNames.add( entityPersister.getPropertyNames()[i] );
}
}
}
this.inLineDirtyChecking = entityPersister.getEntityMode() == EntityMode.POJO
&& SelfDirtinessTracker.class.isAssignableFrom( entityPersister.getMappedClass() );
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to initialise the entity
// because the pre-computed update statement contains even not dirty properties and so we need all the values
initializeBeforeWrite = !inLineDirtyChecking || !entityPersister.getEntityMetamodel().isDynamicUpdate();
initializeBeforeWrite = !( inLineDirtyChecking && entityPersister.getEntityMetamodel().isDynamicUpdate() );
status = Status.UNINITIALIZED;
}
@ -245,7 +257,8 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
return newValue;
}
if ( initializeBeforeWrite ) {
if ( initializeBeforeWrite
|| ( collectionAttributeNames != null && collectionAttributeNames.contains( attributeName ) ) ) {
// we need to force-initialize the proxy - the fetch group to which the `attributeName` belongs
try {
forceInitialize( target, attributeName );
@ -267,6 +280,8 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
writtenFieldNames = new HashSet<>();
}
writtenFieldNames.add( attributeName );
( (SelfDirtinessTracker) target ).$$_hibernate_trackChange( attributeName );
}
return newValue;
@ -323,6 +338,10 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter
status = Status.INITIALIZED;
}
public boolean hasWrittenFieldNames() {
return writtenFieldNames != null && writtenFieldNames.size() != 0;
}
private enum Status {
UNINITIALIZED,
INITIALIZING,

View File

@ -9,7 +9,6 @@ package org.hibernate.engine.internal;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;
import org.hibernate.AssertionFailure;
import org.hibernate.CustomEntityDirtinessStrategy;
@ -353,8 +352,12 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
if ( enhancementAsProxyLazinessInterceptor.hasWrittenFieldNames() ) {
return false;
}
// When a proxy has dirty attributes, we have to treat it like a normal entity to flush changes
uninitializedProxy = !enhancementAsProxyLazinessInterceptor.isInitialized() && !( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
return !enhancementAsProxyLazinessInterceptor.isInitialized()
|| !persister.hasCollections() && !( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
}
}
else if ( entity instanceof HibernateProxy ) {