📄 defaultflushentityeventlistener.java
字号:
//$Id: DefaultFlushEntityEventListener.java 8751 2005-12-05 18:45:52Z steveebersole $
package org.hibernate.event.def;
import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.StaleObjectStateException;
import org.hibernate.action.EntityUpdateAction;
import org.hibernate.classic.Validatable;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.EntityKey;
import org.hibernate.engine.Nullability;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.Status;
import org.hibernate.engine.Versioning;
import org.hibernate.event.EventSource;
import org.hibernate.event.FlushEntityEvent;
import org.hibernate.event.FlushEntityEventListener;
import org.hibernate.intercept.FieldInterceptor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
/**
* An event that occurs for each entity instance at flush time
*
* @author Gavin King
*/
public class DefaultFlushEntityEventListener implements FlushEntityEventListener {
private static final Log log = LogFactory.getLog(DefaultFlushEntityEventListener.class);
/**
* make sure user didn't mangle the id
*/
public void checkId(Object object, EntityPersister persister, Serializable id, EntityMode entityMode)
throws HibernateException {
if ( persister.hasIdentifierPropertyOrEmbeddedCompositeIdentifier() ) {
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(
EntityPersister persister,
Serializable identifier,
Object[] current,
Object[] loaded,
EntityMode entityMode,
SessionImplementor session) {
if ( persister.hasNaturalIdentifier() ) {
if ( loaded == null ) {
loaded = session.getPersistenceContext().getNaturalIdSnapshot( identifier, persister );
}
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] ) {
if ( !types[prop].isEqual( current[prop], loaded[prop], entityMode ) ) {
throw new HibernateException(
"immutable natural identifier of an instance of " +
persister.getEntityName() +
" was altered"
);
}
}
}
}
}
/**
* Flushes a single entity's state to the database, by scheduling
* an update action, if necessary
*/
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 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 Object[] getValues(
Object entity,
EntityEntry entry,
EntityMode entityMode,
boolean mightBeDirty,
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;
}
private boolean wrapCollections(
EventSource session,
EntityPersister persister,
Type[] types,
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;
}
}
private boolean isUpdateNecessary(final FlushEntityEvent event, final 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 {
FieldInterceptor.clearDirty( event.getEntity() );
return false;
}
}
else {
return hasDirtyCollections( event, event.getEntityEntry().getPersister(), status );
}
}
private boolean scheduleUpdate(final 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 substitute;
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
substitute = handleInterception(event);
}
else {
substitute = 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 ( !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!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -