DefaultFlushEntityEventListenerpublic class DefaultFlushEntityEventListener extends Object implements org.hibernate.event.FlushEntityEventListenerAn event that occurs for each entity instance at flush time |
Fields Summary |
---|
private static final Log | log |
Methods Summary |
---|
public void | checkId(java.lang.Object object, org.hibernate.persister.entity.EntityPersister persister, java.io.Serializable id, org.hibernate.EntityMode entityMode)make sure user didn't mangle the id
if ( id != null && id instanceof DelayedPostInsertIdentifier ) {
// this is a situation where the entity id is assigned by a post-insert generator
// and was saved outside the transaction forcing it to be delayed
return;
}
if ( persister.canExtractIdOutOfEntity() ) {
Serializable oid = persister.getIdentifier( object, entityMode );
if (id==null) {
throw new AssertionFailure("null id in " + persister.getEntityName() + " entry (don't flush the Session after an exception occurs)");
}
if ( !persister.getIdentifierType().isEqual(id, oid, entityMode) ) {
throw new HibernateException(
"identifier of an instance of " +
persister.getEntityName() +
" was altered from " + id +
" to " + oid
);
}
}
| private void | checkNaturalId(org.hibernate.persister.entity.EntityPersister persister, java.io.Serializable identifier, java.lang.Object[] current, java.lang.Object[] loaded, org.hibernate.EntityMode entityMode, org.hibernate.engine.SessionImplementor session)
if ( persister.hasNaturalIdentifier() ) {
Object[] snapshot = null;
Type[] types = persister.getPropertyTypes();
int[] props = persister.getNaturalIdentifierProperties();
boolean[] updateable = persister.getPropertyUpdateability();
for ( int i=0; i<props.length; i++ ) {
int prop = props[i];
if ( !updateable[prop] ) {
Object loadedVal;
if ( loaded == null ) {
if ( snapshot == null) {
snapshot = session.getPersistenceContext().getNaturalIdSnapshot( identifier, persister );
}
loadedVal = snapshot[i];
} else {
loadedVal = loaded[prop];
}
if ( !types[prop].isEqual( current[prop], loadedVal, entityMode ) ) {
throw new HibernateException(
"immutable natural identifier of an instance of " +
persister.getEntityName() +
" was altered"
);
}
}
}
}
| protected void | dirtyCheck(org.hibernate.event.FlushEntityEvent event)Perform a dirty check, and attach the results to the event
final Object entity = event.getEntity();
final Object[] values = event.getPropertyValues();
final SessionImplementor session = event.getSession();
final EntityEntry entry = event.getEntityEntry();
final EntityPersister persister = entry.getPersister();
final Serializable id = entry.getId();
final Object[] loadedState = entry.getLoadedState();
int[] dirtyProperties = session.getInterceptor().findDirty(
entity,
id,
values,
loadedState,
persister.getPropertyNames(),
persister.getPropertyTypes()
);
event.setDatabaseSnapshot(null);
final boolean interceptorHandledDirtyCheck;
boolean cannotDirtyCheck;
if ( dirtyProperties==null ) {
// Interceptor returned null, so do the dirtycheck ourself, if possible
interceptorHandledDirtyCheck = false;
cannotDirtyCheck = loadedState==null; // object loaded by update()
if ( !cannotDirtyCheck ) {
// dirty check against the usual snapshot of the entity
dirtyProperties = persister.findDirty( values, loadedState, entity, session );
}
else {
// dirty check against the database snapshot, if possible/necessary
final Object[] databaseSnapshot = getDatabaseSnapshot(session, persister, id);
if ( databaseSnapshot != null ) {
dirtyProperties = persister.findModified(databaseSnapshot, values, entity, session);
cannotDirtyCheck = false;
event.setDatabaseSnapshot(databaseSnapshot);
}
}
}
else {
// the Interceptor handled the dirty checking
cannotDirtyCheck = false;
interceptorHandledDirtyCheck = true;
}
event.setDirtyProperties(dirtyProperties);
event.setDirtyCheckHandledByInterceptor(interceptorHandledDirtyCheck);
event.setDirtyCheckPossible(!cannotDirtyCheck);
| private java.lang.Object[] | getDatabaseSnapshot(org.hibernate.engine.SessionImplementor session, org.hibernate.persister.entity.EntityPersister persister, java.io.Serializable id)
if ( persister.isSelectBeforeUpdateRequired() ) {
Object[] snapshot = session.getPersistenceContext()
.getDatabaseSnapshot(id, persister);
if (snapshot==null) {
//do we even really need this? the update will fail anyway....
if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
session.getFactory().getStatisticsImplementor()
.optimisticFailure( persister.getEntityName() );
}
throw new StaleObjectStateException( persister.getEntityName(), id );
}
else {
return snapshot;
}
}
else {
//TODO: optimize away this lookup for entities w/o unsaved-value="undefined"
EntityKey entityKey = new EntityKey( id, persister, session.getEntityMode() );
return session.getPersistenceContext()
.getCachedDatabaseSnapshot( entityKey );
}
| private java.lang.Object | getNextVersion(org.hibernate.event.FlushEntityEvent event)Convience method to retreive an entities next version value
EntityEntry entry = event.getEntityEntry();
EntityPersister persister = entry.getPersister();
if ( persister.isVersioned() ) {
Object[] values = event.getPropertyValues();
if ( entry.isBeingReplicated() ) {
return Versioning.getVersion(values, persister);
}
else {
int[] dirtyProperties = event.getDirtyProperties();
final boolean isVersionIncrementRequired = isVersionIncrementRequired(
event,
entry,
persister,
dirtyProperties
);
final Object nextVersion = isVersionIncrementRequired ?
Versioning.increment( entry.getVersion(), persister.getVersionType(), event.getSession() ) :
entry.getVersion(); //use the current version
Versioning.setVersion(values, nextVersion, persister);
return nextVersion;
}
}
else {
return null;
}
| private java.lang.Object[] | getValues(java.lang.Object entity, org.hibernate.engine.EntityEntry entry, org.hibernate.EntityMode entityMode, boolean mightBeDirty, org.hibernate.engine.SessionImplementor session)
final Object[] loadedState = entry.getLoadedState();
final Status status = entry.getStatus();
final EntityPersister persister = entry.getPersister();
final Object[] values;
if ( status == Status.DELETED ) {
//grab its state saved at deletion
values = entry.getDeletedState();
}
else if ( !mightBeDirty && loadedState!=null ) {
values = loadedState;
}
else {
checkId( entity, persister, entry.getId(), entityMode );
// grab its current state
values = persister.getPropertyValues( entity, entityMode );
checkNaturalId( persister, entry.getId(), values, loadedState, entityMode, session );
}
return values;
| protected boolean | handleInterception(org.hibernate.event.FlushEntityEvent event)
SessionImplementor session = event.getSession();
EntityEntry entry = event.getEntityEntry();
EntityPersister persister = entry.getPersister();
Object entity = event.getEntity();
//give the Interceptor a chance to modify property values
final Object[] values = event.getPropertyValues();
final boolean intercepted = invokeInterceptor( session, entity, entry, values, persister );
//now we might need to recalculate the dirtyProperties array
if ( intercepted && event.isDirtyCheckPossible() && !event.isDirtyCheckHandledByInterceptor() ) {
int[] dirtyProperties;
if ( event.hasDatabaseSnapshot() ) {
dirtyProperties = persister.findModified( event.getDatabaseSnapshot(), values, entity, session );
}
else {
dirtyProperties = persister.findDirty( values, entry.getLoadedState(), entity, session );
}
event.setDirtyProperties(dirtyProperties);
}
return intercepted;
| private boolean | hasDirtyCollections(org.hibernate.event.FlushEntityEvent event, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.engine.Status status)
if ( isCollectionDirtyCheckNecessary(persister, status) ) {
DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(
event.getSession(),
persister.getPropertyVersionability()
);
visitor.processEntityPropertyValues( event.getPropertyValues(), persister.getPropertyTypes() );
boolean hasDirtyCollections = visitor.wasDirtyCollectionFound();
event.setHasDirtyCollection(hasDirtyCollections);
return hasDirtyCollections;
}
else {
return false;
}
| protected boolean | invokeInterceptor(org.hibernate.engine.SessionImplementor session, java.lang.Object entity, org.hibernate.engine.EntityEntry entry, java.lang.Object[] values, org.hibernate.persister.entity.EntityPersister persister)
return session.getInterceptor().onFlushDirty(
entity,
entry.getId(),
values,
entry.getLoadedState(),
persister.getPropertyNames(),
persister.getPropertyTypes()
);
| private boolean | isCollectionDirtyCheckNecessary(org.hibernate.persister.entity.EntityPersister persister, org.hibernate.engine.Status status)
return status==Status.MANAGED &&
persister.isVersioned() &&
persister.hasCollections();
| protected final boolean | isUpdateNecessary(org.hibernate.event.FlushEntityEvent event)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!
Note: this method is quite slow, avoid calling if possible!
EntityPersister persister = event.getEntityEntry().getPersister();
Status status = event.getEntityEntry().getStatus();
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, persister, status );
}
}
| private boolean | isUpdateNecessary(org.hibernate.event.FlushEntityEvent event, boolean mightBeDirty)
final Status status = event.getEntityEntry().getStatus();
if ( mightBeDirty || status==Status.DELETED ) {
// compare to cached state (ignoring collections unless versioned)
dirtyCheck(event);
if ( isUpdateNecessary(event) ) {
return true;
}
else {
FieldInterceptionHelper.clearDirty( event.getEntity() );
return false;
}
}
else {
return hasDirtyCollections( event, event.getEntityEntry().getPersister(), status );
}
| private boolean | isVersionIncrementRequired(org.hibernate.event.FlushEntityEvent event, org.hibernate.engine.EntityEntry entry, org.hibernate.persister.entity.EntityPersister persister, int[] dirtyProperties)
final boolean isVersionIncrementRequired = entry.getStatus()!=Status.DELETED && (
dirtyProperties==null ||
Versioning.isVersionIncrementRequired(
dirtyProperties,
event.hasDirtyCollection(),
persister.getPropertyVersionability()
)
);
return isVersionIncrementRequired;
| public void | onFlushEntity(org.hibernate.event.FlushEntityEvent event)Flushes a single entity's state to the database, by scheduling
an update action, if necessary
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 EntityMode entityMode = session.getEntityMode();
final Type[] types = persister.getPropertyTypes();
final boolean mightBeDirty = entry.requiresDirtyCheck(entity);
final Object[] values = getValues( entity, entry, entityMode, mightBeDirty, session );
event.setPropertyValues(values);
//TODO: avoid this for non-new instances where mightBeDirty==false
boolean substitute = wrapCollections( session, persister, types, values);
if ( isUpdateNecessary( event, mightBeDirty ) ) {
substitute = scheduleUpdate( event ) || substitute;
}
if ( status != Status.DELETED ) {
// now update the object .. has to be outside the main if block above (because of collections)
if (substitute) persister.setPropertyValues( entity, values, entityMode );
// 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);
}
}
| private boolean | scheduleUpdate(org.hibernate.event.FlushEntityEvent event)
final EntityEntry entry = event.getEntityEntry();
final EventSource session = event.getSession();
final Object entity = event.getEntity();
final Status status = entry.getStatus();
final EntityMode entityMode = session.getEntityMode();
final EntityPersister persister = entry.getPersister();
final Object[] values = event.getPropertyValues();
if ( log.isTraceEnabled() ) {
if ( status == Status.DELETED ) {
log.trace(
"Updating deleted entity: " +
MessageHelper.infoString( persister, entry.getId(), session.getFactory() )
);
}
else {
log.trace(
"Updating entity: " +
MessageHelper.infoString( persister, entry.getId(), session.getFactory() )
);
}
}
boolean intercepted;
if ( !entry.isBeingReplicated() ) {
// give the Interceptor a chance to process property values, if the properties
// were modified by the Interceptor, we need to set them back to the object
intercepted = handleInterception( event );
}
else {
intercepted = false;
}
validate( entity, persister, status, entityMode );
// 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;
}
// check nullability but do not perform command execute
// we'll use scheduled updates for that.
new Nullability(session).checkNullability( values, persister, true );
// schedule the update
// note that we intentionally do _not_ pass in currentPersistentState!
session.getActionQueue().addAction(
new EntityUpdateAction(
entry.getId(),
values,
dirtyProperties,
event.hasDirtyCollection(),
entry.getLoadedState(),
entry.getVersion(),
nextVersion,
entity,
entry.getRowId(),
persister,
session
)
);
return intercepted;
| protected void | validate(java.lang.Object entity, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.engine.Status status, org.hibernate.EntityMode entityMode)
// validate() instances of Validatable
if ( status == Status.MANAGED && persister.implementsValidatable( entityMode ) ) {
( (Validatable) entity ).validate();
}
| private boolean | wrapCollections(org.hibernate.event.EventSource session, org.hibernate.persister.entity.EntityPersister persister, org.hibernate.type.Type[] types, java.lang.Object[] values)
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",
// 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(session);
// substitutes into values by side-effect
visitor.processEntityPropertyValues(values, types);
return visitor.isSubstitutionRequired();
}
else {
return false;
}
|
|