split up TypeHelper

This commit is contained in:
Gavin King 2022-01-29 12:51:57 +01:00
parent 6c3b74d39e
commit 38f4c70e7b
8 changed files with 296 additions and 292 deletions

View File

@ -0,0 +1,98 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.spi.entry;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.type.Type;
import java.io.Serializable;
/**
* Operations for assembly and disassembly of an array of property values.
*/
class CacheEntryHelper {
/**
* Apply the {@link Type#disassemble} operation across a series of values.
*
* @param row The values
* @param types The value types
* @param nonCacheable An array indicating which values to include in the disassembled state
* @param session The originating session
* @param owner The entity "owning" the values
*
* @return The disassembled state
*/
public static Serializable[] disassemble(
final Object[] row,
final Type[] types,
final boolean[] nonCacheable,
final SharedSessionContractImplementor session,
final Object owner) {
Serializable[] disassembled = new Serializable[row.length];
for ( int i = 0; i < row.length; i++ ) {
if ( nonCacheable!=null && nonCacheable[i] ) {
disassembled[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
else if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
| row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
disassembled[i] = (Serializable) row[i];
}
else {
disassembled[i] = types[i].disassemble( row[i], session, owner );
}
}
return disassembled;
}
/**
* Apply the {@link Type#assemble} operation across a series of values.
*
* @param row The values
* @param types The value types
* @param session The originating session
* @param owner The entity "owning" the values
* @return The assembled state
*/
public static Object[] assemble(
final Serializable[] row,
final Type[] types,
final SharedSessionContractImplementor session,
final Object owner) {
Object[] assembled = new Object[row.length];
for ( int i = 0; i < types.length; i++ ) {
if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
assembled[i] = row[i];
}
else {
assembled[i] = types[i].assemble( row[i], session, owner );
}
}
return assembled;
}
// public static Object[] assemble(
// final Object[] row,
// final Type[] types,
// final SharedSessionContractImplementor session,
// final Object owner) {
// Object[] assembled = new Object[row.length];
// for ( int i = 0; i < types.length; i++ ) {
// if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
// assembled[i] = row[i];
// }
// else {
// assembled[i] = types[i].assemble( (Serializable) row[i], session, owner );
// }
// }
// return assembled;
// }
}

View File

