Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » engine » [javadoc | source]
    1   //$Id: CollectionLoadContext.java 7764 2005-08-05 16:16:46Z oneovthafew $
    2   package org.hibernate.engine;
    3   
    4   import java.io.Serializable;
    5   import java.util.ArrayList;
    6   import java.util.Comparator;
    7   import java.util.HashMap;
    8   import java.util.Iterator;
    9   import java.util.List;
   10   import java.util.Map;
   11   
   12   import org.apache.commons.logging.Log;
   13   import org.apache.commons.logging.LogFactory;
   14   import org.hibernate.CacheMode;
   15   import org.hibernate.EntityMode;
   16   import org.hibernate.HibernateException;
   17   import org.hibernate.cache.CacheKey;
   18   import org.hibernate.cache.entry.CollectionCacheEntry;
   19   import org.hibernate.collection.PersistentCollection;
   20   import org.hibernate.persister.collection.CollectionPersister;
   21   import org.hibernate.pretty.MessageHelper;
   22   
   23   /**
   24    * Represents the state of collections currently being loaded. Eventually, I
   25    * would like to have multiple instances of this per session - one per JDBC
   26    * result set, instead of the resultSetId being passed.
   27    * @author Gavin King
   28    */
   29   public class CollectionLoadContext {
   30   	
   31   	private static final Log log = LogFactory.getLog(CollectionLoadContext.class);
   32   
   33   	// The collections we are currently loading
   34   	private final Map loadingCollections = new HashMap(8);
   35   	private final PersistenceContext context;
   36   	
   37   	public CollectionLoadContext(PersistenceContext context) {
   38   		this.context = context;
   39   	}
   40   	
   41   	private static final class LoadingCollectionEntry {
   42   
   43   		final PersistentCollection collection;
   44   		final Serializable key;
   45   		final Object resultSetId;
   46   		final CollectionPersister persister;
   47   
   48   		LoadingCollectionEntry(
   49   				final PersistentCollection collection, 
   50   				final Serializable key, 
   51   				final CollectionPersister persister, 
   52   				final Object resultSetId
   53   		) {
   54   			this.collection = collection;
   55   			this.key = key;
   56   			this.persister = persister;
   57   			this.resultSetId = resultSetId;
   58   		}
   59   	}
   60   
   61   	/**
   62   	 * Retrieve a collection that is in the process of being loaded, instantiating
   63   	 * a new collection if there is nothing for the given id, or returning null
   64   	 * if the collection with the given id is already fully loaded in the session
   65   	 */
   66   	public PersistentCollection getLoadingCollection(
   67   			final CollectionPersister persister, 
   68   			final Serializable key, 
   69   			final Object resultSetId, 
   70   			final EntityMode em)
   71   	throws HibernateException {
   72   		CollectionKey ckey = new CollectionKey(persister, key, em);
   73   		LoadingCollectionEntry lce = getLoadingCollectionEntry(ckey);
   74   		if ( lce == null ) {
   75   			//look for existing collection
   76   			PersistentCollection collection = context.getCollection(ckey);
   77   			if ( collection != null ) {
   78   				if ( collection.wasInitialized() ) {
   79   					log.trace( "collection already initialized: ignoring" );
   80   					return null; //ignore this row of results! Note the early exit
   81   				}
   82   				else {
   83   					//initialize this collection
   84   					log.trace( "uninitialized collection: initializing" );
   85   				}
   86   			}
   87   			else {
   88   				Object entity = context.getCollectionOwner(key, persister);
   89   				final boolean newlySavedEntity = entity != null && 
   90   						context.getEntry(entity).getStatus() != Status.LOADING && 
   91   						em!=EntityMode.DOM4J;
   92   				if ( newlySavedEntity ) {
   93   					//important, to account for newly saved entities in query
   94   					//TODO: some kind of check for new status...
   95   					log.trace( "owning entity already loaded: ignoring" );
   96   					return null;
   97   				}
   98   				else {
   99   					//create one
  100   					log.trace( "new collection: instantiating" );
  101   					collection = persister.getCollectionType()
  102   							.instantiate( context.getSession(), persister, key );
  103   				}
  104   			}
  105   			collection.beforeInitialize(persister);
  106   			collection.beginRead();
  107   			addLoadingCollectionEntry(ckey, collection, persister, resultSetId);
  108   			return collection;
  109   		}
  110   		else {
  111   			if ( lce.resultSetId == resultSetId ) {
  112   				log.trace( "reading row" );
  113   				return lce.collection;
  114   			}
  115   			else {
  116   				// ignore this row, the collection is in process of
  117   				// being loaded somewhere further "up" the stack
  118   				log.trace( "collection is already being initialized: ignoring row" );
  119   				return null;
  120   			}
  121   		}
  122   	}
  123   
  124   	/**
  125   	 * Retrieve a collection that is in the process of being loaded, returning null
  126   	 * if there is no loading collection with the given id
  127   	 */
  128   	public PersistentCollection getLoadingCollection(CollectionPersister persister, Serializable id, EntityMode em) {
  129   		LoadingCollectionEntry lce = getLoadingCollectionEntry( new CollectionKey(persister, id, em) );
  130   		if ( lce != null ) {
  131   			if ( log.isTraceEnabled() ) {
  132   				log.trace( 
  133   						"returning loading collection:" + 
  134   						MessageHelper.collectionInfoString(persister, id, context.getSession().getFactory()) 
  135   					);
  136   			}		
  137   			return lce.collection;
  138   		}
  139   		else {
  140   			if ( log.isTraceEnabled() ) {
  141   				log.trace( 
  142   						"creating collection wrapper:" + 
  143   						MessageHelper.collectionInfoString(persister, id, context.getSession().getFactory()) 
  144   					);
  145   			}
  146   			return null;
  147   		}
  148   	}
  149   
  150   	/**
  151   	 * Create a new loading collection entry
  152   	 */
  153   	private void addLoadingCollectionEntry(
  154   			final CollectionKey collectionKey, 
  155   			final PersistentCollection collection, 
  156   			final CollectionPersister persister, 
  157   			final Object resultSetId 
  158   	) {
  159   		loadingCollections.put(
  160   				collectionKey,
  161   				new LoadingCollectionEntry(
  162   						collection,
  163   						collectionKey.getKey(),
  164   						persister,
  165   						resultSetId
  166   					)
  167   			);
  168   	}
  169   
  170   	/**
  171   	 * get an existing new loading collection entry
  172   	 */
  173   	private LoadingCollectionEntry getLoadingCollectionEntry(CollectionKey collectionKey) {
  174   		return ( LoadingCollectionEntry ) loadingCollections.get( collectionKey );
  175   	}
  176   
  177   	/**
  178   	 * After we have finished processing a result set, a particular loading collection that
  179   	 * we are done.
  180   	 */
  181   	private void endLoadingCollection(LoadingCollectionEntry lce, CollectionPersister persister, EntityMode em) {
  182   
  183   		boolean hasNoQueuedAdds = lce.collection.endRead(); //warning: can cause a recursive query! (proxy initialization)
  184   
  185   		if ( persister.getCollectionType().hasHolder(em) ) {
  186   			context.addCollectionHolder(lce.collection);
  187   		}
  188   		
  189   		CollectionEntry ce = context.getCollectionEntry(lce.collection);
  190   		if ( ce==null ) {
  191   			ce = context.addInitializedCollection(persister, lce.collection, lce.key);
  192   		}
  193   		else {
  194   			ce.postInitialize(lce.collection);
  195   		}
  196   
  197   		final SessionImplementor session = context.getSession();
  198   		
  199   		boolean addToCache = hasNoQueuedAdds && // there were no queued additions
  200   				persister.hasCache() &&             // and the role has a cache
  201   				session.getCacheMode().isPutEnabled() &&
  202   				!ce.isDoremove();                   // and this is not a forced initialization during flush
  203   		if (addToCache) addCollectionToCache(lce, persister);
  204   
  205   		if ( log.isDebugEnabled() ) {
  206   			log.debug(
  207   					"collection fully initialized: " +
  208   					MessageHelper.collectionInfoString(persister, lce.key, context.getSession().getFactory())
  209   				);
  210   		}
  211   
  212   		if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
  213   			session.getFactory().getStatisticsImplementor().loadCollection(
  214   					persister.getRole()
  215   				);
  216   		}
  217   
  218   	}
  219   	/**
  220   	 * Finish the process of loading collections for a particular result set
  221   	 */
  222   	public void endLoadingCollections(CollectionPersister persister, Object resultSetId, SessionImplementor session)
  223   	throws HibernateException {
  224   
  225   		// scan the loading collections for collections from this result set
  226   		// put them in a new temp collection so that we are safe from concurrent
  227   		// modification when the call to endRead() causes a proxy to be
  228   		// initialized
  229   		List resultSetCollections = null; //TODO: make this the resultSetId?
  230   		Iterator iter = loadingCollections.values().iterator();
  231   		while ( iter.hasNext() ) {
  232   			LoadingCollectionEntry lce = (LoadingCollectionEntry) iter.next();
  233   			if ( lce.resultSetId == resultSetId && lce.persister==persister) {
  234   				if ( resultSetCollections == null ) {
  235   					resultSetCollections = new ArrayList();
  236   				}
  237   				resultSetCollections.add(lce);
  238   				if ( lce.collection.getOwner()==null ) {
  239   					session.getPersistenceContext()
  240   							.addUnownedCollection( 
  241   									new CollectionKey( persister, lce.key, session.getEntityMode() ), 
  242   									lce.collection
  243   								);
  244   				}
  245   				iter.remove();
  246   			}
  247   		}
  248   
  249   		endLoadingCollections( persister, resultSetCollections, session.getEntityMode() );
  250   	}
  251   	
  252   	/**
  253   	 * After we have finished processing a result set, notify the loading collections that
  254   	 * we are done.
  255   	 */
  256   	private void endLoadingCollections(CollectionPersister persister, List resultSetCollections, EntityMode em)
  257   	throws HibernateException {
  258   
  259   		final int count = (resultSetCollections == null) ? 0 : resultSetCollections.size();
  260   
  261   		if ( log.isDebugEnabled() ) {
  262   			log.debug( count + " collections were found in result set for role: " + persister.getRole() );
  263   		}
  264   
  265   		//now finish them
  266   		for ( int i = 0; i < count; i++ ) {
  267   			LoadingCollectionEntry lce = (LoadingCollectionEntry) resultSetCollections.get(i);
  268   			endLoadingCollection(lce, persister, em);
  269   		}
  270   
  271   		if ( log.isDebugEnabled() ) {
  272   			log.debug( count + " collections initialized for role: " + persister.getRole() );
  273   		}
  274   	}
  275   
  276   	/**
  277   	 * Add a collection to the second-level cache
  278   	 */
  279   	private void addCollectionToCache(LoadingCollectionEntry lce, CollectionPersister persister) {
  280   
  281   		if ( log.isDebugEnabled() ) {
  282   			log.debug(
  283   					"Caching collection: " +
  284   					MessageHelper.collectionInfoString( persister, lce.key, context.getSession().getFactory() )
  285   				);
  286   		}
  287   
  288   		final SessionImplementor session = context.getSession();
  289   		final SessionFactoryImplementor factory = session.getFactory();
  290   
  291   		if ( !session.getEnabledFilters().isEmpty() && persister.isAffectedByEnabledFilters( session ) ) {
  292   			// some filters affecting the collection are enabled on the session, so do not do the put into the cache.
  293   			log.debug( "Refusing to add to cache due to enabled filters" );
  294   			// todo : add the notion of enabled filters to the CacheKey to differentiate filtered collections from non-filtered;
  295   			//      but CacheKey is currently used for both collections and entities; would ideally need to define two seperate ones;
  296   			//      currently this works in conjuction with the check on
  297   			//      DefaultInitializeCollectionEventHandler.initializeCollectionFromCache() (which makes sure to not read from
  298   			//      cache with enabled filters).
  299   			return; // EARLY EXIT!!!!!
  300   		}
  301   
  302   		final Comparator versionComparator;
  303   		final Object version;
  304   		if ( persister.isVersioned() ) {
  305   			versionComparator = persister.getOwnerEntityPersister().getVersionType().getComparator();
  306   			version = context.getEntry( context.getCollectionOwner(lce.key, persister) ).getVersion();
  307   		}
  308   		else {
  309   			version = null;
  310   			versionComparator = null;
  311   		}
  312   		
  313   		CollectionCacheEntry entry = new CollectionCacheEntry(lce.collection, persister);
  314   
  315   		CacheKey cacheKey = new CacheKey( 
  316   				lce.key, 
  317   				persister.getKeyType(), 
  318   				persister.getRole(), 
  319   				session.getEntityMode(), 
  320   				session.getFactory() 
  321   			);
  322   		boolean put = persister.getCache().put(
  323   				cacheKey,
  324   				persister.getCacheEntryStructure().structure(entry),
  325   				session.getTimestamp(),
  326   				version,
  327   				versionComparator,
  328   				factory.getSettings().isMinimalPutsEnabled() && 
  329   						session.getCacheMode()!=CacheMode.REFRESH
  330   			);
  331   
  332   		if ( put && factory.getStatistics().isStatisticsEnabled() ) {
  333   			factory.getStatisticsImplementor().secondLevelCachePut(
  334   					persister.getCache().getRegionName()
  335   				);
  336   		}
  337   	}
  338   
  339   
  340   }

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