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

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