diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index d8019ed080..66a74b1281 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3628,7 +3628,7 @@ public abstract class AbstractEntityPersister public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SharedSessionContractImplementor session) throws HibernateException { int[] props = DirtyHelper.findDirty( - entityMetamodel.getProperties(), + entityMetamodel.getDirtyCheckablePropertyTypes(), currentState, previousState, propertyColumnUpdateable, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java index 289677fcf2..124a161851 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java @@ -11,6 +11,14 @@ import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.tuple.NonIdentifierAttribute; +import org.hibernate.type.AnyType; +import org.hibernate.type.BasicType; +import org.hibernate.type.CollectionType; +import org.hibernate.type.ComponentType; +import org.hibernate.type.ManyToOneType; +import org.hibernate.type.Type; + +import org.checkerframework.checker.nullness.qual.Nullable; /** * Operations for searching an array of property values for modified elements. @@ -72,6 +80,62 @@ class DirtyHelper { } } + /** + * Determine if any of the given field values are dirty, returning an array containing + * indices of the dirty fields. + *

+ * If it is determined that no fields are dirty, null is returned. + * + * @param propertyTypes The property types that are dirty checkable. null entry for non-dirty checkable properties + * @param currentState The current state of the entity + * @param previousState The baseline state of the entity + * @param includeColumns Columns to be included in the dirty checking, per property + * @param session The session from which the dirty check request originated. + * + * @return Array containing indices of the dirty properties, or null if no properties considered dirty. + */ + public static int[] findDirty( + @Nullable Type[] propertyTypes, + final Object[] currentState, + final Object[] previousState, + final boolean[][] includeColumns, + final SharedSessionContractImplementor session) { + int[] results = null; + int count = 0; + int span = propertyTypes.length; + + for ( int i = 0; i < span; i++ ) { + + if ( isDirty( propertyTypes, currentState, previousState, includeColumns, session, i ) ) { + if ( results == null ) { + results = new int[span]; + } + results[count++] = i; + } + } + + return count == 0 ? null : ArrayHelper.trim( results, count ); + } + + private static boolean isDirty( + @Nullable Type[] propertyTypes, + Object[] currentState, + Object[] previousState, + boolean[][] includeColumns, + SharedSessionContractImplementor session, int i) { + final Type propertyType; + if ( currentState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY + || ( propertyType = propertyTypes[i] ) == null ) { + return false; + } + else if ( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) { + return true; + } + else { + return propertyType.isDirty( previousState[i], currentState[i], includeColumns[i], session ); + } + } + /** * Determine if any of the given field values are modified, returning an array containing * indices of the modified fields. diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 26035efea4..3c8b4610bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -54,8 +54,11 @@ import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.ManyToOneType; +import org.hibernate.type.OneToOneType; import org.hibernate.type.Type; +import org.checkerframework.checker.nullness.qual.Nullable; + import static java.util.Collections.singleton; import static org.hibernate.internal.CoreLogging.messageLogger; import static org.hibernate.internal.util.ReflectHelper.isAbstractClass; @@ -93,6 +96,7 @@ public class EntityMetamodel implements Serializable { // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final String[] propertyNames; private final Type[] propertyTypes; + private final @Nullable Type[] dirtyCheckablePropertyTypes; private final boolean[] propertyLaziness; private final boolean[] propertyUpdateability; private final boolean[] nonlazyPropertyUpdateability; @@ -206,6 +210,7 @@ public class EntityMetamodel implements Serializable { // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ propertyNames = new String[propertySpan]; propertyTypes = new Type[propertySpan]; + dirtyCheckablePropertyTypes = new Type[propertySpan]; propertyUpdateability = new boolean[propertySpan]; propertyInsertability = new boolean[propertySpan]; nonlazyPropertyUpdateability = new boolean[propertySpan]; @@ -295,6 +300,9 @@ public class EntityMetamodel implements Serializable { propertyNames[i] = attribute.getName(); final Type propertyType = attribute.getType(); propertyTypes[i] = propertyType; + if ( attribute.isDirtyCheckable() && !( propertyType instanceof OneToOneType ) ) { + dirtyCheckablePropertyTypes[i] = propertyType; + } propertyNullability[i] = attribute.isNullable(); propertyUpdateability[i] = attribute.isUpdateable(); propertyInsertability[i] = attribute.isInsertable(); @@ -786,6 +794,10 @@ public class EntityMetamodel implements Serializable { return propertyTypes; } + public @Nullable Type[] getDirtyCheckablePropertyTypes() { + return dirtyCheckablePropertyTypes; + } + public boolean[] getPropertyLaziness() { return propertyLaziness; }