Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » event » def » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.event.def;
   26   
   27   import java.io.Serializable;
   28   
   29   import org.slf4j.Logger;
   30   import org.slf4j.LoggerFactory;
   31   
   32   import org.hibernate.AssertionFailure;
   33   import org.hibernate.EntityMode;
   34   import org.hibernate.HibernateException;
   35   import org.hibernate.LockMode;
   36   import org.hibernate.PersistentObjectException;
   37   import org.hibernate.TransientObjectException;
   38   import org.hibernate.classic.Lifecycle;
   39   import org.hibernate.engine.Cascade;
   40   import org.hibernate.engine.CascadingAction;
   41   import org.hibernate.engine.EntityEntry;
   42   import org.hibernate.engine.EntityKey;
   43   import org.hibernate.engine.SessionFactoryImplementor;
   44   import org.hibernate.engine.SessionImplementor;
   45   import org.hibernate.engine.Status;
   46   import org.hibernate.event.EventSource;
   47   import org.hibernate.event.SaveOrUpdateEvent;
   48   import org.hibernate.event.SaveOrUpdateEventListener;
   49   import org.hibernate.persister.entity.EntityPersister;
   50   import org.hibernate.pretty.MessageHelper;
   51   import org.hibernate.proxy.HibernateProxy;
   52   
   53   /**
   54    * Defines the default listener used by Hibernate for handling save-update
   55    * events.
   56    *
   57    * @author Steve Ebersole
   58    * @author Gavin King
   59    */
   60   public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener implements SaveOrUpdateEventListener {
   61   
   62   	private static final Logger log = LoggerFactory.getLogger( DefaultSaveOrUpdateEventListener.class );
   63   
   64   	/**
   65   	 * Handle the given update event.
   66   	 *
   67   	 * @param event The update event to be handled.
   68   	 */
   69   	public void onSaveOrUpdate(SaveOrUpdateEvent event) {
   70   		final SessionImplementor source = event.getSession();
   71   		final Object object = event.getObject();
   72   		final Serializable requestedId = event.getRequestedId();
   73   
   74   		if ( requestedId != null ) {
   75   			//assign the requested id to the proxy, *before*
   76   			//reassociating the proxy
   77   			if ( object instanceof HibernateProxy ) {
   78   				( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
   79   			}
   80   		}
   81   
   82   		if ( reassociateIfUninitializedProxy( object, source ) ) {
   83   			log.trace( "reassociated uninitialized proxy" );
   84   			// an uninitialized proxy, noop, don't even need to
   85   			// return an id, since it is never a save()
   86   		}
   87   		else {
   88   			//initialize properties of the event:
   89   			final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
   90   			event.setEntity( entity );
   91   			event.setEntry( source.getPersistenceContext().getEntry( entity ) );
   92   			//return the id in the event object
   93   			event.setResultId( performSaveOrUpdate( event ) );
   94   		}
   95   
   96   	}
   97   
   98   	protected boolean reassociateIfUninitializedProxy(Object object, SessionImplementor source) {
   99   		return source.getPersistenceContext().reassociateIfUninitializedProxy( object );
  100   	}
  101   
  102   	protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
  103   		int entityState = getEntityState(
  104   				event.getEntity(),
  105   				event.getEntityName(),
  106   				event.getEntry(),
  107   				event.getSession()
  108   		);
  109   
  110   		switch ( entityState ) {
  111   			case DETACHED:
  112   				entityIsDetached( event );
  113   				return null;
  114   			case PERSISTENT:
  115   				return entityIsPersistent( event );
  116   			default: //TRANSIENT or DELETED
  117   				return entityIsTransient( event );
  118   		}
  119   	}
  120   
  121   	protected Serializable entityIsPersistent(SaveOrUpdateEvent event) throws HibernateException {
  122   		log.trace( "ignoring persistent instance" );
  123   
  124   		EntityEntry entityEntry = event.getEntry();
  125   		if ( entityEntry == null ) {
  126   			throw new AssertionFailure( "entity was transient or detached" );
  127   		}
  128   		else {
  129   
  130   			if ( entityEntry.getStatus() == Status.DELETED ) {
  131   				throw new AssertionFailure( "entity was deleted" );
  132   			}
  133   
  134   			final SessionFactoryImplementor factory = event.getSession().getFactory();
  135   
  136   			Serializable requestedId = event.getRequestedId();
  137   
  138   			Serializable savedId;
  139   			if ( requestedId == null ) {
  140   				savedId = entityEntry.getId();
  141   			}
  142   			else {
  143   
  144   				final boolean isEqual = !entityEntry.getPersister().getIdentifierType()
  145   						.isEqual( requestedId, entityEntry.getId(), event.getSession().getEntityMode(), factory );
  146   
  147   				if ( isEqual ) {
  148   					throw new PersistentObjectException(
  149   							"object passed to save() was already persistent: " +
  150   									MessageHelper.infoString( entityEntry.getPersister(), requestedId, factory )
  151   					);
  152   				}
  153   
  154   				savedId = requestedId;
  155   
  156   			}
  157   
  158   			if ( log.isTraceEnabled() ) {
  159   				log.trace(
  160   						"object already associated with session: " +
  161   								MessageHelper.infoString( entityEntry.getPersister(), savedId, factory )
  162   				);
  163   			}
  164   
  165   			return savedId;
  166   
  167   		}
  168   	}
  169   
  170   	/**
  171   	 * The given save-update event named a transient entity.
  172   	 * <p/>
  173   	 * Here, we will perform the save processing.
  174   	 *
  175   	 * @param event The save event to be handled.
  176   	 *
  177   	 * @return The entity's identifier after saving.
  178   	 */
  179   	protected Serializable entityIsTransient(SaveOrUpdateEvent event) {
  180   
  181   		log.trace( "saving transient instance" );
  182   
  183   		final EventSource source = event.getSession();
  184   
  185   		EntityEntry entityEntry = event.getEntry();
  186   		if ( entityEntry != null ) {
  187   			if ( entityEntry.getStatus() == Status.DELETED ) {
  188   				source.forceFlush( entityEntry );
  189   			}
  190   			else {
  191   				throw new AssertionFailure( "entity was persistent" );
  192   			}
  193   		}
  194   
  195   		Serializable id = saveWithGeneratedOrRequestedId( event );
  196   
  197   		source.getPersistenceContext().reassociateProxy( event.getObject(), id );
  198   
  199   		return id;
  200   	}
  201   
  202   	/**
  203   	 * Save the transient instance, assigning the right identifier
  204   	 *
  205   	 * @param event The initiating event.
  206   	 *
  207   	 * @return The entity's identifier value after saving.
  208   	 */
  209   	protected Serializable saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
  210   		return saveWithGeneratedId(
  211   				event.getEntity(),
  212   				event.getEntityName(),
  213   				null,
  214   				event.getSession(),
  215   				true
  216   		);
  217   	}
  218   
  219   	/**
  220   	 * The given save-update event named a detached entity.
  221   	 * <p/>
  222   	 * Here, we will perform the update processing.
  223   	 *
  224   	 * @param event The update event to be handled.
  225   	 */
  226   	protected void entityIsDetached(SaveOrUpdateEvent event) {
  227   
  228   		log.trace( "updating detached instance" );
  229   
  230   
  231   		if ( event.getSession().getPersistenceContext().isEntryFor( event.getEntity() ) ) {
  232   			//TODO: assertion only, could be optimized away
  233   			throw new AssertionFailure( "entity was persistent" );
  234   		}
  235   
  236   		Object entity = event.getEntity();
  237   
  238   		EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), entity );
  239   
  240   		event.setRequestedId(
  241   				getUpdateId(
  242   						entity, persister, event.getRequestedId(), event.getSession().getEntityMode()
  243   				)
  244   		);
  245   
  246   		performUpdate( event, entity, persister );
  247   
  248   	}
  249   
  250   	/**
  251   	 * Determine the id to use for updating.
  252   	 *
  253   	 * @param entity The entity.
  254   	 * @param persister The entity persister
  255   	 * @param requestedId The requested identifier
  256   	 * @param entityMode The entity mode.
  257   	 *
  258   	 * @return The id.
  259   	 *
  260   	 * @throws TransientObjectException If the entity is considered transient.
  261   	 */
  262   	protected Serializable getUpdateId(
  263   			Object entity,
  264   			EntityPersister persister,
  265   			Serializable requestedId,
  266   			EntityMode entityMode) {
  267   		// use the id assigned to the instance
  268   		Serializable id = persister.getIdentifier( entity, entityMode );
  269   		if ( id == null ) {
  270   			// assume this is a newly instantiated transient object
  271   			// which should be saved rather than updated
  272   			throw new TransientObjectException(
  273   					"The given object has a null identifier: " +
  274   							persister.getEntityName()
  275   			);
  276   		}
  277   		else {
  278   			return id;
  279   		}
  280   
  281   	}
  282   
  283   	protected void performUpdate(
  284   			SaveOrUpdateEvent event,
  285   			Object entity,
  286   			EntityPersister persister) throws HibernateException {
  287   
  288   		if ( !persister.isMutable() ) {
  289   			log.trace( "immutable instance passed to doUpdate(), locking" );
  290   			reassociate( event, entity, event.getRequestedId(), persister );
  291   		}
  292   		else {
  293   
  294   			if ( log.isTraceEnabled() ) {
  295   				log.trace(
  296   						"updating " +
  297   								MessageHelper.infoString(
  298   										persister, event.getRequestedId(), event.getSession().getFactory()
  299   								)
  300   				);
  301   			}
  302   
  303   			final EventSource source = event.getSession();
  304   
  305   			EntityKey key = new EntityKey( event.getRequestedId(), persister, source.getEntityMode() );
  306   
  307   			source.getPersistenceContext().checkUniqueness( key, entity );
  308   
  309   			if ( invokeUpdateLifecycle( entity, persister, source ) ) {
  310   				reassociate( event, event.getObject(), event.getRequestedId(), persister );
  311   				return;
  312   			}
  313   
  314   			// this is a transient object with existing persistent state not loaded by the session
  315   
  316   			new OnUpdateVisitor( source, event.getRequestedId(), entity ).process( entity, persister );
  317   
  318   			//TODO: put this stuff back in to read snapshot from
  319   			//      the second-level cache (needs some extra work)
  320   			/*Object[] cachedState = null;
  321   
  322   			if ( persister.hasCache() ) {
  323   				CacheEntry entry = (CacheEntry) persister.getCache()
  324   						.get( event.getRequestedId(), source.getTimestamp() );
  325   			    cachedState = entry==null ?
  326   			    		null :
  327   			    		entry.getState(); //TODO: half-assemble this stuff
  328   			}*/
  329   
  330   			source.getPersistenceContext().addEntity(
  331   					entity,
  332   					Status.MANAGED,
  333   					null, //cachedState,
  334   					key,
  335   					persister.getVersion( entity, source.getEntityMode() ),
  336   					LockMode.NONE,
  337   					true,
  338   					persister,
  339   					false,
  340   					true //assume true, since we don't really know, and it doesn't matter
  341   			);
  342   
  343   			persister.afterReassociate( entity, source );
  344   
  345   			if ( log.isTraceEnabled() ) {
  346   				log.trace(
  347   						"updating " +
  348   								MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
  349   				);
  350   			}
  351   
  352   			cascadeOnUpdate( event, persister, entity );
  353   
  354   		}
  355   	}
  356   
  357   	protected boolean invokeUpdateLifecycle(Object entity, EntityPersister persister, EventSource source) {
  358   		if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
  359   			log.debug( "calling onUpdate()" );
  360   			if ( ( ( Lifecycle ) entity ).onUpdate( source ) ) {
  361   				log.debug( "update vetoed by onUpdate()" );
  362   				return true;
  363   			}
  364   		}
  365   		return false;
  366   	}
  367   
  368   	/**
  369   	 * Handles the calls needed to perform cascades as part of an update request
  370   	 * for the given entity.
  371   	 *
  372   	 * @param event The event currently being processed.
  373   	 * @param persister The defined persister for the entity being updated.
  374   	 * @param entity The entity being updated.
  375   	 */
  376   	private void cascadeOnUpdate(SaveOrUpdateEvent event, EntityPersister persister, Object entity) {
  377   		EventSource source = event.getSession();
  378   		source.getPersistenceContext().incrementCascadeLevel();
  379   		try {
  380   			new Cascade( CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source )
  381   					.cascade( persister, entity );
  382   		}
  383   		finally {
  384   			source.getPersistenceContext().decrementCascadeLevel();
  385   		}
  386   	}
  387   
  388   	protected CascadingAction getCascadeAction() {
  389   		return CascadingAction.SAVE_UPDATE;
  390   	}
  391   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » event » def » [javadoc | source]