big code cleanup to DefaultFlushEntityEventListener

This commit is contained in:
Gavin King 2022-09-27 09:46:25 +02:00
parent 1d12490dab
commit e930e7c68d
2 changed files with 234 additions and 245 deletions

View File

@ -11,16 +11,13 @@ import java.util.Arrays;
import org.hibernate.AssertionFailure;
import org.hibernate.CustomEntityDirtinessStrategy;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.action.internal.DelayedPostInsertIdentifier;
import org.hibernate.action.internal.EntityUpdateAction;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
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.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
@ -42,6 +39,8 @@ import org.hibernate.pretty.MessageHelper;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.Type;
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
/**
* An event that occurs for each entity instance at flush time
*
@ -73,14 +72,13 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
final Object oid = persister.getIdentifier( object, session );
if ( id == null ) {
throw new AssertionFailure( "null id in " + persister.getEntityName() + " entry (don't flush the Session after an exception occurs)" );
throw new AssertionFailure( "null id in " + persister.getEntityName()
+ " entry (don't flush the Session after an exception occurs)" );
}
if ( !persister.getIdentifierType().isEqual( id, oid, session.getFactory() ) ) {
throw new HibernateException(
"identifier of an instance of " + persister.getEntityName() + " was altered from "
+ oid + " to " + id
);
throw new HibernateException( "identifier of an instance of " + persister.getEntityName()
+ " was altered from " + oid + " to " + id );
}
}
}
@ -92,19 +90,23 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
Object[] current,
Object[] loaded,
SessionImplementor session) {
if ( entity instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor =
( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// EARLY EXIT!!!
// nothing to check - the entity is an un-initialized enhancement-as-proxy reference
return;
if ( !isUninitializedEnhanced( entity ) ) {
final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
if ( naturalIdMapping != null && entry.getStatus() != Status.READ_ONLY ) {
naturalIdMapping.verifyFlushState( entry.getId(), current, loaded, session );
}
}
}
final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
if ( naturalIdMapping != null && entry.getStatus() != Status.READ_ONLY ) {
naturalIdMapping.verifyFlushState( entry.getId(), current, loaded, session );
private static boolean isUninitializedEnhanced(Object entity) {
if ( entity instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor =
( (PersistentAttributeInterceptable) entity).$$_hibernate_getInterceptor();
// the entity is an un-initialized enhancement-as-proxy reference
return interceptor instanceof EnhancementAsProxyLazinessInterceptor;
}
else {
return false;
}
}
@ -115,36 +117,33 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
public void onFlushEntity(FlushEntityEvent event) throws HibernateException {
final Object entity = event.getEntity();
final EntityEntry entry = event.getEntityEntry();
final EventSource session = event.getSession();
final EntityPersister persister = entry.getPersister();
final Status status = entry.getStatus();
final Type[] types = persister.getPropertyTypes();
final boolean mightBeDirty = entry.requiresDirtyCheck( entity );
final Object[] values = getValues( entity, entry, mightBeDirty, session );
final Object[] values = getValues( entity, entry, mightBeDirty, event.getSession() );
event.setPropertyValues( values );
//TODO: avoid this for non-new instances where mightBeDirty==false
boolean substitute = wrapCollections( session, persister, entity, entry.getId(), types, values );
boolean substitute = wrapCollections( event, values );
if ( isUpdateNecessary( event, mightBeDirty ) ) {
substitute = scheduleUpdate( event ) || substitute;
}
if ( status != Status.DELETED ) {
if ( entry.getStatus() != Status.DELETED ) {
final EntityPersister persister = entry.getPersister();
// now update the object
// has to be outside the main if block above (because of collections)
if ( substitute ) {
persister.setPropertyValues( entity, values );
}
// Search for collections by reachability, updating their role.
// We don't want to touch collections reachable from a deleted object
if ( persister.hasCollections() ) {
new FlushVisitor( session, entity ).processEntityPropertyValues( values, types );
new FlushVisitor(event.getSession(), entity )
.processEntityPropertyValues( values, persister.getPropertyTypes() );
}
}
@ -162,39 +161,31 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
else {
final EntityPersister persister = entry.getPersister();
checkId( entity, persister, entry.getId(), session );
// grab its current state
Object[] values = persister.getValues( entity );
checkNaturalId( persister, entity, entry, values, loadedState, session );
return values;
}
}
/**
* Wrap up any new collections directly referenced by the object
* or its components.
*/
private boolean wrapCollections(
EventSource session,
EntityPersister persister,
Object entity,
Object id,
Type[] types,
Object[] values
) {
FlushEntityEvent event,
Object[] values) {
final EntityEntry entry = event.getEntityEntry();
final EntityPersister persister = entry.getPersister();
if ( persister.hasCollections() ) {
// wrap up any new collections directly referenced by the object
// or its components
// NOTE: we need to do the wrap here even if its not "dirty",
// NOTE: we need to do the wrap here even if it's not "dirty",
// because collections need wrapping but changes to _them_
// don't dirty the container. Also, for versioned data, we
// need to wrap before calling searchForDirtyCollections
WrapVisitor visitor = new WrapVisitor( entity, id ,session );
// substitutes into values by side-effect
visitor.processEntityPropertyValues( values, types );
WrapVisitor visitor = new WrapVisitor( event.getEntity(), entry.getId() , event.getSession() );
// substitutes into values by side effect
visitor.processEntityPropertyValues( values, persister.getPropertyTypes() );
return visitor.isSubstitutionRequired();
}
else {
@ -241,14 +232,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
// increment the version number (if necessary)
final Object nextVersion = getNextVersion( event );
// if it was dirtied by a collection only
int[] dirtyProperties = event.getDirtyProperties();
if ( event.isDirtyCheckPossible() && dirtyProperties == null ) {
if ( !intercepted && !event.hasDirtyCollection() ) {
throw new AssertionFailure( "dirty, but no dirty properties" );
}
dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
}
int[] dirtyProperties = getDirtyProperties( event, intercepted );
// check nullability but do not doAfterTransactionCompletion command execute
// we'll use scheduled updates for that.
@ -277,6 +261,22 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
return intercepted;
}
private static int[] getDirtyProperties(FlushEntityEvent event, boolean intercepted) {
int[] dirtyProperties = event.getDirtyProperties();
if ( event.isDirtyCheckPossible() && dirtyProperties == null ) {
if ( !intercepted && !event.hasDirtyCollection() ) {
throw new AssertionFailure( "dirty, but no dirty properties" );
}
else {
// it was dirtied by a collection only
return ArrayHelper.EMPTY_INT_ARRAY;
}
}
else {
return dirtyProperties;
}
}
private static void logScheduleUpdate(EntityEntry entry, EventSource session, Status status, EntityPersister persister) {
if ( LOG.isTraceEnabled() ) {
if ( status == Status.DELETED ) {
@ -309,63 +309,53 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
protected boolean handleInterception(FlushEntityEvent event) {
//give the Interceptor a chance to modify property values
final boolean intercepted = invokeInterceptor(
event.getSession(),
event.getEntity(),
event.getEntityEntry(),
event.getPropertyValues(),
event.getEntityEntry().getPersister()
);
final boolean intercepted = invokeInterceptor( event );
//now we might need to recalculate the dirtyProperties array
if ( intercepted && event.isDirtyCheckPossible() ) {
dirtyCheck( event );
}
return intercepted;
}
protected boolean invokeInterceptor(
SessionImplementor session,
Object entity,
EntityEntry entry,
final Object[] values,
EntityPersister persister) {
protected boolean invokeInterceptor(FlushEntityEvent event) {
final EntityEntry entry = event.getEntityEntry();
final Object entity = event.getEntity();
final Object id = entry.getId();
final Object[] values = event.getPropertyValues();
final EntityPersister persister = entry.getPersister();
final EventSource session = event.getSession();
boolean isDirty = false;
if ( entry.getStatus() != Status.DELETED ) {
if ( callbackRegistry.preUpdate( entity ) ) {
isDirty = copyState( entity, persister.getPropertyTypes(), values, session.getFactory() );
}
}
final boolean answerFromInterceptor = session.getInterceptor().onFlushDirty(
final boolean stateModified = session.getInterceptor().onFlushDirty(
entity,
entry.getId(),
id,
values,
entry.getLoadedState(),
persister.getPropertyNames(),
persister.getPropertyTypes()
);
return answerFromInterceptor || isDirty;
return stateModified || isDirty;
}
private boolean copyState(Object entity, Type[] types, Object[] state, SessionFactory sf) {
private boolean copyState(Object entity, Type[] types, Object[] state, SessionFactoryImplementor factory) {
// copy the entity state into the state array and return true if the state has changed
final Object[] newState = sf.unwrap( SessionFactoryImplementor.class )
.getRuntimeMetamodels()
final Object[] newState = factory.getRuntimeMetamodels()
.getEntityMappingType( entity.getClass() )
.getEntityPersister()
.getValues( entity );
boolean isDirty = false;
for ( int index = 0, size = newState.length; index < size; index++ ) {
if ( ( state[index] == LazyPropertyInitializer.UNFETCHED_PROPERTY &&
newState[index] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) ||
( state[index] != newState[index] && !types[index].isEqual( state[index], newState[index] ) ) ) {
if ( isDirty( types[index], state[index], newState[index] ) ) {
isDirty = true;
state[index] = newState[index];
}
@ -373,35 +363,27 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
return isDirty;
}
private static boolean isDirty(Type types, Object state, Object newState) {
return state == UNFETCHED_PROPERTY && newState != UNFETCHED_PROPERTY
|| state != newState && !types.isEqual( state, newState );
}
/**
* Convenience method to retrieve an entities next version value
*/
private Object getNextVersion(FlushEntityEvent event) throws HibernateException {
EntityEntry entry = event.getEntityEntry();
EntityPersister persister = entry.getPersister();
if ( persister.isVersioned() ) {
Object[] values = event.getPropertyValues();
if ( entry.isBeingReplicated() ) {
return Versioning.getVersion( values, persister );
}
else {
final boolean isVersionIncrementRequired = isVersionIncrementRequired(
event,
entry,
persister,
event.getDirtyProperties()
);
final Object nextVersion = isVersionIncrementRequired
final Object nextVersion = isVersionIncrementRequired( event, entry )
? Versioning.increment( entry.getVersion(), persister.getVersionMapping(), event.getSession() )
: entry.getVersion(); //use the current version
Versioning.setVersion( values, nextVersion, persister );
return nextVersion;
}
}
@ -411,41 +393,30 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
private boolean isVersionIncrementRequired(
FlushEntityEvent event,
EntityEntry entry,
EntityPersister persister,
int[] dirtyProperties
) {
return entry.getStatus() != Status.DELETED && (
dirtyProperties == null ||
Versioning.isVersionIncrementRequired(
dirtyProperties,
event.hasDirtyCollection(),
persister.getPropertyVersionability()
)
);
private boolean isVersionIncrementRequired(FlushEntityEvent event, EntityEntry entry) {
if ( entry.getStatus() == Status.DELETED ) {
return false;
}
else {
int[] dirtyProperties = event.getDirtyProperties();
return dirtyProperties == null
|| Versioning.isVersionIncrementRequired(
dirtyProperties,
event.hasDirtyCollection(),
event.getEntityEntry().getPersister().getPropertyVersionability()
);
}
}
/**
* Performs all necessary checking to determine if an entity needs an SQL update
* to synchronize its state to the database. Modifies the event by side-effect!
* to synchronize its state to the database. Modifies the event by side effect!
* Note: this method is quite slow, avoid calling if possible!
*/
protected final boolean isUpdateNecessary(FlushEntityEvent event) throws HibernateException {
if ( !event.isDirtyCheckPossible() ) {
return true;
}
else {
int[] dirtyProperties = event.getDirtyProperties();
if ( dirtyProperties != null && dirtyProperties.length != 0 ) {
return true; //TODO: suck into event class
}
else {
return hasDirtyCollections( event );
}
}
return !event.isDirtyCheckPossible()
|| event.hasDirtyProperties()
|| hasDirtyCollections(event);
}
private boolean hasDirtyCollections(FlushEntityEvent event) {
@ -476,130 +447,147 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
* Perform a dirty check, and attach the results to the event
*/
protected void dirtyCheck(final FlushEntityEvent event) throws HibernateException {
int[] dirtyProperties = getDirtyProperties( event );
event.setDatabaseSnapshot( null );
if ( dirtyProperties == null ) {
// do the dirty check the hard way
dirtyProperties = performDirtyCheck( event );
}
else {
// the Interceptor, SelfDirtinessTracker, or CustomEntityDirtinessStrategy
// already handled the dirty check for us
event.setDirtyProperties( dirtyProperties );
event.setDirtyCheckHandledByInterceptor( true );
event.setDirtyCheckPossible( true );
}
logDirtyProperties( event.getEntityEntry(), dirtyProperties );
}
final Object entity = event.getEntity();
final Object[] values = event.getPropertyValues();
private static int[] performDirtyCheck(FlushEntityEvent event) {
final SessionImplementor session = event.getSession();
boolean dirtyCheckPossible;
int[] dirtyProperties = null;
try {
session.getEventListenerManager().dirtyCalculationStart();
// object loaded by update()
final EntityEntry entry = event.getEntityEntry();
final EntityPersister persister = entry.getPersister();
final Object[] values = event.getPropertyValues();
final Object[] loadedState = entry.getLoadedState();
final Object entity = event.getEntity();
if ( loadedState != null ) {
// dirty check against the usual snapshot of the entity
dirtyProperties = persister.findDirty( values, loadedState, entity, session );
dirtyCheckPossible = true;
}
else if ( entry.getStatus() == Status.DELETED && !entry.isModifiableEntity() ) {
// A non-modifiable (e.g., read-only or immutable) entity needs to be have
// references to transient entities set to null before being deleted. No other
// fields should be updated.
if ( values != entry.getDeletedState() ) {
throw new IllegalStateException(
"Entity has status Status.DELETED but values != entry.getDeletedState"
);
}
// Even if loadedState == null, we can dirty-check by comparing currentState and
// entry.getDeletedState() because the only fields to be updated are those that
// refer to transient entities that are being set to null.
// - currentState contains the entity's current property values.
// - entry.getDeletedState() contains the entity's current property values with
// references to transient entities set to null.
// - dirtyProperties will only contain properties that refer to transient entities
final Object[] currentState = persister.getValues( event.getEntity() );
dirtyProperties = persister.findDirty( entry.getDeletedState(), currentState, entity, session );
dirtyCheckPossible = true;
}
else {
// dirty check against the database snapshot, if possible/necessary
final Object[] databaseSnapshot = getDatabaseSnapshot( persister, entry.getId(), session );
if ( databaseSnapshot != null ) {
dirtyProperties = persister.findModified( databaseSnapshot, values, entity, session );
dirtyCheckPossible = true;
event.setDatabaseSnapshot( databaseSnapshot );
}
else {
dirtyCheckPossible = false;
}
}
event.setDirtyProperties( dirtyProperties );
event.setDirtyCheckHandledByInterceptor( false );
event.setDirtyCheckPossible( dirtyCheckPossible );
}
finally {
session.getEventListenerManager().dirtyCalculationEnd( dirtyProperties != null );
}
return dirtyProperties;
}
/**
* Attempt to get the dirty properties from either the Interceptor,
* the bytecode enhancement, or a custom dirtiness strategy.
*/
private static int[] getDirtyProperties(FlushEntityEvent event) {
int[] dirtyProperties = getDirtyPropertiesFromInterceptor( event );
if ( dirtyProperties != null ) {
return dirtyProperties;
}
else {
final Object entity = event.getEntity();
return entity instanceof SelfDirtinessTracker
? getDirtyPropertiesFromSelfDirtinessTracker( (SelfDirtinessTracker) entity, event )
: getDirtyPropertiesFromCustomEntityDirtinessStrategy( event );
}
}
private static int[] getDirtyPropertiesFromInterceptor(FlushEntityEvent event) {
final EntityEntry entry = event.getEntityEntry();
final EntityPersister persister = entry.getPersister();
final Object id = entry.getId();
final Object[] loadedState = entry.getLoadedState();
int[] dirtyProperties = session.getInterceptor().findDirty(
entity,
id,
values,
loadedState,
return event.getSession().getInterceptor().findDirty(
event.getEntity(),
entry.getId(),
event.getPropertyValues(),
entry.getLoadedState(),
persister.getPropertyNames(),
persister.getPropertyTypes()
);
if ( dirtyProperties == null ) {
if ( entity instanceof SelfDirtinessTracker ) {
boolean hasDirtyAttributes = ((SelfDirtinessTracker) entity).$$_hibernate_hasDirtyAttributes();
if ( hasDirtyAttributes || persister.hasMutableProperties() ) {
String[] dirtyAttributes = ((SelfDirtinessTracker) entity).$$_hibernate_getDirtyAttributes();
dirtyProperties = persister.resolveDirtyAttributeIndexes(
values,
loadedState,
dirtyAttributes,
session
);
}
else {
dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
}
}
else {
// see if the custom dirtiness strategy can tell us...
class DirtyCheckContextImpl implements CustomEntityDirtinessStrategy.DirtyCheckContext {
private int[] found;
@Override
public void doDirtyChecking(CustomEntityDirtinessStrategy.AttributeChecker attributeChecker) {
found = new DirtyCheckAttributeInfoImpl( event ).visitAttributes( attributeChecker );
if ( found.length == 0 ) {
found = null;
}
}
}
DirtyCheckContextImpl context = new DirtyCheckContextImpl();
session.getFactory().getCustomEntityDirtinessStrategy().findDirty(
entity,
persister,
session,
context
);
dirtyProperties = context.found;
}
}
event.setDatabaseSnapshot( null );
final boolean interceptorHandledDirtyCheck;
//The dirty check is considered possible unless proven otherwise (see below)
boolean dirtyCheckPossible = true;
if ( dirtyProperties == null ) {
// Interceptor returned null, so do the dirty check ourself, if possible
try {
session.getEventListenerManager().dirtyCalculationStart();
interceptorHandledDirtyCheck = false;
// object loaded by update()
dirtyCheckPossible = loadedState != null;
if ( dirtyCheckPossible ) {
// dirty check against the usual snapshot of the entity
dirtyProperties = persister.findDirty( values, loadedState, entity, session );
}
else if ( entry.getStatus() == Status.DELETED && !entry.isModifiableEntity() ) {
// A non-modifiable (e.g., read-only or immutable) entity needs to be have
// references to transient entities set to null before being deleted. No other
// fields should be updated.
if ( values != entry.getDeletedState() ) {
throw new IllegalStateException(
"Entity has status Status.DELETED but values != entry.getDeletedState"
);
}
// Even if loadedState == null, we can dirty-check by comparing currentState and
// entry.getDeletedState() because the only fields to be updated are those that
// refer to transient entities that are being set to null.
// - currentState contains the entity's current property values.
// - entry.getDeletedState() contains the entity's current property values with
// references to transient entities set to null.
// - dirtyProperties will only contain properties that refer to transient entities
final Object[] currentState = persister.getValues( event.getEntity() );
dirtyProperties = persister.findDirty( entry.getDeletedState(), currentState, entity, session );
dirtyCheckPossible = true;
}
else {
// dirty check against the database snapshot, if possible/necessary
final Object[] databaseSnapshot = getDatabaseSnapshot( persister, id, session );
if ( databaseSnapshot != null ) {
dirtyProperties = persister.findModified( databaseSnapshot, values, entity, session );
dirtyCheckPossible = true;
event.setDatabaseSnapshot( databaseSnapshot );
}
}
}
finally {
session.getEventListenerManager().dirtyCalculationEnd( dirtyProperties != null );
}
}
else {
// either the Interceptor, the bytecode enhancement or a custom dirtiness strategy handled the dirty checking
interceptorHandledDirtyCheck = true;
}
logDirtyProperties( id, dirtyProperties, persister );
event.setDirtyProperties( dirtyProperties );
event.setDirtyCheckHandledByInterceptor( interceptorHandledDirtyCheck );
event.setDirtyCheckPossible( dirtyCheckPossible );
}
private class DirtyCheckAttributeInfoImpl implements CustomEntityDirtinessStrategy.AttributeInformation {
private static int[] getDirtyPropertiesFromCustomEntityDirtinessStrategy(FlushEntityEvent event) {
// see if the custom dirtiness strategy can tell us...
class DirtyCheckContextImpl implements CustomEntityDirtinessStrategy.DirtyCheckContext {
private int[] found;
@Override
public void doDirtyChecking(CustomEntityDirtinessStrategy.AttributeChecker attributeChecker) {
found = new DirtyCheckAttributeInfoImpl( event ).visitAttributes( attributeChecker );
if ( found.length == 0 ) {
found = null;
}
}
}
EventSource session = event.getSession();
DirtyCheckContextImpl context = new DirtyCheckContextImpl();
session.getFactory().getCustomEntityDirtinessStrategy()
.findDirty( event.getEntity(), event.getEntityEntry().getPersister(), session, context );
return context.found;
}
private static int[] getDirtyPropertiesFromSelfDirtinessTracker(SelfDirtinessTracker tracker, FlushEntityEvent event) {
final EntityEntry entry = event.getEntityEntry();
final EntityPersister persister = entry.getPersister();
if ( tracker.$$_hibernate_hasDirtyAttributes() || persister.hasMutableProperties() ) {
return persister.resolveDirtyAttributeIndexes(
event.getPropertyValues(),
entry.getLoadedState(),
tracker.$$_hibernate_getDirtyAttributes(),
event.getSession()
);
}
else {
return ArrayHelper.EMPTY_INT_ARRAY;
}
}
private static class DirtyCheckAttributeInfoImpl implements CustomEntityDirtinessStrategy.AttributeInformation {
private final FlushEntityEvent event;
private final EntityPersister persister;
private final int numberOfAttributes;
@ -648,11 +636,9 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
public int[] visitAttributes(CustomEntityDirtinessStrategy.AttributeChecker attributeChecker) {
databaseSnapshot = null;
index = 0;
final int[] indexes = new int[numberOfAttributes];
int count = 0;
for (; index < numberOfAttributes; index++ ) {
for ( index = 0; index < numberOfAttributes; index++ ) {
if ( attributeChecker.isDirty( this ) ) {
indexes[count++] = index;
}
@ -661,8 +647,9 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
}
private void logDirtyProperties(Object id, int[] dirtyProperties, EntityPersister persister) {
private void logDirtyProperties(EntityEntry entry, int[] dirtyProperties) {
if ( dirtyProperties != null && dirtyProperties.length > 0 && LOG.isTraceEnabled() ) {
final EntityPersister persister = entry.getPersister();
final String[] allPropertyNames = persister.getPropertyNames();
final String[] dirtyPropertyNames = new String[dirtyProperties.length];
for ( int i = 0; i < dirtyProperties.length; i++ ) {
@ -670,13 +657,13 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
}
LOG.tracev(
"Found dirty properties [{0}] : {1}",
MessageHelper.infoString( persister.getEntityName(), id ),
MessageHelper.infoString( persister.getEntityName(), entry.getId() ),
Arrays.toString( dirtyPropertyNames )
);
}
}
private Object[] getDatabaseSnapshot(
private static Object[] getDatabaseSnapshot(
EntityPersister persister,
Object id,
SessionImplementor session) {
@ -694,7 +681,6 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
return snapshot;
}
// TODO: optimize away this lookup for entities w/o unsaved-value="undefined"
final EntityKey entityKey = session.generateEntityKey( id, persister );
return persistenceContext.getCachedDatabaseSnapshot( entityKey );
return persistenceContext.getCachedDatabaseSnapshot( session.generateEntityKey( id, persister ) );
}
}

View File

@ -57,6 +57,9 @@ public class FlushEntityEvent extends AbstractEvent {
public void setDirtyProperties(int[] dirtyProperties) {
this.dirtyProperties = dirtyProperties;
}
public boolean hasDirtyProperties() {
return dirtyProperties != null && dirtyProperties.length != 0;
}
public boolean hasDirtyCollection() {
return hasDirtyCollection;
}