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.Iterator;
   28   import java.util.Map;
   29   import java.util.Set;
   30   
   31   import org.slf4j.Logger;
   32   import org.slf4j.LoggerFactory;
   33   import org.hibernate.HibernateException;
   34   import org.hibernate.LockMode;
   35   import org.hibernate.ReplicationMode;
   36   import org.hibernate.TransientObjectException;
   37   import org.hibernate.proxy.HibernateProxy;
   38   import org.hibernate.persister.entity.EntityPersister;
   39   import org.hibernate.collection.PersistentCollection;
   40   import org.hibernate.event.EventSource;
   41   import org.hibernate.type.CollectionType;
   42   import org.hibernate.type.Type;
   43   import org.hibernate.type.EntityType;
   44   
   45   /**
   46    * A session action that may be cascaded from parent entity to its children
   47    *
   48    * @author Gavin King
   49    */
   50   public abstract class CascadingAction {
   51   
   52   	private static final Logger log = LoggerFactory.getLogger( CascadingAction.class );
   53   
   54   
   55   	// the CascadingAction contract ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   56   
   57   	/**
   58   	 * protected constructor
   59   	 */
   60   	CascadingAction() {
   61   	}
   62   
   63   	/**
   64   	 * Cascade the action to the child object.
   65   	 *
   66   	 * @param session The session within which the cascade is occuring.
   67   	 * @param child The child to which cascading should be performed.
   68   	 * @param entityName The child's entity name
   69   	 * @param anything Anything ;)  Typically some form of cascade-local cache
   70   	 * which is specific to each CascadingAction type
   71   	 * @param isCascadeDeleteEnabled Are cascading deletes enabled.
   72   	 * @throws HibernateException
   73   	 */
   74   	public abstract void cascade(
   75   			EventSource session,
   76   			Object child,
   77   			String entityName,
   78   			Object anything,
   79   			boolean isCascadeDeleteEnabled) throws HibernateException;
   80   
   81   	/**
   82   	 * Given a collection, get an iterator of the children upon which the
   83   	 * current cascading action should be visited.
   84   	 *
   85   	 * @param session The session within which the cascade is occuring.
   86   	 * @param collectionType The mapping type of the collection.
   87   	 * @param collection The collection instance.
   88   	 * @return The children iterator.
   89   	 */
   90   	public abstract Iterator getCascadableChildrenIterator(
   91   			EventSource session,
   92   			CollectionType collectionType,
   93   			Object collection);
   94   
   95   	/**
   96   	 * Does this action potentially extrapolate to orphan deletes?
   97   	 *
   98   	 * @return True if this action can lead to deletions of orphans.
   99   	 */
  100   	public abstract boolean deleteOrphans();
  101   
  102   
  103   	/**
  104   	 * Does the specified cascading action require verification of no cascade validity?
  105   	 *
  106   	 * @return True if this action requires no-cascade verification; false otherwise.
  107   	 */
  108   	public boolean requiresNoCascadeChecking() {
  109   		return false;
  110   	}
  111   
  112   	/**
  113   	 * Called (in the case of {@link #requiresNoCascadeChecking} returning true) to validate
  114   	 * that no cascade on the given property is considered a valid semantic.
  115   	 *
  116   	 * @param session The session witin which the cascade is occurring.
  117   	 * @param child The property value
  118   	 * @param parent The property value owner
  119   	 * @param persister The entity persister for the owner
  120   	 * @param propertyIndex The index of the property within the owner.
  121   	 */
  122   	public void noCascade(EventSource session, Object child, Object parent, EntityPersister persister, int propertyIndex) {
  123   	}
  124   
  125   	/**
  126   	 * Should this action be performed (or noCascade consulted) in the case of lazy properties.
  127   	 */
  128   	public boolean performOnLazyProperty() {
  129   		return true;
  130   	}
  131   
  132   
  133   	// the CascadingAction implementations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  134   
  135   	/**
  136   	 * @see org.hibernate.Session#delete(Object)
  137   	 */
  138   	public static final CascadingAction DELETE = new CascadingAction() {
  139   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  140   		throws HibernateException {
  141   			if ( log.isTraceEnabled() ) {
  142   				log.trace("cascading to delete: " + entityName);
  143   			}
  144   			session.delete( entityName, child, isCascadeDeleteEnabled, ( Set ) anything );
  145   		}
  146   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  147   			// delete does cascade to uninitialized collections
  148   			return CascadingAction.getAllElementsIterator(session, collectionType, collection);
  149   		}
  150   		public boolean deleteOrphans() {
  151   			// orphans should be deleted during delete
  152   			return true;
  153   		}
  154   		public String toString() {
  155   			return "ACTION_DELETE";
  156   		}
  157   	};
  158   
  159   	/**
  160   	 * @see org.hibernate.Session#lock(Object, LockMode)
  161   	 */
  162   	public static final CascadingAction LOCK = new CascadingAction() {
  163   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  164   		throws HibernateException {
  165   			if ( log.isTraceEnabled() ) {
  166   				log.trace( "cascading to lock: " + entityName );
  167   			}
  168   			session.lock( entityName, child, LockMode.NONE/*(LockMode) anything*/ );
  169   		}
  170   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  171   			// lock doesn't cascade to uninitialized collections
  172   			return getLoadedElementsIterator(session, collectionType, collection);
  173   		}
  174   		public boolean deleteOrphans() {
  175   			//TODO: should orphans really be deleted during lock???
  176   			return false;
  177   		}
  178   		public String toString() {
  179   			return "ACTION_LOCK";
  180   		}
  181   	};
  182   
  183   	/**
  184   	 * @see org.hibernate.Session#refresh(Object)
  185   	 */
  186   	public static final CascadingAction REFRESH = new CascadingAction() {
  187   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  188   		throws HibernateException {
  189   			if ( log.isTraceEnabled() ) {
  190   				log.trace( "cascading to refresh: " + entityName );
  191   			}
  192   			session.refresh( child, (Map) anything );
  193   		}
  194   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  195   			// refresh doesn't cascade to uninitialized collections
  196   			return getLoadedElementsIterator(session, collectionType, collection);
  197   		}
  198   		public boolean deleteOrphans() {
  199   			return false;
  200   		}
  201   		public String toString() {
  202   			return "ACTION_REFRESH";
  203   		}
  204   	};
  205   
  206   	/**
  207   	 * @see org.hibernate.Session#evict(Object)
  208   	 */
  209   	public static final CascadingAction EVICT = new CascadingAction() {
  210   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  211   		throws HibernateException {
  212   			if ( log.isTraceEnabled() ) {
  213   				log.trace( "cascading to evict: " + entityName );
  214   			}
  215   			session.evict(child);
  216   		}
  217   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  218   			// evicts don't cascade to uninitialized collections
  219   			return getLoadedElementsIterator(session, collectionType, collection);
  220   		}
  221   		public boolean deleteOrphans() {
  222   			return false;
  223   		}
  224   		public boolean performOnLazyProperty() {
  225   			return false;
  226   		}
  227   		public String toString() {
  228   			return "ACTION_EVICT";
  229   		}
  230   	};
  231   
  232   	/**
  233   	 * @see org.hibernate.Session#saveOrUpdate(Object)
  234   	 */
  235   	public static final CascadingAction SAVE_UPDATE = new CascadingAction() {
  236   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  237   		throws HibernateException {
  238   			if ( log.isTraceEnabled() ) {
  239   				log.trace( "cascading to saveOrUpdate: " + entityName );
  240   			}
  241   			session.saveOrUpdate(entityName, child);
  242   		}
  243   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  244   			// saves / updates don't cascade to uninitialized collections
  245   			return getLoadedElementsIterator(session, collectionType, collection);
  246   		}
  247   		public boolean deleteOrphans() {
  248   			// orphans should be deleted during save/update
  249   			return true;
  250   		}
  251   		public boolean performOnLazyProperty() {
  252   			return false;
  253   		}
  254   		public String toString() {
  255   			return "ACTION_SAVE_UPDATE";
  256   		}
  257   	};
  258   
  259   	/**
  260   	 * @see org.hibernate.Session#merge(Object)
  261   	 */
  262   	public static final CascadingAction MERGE = new CascadingAction() {
  263   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  264   		throws HibernateException {
  265   			if ( log.isTraceEnabled() ) {
  266   				log.trace( "cascading to merge: " + entityName );
  267   			}
  268   			session.merge( entityName, child, (Map) anything );
  269   		}
  270   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  271   			// merges don't cascade to uninitialized collections
  272   //			//TODO: perhaps this does need to cascade after all....
  273   			return getLoadedElementsIterator(session, collectionType, collection);
  274   		}
  275   		public boolean deleteOrphans() {
  276   			// orphans should not be deleted during merge??
  277   			return false;
  278   		}
  279   		public String toString() {
  280   			return "ACTION_MERGE";
  281   		}
  282   	};
  283   
  284   	/**
  285   	 * @see org.hibernate.classic.Session#saveOrUpdateCopy(Object)
  286   	 */
  287   	public static final CascadingAction SAVE_UPDATE_COPY = new CascadingAction() {
  288   		// for deprecated saveOrUpdateCopy()
  289   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  290   		throws HibernateException {
  291   			if ( log.isTraceEnabled() ) {
  292   				log.trace( "cascading to saveOrUpdateCopy: " + entityName );
  293   			}
  294   			session.saveOrUpdateCopy( entityName, child, (Map) anything );
  295   		}
  296   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  297   			// saves / updates don't cascade to uninitialized collections
  298   			return getLoadedElementsIterator(session, collectionType, collection);
  299   		}
  300   		public boolean deleteOrphans() {
  301   			// orphans should not be deleted during copy??
  302   			return false;
  303   		}
  304   		public String toString() {
  305   			return "ACTION_SAVE_UPDATE_COPY";
  306   		}
  307   	};
  308   
  309   	/**
  310   	 * @see org.hibernate.Session#persist(Object)
  311   	 */
  312   	public static final CascadingAction PERSIST = new CascadingAction() {
  313   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  314   		throws HibernateException {
  315   			if ( log.isTraceEnabled() ) {
  316   				log.trace( "cascading to persist: " + entityName );
  317   			}
  318   			session.persist( entityName, child, (Map) anything );
  319   		}
  320   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  321   			// persists don't cascade to uninitialized collections
  322   			return CascadingAction.getAllElementsIterator(session, collectionType, collection);
  323   		}
  324   		public boolean deleteOrphans() {
  325   			return false;
  326   		}
  327   		public boolean performOnLazyProperty() {
  328   			return false;
  329   		}
  330   		public String toString() {
  331   			return "ACTION_PERSIST";
  332   		}
  333   	};
  334   
  335   	/**
  336   	 * Execute persist during flush time
  337   	 *
  338   	 * @see org.hibernate.Session#persist(Object)
  339   	 */
  340   	public static final CascadingAction PERSIST_ON_FLUSH = new CascadingAction() {
  341   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  342   		throws HibernateException {
  343   			if ( log.isTraceEnabled() ) {
  344   				log.trace( "cascading to persistOnFlush: " + entityName );
  345   			}
  346   			session.persistOnFlush( entityName, child, (Map) anything );
  347   		}
  348   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  349   			// persists don't cascade to uninitialized collections
  350   			return CascadingAction.getLoadedElementsIterator(session, collectionType, collection);
  351   		}
  352   		public boolean deleteOrphans() {
  353   			return true;
  354   		}
  355   		public boolean requiresNoCascadeChecking() {
  356   			return true;
  357   		}
  358   		public void noCascade(
  359   				EventSource session,
  360   				Object child,
  361   				Object parent,
  362   				EntityPersister persister,
  363   				int propertyIndex) {
  364   			if ( child == null ) {
  365   				return;
  366   			}
  367   			Type type = persister.getPropertyTypes()[propertyIndex];
  368   			if ( type.isEntityType() ) {
  369   				String childEntityName = ( ( EntityType ) type ).getAssociatedEntityName( session.getFactory() );
  370   
  371   				if ( ! isInManagedState( child, session )
  372   						&& ! ( child instanceof HibernateProxy ) //a proxy cannot be transient and it breaks ForeignKeys.isTransient
  373   						&& ForeignKeys.isTransient( childEntityName, child, null, session ) ) {
  374   					String parentEntiytName = persister.getEntityName();
  375   					String propertyName = persister.getPropertyNames()[propertyIndex];
  376   					throw new TransientObjectException(
  377   							"object references an unsaved transient instance - " +
  378   							"save the transient instance before flushing: " +
  379   							parentEntiytName + "." + propertyName + " -> " + childEntityName
  380   					);
  381   
  382   				}
  383   			}
  384   		}
  385   		public boolean performOnLazyProperty() {
  386   			return false;
  387   		}
  388   
  389   		private boolean isInManagedState(Object child, EventSource session) {
  390   			EntityEntry entry = session.getPersistenceContext().getEntry( child );
  391   			return entry != null && (entry.getStatus() == Status.MANAGED || entry.getStatus() == Status.READ_ONLY);
  392   		}
  393   
  394   		public String toString() {
  395   			return "ACTION_PERSIST_ON_FLUSH";
  396   		}
  397   	};
  398   
  399   	/**
  400   	 * @see org.hibernate.Session#replicate(Object, org.hibernate.ReplicationMode)
  401   	 */
  402   	public static final CascadingAction REPLICATE = new CascadingAction() {
  403   		public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
  404   		throws HibernateException {
  405   			if ( log.isTraceEnabled() ) {
  406   				log.trace( "cascading to replicate: " + entityName );
  407   			}
  408   			session.replicate( entityName, child, (ReplicationMode) anything );
  409   		}
  410   		public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
  411   			// replicate does cascade to uninitialized collections
  412   			return getLoadedElementsIterator(session, collectionType, collection);
  413   		}
  414   		public boolean deleteOrphans() {
  415   			return false; //I suppose?
  416   		}
  417   		public String toString() {
  418   			return "ACTION_REPLICATE";
  419   		}
  420   	};
  421   
  422   
  423   	// static helper methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  424   
  425   	/**
  426   	 * Given a collection, get an iterator of all its children, loading them
  427   	 * from the database if necessary.
  428   	 *
  429   	 * @param session The session within which the cascade is occuring.
  430   	 * @param collectionType The mapping type of the collection.
  431   	 * @param collection The collection instance.
  432   	 * @return The children iterator.
  433   	 */
  434   	private static Iterator getAllElementsIterator(
  435   			EventSource session,
  436   			CollectionType collectionType,
  437   			Object collection) {
  438   		return collectionType.getElementsIterator( collection, session );
  439   	}
  440   
  441   	/**
  442   	 * Iterate just the elements of the collection that are already there. Don't load
  443   	 * any new elements from the database.
  444   	 */
  445   	public static Iterator getLoadedElementsIterator(SessionImplementor session, CollectionType collectionType, Object collection) {
  446   		if ( collectionIsInitialized(collection) ) {
  447   			// handles arrays and newly instantiated collections
  448   			return collectionType.getElementsIterator(collection, session);
  449   		}
  450   		else {
  451   			// does not handle arrays (thats ok, cos they can't be lazy)
  452   			// or newly instantiated collections, so we can do the cast
  453   			return ( (PersistentCollection) collection ).queuedAdditionIterator();
  454   		}
  455   	}
  456   
  457   	private static boolean collectionIsInitialized(Object collection) {
  458   		return !(collection instanceof PersistentCollection) || ( (PersistentCollection) collection ).wasInitialized();
  459   	}
  460   
  461   }

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