Save This Page
Home » Hibernate-3.3.2.GA » 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.Iterator;
   29   import java.util.List;
   30   import java.util.Map;
   31   
   32   import org.slf4j.Logger;
   33   import org.slf4j.LoggerFactory;
   34   import org.hibernate.HibernateException;
   35   import org.hibernate.action.CollectionRecreateAction;
   36   import org.hibernate.action.CollectionRemoveAction;
   37   import org.hibernate.action.CollectionUpdateAction;
   38   import org.hibernate.collection.PersistentCollection;
   39   import org.hibernate.engine.ActionQueue;
   40   import org.hibernate.engine.Cascade;
   41   import org.hibernate.engine.CascadingAction;
   42   import org.hibernate.engine.CollectionEntry;
   43   import org.hibernate.engine.CollectionKey;
   44   import org.hibernate.engine.Collections;
   45   import org.hibernate.engine.EntityEntry;
   46   import org.hibernate.engine.PersistenceContext;
   47   import org.hibernate.engine.Status;
   48   import org.hibernate.event.EventSource;
   49   import org.hibernate.event.FlushEntityEvent;
   50   import org.hibernate.event.FlushEntityEventListener;
   51   import org.hibernate.event.FlushEvent;
   52   import org.hibernate.engine.SessionImplementor;
   53   import org.hibernate.persister.entity.EntityPersister;
   54   import org.hibernate.pretty.Printer;
   55   import org.hibernate.util.IdentityMap;
   56   import org.hibernate.util.LazyIterator;
   57   
   58   /**
   59    * A convenience base class for listeners whose functionality results in flushing.
   60    *
   61    * @author Steve Eberole
   62    */
   63   public abstract class AbstractFlushingEventListener implements Serializable {
   64   
   65   	private static final Logger log = LoggerFactory.getLogger(AbstractFlushingEventListener.class);
   66   	
   67   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   68   	// Pre-flushing section
   69   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   70   
   71   	/** 
   72   	 * Coordinates the processing necessary to get things ready for executions
   73   	 * as db calls by preping the session caches and moving the appropriate
   74   	 * entities and collections to their respective execution queues.
   75   	 *
   76   	 * @param event The flush event.
   77   	 * @throws HibernateException Error flushing caches to execution queues.
   78   	 */
   79   	protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
   80   
   81   		log.trace("flushing session");
   82   		
   83   		EventSource session = event.getSession();
   84   		
   85   		final PersistenceContext persistenceContext = session.getPersistenceContext();
   86   		session.getInterceptor().preFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) );
   87   
   88   		prepareEntityFlushes(session);
   89   		// we could move this inside if we wanted to
   90   		// tolerate collection initializations during
   91   		// collection dirty checking:
   92   		prepareCollectionFlushes(session);
   93   		// now, any collections that are initialized
   94   		// inside this block do not get updated - they
   95   		// are ignored until the next flush
   96   				
   97   		persistenceContext.setFlushing(true);
   98   		try {
   99   			flushEntities(event);
  100   			flushCollections(session);
  101   		}
  102   		finally {
  103   			persistenceContext.setFlushing(false);
  104   		}
  105   
  106   		//some statistics
  107   		if ( log.isDebugEnabled() ) {
  108   			log.debug( "Flushed: " +
  109   					session.getActionQueue().numberOfInsertions() + " insertions, " +
  110   					session.getActionQueue().numberOfUpdates() + " updates, " +
  111   					session.getActionQueue().numberOfDeletions() + " deletions to " +
  112   					persistenceContext.getEntityEntries().size() + " objects"
  113   				);
  114   			log.debug( "Flushed: " +
  115   					session.getActionQueue().numberOfCollectionCreations() + " (re)creations, " +
  116   					session.getActionQueue().numberOfCollectionUpdates() + " updates, " +
  117   					session.getActionQueue().numberOfCollectionRemovals() + " removals to " +
  118   					persistenceContext.getCollectionEntries().size() + " collections"
  119   				);
  120   			new Printer( session.getFactory() ).toString( 
  121   					persistenceContext.getEntitiesByKey().values().iterator(), 
  122   					session.getEntityMode() 
  123   				);
  124   		}
  125   	}
  126   
  127   	/**
  128   	 * process cascade save/update at the start of a flush to discover
  129   	 * any newly referenced entity that must be passed to saveOrUpdate(),
  130   	 * and also apply orphan delete
  131   	 */
  132   	private void prepareEntityFlushes(EventSource session) throws HibernateException {
  133   		
  134   		log.debug("processing flush-time cascades");
  135   
  136   		final Map.Entry[] list = IdentityMap.concurrentEntries( session.getPersistenceContext().getEntityEntries() );
  137   		//safe from concurrent modification because of how entryList() is implemented on IdentityMap
  138   		final int size = list.length;
  139   		final Object anything = getAnything();
  140   		for ( int i=0; i<size; i++ ) {
  141   			Map.Entry me = list[i];
  142   			EntityEntry entry = (EntityEntry) me.getValue();
  143   			Status status = entry.getStatus();
  144   			if ( status == Status.MANAGED || status == Status.SAVING ) {
  145   				cascadeOnFlush( session, entry.getPersister(), me.getKey(), anything );
  146   			}
  147   		}
  148   	}
  149   	
  150   	private void cascadeOnFlush(EventSource session, EntityPersister persister, Object object, Object anything) 
  151   	throws HibernateException {
  152   		session.getPersistenceContext().incrementCascadeLevel();
  153   		try {
  154   			new Cascade( getCascadingAction(), Cascade.BEFORE_FLUSH, session )
  155   			.cascade( persister, object, anything );
  156   		}
  157   		finally {
  158   			session.getPersistenceContext().decrementCascadeLevel();
  159   		}
  160   	}
  161   	
  162   	protected Object getAnything() { return null; }
  163   	
  164   	protected CascadingAction getCascadingAction() {
  165   		return CascadingAction.SAVE_UPDATE;
  166   	}
  167   
  168   	/**
  169   	 * Initialize the flags of the CollectionEntry, including the
  170   	 * dirty check.
  171   	 */
  172   	private void prepareCollectionFlushes(SessionImplementor session) throws HibernateException {
  173   
  174   		// Initialize dirty flags for arrays + collections with composite elements
  175   		// and reset reached, doupdate, etc.
  176   		
  177   		log.debug("dirty checking collections");
  178   
  179   		final List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
  180   		final int size = list.size();
  181   		for ( int i = 0; i < size; i++ ) {
  182   			Map.Entry e = ( Map.Entry ) list.get( i );
  183   			( (CollectionEntry) e.getValue() ).preFlush( (PersistentCollection) e.getKey() );
  184   		}
  185   	}
  186   
  187   	/**
  188   	 * 1. detect any dirty entities
  189   	 * 2. schedule any entity updates
  190   	 * 3. search out any reachable collections
  191   	 */
  192   	private void flushEntities(FlushEvent event) throws HibernateException {
  193   
  194   		log.trace("Flushing entities and processing referenced collections");
  195   
  196   		// Among other things, updateReachables() will recursively load all
  197   		// collections that are moving roles. This might cause entities to
  198   		// be loaded.
  199   
  200   		// So this needs to be safe from concurrent modification problems.
  201   		// It is safe because of how IdentityMap implements entrySet()
  202   
  203   		final EventSource source = event.getSession();
  204   		
  205   		final Map.Entry[] list = IdentityMap.concurrentEntries( source.getPersistenceContext().getEntityEntries() );
  206   		final int size = list.length;
  207   		for ( int i = 0; i < size; i++ ) {
  208   
  209   			// Update the status of the object and if necessary, schedule an update
  210   
  211   			Map.Entry me = list[i];
  212   			EntityEntry entry = (EntityEntry) me.getValue();
  213   			Status status = entry.getStatus();
  214   
  215   			if ( status != Status.LOADING && status != Status.GONE ) {
  216   				FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry );
  217   				FlushEntityEventListener[] listeners = source.getListeners().getFlushEntityEventListeners();
  218   				for ( int j = 0; j < listeners.length; j++ ) {
  219   					listeners[j].onFlushEntity(entityEvent);
  220   				}
  221   			}
  222   		}
  223   
  224   		source.getActionQueue().sortActions();
  225   	}
  226   
  227   	/**
  228   	 * process any unreferenced collections and then inspect all known collections,
  229   	 * scheduling creates/removes/updates
  230   	 */
  231   	private void flushCollections(EventSource session) throws HibernateException {
  232   
  233   		log.trace("Processing unreferenced collections");
  234   
  235   		List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
  236   		int size = list.size();
  237   		for ( int i = 0; i < size; i++ ) {
  238   			Map.Entry me = ( Map.Entry ) list.get( i );
  239   			CollectionEntry ce = (CollectionEntry) me.getValue();
  240   			if ( !ce.isReached() && !ce.isIgnore() ) {
  241   				Collections.processUnreachableCollection( (PersistentCollection) me.getKey(), session );
  242   			}
  243   		}
  244   
  245   		// Schedule updates to collections:
  246   
  247   		log.trace( "Scheduling collection removes/(re)creates/updates" );
  248   
  249   		list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
  250   		size = list.size();
  251   		ActionQueue actionQueue = session.getActionQueue();
  252   		for ( int i = 0; i < size; i++ ) {
  253   			Map.Entry me = (Map.Entry) list.get(i);
  254   			PersistentCollection coll = (PersistentCollection) me.getKey();
  255   			CollectionEntry ce = (CollectionEntry) me.getValue();
  256   
  257   			if ( ce.isDorecreate() ) {
  258   				session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() );
  259   				actionQueue.addAction(
  260   						new CollectionRecreateAction( 
  261   								coll, 
  262   								ce.getCurrentPersister(), 
  263   								ce.getCurrentKey(), 
  264   								session 
  265   							)
  266   					);
  267   			}
  268   			if ( ce.isDoremove() ) {
  269   				session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() );
  270   				actionQueue.addAction(
  271   						new CollectionRemoveAction( 
  272   								coll, 
  273   								ce.getLoadedPersister(), 
  274   								ce.getLoadedKey(), 
  275   								ce.isSnapshotEmpty(coll), 
  276   								session 
  277   							)
  278   					);
  279   			}
  280   			if ( ce.isDoupdate() ) {
  281   				session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() );
  282   				actionQueue.addAction(
  283   						new CollectionUpdateAction( 
  284   								coll, 
  285   								ce.getLoadedPersister(), 
  286   								ce.getLoadedKey(), 
  287   								ce.isSnapshotEmpty(coll), 
  288   								session 
  289   							)
  290   					);
  291   			}
  292   
  293   		}
  294   
  295   		actionQueue.sortCollectionActions();
  296   		
  297   	}
  298   
  299   	/**
  300   	 * Execute all SQL and second-level cache updates, in a
  301   	 * special order so that foreign-key constraints cannot
  302   	 * be violated:
  303   	 * <ol>
  304   	 * <li> Inserts, in the order they were performed
  305   	 * <li> Updates
  306   	 * <li> Deletion of collection elements
  307   	 * <li> Insertion of collection elements
  308   	 * <li> Deletes, in the order they were performed
  309   	 * </ol>
  310   	 */
  311   	protected void performExecutions(EventSource session) throws HibernateException {
  312   
  313   		log.trace("executing flush");
  314   
  315   		try {
  316   			session.getJDBCContext().getConnectionManager().flushBeginning();
  317   			// we need to lock the collection caches before
  318   			// executing entity inserts/updates in order to
  319   			// account for bidi associations
  320   			session.getActionQueue().prepareActions();
  321   			session.getActionQueue().executeActions();
  322   		}
  323   		catch (HibernateException he) {
  324   			log.error("Could not synchronize database state with session", he);
  325   			throw he;
  326   		}
  327   		finally {
  328   			session.getJDBCContext().getConnectionManager().flushEnding();
  329   		}
  330   	}
  331   
  332   
  333   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  334   	// Post-flushing section
  335   	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  336   
  337   	/**
  338   	 * 1. Recreate the collection key -> collection map
  339   	 * 2. rebuild the collection entries
  340   	 * 3. call Interceptor.postFlush()
  341   	 */
  342   	protected void postFlush(SessionImplementor session) throws HibernateException {
  343   
  344   		log.trace( "post flush" );
  345   
  346   		final PersistenceContext persistenceContext = session.getPersistenceContext();
  347   		persistenceContext.getCollectionsByKey().clear();
  348   		persistenceContext.getBatchFetchQueue()
  349   				.clearSubselects(); //the database has changed now, so the subselect results need to be invalidated
  350   
  351   		Iterator iter = persistenceContext.getCollectionEntries().entrySet().iterator();
  352   		while ( iter.hasNext() ) {
  353   			Map.Entry me = (Map.Entry) iter.next();
  354   			CollectionEntry collectionEntry = (CollectionEntry) me.getValue();
  355   			PersistentCollection persistentCollection = (PersistentCollection) me.getKey();
  356   			collectionEntry.postFlush(persistentCollection);
  357   			if ( collectionEntry.getLoadedPersister() == null ) {
  358   				//if the collection is dereferenced, remove from the session cache
  359   				//iter.remove(); //does not work, since the entrySet is not backed by the set
  360   				persistenceContext.getCollectionEntries()
  361   						.remove(persistentCollection);
  362   			}
  363   			else {
  364   				//otherwise recreate the mapping between the collection and its key
  365   				CollectionKey collectionKey = new CollectionKey( 
  366   						collectionEntry.getLoadedPersister(), 
  367   						collectionEntry.getLoadedKey(), 
  368   						session.getEntityMode() 
  369   					);
  370   				persistenceContext.getCollectionsByKey()
  371   						.put(collectionKey, persistentCollection);
  372   			}
  373   		}
  374   		
  375   		session.getInterceptor().postFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) );
  376   
  377   	}
  378   
  379   }

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