Filter:   InfoImg
download Cascades.java
Language: Java
LOC: 170
Project Info
Hibernate
Server: SourceForge
Type: cvs
...te\cirrus\hibernate\engine\
   Batcher.java
   Cascades.java
   Key.java
   Mapping.java
   ...FactoryImplementor.java
   SessionImplementor.java
   Versioning.java

//$Id: Cascades.java,v 1.9 2003/03/24 10:29:01 oneovthafew Exp $
package cirrus.hibernate.engine;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cirrus.hibernate.HibernateException;
import cirrus.hibernate.Session;
import cirrus.hibernate.collections.PersistentCollection;
import cirrus.hibernate.impl.CollectionPersister;
import cirrus.hibernate.persister.ClassPersister;
import cirrus.hibernate.proxy.HibernateProxy;
import cirrus.hibernate.proxy.HibernateProxyHelper;
import cirrus.hibernate.type.AbstractComponentType;
import cirrus.hibernate.type.AssociationType;
import cirrus.hibernate.type.PersistentCollectionType;
import cirrus.hibernate.type.Type;

/** 
 * Implements cascaded save / delete / update
 * @see cirrus.hibernate.type.AssociationType
 */
public final class Cascades {

	private static final Log log = LogFactory.getLog(Cascades.class);

	// The available cascade actions:
	
	/**
	 * A session action that may be cascaded from parent entity to its children
	 */
	public static abstract class CascadingAction {
		protected CascadingAction() {}
		/**
		 * cascade the action to the child object
		 */
		abstract void cascade(Session session, Object child) throws SQLException, HibernateException;
		/**
		 * Should this action be cascaded to the given (possibly uninitialized) collection?
		 */
		abstract boolean shouldCascadeCollection(Object collection);
	}
	
	/**
	 * @see cirrus.hibernate.Session#delete(Object)
	 */
	public static final CascadingAction ACTION_DELETE = new CascadingAction() {
		void cascade(Session session, Object child) throws SQLException, HibernateException {
			log.trace("cascading to delete()");
			session.delete(child);
		}
		boolean shouldCascadeCollection(Object collection) {
 			return true;
 		}
 	};

	/**
	 * @see cirrus.hibernate.Session#saveOrUpdate(Object)
	 */
	public static final CascadingAction ACTION_SAVE_UPDATE = new CascadingAction() {
		void cascade(Session session, Object child) throws SQLException, HibernateException {
			if (
				!(child instanceof HibernateProxy) ||
				!HibernateProxyHelper.getLazyInitializer( (HibernateProxy) child ).isUninitialized()
				// saves / updates don't cascade to uninitialized proxies
			) {
				log.trace("cascading to saveOrUpdate()");
				session.saveOrUpdate(child);
			}
		}
		boolean shouldCascadeCollection(Object collection) {
			return !(collection instanceof PersistentCollection) || ( (PersistentCollection) collection ).wasInitialized();
			// saves / updates don't cascade to uninitialized collections
		}
	};

	// The types of children to cascade to:
	
	/**
	 * A cascade point that occurs just after the insertion of the parent entity and 
	 * just before deletion
	 */
	public static final int CASCADE_AFTER_INSERT_BEFORE_DELETE = 1;
	/**
	 * A cascade point that occurs just before the insertion of the parent entity and 
	 * just after deletion
	 */
	public static final int CASCADE_BEFORE_INSERT_AFTER_DELETE = 2;
	/**
	 * A cascade point that occurs just after the insertion of the parent entity and 
	 * just before deletion, inside a collection
	 */
	public static final int CASCADE_AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION = 3;
	/**
	 * A cascade point that occurs just after the update of the parent entity
	 */
	public static final int CASCADE_ON_UPDATE = 0;
	
	// The allowable cascade styles for a property:

	/**
	 * A style of cascade that can be specified by the mapping for an association.
	 * The style is specified by the <tt>cascade</tt> attribute in the mapping file.
	 */
	public static abstract class CascadeStyle {
		protected CascadeStyle() {}
		/**
		 * Sould the given action be cascaded?
		 */
		abstract boolean doCascade(CascadingAction action);
	};
	/**
	 * save / delete / update
	 */
	public static final CascadeStyle STYLE_ALL = new CascadeStyle() {
		boolean doCascade(CascadingAction action) {
			return true;
		}
	};
	/**
	 * save / update
	 */
	public static final CascadeStyle STYLE_EXCEPT_DELETE = new CascadeStyle() {
		boolean doCascade(CascadingAction action) {
			return action!=ACTION_DELETE;
		}
	};
	/**
	 * delete
	 */
	public static final CascadeStyle STYLE_ONLY_DELETE = new CascadeStyle() {
		boolean doCascade(CascadingAction action) {
			return action==ACTION_DELETE;
		}
	};
	/**
	 * no cascades
	 */
	public static final CascadeStyle STYLE_NONE = new CascadeStyle() {
		boolean doCascade(CascadingAction action) {
			return false;
		}
	};
	
	// The allowable unsaved-value settings:
	
	/**
	 * A strategy for determining if an identifier value is an identifier of
	 * a new transient instance or a previously persistent transient instance.
	 * The strategy is determined by the <tt>unsaved-value</tt> attribute in
	 * the mapping file.
	 */
	public static class IdentifierValue {
		private final Object value;
		protected IdentifierValue() {
			this.value = null;
		}
		/**
		 * Assume the transient instance is newly instantiated if
		 * its identifier is null or equal to <tt>value</tt>
		 */
		public IdentifierValue(Object value) {
			this.value = value;
		}
		/**
		 * Does the given identifier belong to a new instance?
		 */
		public boolean isUnsaved(Serializable id) {
			return id==null || value.equals(id);
		}
	}
	
	/**
	 * Always assume the transient instance is newly instantiated
	 */
	public static final IdentifierValue SAVE_ANY = new IdentifierValue() {
		public final boolean isUnsaved(Serializable id) {
 			return true;
 		}
 	};
	/**
	 * Never assume the transient instance is newly instantiated
	 */
	public static final IdentifierValue SAVE_NONE = new IdentifierValue() {
		public final boolean isUnsaved(Serializable id) {
 			return false;
 		}
 	};
	/**
	 * Assume the transient instance is newly instantiated if the identifier
	 * is null.
	 */
	public static final IdentifierValue SAVE_NULL = new IdentifierValue() {
		public final boolean isUnsaved(Serializable id) {
 			return id==null;
 		}
 	};
 	
 	/**
 	 * Cascade an action to the child
 	 */
	private static void cascade(SessionImplementor session, Object child, Type type, CascadingAction action, int cascadeTo) throws SQLException, HibernateException {
		if (child!=null) {
			if ( type.isAssociationType() ) {
				if ( ( (AssociationType) type ).getForeignKeyType().cascadeNow(cascadeTo) ) {
					if ( type.isEntityType() ) {
						action.cascade(session, child);
					}
					else if ( type.isPersistentCollectionType() ) {
						final int cascadeVia;
						if ( cascadeTo==CASCADE_AFTER_INSERT_BEFORE_DELETE) {
							cascadeVia = CASCADE_AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION;
						}
						else {
							cascadeVia = cascadeTo;
						}
						PersistentCollectionType pctype = (PersistentCollectionType) type;
						CollectionPersister persister = session.getFactory().getCollectionPersister( pctype.getRole() );
						Type elemType = persister.getElementType();
						Iterator iter;
						if ( action.shouldCascadeCollection(child) ) {
							if ( log.isTraceEnabled() ) log.trace( "cascading to collection: " + pctype.getRole() );
							iter = pctype.getElementsIterator(child);
							
						}
						else {
							if (child instanceof PersistentCollection ) {
								PersistentCollection pc = (PersistentCollection) child;
								if ( pc.hasQueuedAdds() ) {
									iter = pc.queuedAddsIterator();
								}
								else {
									iter = null;
								}
							}
							else {
								iter = null;
							}
						}
						if (iter!=null) {
							while ( iter.hasNext() ) cascade( session, iter.next(), elemType, action, cascadeVia );
						}
					}
				}
			}
			else if ( type.isComponentType() ) {
				AbstractComponentType ctype = ( (AbstractComponentType) type );
				Object[] children = ctype.getPropertyValues(child);
				Type[] types = ctype.getSubtypes();
				for ( int i=0; i<types.length; i++ ) {
					if ( ctype.cascade(i).doCascade(action) ) cascade(
						session, children[i], types[i], action, cascadeTo
					);
				}
			}
		}
	}
	
	/**
	 * Cascade an action from the parent object to all its children
	 */
	public static void cascade(SessionImplementor session, ClassPersister persister, Object parent, Cascades.CascadingAction action, int cascadeTo) throws SQLException, HibernateException {
		if ( persister.hasCascades() ) { // performance opt
			if ( log.isTraceEnabled() ) log.trace( "processing cascades for: " + persister.getClassName() );
			Type[] types = persister.getPropertyTypes();
			Cascades.CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles();
			for ( int i=0; i<types.length; i++) {
				if ( cascadeStyles[i].doCascade(action) )
					cascade( session, persister.getPropertyValue(parent,i), types[i], action, cascadeTo );
			}
			if ( log.isTraceEnabled() ) log.trace( "done processing cascades for: " + persister.getClassName() );
		}
	}

}