HHH-14329 consider mutable types always as potentially dirty when using DirtinessTracker
This commit is contained in:
parent
b15974732b
commit
e891a0296c
|
@ -411,7 +411,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
continue;
|
||||
}
|
||||
AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField );
|
||||
if ( enhancementContext.isPersistentField( annotatedField ) && !enhancementContext.isMappedCollection( annotatedField ) ) {
|
||||
if ( enhancementContext.isPersistentField( annotatedField ) && enhancementContext.isMappedCollection( annotatedField ) ) {
|
||||
if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) {
|
||||
collectionList.add( annotatedField );
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
for ( FieldDescription ctField : managedCtSuperclass.getDeclaredFields() ) {
|
||||
if ( !Modifier.isStatic( ctField.getModifiers() ) ) {
|
||||
AnnotatedFieldDescription annotatedField = new AnnotatedFieldDescription( enhancementContext, ctField );
|
||||
if ( enhancementContext.isPersistentField( annotatedField ) && !enhancementContext.isMappedCollection( annotatedField ) ) {
|
||||
if ( enhancementContext.isPersistentField( annotatedField ) && enhancementContext.isMappedCollection( annotatedField ) ) {
|
||||
if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) {
|
||||
collectionList.add( annotatedField );
|
||||
}
|
||||
|
|
|
@ -283,7 +283,7 @@ public class EntityEnhancer extends PersistentAttributesEnhancer {
|
|||
if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) {
|
||||
if ( enhancementContext.isPersistentField( ctField ) && enhancementContext.isMappedCollection( ctField ) ) {
|
||||
if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) ||
|
||||
PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) {
|
||||
collectionList.add( ctField );
|
||||
|
@ -314,7 +314,7 @@ public class EntityEnhancer extends PersistentAttributesEnhancer {
|
|||
|
||||
for ( CtField ctField : managedCtSuperclass.getDeclaredFields() ) {
|
||||
if ( !Modifier.isStatic( ctField.getModifiers() ) ) {
|
||||
if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) {
|
||||
if ( enhancementContext.isPersistentField( ctField ) && enhancementContext.isMappedCollection( ctField ) ) {
|
||||
if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) ||
|
||||
PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) {
|
||||
collectionList.add( ctField );
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
|
@ -106,7 +108,13 @@ public class DefaultEnhancementContext implements EnhancementContext {
|
|||
*/
|
||||
@Override
|
||||
public boolean isMappedCollection(UnloadedField field) {
|
||||
return field.hasAnnotation( OneToMany.class ) || field.hasAnnotation( ManyToMany.class ) || field.hasAnnotation( ElementCollection.class );
|
||||
// If the collection is definitely a plural attribute, we respect that
|
||||
if (field.hasAnnotation( OneToMany.class ) || field.hasAnnotation( ManyToMany.class ) || field.hasAnnotation( ElementCollection.class )) {
|
||||
return true;
|
||||
}
|
||||
// But a collection might be treated like a singular attribute if it is annotated with `@Basic`
|
||||
// If no annotations are given though, a collection is treated like a OneToMany
|
||||
return !field.hasAnnotation( Basic.class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.engine.spi.Status;
|
|||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.UniqueKeyLoadable;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
||||
/**
|
||||
* A base implementation of EntityEntry
|
||||
|
@ -346,7 +347,24 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
|
|||
@SuppressWarnings( {"SimplifiableIfStatement"})
|
||||
private boolean isUnequivocallyNonDirty(Object entity) {
|
||||
if ( entity instanceof SelfDirtinessTracker ) {
|
||||
return ! persister.hasCollections() && ! ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
|
||||
boolean uninitializedProxy = false;
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
|
||||
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
|
||||
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
else if ( entity instanceof HibernateProxy ) {
|
||||
uninitializedProxy = ( (HibernateProxy) entity ).getHibernateLazyInitializer()
|
||||
.isUninitialized();
|
||||
}
|
||||
// we never have to check an uninitialized proxy
|
||||
return uninitializedProxy || !persister.hasCollections()
|
||||
&& !persister.hasMutableProperties()
|
||||
&& !( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
|
||||
}
|
||||
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.engine.internal.Nullability;
|
|||
import org.hibernate.engine.internal.Versioning;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
|
@ -39,6 +40,7 @@ import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
|
|||
import org.hibernate.metadata.ClassMetadata;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -525,18 +527,13 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
|
||||
if ( dirtyProperties == null ) {
|
||||
if ( entity instanceof SelfDirtinessTracker ) {
|
||||
if ( ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes() ) {
|
||||
int[] dirty = persister.resolveAttributeIndexes( ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() );
|
||||
|
||||
// HHH-12051 - filter non-updatable attributes
|
||||
// TODO: add Updatability to EnhancementContext and skip dirty tracking of those attributes
|
||||
int count = 0;
|
||||
for ( int i : dirty ) {
|
||||
if ( persister.getPropertyUpdateability()[i] ) {
|
||||
dirty[count++] = i;
|
||||
}
|
||||
}
|
||||
dirtyProperties = count == 0 ? ArrayHelper.EMPTY_INT_ARRAY : count == dirty.length ? dirty : Arrays.copyOf( dirty, count );
|
||||
if ( ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes() || persister.hasMutableProperties() ) {
|
||||
dirtyProperties = persister.resolveDirtyAttributeIndexes(
|
||||
values,
|
||||
loadedState,
|
||||
( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes(),
|
||||
session
|
||||
);
|
||||
}
|
||||
else {
|
||||
dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -67,7 +68,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.CascadingAction;
|
||||
import org.hibernate.engine.spi.CascadingActions;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
|
@ -81,6 +81,7 @@ import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper;
|
|||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.ValueInclusion;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
|
@ -104,7 +105,6 @@ import org.hibernate.loader.custom.sql.SQLQueryParser;
|
|||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.CacheEntityLoaderHelper;
|
||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||
import org.hibernate.loader.entity.plan.DynamicBatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.EntityLoader;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.loader.entity.plan.MultiEntityLoadingSupport;
|
||||
|
@ -2261,6 +2261,52 @@ public abstract class AbstractEntityPersister
|
|||
return Arrays.copyOf( fields, counter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] resolveDirtyAttributeIndexes(
|
||||
final Object[] currentState,
|
||||
final Object[] previousState,
|
||||
final String[] attributeNames,
|
||||
final SessionImplementor session) {
|
||||
final BitSet mutablePropertiesIndexes = entityMetamodel.getMutablePropertiesIndexes();
|
||||
final int estimatedSize = attributeNames == null ? 0 : attributeNames.length + mutablePropertiesIndexes.cardinality();
|
||||
final List<Integer> fields = new ArrayList<>( estimatedSize );
|
||||
if ( estimatedSize == 0 ) {
|
||||
return ArrayHelper.EMPTY_INT_ARRAY;
|
||||
}
|
||||
if ( !mutablePropertiesIndexes.isEmpty() ) {
|
||||
// We have to check the state for "mutable" properties as dirty tracking isn't aware of mutable types
|
||||
final Type[] propertyTypes = entityMetamodel.getPropertyTypes();
|
||||
final boolean[] propertyCheckability = entityMetamodel.getPropertyCheckability();
|
||||
mutablePropertiesIndexes.stream().forEach( i -> {
|
||||
// This is kindly borrowed from org.hibernate.type.TypeHelper.findDirty
|
||||
final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
|
||||
( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ||
|
||||
( propertyCheckability[i]
|
||||
&& propertyTypes[i].isDirty(
|
||||
previousState[i],
|
||||
currentState[i],
|
||||
propertyColumnUpdateable[i],
|
||||
session
|
||||
) ) );
|
||||
if ( dirty ) {
|
||||
fields.add( i );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
if ( attributeNames != null ) {
|
||||
final boolean[] propertyUpdateability = entityMetamodel.getPropertyUpdateability();
|
||||
for ( String attributeName : attributeNames ) {
|
||||
final Integer index = entityMetamodel.getPropertyIndexOrNull( attributeName );
|
||||
if ( index != null && propertyUpdateability[index] && !fields.contains( index ) ) {
|
||||
fields.add( index );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayHelper.toIntArray( fields );
|
||||
}
|
||||
|
||||
protected String[] getSubclassPropertySubclassNameClosure() {
|
||||
return subclassPropertySubclassNameClosure;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
|
||||
import org.hibernate.bytecode.spi.NotInstrumentedException;
|
||||
import org.hibernate.cache.spi.access.EntityDataAccess;
|
||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
||||
import org.hibernate.cache.spi.entry.CacheEntry;
|
||||
|
@ -25,6 +24,7 @@ import org.hibernate.cache.spi.entry.CacheEntryStructure;
|
|||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.EntityEntryFactory;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.ValueInclusion;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
|
@ -840,6 +840,25 @@ public interface EntityPersister extends EntityDefinition {
|
|||
*/
|
||||
int[] resolveAttributeIndexes(String[] attributeNames);
|
||||
|
||||
/**
|
||||
* Like {@link #resolveAttributeIndexes(String[])} but also always returns mutable attributes
|
||||
*
|
||||
*
|
||||
* @param values
|
||||
* @param loadedState
|
||||
* @param attributeNames Array of names to be resolved
|
||||
*
|
||||
* @param session
|
||||
* @return A set of unique indexes of the attribute names found in the metamodel
|
||||
*/
|
||||
default int[] resolveDirtyAttributeIndexes(
|
||||
Object[] values,
|
||||
Object[] loadedState,
|
||||
String[] attributeNames,
|
||||
SessionImplementor session) {
|
||||
return resolveAttributeIndexes( attributeNames );
|
||||
}
|
||||
|
||||
boolean canUseReferenceCacheEntries();
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.tuple.entity;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -44,6 +45,7 @@ import org.hibernate.tuple.PropertyFactory;
|
|||
import org.hibernate.tuple.ValueGeneration;
|
||||
import org.hibernate.tuple.ValueGenerator;
|
||||
import org.hibernate.type.AssociationType;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -97,7 +99,7 @@ public class EntityMetamodel implements Serializable {
|
|||
|
||||
private final Map<String, Integer> propertyIndexes = new HashMap<>();
|
||||
private final boolean hasCollections;
|
||||
private final boolean hasMutableProperties;
|
||||
private final BitSet mutablePropertiesIndexes;
|
||||
private final boolean hasLazyProperties;
|
||||
private final boolean hasNonIdentifierPropertyNamedId;
|
||||
|
||||
|
@ -208,7 +210,7 @@ public class EntityMetamodel implements Serializable {
|
|||
int tempVersionProperty = NO_VERSION_INDX;
|
||||
boolean foundCascade = false;
|
||||
boolean foundCollection = false;
|
||||
boolean foundMutable = false;
|
||||
BitSet mutableIndexes = new BitSet();
|
||||
boolean foundNonIdentifierPropertyNamedId = false;
|
||||
boolean foundUpdateableNaturalIdProperty = false;
|
||||
|
||||
|
@ -318,8 +320,9 @@ public class EntityMetamodel implements Serializable {
|
|||
foundCollection = true;
|
||||
}
|
||||
|
||||
if ( propertyTypes[i].isMutable() && propertyCheckability[i] ) {
|
||||
foundMutable = true;
|
||||
// Component types are dirty tracked as well so they are not exactly mutable for the "maybeDirty" check
|
||||
if ( propertyTypes[i].isMutable() && propertyCheckability[i] && !( propertyTypes[i] instanceof ComponentType ) ) {
|
||||
mutableIndexes.set( i );
|
||||
}
|
||||
|
||||
mapPropertyToIndex(prop, i);
|
||||
|
@ -395,7 +398,7 @@ public class EntityMetamodel implements Serializable {
|
|||
}
|
||||
|
||||
hasCollections = foundCollection;
|
||||
hasMutableProperties = foundMutable;
|
||||
mutablePropertiesIndexes = mutableIndexes;
|
||||
|
||||
iter = persistentClass.getSubclassIterator();
|
||||
final Set<String> subclassEntityNamesLocal = new HashSet<>();
|
||||
|
@ -890,7 +893,11 @@ public class EntityMetamodel implements Serializable {
|
|||
}
|
||||
|
||||
public boolean hasMutableProperties() {
|
||||
return hasMutableProperties;
|
||||
return !mutablePropertiesIndexes.isEmpty();
|
||||
}
|
||||
|
||||
public BitSet getMutablePropertiesIndexes() {
|
||||
return mutablePropertiesIndexes;
|
||||
}
|
||||
|
||||
public boolean hasNonIdentifierPropertyNamedId() {
|
||||
|
|
Loading…
Reference in New Issue