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   import java.util.Set;
   29   
   30   import org.slf4j.Logger;
   31   import org.slf4j.LoggerFactory;
   32   
   33   import org.hibernate.CacheMode;
   34   import org.hibernate.HibernateException;
   35   import org.hibernate.LockMode;
   36   import org.hibernate.TransientObjectException;
   37   import org.hibernate.util.IdentitySet;
   38   import org.hibernate.action.EntityDeleteAction;
   39   import org.hibernate.classic.Lifecycle;
   40   import org.hibernate.engine.Cascade;
   41   import org.hibernate.engine.CascadingAction;
   42   import org.hibernate.engine.EntityEntry;
   43   import org.hibernate.engine.EntityKey;
   44   import org.hibernate.engine.ForeignKeys;
   45   import org.hibernate.engine.Nullability;
   46   import org.hibernate.engine.PersistenceContext;
   47   import org.hibernate.engine.Status;
   48   import org.hibernate.event.DeleteEvent;
   49   import org.hibernate.event.DeleteEventListener;
   50   import org.hibernate.event.EventSource;
   51   import org.hibernate.persister.entity.EntityPersister;
   52   import org.hibernate.pretty.MessageHelper;
   53   import org.hibernate.type.Type;
   54   import org.hibernate.type.TypeFactory;
   55   
   56   /**
   57    * Defines the default delete event listener used by hibernate for deleting entities
   58    * from the datastore in response to generated delete events.
   59    *
   60    * @author Steve Ebersole
   61    */
   62   public class DefaultDeleteEventListener implements DeleteEventListener {
   63   
   64   	private static final Logger log = LoggerFactory.getLogger( DefaultDeleteEventListener.class );
   65   
   66   	/**
   67   	 * Handle the given delete event.
   68   	 *
   69   	 * @param event The delete event to be handled.
   70   	 *
   71   	 * @throws HibernateException
   72   	 */
   73   	public void onDelete(DeleteEvent event) throws HibernateException {
   74   		onDelete( event, new IdentitySet() );
   75   	}
   76   
   77   	/**
   78   	 * Handle the given delete event.  This is the cascaded form.
   79   	 *
   80   	 * @param event The delete event.
   81   	 * @param transientEntities The cache of entities already deleted
   82   	 *
   83   	 * @throws HibernateException
   84   	 */
   85   	public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
   86   
   87   		final EventSource source = event.getSession();
   88   
   89   		final PersistenceContext persistenceContext = source.getPersistenceContext();
   90   		Object entity = persistenceContext.unproxyAndReassociate( event.getObject() );
   91   
   92   		EntityEntry entityEntry = persistenceContext.getEntry( entity );
   93   		final EntityPersister persister;
   94   		final Serializable id;
   95   		final Object version;
   96   
   97   		if ( entityEntry == null ) {
   98   			log.trace( "entity was not persistent in delete processing" );
   99   
  100   			persister = source.getEntityPersister( event.getEntityName(), entity );
  101   
  102   			if ( ForeignKeys.isTransient( persister.getEntityName(), entity, null, source ) ) {
  103   				deleteTransientEntity( source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities );
  104   				// EARLY EXIT!!!
  105   				return;
  106   			}
  107   			else {
  108   				performDetachedEntityDeletionCheck( event );
  109   			}
  110   
  111   			id = persister.getIdentifier( entity, source.getEntityMode() );
  112   
  113   			if ( id == null ) {
  114   				throw new TransientObjectException(
  115   						"the detached instance passed to delete() had a null identifier"
  116   				);
  117   			}
  118   
  119   			EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
  120   
  121   			persistenceContext.checkUniqueness( key, entity );
  122   
  123   			new OnUpdateVisitor( source, id, entity ).process( entity, persister );
  124   
  125   			version = persister.getVersion( entity, source.getEntityMode() );
  126   
  127   			entityEntry = persistenceContext.addEntity(
  128   					entity,
  129   					Status.MANAGED,
  130   					persister.getPropertyValues( entity, source.getEntityMode() ),
  131   					key,
  132   					version,
  133   					LockMode.NONE,
  134   					true,
  135   					persister,
  136   					false,
  137   					false
  138   			);
  139   		}
  140   		else {
  141   			log.trace( "deleting a persistent instance" );
  142   
  143   			if ( entityEntry.getStatus() == Status.DELETED || entityEntry.getStatus() == Status.GONE ) {
  144   				log.trace( "object was already deleted" );
  145   				return;
  146   			}
  147   			persister = entityEntry.getPersister();
  148   			id = entityEntry.getId();
  149   			version = entityEntry.getVersion();
  150   		}
  151   
  152   		/*if ( !persister.isMutable() ) {
  153   			throw new HibernateException(
  154   					"attempted to delete an object of immutable class: " +
  155   					MessageHelper.infoString(persister)
  156   				);
  157   		}*/
  158   
  159   		if ( invokeDeleteLifecycle( source, entity, persister ) ) {
  160   			return;
  161   		}
  162   
  163   		deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister, transientEntities );
  164   
  165   		if ( source.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
  166   			persister.resetIdentifier( entity, id, version, source.getEntityMode() );
  167   		}
  168   	}
  169   
  170   	/**
  171   	 * Called when we have recognized an attempt to delete a detached entity.
  172   	 * <p/>
  173   	 * This is perfectly valid in Hibernate usage; JPA, however, forbids this.
  174   	 * Thus, this is a hook for HEM to affect this behavior.
  175   	 *
  176   	 * @param event The event.
  177   	 */
  178   	protected void performDetachedEntityDeletionCheck(DeleteEvent event) {
  179   		// ok in normal Hibernate usage to delete a detached entity; JPA however
  180   		// forbids it, thus this is a hook for HEM to affect this behavior
  181   	}
  182   
  183   	/**
  184   	 * We encountered a delete request on a transient instance.
  185   	 * <p/>
  186   	 * This is a deviation from historical Hibernate (pre-3.2) behavior to
  187   	 * align with the JPA spec, which states that transient entities can be
  188   	 * passed to remove operation in which case cascades still need to be
  189   	 * performed.
  190   	 *
  191   	 * @param session The session which is the source of the event
  192   	 * @param entity The entity being delete processed
  193   	 * @param cascadeDeleteEnabled Is cascading of deletes enabled
  194   	 * @param persister The entity persister
  195   	 * @param transientEntities A cache of already visited transient entities
  196   	 * (to avoid infinite recursion).
  197   	 */
  198   	protected void deleteTransientEntity(
  199   			EventSource session,
  200   			Object entity,
  201   			boolean cascadeDeleteEnabled,
  202   			EntityPersister persister,
  203   			Set transientEntities) {
  204   		log.info( "handling transient entity in delete processing" );
  205   		if ( transientEntities.contains( entity ) ) {
  206   			log.trace( "already handled transient entity; skipping" );
  207   			return;
  208   		}
  209   		transientEntities.add( entity );
  210   		cascadeBeforeDelete( session, persister, entity, null, transientEntities );
  211   		cascadeAfterDelete( session, persister, entity, transientEntities );
  212   	}
  213   
  214   	/**
  215   	 * Perform the entity deletion.  Well, as with most operations, does not
  216   	 * really perform it; just schedules an action/execution with the
  217   	 * {@link org.hibernate.engine.ActionQueue} for execution during flush.
  218   	 *
  219   	 * @param session The originating session
  220   	 * @param entity The entity to delete
  221   	 * @param entityEntry The entity's entry in the {@link PersistenceContext}
  222   	 * @param isCascadeDeleteEnabled Is delete cascading enabled?
  223   	 * @param persister The entity persister.
  224   	 * @param transientEntities A cache of already deleted entities.
  225   	 */
  226   	protected final void deleteEntity(
  227   			final EventSource session,
  228   			final Object entity,
  229   			final EntityEntry entityEntry,
  230   			final boolean isCascadeDeleteEnabled,
  231   			final EntityPersister persister,
  232   			final Set transientEntities) {
  233   
  234   		if ( log.isTraceEnabled() ) {
  235   			log.trace(
  236   					"deleting " +
  237   							MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
  238   			);
  239   		}
  240   
  241   		final PersistenceContext persistenceContext = session.getPersistenceContext();
  242   		final Type[] propTypes = persister.getPropertyTypes();
  243   		final Object version = entityEntry.getVersion();
  244   
  245   		final Object[] currentState;
  246   		if ( entityEntry.getLoadedState() == null ) { //ie. the entity came in from update()
  247   			currentState = persister.getPropertyValues( entity, session.getEntityMode() );
  248   		}
  249   		else {
  250   			currentState = entityEntry.getLoadedState();
  251   		}
  252   
  253   		final Object[] deletedState = createDeletedState( persister, currentState, session );
  254   		entityEntry.setDeletedState( deletedState );
  255   
  256   		session.getInterceptor().onDelete(
  257   				entity,
  258   				entityEntry.getId(),
  259   				deletedState,
  260   				persister.getPropertyNames(),
  261   				propTypes
  262   		);
  263   
  264   		// before any callbacks, etc, so subdeletions see that this deletion happened first
  265   		persistenceContext.setEntryStatus( entityEntry, Status.DELETED );
  266   		EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
  267   
  268   		cascadeBeforeDelete( session, persister, entity, entityEntry, transientEntities );
  269   
  270   		new ForeignKeys.Nullifier( entity, true, false, session )
  271   				.nullifyTransientReferences( entityEntry.getDeletedState(), propTypes );
  272   		new Nullability( session ).checkNullability( entityEntry.getDeletedState(), persister, true );
  273   		persistenceContext.getNullifiableEntityKeys().add( key );
  274   
  275   		// Ensures that containing deletions happen before sub-deletions
  276   		session.getActionQueue().addAction(
  277   				new EntityDeleteAction(
  278   						entityEntry.getId(),
  279   						deletedState,
  280   						version,
  281   						entity,
  282   						persister,
  283   						isCascadeDeleteEnabled,
  284   						session
  285   				)
  286   		);
  287   
  288   		cascadeAfterDelete( session, persister, entity, transientEntities );
  289   
  290   		// the entry will be removed after the flush, and will no longer
  291   		// override the stale snapshot
  292   		// This is now handled by removeEntity() in EntityDeleteAction
  293   		//persistenceContext.removeDatabaseSnapshot(key);
  294   	}
  295   
  296   	private Object[] createDeletedState(EntityPersister persister, Object[] currentState, EventSource session) {
  297   		Type[] propTypes = persister.getPropertyTypes();
  298   		final Object[] deletedState = new Object[propTypes.length];
  299   //		TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), deletedState, session );
  300   		boolean[] copyability = new boolean[propTypes.length];
  301   		java.util.Arrays.fill( copyability, true );
  302   		TypeFactory.deepCopy( currentState, propTypes, copyability, deletedState, session );
  303   		return deletedState;
  304   	}
  305   
  306   	protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
  307   		if ( persister.implementsLifecycle( session.getEntityMode() ) ) {
  308   			log.debug( "calling onDelete()" );
  309   			if ( ( ( Lifecycle ) entity ).onDelete( session ) ) {
  310   				log.debug( "deletion vetoed by onDelete()" );
  311   				return true;
  312   			}
  313   		}
  314   		return false;
  315   	}
  316   
  317   	protected void cascadeBeforeDelete(
  318   			EventSource session,
  319   			EntityPersister persister,
  320   			Object entity,
  321   			EntityEntry entityEntry,
  322   			Set transientEntities) throws HibernateException {
  323   
  324   		CacheMode cacheMode = session.getCacheMode();
  325   		session.setCacheMode( CacheMode.GET );
  326   		session.getPersistenceContext().incrementCascadeLevel();
  327   		try {
  328   			// cascade-delete to collections BEFORE the collection owner is deleted
  329   			new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session )
  330   					.cascade( persister, entity, transientEntities );
  331   		}
  332   		finally {
  333   			session.getPersistenceContext().decrementCascadeLevel();
  334   			session.setCacheMode( cacheMode );
  335   		}
  336   	}
  337   
  338   	protected void cascadeAfterDelete(
  339   			EventSource session,
  340   			EntityPersister persister,
  341   			Object entity,
  342   			Set transientEntities) throws HibernateException {
  343   
  344   		CacheMode cacheMode = session.getCacheMode();
  345   		session.setCacheMode( CacheMode.GET );
  346   		session.getPersistenceContext().incrementCascadeLevel();
  347   		try {
  348   			// cascade-delete to many-to-one AFTER the parent was deleted
  349   			new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
  350   					.cascade( persister, entity, transientEntities );
  351   		}
  352   		finally {
  353   			session.getPersistenceContext().decrementCascadeLevel();
  354   			session.setCacheMode( cacheMode );
  355   		}
  356   	}
  357   
  358   }

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