@ -11,16 +11,12 @@ import java.io.Serializable;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.TypeHelper;
/**
* Standard representation of entity cached data using the "disassembled state".
@ -51,7 +47,7 @@ public class StandardCacheEntryImpl implements CacheEntry {
final SharedSessionContractImplementor session,
final Object owner) throws HibernateException {
// disassembled state gets put in a new array (we write to cache by value!)
this.disassembledState = TypeHelper.disassemble(
this.disassembledState = CacheEntryHelper.disassemble(
state,
persister.getPropertyTypes(),
persister.isLazyPropertiesCacheable() ? null : persister.getPropertyLaziness(),
@ -133,7 +129,7 @@ public class StandardCacheEntryImpl implements CacheEntry {
}
//assembled state gets put in a new array (we read from cache by value!)
final Object[] state = TypeHelper.assemble(
final Object[] state = CacheEntryHelper.assemble(
disassembledState,
persister.getPropertyTypes(),
session, instance

View File

@ -262,7 +262,6 @@ import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
@ -4326,8 +4325,7 @@ public abstract class AbstractEntityPersister
return true;
}
// we still need to verify collection fields to be eagerly loaded by 'join'
final NonIdentifierAttribute[] attributes = entityMetamodel.getProperties();
for ( NonIdentifierAttribute attribute : attributes ) {
for ( NonIdentifierAttribute attribute : entityMetamodel.getProperties() ) {
if ( attribute instanceof EntityBasedAssociationAttribute ) {
final AssociationType associationType = ( (EntityBasedAssociationAttribute) attribute ).getType();
if ( associationType instanceof CollectionType ) {
@ -4408,7 +4406,7 @@ public abstract class AbstractEntityPersister
*/
public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SharedSessionContractImplementor session)
throws HibernateException {
int[] props = TypeHelper.findDirty(
int[] props = DirtyHelper.findDirty(
entityMetamodel.getProperties(),
currentState,
previousState,
@ -4437,7 +4435,7 @@ public abstract class AbstractEntityPersister
*/
public int[] findModified(Object[] old, Object[] current, Object entity, SharedSessionContractImplementor session)
throws HibernateException {
int[] props = TypeHelper.findModified(
int[] props = DirtyHelper.findModified(
entityMetamodel.getProperties(),
current,
old,

View File

@ -0,0 +1,131 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.persister.entity;
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;
/**
* Operations for searching an array of property values for modified elements.
*/
class DirtyHelper {
/**
* Determine if any of the given field values are dirty, returning an array containing
* indices of the dirty fields.
* <p/>
* If it is determined that no fields are dirty, null is returned.
*
* @param properties The property definitions
* @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(
final NonIdentifierAttribute[] properties,
final Object[] currentState,
final Object[] previousState,
final boolean[][] includeColumns,
final SharedSessionContractImplementor session) {
int[] results = null;
int count = 0;
int span = properties.length;
for ( int i = 0; i < span; i++ ) {
if ( isDirty( properties, 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(
NonIdentifierAttribute[] properties,
Object[] currentState,
Object[] previousState,
boolean[][] includeColumns,
SharedSessionContractImplementor session, int i) {
if ( currentState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
return false;
}
else if ( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
return true;
}
else {
return properties[i].isDirtyCheckable()
&& properties[i].getType().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.
* <p/>
* If it is determined that no fields are dirty, null is returned.
*
* @param properties The property definitions
* @param currentState The current state of the entity
* @param previousState The baseline state of the entity
* @param includeColumns Columns to be included in the mod checking, per property
* @param includeProperties Array of property indices that identify which properties participate in check
* @param session The session from which the dirty check request originated.
*
* @return Array containing indices of the modified properties, or null if no properties considered modified.
**/
public static int[] findModified(
final NonIdentifierAttribute[] properties,
final Object[] currentState,
final Object[] previousState,
final boolean[][] includeColumns,
final boolean[] includeProperties,
final SharedSessionContractImplementor session) {
int[] results = null;
int count = 0;
int span = properties.length;
for ( int i = 0; i < span; i++ ) {
if ( isModified( properties, currentState, previousState, includeColumns, includeProperties, session, i ) ) {
if ( results == null ) {
results = new int[ span ];
}
results[ count++ ] = i;
}
}
if ( count == 0 ) {
return null;
}
else {
int[] trimmed = new int[ count ];
System.arraycopy( results, 0, trimmed, 0, count );
return trimmed;
}
}
private static boolean isModified(
NonIdentifierAttribute[] properties,
Object[] currentState,
Object[] previousState,
boolean[][] includeColumns,
boolean[] includeProperties,
SharedSessionContractImplementor session,
int i) {
return currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
&& includeProperties[i]
&& properties[i].isDirtyCheckable()
&& properties[i].getType().isModified( previousState[i], currentState[i], includeColumns[i], session );
}
}

View File

@ -136,7 +136,6 @@ import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
import org.hibernate.query.sqm.sql.internal.SqmMapEntryResult;
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.query.sqm.sql.internal.TypeHelper;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.SqmTypedNode;
@ -5106,7 +5105,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
final Expression resultExpression = (Expression) whenFragment.getResult().accept( this );
inferrableTypeAccessStack.pop();
resolved = (MappingModelExpressible<?>) TypeHelper.highestPrecedence( resolved, resultExpression.getExpressionType() );
resolved = (MappingModelExpressible<?>) highestPrecedence( resolved, resultExpression.getExpressionType() );
whenFragments.add(
new CaseSimpleExpression.WhenFragment(
@ -5123,7 +5122,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
otherwise = (Expression) expression.getOtherwise().accept( this );
inferrableTypeAccessStack.pop();
resolved = (MappingModelExpressible<?>) TypeHelper.highestPrecedence( resolved, otherwise.getExpressionType() );
resolved = (MappingModelExpressible<?>) highestPrecedence( resolved, otherwise.getExpressionType() );
}
return new CaseSimpleExpression(
@ -5151,7 +5150,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
final Expression resultExpression = (Expression) whenFragment.getResult().accept( this );
inferrableTypeAccessStack.pop();
resolved = (MappingModelExpressible<?>) TypeHelper.highestPrecedence( resolved, resultExpression.getExpressionType() );
resolved = (MappingModelExpressible<?>) highestPrecedence( resolved, resultExpression.getExpressionType() );
whenFragments.add( new CaseSearchedExpression.WhenFragment( whenPredicate, resultExpression ) );
}
@ -5163,7 +5162,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
otherwise = (Expression) expression.getOtherwise().accept( this );
inferrableTypeAccessStack.pop();
resolved = (MappingModelExpressible<?>) TypeHelper.highestPrecedence( resolved, otherwise.getExpressionType() );
resolved = (MappingModelExpressible<?>) highestPrecedence( resolved, otherwise.getExpressionType() );
}
return new CaseSearchedExpression( resolved, whenFragments, otherwise );
@ -6265,4 +6264,28 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
return result;
}
private static JdbcMappingContainer highestPrecedence(JdbcMappingContainer type1, JdbcMappingContainer type2) {
if ( type1 == null ) {
return type2;
}
if ( type2 == null ) {
return type1;
}
if ( type1 instanceof ModelPart ) {
return type1;
}
if ( type2 instanceof ModelPart ) {
return type2;
}
// todo (6.0) : we probably want a precedence based on generic resolutions such as those based on Serializable
// todo (6.0) : anything else to consider?
return type1;
}
}

View File

@ -1,39 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.sql.internal;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.metamodel.mapping.ModelPart;
/**
* @author Steve Ebersole
*/
public class TypeHelper {
public static JdbcMappingContainer highestPrecedence(JdbcMappingContainer type1, JdbcMappingContainer type2) {
if ( type1 == null ) {
return type2;
}
if ( type2 == null ) {
return type1;
}
if ( type1 instanceof ModelPart ) {
return type1;
}
if ( type2 instanceof ModelPart ) {
return type2;
}
// todo (6.0) : we probably want a precedence based on generic resolutions such as those based on Serializable
// todo (6.0) : anything else to consider?
return type1;
}
}

View File

@ -14,6 +14,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.entry.CacheEntry;
@ -36,9 +37,11 @@ import org.hibernate.loader.entity.CacheEntityLoaderHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.proxy.map.MapProxy;
@ -56,7 +59,6 @@ import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.AssociationType;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
@ -189,6 +191,32 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
);
}
private static void deepCopy(
ManagedMappingType containerDescriptor,
Object[] source,
Object[] target,
EntityPersister concreteDescriptor) {
containerDescriptor.visitStateArrayContributors(
contributor -> {
if ( contributor.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable() ) {
final int position = contributor.getStateArrayPosition();
Object result;
if ( source[position] == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| source[position] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
result = source[position];
}
else {
result = contributor.getAttributeMetadataAccess()
.resolveAttributeMetadata(null)
.getMutabilityPlan()
.deepCopy( source[position] );
}
target[position] = result;
}
}
);
}
@Override
public ModelPart getInitializedPart() {
return referencedModelPart;
@ -832,11 +860,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
else {
//take a snapshot
TypeHelper.deepCopy(
deepCopy(
concreteDescriptor,
resolvedEntityState,
resolvedEntityState,
attributeMapping -> attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable()
concreteDescriptor
);
persistenceContext.setEntryStatus( entityEntry, Status.MANAGED );
}

View File

@ -6,27 +6,19 @@
*/
package org.hibernate.type;
import java.io.Serializable;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import org.hibernate.Internal;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.tuple.NonIdentifierAttribute;
/**
* Collection of convenience methods relating to operations across arrays of types...
* Certain operations for working with arrays of property values.
*
* @author Steve Ebersole
*
* @deprecated with no real replacement. this was always intended as an internal class
*/
@Deprecated(since = "6.0")
@Internal
public class TypeHelper {
/**
* Disallow instantiation
@ -34,49 +26,6 @@ public class TypeHelper {
private TypeHelper() {
}
public static final BiFunction<StateArrayContributorMapping,Object,Object> DEEP_COPY_VALUE_PRODUCER = (navigable, sourceValue) -> {
if ( sourceValue == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| sourceValue == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
return sourceValue;
}
else {
return navigable.getAttributeMetadataAccess()
.resolveAttributeMetadata( null )
.getMutabilityPlan()
.deepCopy( sourceValue );
}
};
public static void deepCopy(
ManagedMappingType containerDescriptor,
Object[] source,
Object[] target,
Predicate<StateArrayContributorMapping> copyConditions) {
deepCopy(
containerDescriptor,
source,
target,
copyConditions,
DEEP_COPY_VALUE_PRODUCER
);
}
public static void deepCopy(
ManagedMappingType containerDescriptor,
Object[] source,
Object[] target,
Predicate<StateArrayContributorMapping> copyConditions,
BiFunction<StateArrayContributorMapping, Object, Object> targetValueProducer) {
containerDescriptor.visitStateArrayContributors(
contributor -> {
if ( copyConditions.test( contributor ) ) {
final int position = contributor.getStateArrayPosition();
target[position] = targetValueProducer.apply( contributor, source[position] );
}
}
);
}
/**
* Deep copy a series of values from one array to another...
*
@ -106,81 +55,6 @@ public class TypeHelper {
}
}
/**
* Apply the {@link Type#assemble} operation across a series of values.
*
* @param row The values
* @param types The value types
* @param session The originating session
* @param owner The entity "owning" the values
* @return The assembled state
*/
public static Object[] assemble(
final Serializable[] row,
final Type[] types,
final SharedSessionContractImplementor session,
final Object owner) {
Object[] assembled = new Object[row.length];
for ( int i = 0; i < types.length; i++ ) {
if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
assembled[i] = row[i];
}
else {
assembled[i] = types[i].assemble( row[i], session, owner );
}
}
return assembled;
}
public static Object[] assemble(
final Object[] row,
final Type[] types,
final SharedSessionContractImplementor session,
final Object owner) {
Object[] assembled = new Object[row.length];
for ( int i = 0; i < types.length; i++ ) {
if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
assembled[i] = row[i];
}
else {
assembled[i] = types[i].assemble( (Serializable) row[i], session, owner );
}
}
return assembled;
}
/**
* Apply the {@link Type#disassemble} operation across a series of values.
*
* @param row The values
* @param types The value types
* @param nonCacheable An array indicating which values to include in the disassembled state
* @param session The originating session
* @param owner The entity "owning" the values
*
* @return The disassembled state
*/
public static Serializable[] disassemble(
final Object[] row,
final Type[] types,
final boolean[] nonCacheable,
final SharedSessionContractImplementor session,
final Object owner) {
Serializable[] disassembled = new Serializable[row.length];
for ( int i = 0; i < row.length; i++ ) {
if ( nonCacheable!=null && nonCacheable[i] ) {
disassembled[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
}
else if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
disassembled[i] = (Serializable) row[i];
}
else {
disassembled[i] = types[i].disassemble( row[i], session, owner );
}
}
return disassembled;
}
/**
* Apply the {@link Type#replace} operation across a series of values.
*
@ -302,109 +176,4 @@ public class TypeHelper {
return copied;
}
/**
* Determine if any of the given field values are dirty, returning an array containing
* indices of the dirty fields.
* <p/>
* If it is determined that no fields are dirty, null is returned.
*
* @param properties The property definitions
* @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(
final NonIdentifierAttribute[] properties,
final Object[] currentState,
final Object[] previousState,
final boolean[][] includeColumns,
final SharedSessionContractImplementor session) {
int[] results = null;
int count = 0;
int span = properties.length;
for ( int i = 0; i < span; i++ ) {
final boolean dirty;
if ( currentState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
dirty = false;
}
else if ( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
dirty = true;
}
else if ( properties[i].isDirtyCheckable()
&& properties[i].getType().isDirty( previousState[i], currentState[i], includeColumns[i], session ) ) {
dirty = true;
}
else {
dirty = false;
}
if ( dirty ) {
if ( results == null ) {
results = new int[span];
}
results[count++] = i;
}
}
if ( count == 0 ) {
return null;
}
else {
return ArrayHelper.trim(results, count);
}
}
/**
* Determine if any of the given field values are modified, returning an array containing
* indices of the modified fields.
* <p/>
* If it is determined that no fields are dirty, null is returned.
*
* @param properties The property definitions
* @param currentState The current state of the entity
* @param previousState The baseline state of the entity
* @param includeColumns Columns to be included in the mod checking, per property
* @param includeProperties Array of property indices that identify which properties participate in check
* @param session The session from which the dirty check request originated.
*
* @return Array containing indices of the modified properties, or null if no properties considered modified.
**/
public static int[] findModified(
final NonIdentifierAttribute[] properties,
final Object[] currentState,
final Object[] previousState,
final boolean[][] includeColumns,
final boolean[] includeProperties,
final SharedSessionContractImplementor session) {
int[] results = null;
int count = 0;
int span = properties.length;
for ( int i = 0; i < span; i++ ) {
final boolean modified = currentState[ i ] != LazyPropertyInitializer.UNFETCHED_PROPERTY
&& includeProperties[ i ]
&& properties[ i ].isDirtyCheckable()
&& properties[ i ].getType().isModified( previousState[ i ], currentState[ i ], includeColumns[ i ], session );
if ( modified ) {
if ( results == null ) {
results = new int[ span ];
}
results[ count++ ] = i;
}
}
if ( count == 0 ) {
return null;
}
else {
int[] trimmed = new int[ count ];
System.arraycopy( results, 0, trimmed, 0, count );
return trimmed;
}
}
}