📄 defaultflushentityeventlistener.java
字号:
//$Id: DefaultFlushEntityEventListener.java 10784 2006-11-11 05:13:01Z steve.ebersole@jboss.com $package org.hibernate.event.def;import java.io.Serializable;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.hibernate.AssertionFailure;import org.hibernate.EntityMode;import org.hibernate.HibernateException;import org.hibernate.StaleObjectStateException;import org.hibernate.action.EntityUpdateAction;import org.hibernate.action.DelayedPostInsertIdentifier;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.FieldInterceptionHelper;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 Logger log = LoggerFactory.getLogger(DefaultFlushEntityEventListener.class); /** * make sure user didn't mangle the id */ public void checkId(Object object, EntityPersister persister, Serializable id, EntityMode entityMode) throws HibernateException { 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( EntityPersister persister, EntityEntry entry, Object[] current, Object[] loaded, EntityMode entityMode, SessionImplementor session) { if ( persister.hasNaturalIdentifier() && entry.getStatus() != Status.READ_ONLY ) { 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( entry.getId(), 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" ); } } } } } /** * 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, 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 { FieldInterceptionHelper.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() ) ); } } final 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -