Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » engine » [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.engine;
   26   
   27   import java.util.Collection;
   28   import java.util.HashSet;
   29   import java.util.Iterator;
   30   
   31   import org.slf4j.Logger;
   32   import org.slf4j.LoggerFactory;
   33   import org.hibernate.EntityMode;
   34   import org.hibernate.HibernateException;
   35   import org.hibernate.collection.PersistentCollection;
   36   import org.hibernate.event.EventSource;
   37   import org.hibernate.persister.collection.CollectionPersister;
   38   import org.hibernate.persister.entity.EntityPersister;
   39   import org.hibernate.type.AbstractComponentType;
   40   import org.hibernate.type.AssociationType;
   41   import org.hibernate.type.CollectionType;
   42   import org.hibernate.type.EntityType;
   43   import org.hibernate.type.Type;
   44   import org.hibernate.util.CollectionHelper;
   45   
   46   /**
   47    * Delegate responsible for, in conjunction with the various
   48    * {@link CascadingAction actions}, implementing cascade processing.
   49    *
   50    * @author Gavin King
   51    * @see CascadingAction
   52    */
   53   public final class Cascade {
   54   
   55   	/**
   56   	 * A cascade point that occurs just after the insertion of the parent entity and
   57   	 * just before deletion
   58   	 */
   59   	public static final int AFTER_INSERT_BEFORE_DELETE = 1;
   60   	/**
   61   	 * A cascade point that occurs just before the insertion of the parent entity and
   62   	 * just after deletion
   63   	 */
   64   	public static final int BEFORE_INSERT_AFTER_DELETE = 2;
   65   	/**
   66   	 * A cascade point that occurs just after the insertion of the parent entity and
   67   	 * just before deletion, inside a collection
   68   	 */
   69   	public static final int AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION = 3;
   70   	/**
   71   	 * A cascade point that occurs just after update of the parent entity
   72   	 */
   73   	public static final int AFTER_UPDATE = 0;
   74   	/**
   75   	 * A cascade point that occurs just before the session is flushed
   76   	 */
   77   	public static final int BEFORE_FLUSH = 0;
   78   	/**
   79   	 * A cascade point that occurs just after eviction of the parent entity from the
   80   	 * session cache
   81   	 */
   82   	public static final int AFTER_EVICT = 0;
   83   	/**
   84   	 * A cascade point that occurs just after locking a transient parent entity into the
   85   	 * session cache
   86   	 */
   87   	public static final int BEFORE_REFRESH = 0;
   88   	/**
   89   	 * A cascade point that occurs just after refreshing a parent entity
   90   	 */
   91   	public static final int AFTER_LOCK = 0;
   92   	/**
   93   	 * A cascade point that occurs just before merging from a transient parent entity into
   94   	 * the object in the session cache
   95   	 */
   96   	public static final int BEFORE_MERGE = 0;
   97   
   98   
   99   	private static final Logger log = LoggerFactory.getLogger( Cascade.class );
  100   
  101   
  102   	private int cascadeTo;
  103   	private EventSource eventSource;
  104   	private CascadingAction action;
  105   
  106   	public Cascade(final CascadingAction action, final int cascadeTo, final EventSource eventSource) {
  107   		this.cascadeTo = cascadeTo;
  108   		this.eventSource = eventSource;
  109   		this.action = action;
  110   	}
  111   
  112   	/**
  113   	 * Cascade an action from the parent entity instance to all its children.
  114   	 *
  115   	 * @param persister The parent's entity persister
  116   	 * @param parent The parent reference.
  117   	 * @throws HibernateException
  118   	 */
  119   	public void cascade(final EntityPersister persister, final Object parent)
  120   	throws HibernateException {
  121   		cascade( persister, parent, null );
  122   	}
  123   
  124   	/**
  125   	 * Cascade an action from the parent entity instance to all its children.  This
  126   	 * form is typicaly called from within cascade actions.
  127   	 *
  128   	 * @param persister The parent's entity persister
  129   	 * @param parent The parent reference.
  130   	 * @param anything Anything ;)   Typically some form of cascade-local cache
  131   	 * which is specific to each CascadingAction type
  132   	 * @throws HibernateException
  133   	 */
  134   	public void cascade(final EntityPersister persister, final Object parent, final Object anything)
  135   			throws HibernateException {
  136   
  137   		if ( persister.hasCascades() || action.requiresNoCascadeChecking() ) { // performance opt
  138   			if ( log.isTraceEnabled() ) {
  139   				log.trace( "processing cascade " + action + " for: " + persister.getEntityName() );
  140   			}
  141   
  142   			Type[] types = persister.getPropertyTypes();
  143   			CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles();
  144   			EntityMode entityMode = eventSource.getEntityMode();
  145   			boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties( parent, entityMode );
  146   			for ( int i=0; i<types.length; i++) {
  147   				CascadeStyle style = cascadeStyles[i];
  148   				if ( hasUninitializedLazyProperties && persister.getPropertyLaziness()[i] && ! action.performOnLazyProperty() ) {
  149   					//do nothing to avoid a lazy property initialization
  150   					continue;
  151   				}
  152   
  153   				if ( style.doCascade( action ) ) {
  154   					cascadeProperty(
  155   					        persister.getPropertyValue( parent, i, entityMode ),
  156   					        types[i],
  157   					        style,
  158   					        anything,
  159   					        false
  160   					);
  161   				}
  162   				else if ( action.requiresNoCascadeChecking() ) {
  163   					action.noCascade(
  164   							eventSource,
  165   							persister.getPropertyValue( parent, i, entityMode ),
  166   							parent,
  167   							persister,
  168   							i
  169   					);
  170   				}
  171   			}
  172   
  173   			if ( log.isTraceEnabled() ) {
  174   				log.trace( "done processing cascade " + action + " for: " + persister.getEntityName() );
  175   			}
  176   		}
  177   	}
  178   
  179   	/**
  180   	 * Cascade an action to the child or children
  181   	 */
  182   	private void cascadeProperty(
  183   			final Object child,
  184   			final Type type,
  185   			final CascadeStyle style,
  186   			final Object anything,
  187   			final boolean isCascadeDeleteEnabled) throws HibernateException {
  188   
  189   		if (child!=null) {
  190   			if ( type.isAssociationType() ) {
  191   				AssociationType associationType = (AssociationType) type;
  192   				if ( cascadeAssociationNow( associationType ) ) {
  193   					cascadeAssociation(
  194   							child,
  195   							type,
  196   							style,
  197   							anything,
  198   							isCascadeDeleteEnabled
  199   						);
  200   				}
  201   			}
  202   			else if ( type.isComponentType() ) {
  203   				cascadeComponent( child, (AbstractComponentType) type, anything );
  204   			}
  205   		}
  206   	}
  207   
  208   	private boolean cascadeAssociationNow(AssociationType associationType) {
  209   		return associationType.getForeignKeyDirection().cascadeNow(cascadeTo) &&
  210   			( eventSource.getEntityMode()!=EntityMode.DOM4J || associationType.isEmbeddedInXML() );
  211   	}
  212   
  213   	private void cascadeComponent(
  214   			final Object child,
  215   			final AbstractComponentType componentType,
  216   			final Object anything) {
  217   		Object[] children = componentType.getPropertyValues(child, eventSource);
  218   		Type[] types = componentType.getSubtypes();
  219   		for ( int i=0; i<types.length; i++ ) {
  220   			CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
  221   			if ( componentPropertyStyle.doCascade(action) ) {
  222   				cascadeProperty(
  223   						children[i],
  224   						types[i],
  225   						componentPropertyStyle,
  226   						anything,
  227   						false
  228   					);
  229   			}
  230   		}
  231   	}
  232   
  233   	private void cascadeAssociation(
  234   			final Object child,
  235   			final Type type,
  236   			final CascadeStyle style,
  237   			final Object anything,
  238   			final boolean isCascadeDeleteEnabled) {
  239   		if ( type.isEntityType() || type.isAnyType() ) {
  240   			cascadeToOne( child, type, style, anything, isCascadeDeleteEnabled );
  241   		}
  242   		else if ( type.isCollectionType() ) {
  243   			cascadeCollection( child, style, anything, (CollectionType) type );
  244   		}
  245   	}
  246   
  247   	/**
  248   	 * Cascade an action to a collection
  249   	 */
  250   	private void cascadeCollection(
  251   			final Object child,
  252   			final CascadeStyle style,
  253   			final Object anything,
  254   			final CollectionType type) {
  255   		CollectionPersister persister = eventSource.getFactory()
  256   				.getCollectionPersister( type.getRole() );
  257   		Type elemType = persister.getElementType();
  258   
  259   		final int oldCascadeTo = cascadeTo;
  260   		if ( cascadeTo==AFTER_INSERT_BEFORE_DELETE) {
  261   			cascadeTo = AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION;
  262   		}
  263   
  264   		//cascade to current collection elements
  265   		if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) {
  266   			cascadeCollectionElements(
  267   				child,
  268   				type,
  269   				style,
  270   				elemType,
  271   				anything,
  272   				persister.isCascadeDeleteEnabled()
  273   			);
  274   		}
  275   
  276   		cascadeTo = oldCascadeTo;
  277   	}
  278   
  279   	/**
  280   	 * Cascade an action to a to-one association or any type
  281   	 */
  282   	private void cascadeToOne(
  283   			final Object child,
  284   			final Type type,
  285   			final CascadeStyle style,
  286   			final Object anything,
  287   			final boolean isCascadeDeleteEnabled) {
  288   		final String entityName = type.isEntityType()
  289   				? ( (EntityType) type ).getAssociatedEntityName()
  290   				: null;
  291   		if ( style.reallyDoCascade(action) ) { //not really necessary, but good for consistency...
  292   			action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled);
  293   		}
  294   	}
  295   
  296   	/**
  297   	 * Cascade to the collection elements
  298   	 */
  299   	private void cascadeCollectionElements(
  300   			final Object child,
  301   			final CollectionType collectionType,
  302   			final CascadeStyle style,
  303   			final Type elemType,
  304   			final Object anything,
  305   			final boolean isCascadeDeleteEnabled) throws HibernateException {
  306   		// we can't cascade to non-embedded elements
  307   		boolean embeddedElements = eventSource.getEntityMode()!=EntityMode.DOM4J ||
  308   				( (EntityType) collectionType.getElementType( eventSource.getFactory() ) ).isEmbeddedInXML();
  309   		
  310   		boolean reallyDoCascade = style.reallyDoCascade(action) && 
  311   			embeddedElements && child!=CollectionType.UNFETCHED_COLLECTION;
  312   		
  313   		if ( reallyDoCascade ) {
  314   			if ( log.isTraceEnabled() ) {
  315   				log.trace( "cascade " + action + " for collection: " + collectionType.getRole() );
  316   			}
  317   			
  318   			Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType, child);
  319   			while ( iter.hasNext() ) {
  320   				cascadeProperty(
  321   						iter.next(), 
  322   						elemType,
  323   						style, 
  324   						anything, 
  325   						isCascadeDeleteEnabled 
  326   					);
  327   			}
  328   			
  329   			if ( log.isTraceEnabled() ) {
  330   				log.trace( "done cascade " + action + " for collection: " + collectionType.getRole() );
  331   			}
  332   		}
  333   		
  334   		final boolean deleteOrphans = style.hasOrphanDelete() && 
  335   				action.deleteOrphans() && 
  336   				elemType.isEntityType() && 
  337   				child instanceof PersistentCollection; //a newly instantiated collection can't have orphans
  338   		
  339   		if ( deleteOrphans ) { // handle orphaned entities!!
  340   			if ( log.isTraceEnabled() ) {
  341   				log.trace( "deleting orphans for collection: " + collectionType.getRole() );
  342   			}
  343   			
  344   			// we can do the cast since orphan-delete does not apply to:
  345   			// 1. newly instantiated collections
  346   			// 2. arrays (we can't track orphans for detached arrays)
  347   			final String entityName = collectionType.getAssociatedEntityName( eventSource.getFactory() );
  348   			deleteOrphans( entityName, (PersistentCollection) child );
  349   			
  350   			if ( log.isTraceEnabled() ) {
  351   				log.trace( "done deleting orphans for collection: " + collectionType.getRole() );
  352   			}
  353   		}
  354   	}
  355   
  356   	/**
  357   	 * Delete any entities that were removed from the collection
  358   	 */
  359   	private void deleteOrphans(String entityName, PersistentCollection pc) throws HibernateException {
  360   		//TODO: suck this logic into the collection!
  361   		final Collection orphans;
  362   		if ( pc.wasInitialized() ) {
  363   			CollectionEntry ce = eventSource.getPersistenceContext().getCollectionEntry(pc);
  364   			orphans = ce==null ? 
  365   					CollectionHelper.EMPTY_COLLECTION :
  366   					ce.getOrphans(entityName, pc);
  367   		}
  368   		else {
  369   			orphans = pc.getQueuedOrphans(entityName);
  370   		}
  371   		
  372   		final Iterator orphanIter = orphans.iterator();
  373   		while ( orphanIter.hasNext() ) {
  374   			Object orphan = orphanIter.next();
  375   			if (orphan!=null) {
  376   				if ( log.isTraceEnabled() ) {
  377   					log.trace("deleting orphaned entity instance: " + entityName);
  378   				}
  379   				eventSource.delete( entityName, orphan, false, new HashSet() );
  380   			}
  381   		}
  382   	}
  383   
  384   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » engine » [javadoc | source]