Save This Page
Home » Hibernate-3.3.2.GA » 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.io.IOException;
   28   import java.io.InvalidObjectException;
   29   import java.io.ObjectInputStream;
   30   import java.io.ObjectOutputStream;
   31   import java.io.Serializable;
   32   import java.util.ArrayList;
   33   import java.util.HashMap;
   34   import java.util.HashSet;
   35   import java.util.Iterator;
   36   import java.util.List;
   37   import java.util.Map;
   38   
   39   import org.apache.commons.collections.map.ReferenceMap;
   40   import org.slf4j.Logger;
   41   import org.slf4j.LoggerFactory;
   42   import org.hibernate.AssertionFailure;
   43   import org.hibernate.Hibernate;
   44   import org.hibernate.HibernateException;
   45   import org.hibernate.LockMode;
   46   import org.hibernate.MappingException;
   47   import org.hibernate.NonUniqueObjectException;
   48   import org.hibernate.PersistentObjectException;
   49   import org.hibernate.TransientObjectException;
   50   import org.hibernate.engine.loading.LoadContexts;
   51   import org.hibernate.pretty.MessageHelper;
   52   import org.hibernate.collection.PersistentCollection;
   53   import org.hibernate.persister.collection.CollectionPersister;
   54   import org.hibernate.persister.entity.EntityPersister;
   55   import org.hibernate.proxy.HibernateProxy;
   56   import org.hibernate.proxy.LazyInitializer;
   57   import org.hibernate.tuple.ElementWrapper;
   58   import org.hibernate.util.IdentityMap;
   59   import org.hibernate.util.MarkerObject;
   60   
   61   /**
   62    * A <tt>PersistenceContext</tt> represents the state of persistent "stuff" which
   63    * Hibernate is tracking.  This includes persistent entities, collections,
   64    * as well as proxies generated.
   65    * </p>
   66    * There is meant to be a one-to-one correspondence between a SessionImpl and
   67    * a PersistentContext.  The SessionImpl uses the PersistentContext to track
   68    * the current state of its context.  Event-listeners then use the
   69    * PersistentContext to drive their processing.
   70    *
   71    * @author Steve Ebersole
   72    */
   73   public class StatefulPersistenceContext implements PersistenceContext {
   74   
   75   	public static final Object NO_ROW = new MarkerObject( "NO_ROW" );
   76   
   77   	private static final Logger log = LoggerFactory.getLogger( StatefulPersistenceContext.class );
   78   	private static final Logger PROXY_WARN_LOG = LoggerFactory.getLogger( StatefulPersistenceContext.class.getName() + ".ProxyWarnLog" );
   79   	private static final int INIT_COLL_SIZE = 8;
   80   
   81   	private SessionImplementor session;
   82   	
   83   	// Loaded entity instances, by EntityKey
   84   	private Map entitiesByKey;
   85   
   86   	// Loaded entity instances, by EntityUniqueKey
   87   	private Map entitiesByUniqueKey;
   88   	
   89   	// Identity map of EntityEntry instances, by the entity instance
   90   	private Map entityEntries;
   91   	
   92   	// Entity proxies, by EntityKey
   93   	private Map proxiesByKey;
   94   	
   95   	// Snapshots of current database state for entities
   96   	// that have *not* been loaded
   97   	private Map entitySnapshotsByKey;
   98   	
   99   	// Identity map of array holder ArrayHolder instances, by the array instance
  100   	private Map arrayHolders;
  101   	
  102   	// Identity map of CollectionEntry instances, by the collection wrapper
  103   	private Map collectionEntries;
  104   	
  105   	// Collection wrappers, by the CollectionKey
  106   	private Map collectionsByKey; //key=CollectionKey, value=PersistentCollection
  107   	
  108   	// Set of EntityKeys of deleted objects
  109   	private HashSet nullifiableEntityKeys;
  110   	
  111   	// properties that we have tried to load, and not found in the database
  112   	private HashSet nullAssociations;
  113   	
  114   	// A list of collection wrappers that were instantiating during result set
  115   	// processing, that we will need to initialize at the end of the query
  116   	private List nonlazyCollections;
  117   	
  118   	// A container for collections we load up when the owning entity is not
  119   	// yet loaded ... for now, this is purely transient!
  120   	private Map unownedCollections;
  121   	
  122   	private int cascading = 0;
  123   	private int loadCounter = 0;
  124   	private boolean flushing = false;
  125   	
  126   	private boolean hasNonReadOnlyEntities = false;
  127   
  128   	private LoadContexts loadContexts;
  129   	private BatchFetchQueue batchFetchQueue;
  130   
  131   
  132   
  133   	/**
  134   	 * Constructs a PersistentContext, bound to the given session.
  135   	 *
  136   	 * @param session The session "owning" this context.
  137   	 */
  138   	public StatefulPersistenceContext(SessionImplementor session) {
  139   		this.session = session;
  140   
  141   		entitiesByKey = new HashMap( INIT_COLL_SIZE );
  142   		entitiesByUniqueKey = new HashMap( INIT_COLL_SIZE );
  143   		proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK );
  144   		entitySnapshotsByKey = new HashMap( INIT_COLL_SIZE );
  145   
  146   		entityEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
  147   		collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
  148   		collectionsByKey = new HashMap( INIT_COLL_SIZE );
  149   		arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE );
  150   
  151   		nullifiableEntityKeys = new HashSet();
  152   
  153   		initTransientState();
  154   	}
  155   
  156   	private void initTransientState() {
  157   		nullAssociations = new HashSet( INIT_COLL_SIZE );
  158   		nonlazyCollections = new ArrayList( INIT_COLL_SIZE );
  159   	}
  160   
  161   	public boolean isStateless() {
  162   		return false;
  163   	}
  164   	
  165   	public SessionImplementor getSession() {
  166   		return session;
  167   	}
  168   
  169   	public LoadContexts getLoadContexts() {
  170   		if ( loadContexts == null ) {
  171   			loadContexts = new LoadContexts( this );
  172   		}
  173   		return loadContexts;
  174   	}
  175   
  176   	public void addUnownedCollection(CollectionKey key, PersistentCollection collection) {
  177   		if (unownedCollections==null) {
  178   			unownedCollections = new HashMap(8);
  179   		}
  180   		unownedCollections.put(key, collection);
  181   	}
  182   	
  183   	public PersistentCollection useUnownedCollection(CollectionKey key) {
  184   		if (unownedCollections==null) {
  185   			return null;
  186   		}
  187   		else {
  188   			return (PersistentCollection) unownedCollections.remove(key);
  189   		}
  190   	}
  191   	
  192   	/**
  193   	 * Get the <tt>BatchFetchQueue</tt>, instantiating one if
  194   	 * necessary.
  195   	 */
  196   	public BatchFetchQueue getBatchFetchQueue() {
  197   		if (batchFetchQueue==null) {
  198   			batchFetchQueue = new BatchFetchQueue(this);
  199   		}
  200   		return batchFetchQueue;
  201   	}
  202   
  203   	public void clear() {
  204   		Iterator itr = proxiesByKey.values().iterator();
  205   		while ( itr.hasNext() ) {
  206   			final LazyInitializer li = ( ( HibernateProxy ) itr.next() ).getHibernateLazyInitializer();
  207   			li.unsetSession();
  208   		}
  209   		Map.Entry[] collectionEntryArray = IdentityMap.concurrentEntries( collectionEntries );
  210   		for ( int i = 0; i < collectionEntryArray.length; i++ ) {
  211   			( ( PersistentCollection ) collectionEntryArray[i].getKey() ).unsetSession( getSession() );
  212   		}
  213   		arrayHolders.clear();
  214   		entitiesByKey.clear();
  215   		entitiesByUniqueKey.clear();
  216   		entityEntries.clear();
  217   		entitySnapshotsByKey.clear();
  218   		collectionsByKey.clear();
  219   		collectionEntries.clear();
  220   		if ( unownedCollections != null ) {
  221   			unownedCollections.clear();
  222   		}
  223   		proxiesByKey.clear();
  224   		nullifiableEntityKeys.clear();
  225   		if ( batchFetchQueue != null ) {
  226   			batchFetchQueue.clear();
  227   		}
  228   		hasNonReadOnlyEntities = false;
  229   		if ( loadContexts != null ) {
  230   			loadContexts.cleanup();
  231   		}
  232   	}
  233   	
  234   	public boolean hasNonReadOnlyEntities() {
  235   		return hasNonReadOnlyEntities;
  236   	}
  237   	
  238   	public void setEntryStatus(EntityEntry entry, Status status) {
  239   		entry.setStatus(status);
  240   		setHasNonReadOnlyEnties(status);
  241   	}
  242   	
  243   	private void setHasNonReadOnlyEnties(Status status) {
  244   		if ( status==Status.DELETED || status==Status.MANAGED || status==Status.SAVING ) {
  245   			hasNonReadOnlyEntities = true;
  246   		}
  247   	}
  248   
  249   	public void afterTransactionCompletion() {
  250   		// Downgrade locks
  251   		Iterator iter = entityEntries.values().iterator();
  252   		while ( iter.hasNext() ) {
  253   			( (EntityEntry) iter.next() ).setLockMode(LockMode.NONE);
  254   		}
  255   	}
  256   
  257   	/**
  258   	 * Get the current state of the entity as known to the underlying
  259   	 * database, or null if there is no corresponding row 
  260   	 */
  261   	public Object[] getDatabaseSnapshot(Serializable id, EntityPersister persister)
  262   	throws HibernateException {
  263   		EntityKey key = new EntityKey( id, persister, session.getEntityMode() );
  264   		Object cached = entitySnapshotsByKey.get(key);
  265   		if (cached!=null) {
  266   			return cached==NO_ROW ? null : (Object[]) cached;
  267   		}
  268   		else {
  269   			Object[] snapshot = persister.getDatabaseSnapshot( id, session );
  270   			entitySnapshotsByKey.put( key, snapshot==null ? NO_ROW : snapshot );
  271   			return snapshot;
  272   		}
  273   	}
  274   
  275   	public Object[] getNaturalIdSnapshot(Serializable id, EntityPersister persister)
  276   	throws HibernateException {
  277   		if ( !persister.hasNaturalIdentifier() ) {
  278   			return null;
  279   		}
  280   
  281   		// if the natural-id is marked as non-mutable, it is not retrieved during a
  282   		// normal database-snapshot operation...
  283   		int[] props = persister.getNaturalIdentifierProperties();
  284   		boolean[] updateable = persister.getPropertyUpdateability();
  285   		boolean allNatualIdPropsAreUpdateable = true;
  286   		for ( int i = 0; i < props.length; i++ ) {
  287   			if ( !updateable[ props[i] ] ) {
  288   				allNatualIdPropsAreUpdateable = false;
  289   				break;
  290   			}
  291   		}
  292   
  293   		if ( allNatualIdPropsAreUpdateable ) {
  294   			// do this when all the properties are updateable since there is
  295   			// a certain likelihood that the information will already be
  296   			// snapshot-cached.
  297   			Object[] entitySnapshot = getDatabaseSnapshot( id, persister );
  298   			if ( entitySnapshot == NO_ROW ) {
  299   				return null;
  300   			}
  301   			Object[] naturalIdSnapshot = new Object[ props.length ];
  302   			for ( int i = 0; i < props.length; i++ ) {
  303   				naturalIdSnapshot[i] = entitySnapshot[ props[i] ];
  304   			}
  305   			return naturalIdSnapshot;
  306   		}
  307   		else {
  308   			return persister.getNaturalIdentifierSnapshot( id, session );
  309   		}
  310   	}
  311   
  312   	/**
  313   	 * Retrieve the cached database snapshot for the requested entity key.
  314   	 * <p/>
  315   	 * This differs from {@link #getDatabaseSnapshot} is two important respects:<ol>
  316   	 * <li>no snapshot is obtained from the database if not already cached</li>
  317   	 * <li>an entry of {@link #NO_ROW} here is interpretet as an exception</li>
  318   	 * </ol>
  319   	 * @param key The entity key for which to retrieve the cached snapshot
  320   	 * @return The cached snapshot
  321   	 * @throws IllegalStateException if the cached snapshot was == {@link #NO_ROW}.
  322   	 */
  323   	public Object[] getCachedDatabaseSnapshot(EntityKey key) {
  324   		Object snapshot = entitySnapshotsByKey.get( key );
  325   		if ( snapshot == NO_ROW ) {
  326   			throw new IllegalStateException( "persistence context reported no row snapshot for " + MessageHelper.infoString( key.getEntityName(), key.getIdentifier() ) );
  327   		}
  328   		return ( Object[] ) snapshot;
  329   	}
  330   
  331   	/*public void removeDatabaseSnapshot(EntityKey key) {
  332   		entitySnapshotsByKey.remove(key);
  333   	}*/
  334   
  335   	public void addEntity(EntityKey key, Object entity) {
  336   		entitiesByKey.put(key, entity);
  337   		getBatchFetchQueue().removeBatchLoadableEntityKey(key);
  338   	}
  339   
  340   	/**
  341   	 * Get the entity instance associated with the given 
  342   	 * <tt>EntityKey</tt>
  343   	 */
  344   	public Object getEntity(EntityKey key) {
  345   		return entitiesByKey.get(key);
  346   	}
  347   
  348   	public boolean containsEntity(EntityKey key) {
  349   		return entitiesByKey.containsKey(key);
  350   	}
  351   
  352   	/**
  353   	 * Remove an entity from the session cache, also clear
  354   	 * up other state associated with the entity, all except
  355   	 * for the <tt>EntityEntry</tt>
  356   	 */
  357   	public Object removeEntity(EntityKey key) {
  358   		Object entity = entitiesByKey.remove(key);
  359   		Iterator iter = entitiesByUniqueKey.values().iterator();
  360   		while ( iter.hasNext() ) {
  361   			if ( iter.next()==entity ) iter.remove();
  362   		}
  363   		entitySnapshotsByKey.remove(key);
  364   		nullifiableEntityKeys.remove(key);
  365   		getBatchFetchQueue().removeBatchLoadableEntityKey(key);
  366   		getBatchFetchQueue().removeSubselect(key);
  367   		return entity;
  368   	}
  369   
  370   	/**
  371   	 * Get an entity cached by unique key
  372   	 */
  373   	public Object getEntity(EntityUniqueKey euk) {
  374   		return entitiesByUniqueKey.get(euk);
  375   	}
  376   
  377   	/**
  378   	 * Add an entity to the cache by unique key
  379   	 */
  380   	public void addEntity(EntityUniqueKey euk, Object entity) {
  381   		entitiesByUniqueKey.put(euk, entity);
  382   	}
  383   
  384   	/**
  385   	 * Retreive the EntityEntry representation of the given entity.
  386   	 *
  387   	 * @param entity The entity for which to locate the EntityEntry.
  388   	 * @return The EntityEntry for the given entity.
  389   	 */
  390   	public EntityEntry getEntry(Object entity) {
  391   		return (EntityEntry) entityEntries.get(entity);
  392   	}
  393   
  394   	/**
  395   	 * Remove an entity entry from the session cache
  396   	 */
  397   	public EntityEntry removeEntry(Object entity) {
  398   		return (EntityEntry) entityEntries.remove(entity);
  399   	}
  400   
  401   	/**
  402   	 * Is there an EntityEntry for this instance?
  403   	 */
  404   	public boolean isEntryFor(Object entity) {
  405   		return entityEntries.containsKey(entity);
  406   	}
  407   
  408   	/**
  409   	 * Get the collection entry for a persistent collection
  410   	 */
  411   	public CollectionEntry getCollectionEntry(PersistentCollection coll) {
  412   		return (CollectionEntry) collectionEntries.get(coll);
  413   	}
  414   
  415   	/**
  416   	 * Adds an entity to the internal caches.
  417   	 */
  418   	public EntityEntry addEntity(
  419   			final Object entity,
  420   			final Status status,
  421   			final Object[] loadedState,
  422   			final EntityKey entityKey,
  423   			final Object version,
  424   			final LockMode lockMode,
  425   			final boolean existsInDatabase,
  426   			final EntityPersister persister,
  427   			final boolean disableVersionIncrement, 
  428   			boolean lazyPropertiesAreUnfetched
  429   	) {
  430   		
  431   		addEntity( entityKey, entity );
  432   		
  433   		return addEntry(
  434   				entity,
  435   				status,
  436   				loadedState,
  437   				null,
  438   				entityKey.getIdentifier(),
  439   				version,
  440   				lockMode,
  441   				existsInDatabase,
  442   				persister,
  443   				disableVersionIncrement, 
  444   				lazyPropertiesAreUnfetched
  445   			);
  446   	}
  447   
  448   
  449   	/**
  450   	 * Generates an appropriate EntityEntry instance and adds it 
  451   	 * to the event source's internal caches.
  452   	 */
  453   	public EntityEntry addEntry(
  454   			final Object entity,
  455   			final Status status,
  456   			final Object[] loadedState,
  457   			final Object rowId,
  458   			final Serializable id,
  459   			final Object version,
  460   			final LockMode lockMode,
  461   			final boolean existsInDatabase,
  462   			final EntityPersister persister,
  463   			final boolean disableVersionIncrement, 
  464   			boolean lazyPropertiesAreUnfetched) {
  465   		
  466   		EntityEntry e = new EntityEntry(
  467   				status,
  468   				loadedState,
  469   				rowId,
  470   				id,
  471   				version,
  472   				lockMode,
  473   				existsInDatabase,
  474   				persister,
  475   				session.getEntityMode(),
  476   				disableVersionIncrement,
  477   				lazyPropertiesAreUnfetched
  478   			);
  479   		entityEntries.put(entity, e);
  480   		
  481   		setHasNonReadOnlyEnties(status);
  482   		return e;
  483   	}
  484   
  485   	public boolean containsCollection(PersistentCollection collection) {
  486   		return collectionEntries.containsKey(collection);
  487   	}
  488   
  489   	public boolean containsProxy(Object entity) {
  490   		return proxiesByKey.containsValue( entity );
  491   	}
  492   	
  493   	/**
  494   	 * Takes the given object and, if it represents a proxy, reassociates it with this event source.
  495   	 *
  496   	 * @param value The possible proxy to be reassociated.
  497   	 * @return Whether the passed value represented an actual proxy which got initialized.
  498   	 * @throws MappingException
  499   	 */
  500   	public boolean reassociateIfUninitializedProxy(Object value) throws MappingException {
  501   		if ( value instanceof ElementWrapper ) {
  502   			value = ( (ElementWrapper) value ).getElement();
  503   		}
  504   		
  505   		if ( !Hibernate.isInitialized(value) ) {
  506   			HibernateProxy proxy = (HibernateProxy) value;
  507   			LazyInitializer li = proxy.getHibernateLazyInitializer();
  508   			reassociateProxy(li, proxy);
  509   			return true;
  510   		}
  511   		else {
  512   			return false;
  513   		}
  514   	}
  515   
  516   	/**
  517   	 * If a deleted entity instance is re-saved, and it has a proxy, we need to
  518   	 * reset the identifier of the proxy 
  519   	 */
  520   	public void reassociateProxy(Object value, Serializable id) throws MappingException {
  521   		if ( value instanceof ElementWrapper ) {
  522   			value = ( (ElementWrapper) value ).getElement();
  523   		}
  524   		
  525   		if ( value instanceof HibernateProxy ) {
  526   			if ( log.isDebugEnabled() ) log.debug("setting proxy identifier: " + id);
  527   			HibernateProxy proxy = (HibernateProxy) value;
  528   			LazyInitializer li = proxy.getHibernateLazyInitializer();
  529   			li.setIdentifier(id);
  530   			reassociateProxy(li, proxy);
  531   		}
  532   	}
  533   
  534   	/**
  535   	 * Associate a proxy that was instantiated by another session with this session
  536   	 *
  537   	 * @param li The proxy initializer.
  538   	 * @param proxy The proxy to reassociate.
  539   	 */
  540   	private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) {
  541   		if ( li.getSession() != this.getSession() ) {
  542   			EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() );
  543   			EntityKey key = new EntityKey( li.getIdentifier(), persister, session.getEntityMode() );
  544   		  	// any earlier proxy takes precedence
  545   			if ( !proxiesByKey.containsKey( key ) ) {
  546   				proxiesByKey.put( key, proxy );
  547   			}
  548   			proxy.getHibernateLazyInitializer().setSession( session );
  549   		}
  550   	}
  551   
  552   	/**
  553   	 * Get the entity instance underlying the given proxy, throwing
  554   	 * an exception if the proxy is uninitialized. If the given object
  555   	 * is not a proxy, simply return the argument.
  556   	 */
  557   	public Object unproxy(Object maybeProxy) throws HibernateException {
  558   		if ( maybeProxy instanceof ElementWrapper ) {
  559   			maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
  560   		}
  561   		
  562   		if ( maybeProxy instanceof HibernateProxy ) {
  563   			HibernateProxy proxy = (HibernateProxy) maybeProxy;
  564   			LazyInitializer li = proxy.getHibernateLazyInitializer();
  565   			if ( li.isUninitialized() ) {
  566   				throw new PersistentObjectException(
  567   						"object was an uninitialized proxy for " +
  568   						li.getEntityName()
  569   				);
  570   			}
  571   			return li.getImplementation(); //unwrap the object
  572   		}
  573   		else {
  574   			return maybeProxy;
  575   		}
  576   	}
  577   
  578   	/**
  579   	 * Possibly unproxy the given reference and reassociate it with the current session.
  580   	 *
  581   	 * @param maybeProxy The reference to be unproxied if it currently represents a proxy.
  582   	 * @return The unproxied instance.
  583   	 * @throws HibernateException
  584   	 */
  585   	public Object unproxyAndReassociate(Object maybeProxy) throws HibernateException {
  586   		if ( maybeProxy instanceof ElementWrapper ) {
  587   			maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
  588   		}
  589   		
  590   		if ( maybeProxy instanceof HibernateProxy ) {
  591   			HibernateProxy proxy = (HibernateProxy) maybeProxy;
  592   			LazyInitializer li = proxy.getHibernateLazyInitializer();
  593   			reassociateProxy(li, proxy);
  594   			return li.getImplementation(); //initialize + unwrap the object
  595   		}
  596   		else {
  597   			return maybeProxy;
  598   		}
  599   	}
  600   
  601   	/**
  602   	 * Attempts to check whether the given key represents an entity already loaded within the
  603   	 * current session.
  604   	 * @param object The entity reference against which to perform the uniqueness check.
  605   	 * @throws HibernateException
  606   	 */
  607   	public void checkUniqueness(EntityKey key, Object object) throws HibernateException {
  608   		Object entity = getEntity(key);
  609   		if ( entity == object ) {
  610   			throw new AssertionFailure( "object already associated, but no entry was found" );
  611   		}
  612   		if ( entity != null ) {
  613   			throw new NonUniqueObjectException( key.getIdentifier(), key.getEntityName() );
  614   		}
  615   	}
  616   
  617   	/**
  618   	 * If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
  619   	 * and overwrite the registration of the old one. This breaks == and occurs only for
  620   	 * "class" proxies rather than "interface" proxies. Also init the proxy to point to
  621   	 * the given target implementation if necessary.
  622   	 *
  623   	 * @param proxy The proxy instance to be narrowed.
  624   	 * @param persister The persister for the proxied entity.
  625   	 * @param key The internal cache key for the proxied entity.
  626   	 * @param object (optional) the actual proxied entity instance.
  627   	 * @return An appropriately narrowed instance.
  628   	 * @throws HibernateException
  629   	 */
  630   	public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key, Object object)
  631   	throws HibernateException {
  632   		
  633   		boolean alreadyNarrow = persister.getConcreteProxyClass( session.getEntityMode() )
  634   				.isAssignableFrom( proxy.getClass() );
  635   		
  636   		if ( !alreadyNarrow ) {
  637   			if ( PROXY_WARN_LOG.isWarnEnabled() ) {
  638   				PROXY_WARN_LOG.warn(
  639   						"Narrowing proxy to " +
  640   						persister.getConcreteProxyClass( session.getEntityMode() ) +
  641   						" - this operation breaks =="
  642   				);
  643   			}
  644   
  645   			if ( object != null ) {
  646   				proxiesByKey.remove(key);
  647   				return object; //return the proxied object
  648   			}
  649   			else {
  650   				proxy = persister.createProxy( key.getIdentifier(), session );
  651   				proxiesByKey.put(key, proxy); //overwrite old proxy
  652   				return proxy;
  653   			}
  654   			
  655   		}
  656   		else {
  657   			
  658   			if ( object != null ) {
  659   				LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
  660   				li.setImplementation(object);
  661   			}
  662   			
  663   			return proxy;
  664   			
  665   		}
  666   		
  667   	}
  668   
  669   	/**
  670   	 * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
  671   	 * third argument (the entity associated with the key) if no proxy exists. Init
  672   	 * the proxy to the target implementation, if necessary.
  673   	 */
  674   	public Object proxyFor(EntityPersister persister, EntityKey key, Object impl) 
  675   	throws HibernateException {
  676   		if ( !persister.hasProxy() ) return impl;
  677   		Object proxy = proxiesByKey.get(key);
  678   		if ( proxy != null ) {
  679   			return narrowProxy(proxy, persister, key, impl);
  680   		}
  681   		else {
  682   			return impl;
  683   		}
  684   	}
  685   
  686   	/**
  687   	 * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
  688   	 * argument (the entity associated with the key) if no proxy exists.
  689   	 * (slower than the form above)
  690   	 */
  691   	public Object proxyFor(Object impl) throws HibernateException {
  692   		EntityEntry e = getEntry(impl);
  693   		EntityPersister p = e.getPersister();
  694   		return proxyFor( p, new EntityKey( e.getId(), p, session.getEntityMode() ), impl );
  695   	}
  696   
  697   	/**
  698   	 * Get the entity that owns this persistent collection
  699   	 */
  700   	public Object getCollectionOwner(Serializable key, CollectionPersister collectionPersister) throws MappingException {
  701   		return getEntity( new EntityKey( key, collectionPersister.getOwnerEntityPersister(), session.getEntityMode() ) );
  702   	}
  703   
  704   	/**
  705   	 * Get the entity that owned this persistent collection when it was loaded
  706   	 *
  707   	 * @param collection The persistent collection
  708   	 * @return the owner, if its entity ID is available from the collection's loaded key
  709   	 * and the owner entity is in the persistence context; otherwise, returns null
  710   	 */
  711   	public Object getLoadedCollectionOwnerOrNull(PersistentCollection collection) {
  712   		CollectionEntry ce = getCollectionEntry( collection );
  713   		if ( ce.getLoadedPersister() == null ) {
  714   			return null; // early exit...
  715   		}
  716   		Object loadedOwner = null;
  717   		// TODO: an alternative is to check if the owner has changed; if it hasn't then
  718   		// return collection.getOwner()
  719   		Serializable entityId = getLoadedCollectionOwnerIdOrNull( ce );
  720   		if ( entityId != null ) {
  721   			loadedOwner = getCollectionOwner( entityId, ce.getLoadedPersister() );
  722   		}
  723   		return loadedOwner;
  724   	}
  725   
  726   	/**
  727   	 * Get the ID for the entity that owned this persistent collection when it was loaded
  728   	 *
  729   	 * @param collection The persistent collection
  730   	 * @return the owner ID if available from the collection's loaded key; otherwise, returns null
  731   	 */
  732   	public Serializable getLoadedCollectionOwnerIdOrNull(PersistentCollection collection) {
  733   		return getLoadedCollectionOwnerIdOrNull( getCollectionEntry( collection ) );
  734   	}
  735   
  736   	/**
  737   	 * Get the ID for the entity that owned this persistent collection when it was loaded
  738   	 *
  739   	 * @param ce The collection entry
  740   	 * @return the owner ID if available from the collection's loaded key; otherwise, returns null
  741   	 */
  742   	private Serializable getLoadedCollectionOwnerIdOrNull(CollectionEntry ce) {
  743   		if ( ce == null || ce.getLoadedKey() == null || ce.getLoadedPersister() == null ) {
  744   			return null;
  745   		}
  746   		// TODO: an alternative is to check if the owner has changed; if it hasn't then
  747   		// get the ID from collection.getOwner()
  748   		return ce.getLoadedPersister().getCollectionType().getIdOfOwnerOrNull( ce.getLoadedKey(), session );
  749   	}
  750   
  751   	/**
  752   	 * add a collection we just loaded up (still needs initializing)
  753   	 */
  754   	public void addUninitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable id) {
  755   		CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
  756   		addCollection(collection, ce, id);
  757   	}
  758   
  759   	/**
  760   	 * add a detached uninitialized collection
  761   	 */
  762   	public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection collection) {
  763   		CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
  764   		addCollection( collection, ce, collection.getKey() );
  765   	}
  766   
  767   	/**
  768   	 * Add a new collection (ie. a newly created one, just instantiated by the
  769   	 * application, with no database state or snapshot)
  770   	 * @param collection The collection to be associated with the persistence context
  771   	 */
  772   	public void addNewCollection(CollectionPersister persister, PersistentCollection collection)
  773   	throws HibernateException {
  774   		addCollection(collection, persister);
  775   	}
  776   
  777   	/**
  778   	 * Add an collection to the cache, with a given collection entry.
  779   	 *
  780   	 * @param coll The collection for which we are adding an entry.
  781   	 * @param entry The entry representing the collection.
  782   	 * @param key The key of the collection's entry.
  783   	 */
  784   	private void addCollection(PersistentCollection coll, CollectionEntry entry, Serializable key) {
  785   		collectionEntries.put( coll, entry );
  786   		CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key, session.getEntityMode() );
  787   		PersistentCollection old = ( PersistentCollection ) collectionsByKey.put( collectionKey, coll );
  788   		if ( old != null ) {
  789   			if ( old == coll ) {
  790   				throw new AssertionFailure("bug adding collection twice");
  791   			}
  792   			// or should it actually throw an exception?
  793   			old.unsetSession( session );
  794   			collectionEntries.remove( old );
  795   			// watch out for a case where old is still referenced
  796   			// somewhere in the object graph! (which is a user error)
  797   		}
  798   	}
  799   
  800   	/**
  801   	 * Add a collection to the cache, creating a new collection entry for it
  802   	 *
  803   	 * @param collection The collection for which we are adding an entry.
  804   	 * @param persister The collection persister
  805   	 */
  806   	private void addCollection(PersistentCollection collection, CollectionPersister persister) {
  807   		CollectionEntry ce = new CollectionEntry( persister, collection );
  808   		collectionEntries.put( collection, ce );
  809   	}
  810   
  811   	/**
  812   	 * add an (initialized) collection that was created by another session and passed
  813   	 * into update() (ie. one with a snapshot and existing state on the database)
  814   	 */
  815   	public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection collection) 
  816   	throws HibernateException {
  817   		if ( collection.isUnreferenced() ) {
  818   			//treat it just like a new collection
  819   			addCollection( collection, collectionPersister );
  820   		}
  821   		else {
  822   			CollectionEntry ce = new CollectionEntry( collection, session.getFactory() );
  823   			addCollection( collection, ce, collection.getKey() );
  824   		}
  825   	}
  826   
  827   	/**
  828   	 * add a collection we just pulled out of the cache (does not need initializing)
  829   	 */
  830   	public CollectionEntry addInitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable id)
  831   	throws HibernateException {
  832   		CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
  833   		ce.postInitialize(collection);
  834   		addCollection(collection, ce, id);
  835   		return ce;
  836   	}
  837   	
  838   	/**
  839   	 * Get the collection instance associated with the <tt>CollectionKey</tt>
  840   	 */
  841   	public PersistentCollection getCollection(CollectionKey collectionKey) {
  842   		return (PersistentCollection) collectionsByKey.get(collectionKey);
  843   	}
  844   	
  845   	/**
  846   	 * Register a collection for non-lazy loading at the end of the
  847   	 * two-phase load
  848   	 */
  849   	public void addNonLazyCollection(PersistentCollection collection) {
  850   		nonlazyCollections.add(collection);
  851   	}
  852   
  853   	/**
  854   	 * Force initialization of all non-lazy collections encountered during
  855   	 * the current two-phase load (actually, this is a no-op, unless this
  856   	 * is the "outermost" load)
  857   	 */
  858   	public void initializeNonLazyCollections() throws HibernateException {
  859   		if ( loadCounter == 0 ) {
  860   			log.debug( "initializing non-lazy collections" );
  861   			//do this work only at the very highest level of the load
  862   			loadCounter++; //don't let this method be called recursively
  863   			try {
  864   				int size;
  865   				while ( ( size = nonlazyCollections.size() ) > 0 ) {
  866   					//note that each iteration of the loop may add new elements
  867   					( (PersistentCollection) nonlazyCollections.remove( size - 1 ) ).forceInitialization();
  868   				}
  869   			}
  870   			finally {
  871   				loadCounter--;
  872   				clearNullProperties();
  873   			}
  874   		}
  875   	}
  876   
  877   
  878   	/**
  879   	 * Get the <tt>PersistentCollection</tt> object for an array
  880   	 */
  881   	public PersistentCollection getCollectionHolder(Object array) {
  882   		return (PersistentCollection) arrayHolders.get(array);
  883   	}
  884   
  885   	/**
  886   	 * Register a <tt>PersistentCollection</tt> object for an array.
  887   	 * Associates a holder with an array - MUST be called after loading 
  888   	 * array, since the array instance is not created until endLoad().
  889   	 */
  890   	public void addCollectionHolder(PersistentCollection holder) {
  891   		//TODO:refactor + make this method private
  892   		arrayHolders.put( holder.getValue(), holder );
  893   	}
  894   
  895   	public PersistentCollection removeCollectionHolder(Object array) {
  896   		return (PersistentCollection) arrayHolders.remove(array);
  897   	}
  898   
  899   	/**
  900   	 * Get the snapshot of the pre-flush collection state
  901   	 */
  902   	public Serializable getSnapshot(PersistentCollection coll) {
  903   		return getCollectionEntry(coll).getSnapshot();
  904   	}
  905   
  906   	/**
  907   	 * Get the collection entry for a collection passed to filter,
  908   	 * which might be a collection wrapper, an array, or an unwrapped
  909   	 * collection. Return null if there is no entry.
  910   	 */
  911   	public CollectionEntry getCollectionEntryOrNull(Object collection) {
  912   		PersistentCollection coll;
  913   		if ( collection instanceof PersistentCollection ) {
  914   			coll = (PersistentCollection) collection;
  915   			//if (collection==null) throw new TransientObjectException("Collection was not yet persistent");
  916   		}
  917   		else {
  918   			coll = getCollectionHolder(collection);
  919   			if ( coll == null ) {
  920   				//it might be an unwrapped collection reference!
  921   				//try to find a wrapper (slowish)
  922   				Iterator wrappers = IdentityMap.keyIterator(collectionEntries);
  923   				while ( wrappers.hasNext() ) {
  924   					PersistentCollection pc = (PersistentCollection) wrappers.next();
  925   					if ( pc.isWrapper(collection) ) {
  926   						coll = pc;
  927   						break;
  928   					}
  929   				}
  930   			}
  931   		}
  932   
  933   		return (coll == null) ? null : getCollectionEntry(coll);
  934   	}
  935   
  936   	/**
  937   	 * Get an existing proxy by key
  938   	 */
  939   	public Object getProxy(EntityKey key) {
  940   		return proxiesByKey.get(key);
  941   	}
  942   
  943   	/**
  944   	 * Add a proxy to the session cache
  945   	 */
  946   	public void addProxy(EntityKey key, Object proxy) {
  947   		proxiesByKey.put(key, proxy);
  948   	}
  949   
  950   	/**
  951   	 * Remove a proxy from the session cache.
  952   	 * <p/>
  953   	 * Additionally, ensure that any load optimization references
  954   	 * such as batch or subselect loading get cleaned up as well.
  955   	 *
  956   	 * @param key The key of the entity proxy to be removed
  957   	 * @return The proxy reference.
  958   	 */
  959   	public Object removeProxy(EntityKey key) {
  960   		if ( batchFetchQueue != null ) {
  961   			batchFetchQueue.removeBatchLoadableEntityKey( key );
  962   			batchFetchQueue.removeSubselect( key );
  963   		}
  964   		return proxiesByKey.remove( key );
  965   	}
  966   
  967   	/**
  968   	 * Record the fact that an entity does not exist in the database
  969   	 * 
  970   	 * @param key the primary key of the entity
  971   	 */
  972   	/*public void addNonExistantEntityKey(EntityKey key) {
  973   		nonExistantEntityKeys.add(key);
  974   	}*/
  975   
  976   	/**
  977   	 * Record the fact that an entity does not exist in the database
  978   	 * 
  979   	 * @param key a unique key of the entity
  980   	 */
  981   	/*public void addNonExistantEntityUniqueKey(EntityUniqueKey key) {
  982   		nonExistentEntityUniqueKeys.add(key);
  983   	}*/
  984   
  985   	/*public void removeNonExist(EntityKey key) {
  986   		nonExistantEntityKeys.remove(key);
  987   	}*/
  988   
  989   	/** 
  990   	 * Retrieve the set of EntityKeys representing nullifiable references
  991   	 */
  992   	public HashSet getNullifiableEntityKeys() {
  993   		return nullifiableEntityKeys;
  994   	}
  995   
  996   	public Map getEntitiesByKey() {
  997   		return entitiesByKey;
  998   	}
  999   
 1000   	public Map getEntityEntries() {
 1001   		return entityEntries;
 1002   	}
 1003   
 1004   	public Map getCollectionEntries() {
 1005   		return collectionEntries;
 1006   	}
 1007   
 1008   	public Map getCollectionsByKey() {
 1009   		return collectionsByKey;
 1010   	}
 1011   
 1012   	/**
 1013   	 * Do we already know that the entity does not exist in the
 1014   	 * database?
 1015   	 */
 1016   	/*public boolean isNonExistant(EntityKey key) {
 1017   		return nonExistantEntityKeys.contains(key);
 1018   	}*/
 1019   
 1020   	/**
 1021   	 * Do we already know that the entity does not exist in the
 1022   	 * database?
 1023   	 */
 1024   	/*public boolean isNonExistant(EntityUniqueKey key) {
 1025   		return nonExistentEntityUniqueKeys.contains(key);
 1026   	}*/
 1027   
 1028   	public int getCascadeLevel() {
 1029   		return cascading;
 1030   	}
 1031   
 1032   	public int incrementCascadeLevel() {
 1033   		return ++cascading;
 1034   	}
 1035   
 1036   	public int decrementCascadeLevel() {
 1037   		return --cascading;
 1038   	}
 1039   
 1040   	public boolean isFlushing() {
 1041   		return flushing;
 1042   	}
 1043   
 1044   	public void setFlushing(boolean flushing) {
 1045   		this.flushing = flushing;
 1046   	}
 1047   
 1048   	/**
 1049   	 * Call this before begining a two-phase load
 1050   	 */
 1051   	public void beforeLoad() {
 1052   		loadCounter++;
 1053   	}
 1054   
 1055   	/**
 1056   	 * Call this after finishing a two-phase load
 1057   	 */
 1058   	public void afterLoad() {
 1059   		loadCounter--;
 1060   	}
 1061   
 1062   	/**
 1063   	 * Returns a string representation of the object.
 1064   	 *
 1065   	 * @return a string representation of the object.
 1066   	 */
 1067   	public String toString() {
 1068   		return new StringBuffer()
 1069   				.append("PersistenceContext[entityKeys=")
 1070   				.append(entitiesByKey.keySet())
 1071   				.append(",collectionKeys=")
 1072   				.append(collectionsByKey.keySet())
 1073   				.append("]")
 1074   				.toString();
 1075   	}
 1076   
 1077   	/**
 1078   	 * Search <tt>this</tt> persistence context for an associated entity instance which is considered the "owner" of
 1079   	 * the given <tt>childEntity</tt>, and return that owner's id value.  This is performed in the scenario of a
 1080   	 * uni-directional, non-inverse one-to-many collection (which means that the collection elements do not maintain
 1081   	 * a direct reference to the owner).
 1082   	 * <p/>
 1083   	 * As such, the processing here is basically to loop over every entity currently associated with this persistence
 1084   	 * context and for those of the correct entity (sub) type to extract its collection role property value and see
 1085   	 * if the child is contained within that collection.  If so, we have found the owner; if not, we go on.
 1086   	 * <p/>
 1087   	 * Also need to account for <tt>mergeMap</tt> which acts as a local copy cache managed for the duration of a merge
 1088   	 * operation.  It represents a map of the detached entity instances pointing to the corresponding managed instance.
 1089   	 *
 1090   	 * @param entityName The entity name for the entity type which would own the child
 1091   	 * @param propertyName The name of the property on the owning entity type which would name this child association.
 1092   	 * @param childEntity The child entity instance for which to locate the owner instance id.
 1093   	 * @param mergeMap A map of non-persistent instances from an on-going merge operation (possibly null).
 1094   	 *
 1095   	 * @return The id of the entityName instance which is said to own the child; null if an appropriate owner not
 1096   	 * located.
 1097   	 */
 1098   	public Serializable getOwnerId(String entityName, String propertyName, Object childEntity, Map mergeMap) {
 1099   		final String collectionRole = entityName + '.' + propertyName;
 1100   		final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
 1101   		final CollectionPersister collectionPersister = session.getFactory().getCollectionPersister( collectionRole );
 1102   
 1103   		// iterate all the entities currently associated with the persistence context.
 1104   		Iterator entities = entityEntries.entrySet().iterator();
 1105   		while ( entities.hasNext() ) {
 1106   			final Map.Entry me = ( Map.Entry ) entities.next();
 1107   			final EntityEntry entityEntry = ( EntityEntry ) me.getValue();
 1108   			// does this entity entry pertain to the entity persister in which we are interested (owner)?
 1109   			if ( persister.isSubclassEntityName( entityEntry.getEntityName() ) ) {
 1110   				final Object entityEntryInstance = me.getKey();
 1111   
 1112   				//check if the managed object is the parent
 1113   				boolean found = isFoundInParent(
 1114   						propertyName,
 1115   						childEntity,
 1116   						persister,
 1117   						collectionPersister,
 1118   						entityEntryInstance
 1119   				);
 1120   
 1121   				if ( !found && mergeMap != null ) {
 1122   					//check if the detached object being merged is the parent
 1123   					Object unmergedInstance = mergeMap.get( entityEntryInstance );
 1124   					Object unmergedChild = mergeMap.get( childEntity );
 1125   					if ( unmergedInstance != null && unmergedChild != null ) {
 1126   						found = isFoundInParent(
 1127   								propertyName,
 1128   								unmergedChild,
 1129   								persister,
 1130   								collectionPersister,
 1131   								unmergedInstance
 1132   						);
 1133   					}
 1134   				}
 1135   
 1136   				if ( found ) {
 1137   					return entityEntry.getId();
 1138   				}
 1139   
 1140   			}
 1141   		}
 1142   
 1143   		// if we get here, it is possible that we have a proxy 'in the way' of the merge map resolution...
 1144   		// 		NOTE: decided to put this here rather than in the above loop as I was nervous about the performance
 1145   		//		of the loop-in-loop especially considering this is far more likely the 'edge case'
 1146   		if ( mergeMap != null ) {
 1147   			Iterator mergeMapItr = mergeMap.entrySet().iterator();
 1148   			while ( mergeMapItr.hasNext() ) {
 1149   				final Map.Entry mergeMapEntry = ( Map.Entry ) mergeMapItr.next();
 1150   				if ( mergeMapEntry.getKey() instanceof HibernateProxy ) {
 1151   					final HibernateProxy proxy = ( HibernateProxy ) mergeMapEntry.getKey();
 1152   					if ( persister.isSubclassEntityName( proxy.getHibernateLazyInitializer().getEntityName() ) ) {
 1153   						boolean found = isFoundInParent(
 1154   								propertyName,
 1155   								childEntity,
 1156   								persister,
 1157   								collectionPersister,
 1158   								mergeMap.get( proxy )
 1159   						);
 1160   						if ( !found ) {
 1161   							found = isFoundInParent(
 1162   									propertyName,
 1163   									mergeMap.get( childEntity ),
 1164   									persister,
 1165   									collectionPersister,
 1166   									mergeMap.get( proxy )
 1167   							);
 1168   						}
 1169   						if ( found ) {
 1170   							return proxy.getHibernateLazyInitializer().getIdentifier();
 1171   						}
 1172   					}
 1173   				}
 1174   			}
 1175   		}
 1176   
 1177   		return null;
 1178   	}
 1179   
 1180   	private boolean isFoundInParent(
 1181   			String property,
 1182   			Object childEntity,
 1183   			EntityPersister persister,
 1184   			CollectionPersister collectionPersister,
 1185   			Object potentialParent) {
 1186   		Object collection = persister.getPropertyValue(
 1187   				potentialParent,
 1188   				property,
 1189   				session.getEntityMode()
 1190   		);
 1191   		return collection != null
 1192   				&& Hibernate.isInitialized( collection )
 1193   				&& collectionPersister.getCollectionType().contains( collection, childEntity, session );
 1194   	}
 1195   
 1196   	/**
 1197   	 * Search the persistence context for an index of the child object,
 1198   	 * given a collection role
 1199   	 */
 1200   	public Object getIndexInOwner(String entity, String property, Object childEntity, Map mergeMap) {
 1201   
 1202   		EntityPersister persister = session.getFactory()
 1203   				.getEntityPersister(entity);
 1204   		CollectionPersister cp = session.getFactory()
 1205   				.getCollectionPersister(entity + '.' + property);
 1206   		Iterator entities = entityEntries.entrySet().iterator();
 1207   		while ( entities.hasNext() ) {
 1208   			Map.Entry me = (Map.Entry) entities.next();
 1209   			EntityEntry ee = (EntityEntry) me.getValue();
 1210   			if ( persister.isSubclassEntityName( ee.getEntityName() ) ) {
 1211   				Object instance = me.getKey();
 1212   				
 1213   				Object index = getIndexInParent(property, childEntity, persister, cp, instance);
 1214   				
 1215   				if (index==null && mergeMap!=null) {
 1216   					Object unmergedInstance = mergeMap.get(instance);
 1217   					Object unmergedChild = mergeMap.get(childEntity);
 1218   					if ( unmergedInstance!=null && unmergedChild!=null ) {
 1219   						index = getIndexInParent(property, unmergedChild, persister, cp, unmergedInstance);
 1220   					}
 1221   				}
 1222   				
 1223   				if (index!=null) return index;
 1224   			}
 1225   		}
 1226   		return null;
 1227   	}
 1228   	
 1229   	private Object getIndexInParent(
 1230   			String property, 
 1231   			Object childEntity, 
 1232   			EntityPersister persister, 
 1233   			CollectionPersister collectionPersister,
 1234   			Object potentialParent
 1235   	){	
 1236   		Object collection = persister.getPropertyValue( potentialParent, property, session.getEntityMode() );
 1237   		if ( collection!=null && Hibernate.isInitialized(collection) ) {
 1238   			return collectionPersister.getCollectionType().indexOf(collection, childEntity);
 1239   		}
 1240   		else {
 1241   			return null;
 1242   		}
 1243   	}
 1244   	
 1245   	/**
 1246   	 * Record the fact that the association belonging to the keyed
 1247   	 * entity is null.
 1248   	 */
 1249   	public void addNullProperty(EntityKey ownerKey, String propertyName) {
 1250   		nullAssociations.add( new AssociationKey(ownerKey, propertyName) );
 1251   	}
 1252   	
 1253   	/**
 1254   	 * Is the association property belonging to the keyed entity null?
 1255   	 */
 1256   	public boolean isPropertyNull(EntityKey ownerKey, String propertyName) {
 1257   		return nullAssociations.contains( new AssociationKey(ownerKey, propertyName) );
 1258   	}
 1259   	
 1260   	private void clearNullProperties() {
 1261   		nullAssociations.clear();
 1262   	}
 1263   
 1264   	public void setReadOnly(Object entity, boolean readOnly) {
 1265   		EntityEntry entry = getEntry(entity);
 1266   		if (entry==null) {
 1267   			throw new TransientObjectException("Instance was not associated with the session");
 1268   		}
 1269   		entry.setReadOnly(readOnly, entity);
 1270   		hasNonReadOnlyEntities = hasNonReadOnlyEntities || !readOnly;
 1271   	}
 1272   
 1273   	public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) {
 1274   		Object entity = entitiesByKey.remove( oldKey );
 1275   		EntityEntry oldEntry = ( EntityEntry ) entityEntries.remove( entity );
 1276   
 1277   		EntityKey newKey = new EntityKey( generatedId, oldEntry.getPersister(), getSession().getEntityMode() );
 1278   		addEntity( newKey, entity );
 1279   		addEntry(
 1280   				entity,
 1281   		        oldEntry.getStatus(),
 1282   		        oldEntry.getLoadedState(),
 1283   		        oldEntry.getRowId(),
 1284   		        generatedId,
 1285   		        oldEntry.getVersion(),
 1286   		        oldEntry.getLockMode(),
 1287   		        oldEntry.isExistsInDatabase(),
 1288   		        oldEntry.getPersister(),
 1289   		        oldEntry.isBeingReplicated(),
 1290   		        oldEntry.isLoadedWithLazyPropertiesUnfetched()
 1291   		);
 1292   	}
 1293   
 1294   	/**
 1295   	 * Used by the owning session to explicitly control serialization of the
 1296   	 * persistence context.
 1297   	 *
 1298   	 * @param oos The stream to which the persistence context should get written
 1299   	 * @throws IOException serialization errors.
 1300   	 */
 1301   	public void serialize(ObjectOutputStream oos) throws IOException {
 1302   		log.trace( "serializing persistent-context" );
 1303   
 1304   		oos.writeBoolean( hasNonReadOnlyEntities );
 1305   
 1306   		oos.writeInt( entitiesByKey.size() );
 1307   		log.trace( "starting serialization of [" + entitiesByKey.size() + "] entitiesByKey entries" );
 1308   		Iterator itr = entitiesByKey.entrySet().iterator();
 1309   		while ( itr.hasNext() ) {
 1310   			Map.Entry entry = ( Map.Entry ) itr.next();
 1311   			( ( EntityKey ) entry.getKey() ).serialize( oos );
 1312   			oos.writeObject( entry.getValue() );
 1313   		}
 1314   
 1315   		oos.writeInt( entitiesByUniqueKey.size() );
 1316   		log.trace( "starting serialization of [" + entitiesByUniqueKey.size() + "] entitiesByUniqueKey entries" );
 1317   		itr = entitiesByUniqueKey.entrySet().iterator();
 1318   		while ( itr.hasNext() ) {
 1319   			Map.Entry entry = ( Map.Entry ) itr.next();
 1320   			( ( EntityUniqueKey ) entry.getKey() ).serialize( oos );
 1321   			oos.writeObject( entry.getValue() );
 1322   		}
 1323   
 1324   		oos.writeInt( proxiesByKey.size() );
 1325   		log.trace( "starting serialization of [" + proxiesByKey.size() + "] proxiesByKey entries" );
 1326   		itr = proxiesByKey.entrySet().iterator();
 1327   		while ( itr.hasNext() ) {
 1328   			Map.Entry entry = ( Map.Entry ) itr.next();
 1329   			( ( EntityKey ) entry.getKey() ).serialize( oos );
 1330   			oos.writeObject( entry.getValue() );
 1331   		}
 1332   
 1333   		oos.writeInt( entitySnapshotsByKey.size() );
 1334   		log.trace( "starting serialization of [" + entitySnapshotsByKey.size() + "] entitySnapshotsByKey entries" );
 1335   		itr = entitySnapshotsByKey.entrySet().iterator();
 1336   		while ( itr.hasNext() ) {
 1337   			Map.Entry entry = ( Map.Entry ) itr.next();
 1338   			( ( EntityKey ) entry.getKey() ).serialize( oos );
 1339   			oos.writeObject( entry.getValue() );
 1340   		}
 1341   
 1342   		oos.writeInt( entityEntries.size() );
 1343   		log.trace( "starting serialization of [" + entityEntries.size() + "] entityEntries entries" );
 1344   		itr = entityEntries.entrySet().iterator();
 1345   		while ( itr.hasNext() ) {
 1346   			Map.Entry entry = ( Map.Entry ) itr.next();
 1347   			oos.writeObject( entry.getKey() );
 1348   			( ( EntityEntry ) entry.getValue() ).serialize( oos );
 1349   		}
 1350   
 1351   		oos.writeInt( collectionsByKey.size() );
 1352   		log.trace( "starting serialization of [" + collectionsByKey.size() + "] collectionsByKey entries" );
 1353   		itr = collectionsByKey.entrySet().iterator();
 1354   		while ( itr.hasNext() ) {
 1355   			Map.Entry entry = ( Map.Entry ) itr.next();
 1356   			( ( CollectionKey ) entry.getKey() ).serialize( oos );
 1357   			oos.writeObject( entry.getValue() );
 1358   		}
 1359   
 1360   		oos.writeInt( collectionEntries.size() );
 1361   		log.trace( "starting serialization of [" + collectionEntries.size() + "] collectionEntries entries" );
 1362   		itr = collectionEntries.entrySet().iterator();
 1363   		while ( itr.hasNext() ) {
 1364   			Map.Entry entry = ( Map.Entry ) itr.next();
 1365   			oos.writeObject( entry.getKey() );
 1366   			( ( CollectionEntry ) entry.getValue() ).serialize( oos );
 1367   		}
 1368   
 1369   		oos.writeInt( arrayHolders.size() );
 1370   		log.trace( "starting serialization of [" + arrayHolders.size() + "] arrayHolders entries" );
 1371   		itr = arrayHolders.entrySet().iterator();
 1372   		while ( itr.hasNext() ) {
 1373   			Map.Entry entry = ( Map.Entry ) itr.next();
 1374   			oos.writeObject( entry.getKey() );
 1375   			oos.writeObject( entry.getValue() );
 1376   		}
 1377   
 1378   		oos.writeInt( nullifiableEntityKeys.size() );
 1379   		log.trace( "starting serialization of [" + nullifiableEntityKeys.size() + "] nullifiableEntityKeys entries" );
 1380   		itr = nullifiableEntityKeys.iterator();
 1381   		while ( itr.hasNext() ) {
 1382   			EntityKey entry = ( EntityKey ) itr.next();
 1383   			entry.serialize( oos );
 1384   		}
 1385   	}
 1386   
 1387   	public static StatefulPersistenceContext deserialize(
 1388   			ObjectInputStream ois,
 1389   	        SessionImplementor session) throws IOException, ClassNotFoundException {
 1390   		log.trace( "deserializing persistent-context" );
 1391   		StatefulPersistenceContext rtn = new StatefulPersistenceContext( session );
 1392   
 1393   		// during deserialization, we need to reconnect all proxies and
 1394   		// collections to this session, as well as the EntityEntry and
 1395   		// CollectionEntry instances; these associations are transient
 1396   		// because serialization is used for different things.
 1397   
 1398   		try {
 1399   			// todo : we can actually just determine this from the incoming EntityEntry-s
 1400   			rtn.hasNonReadOnlyEntities = ois.readBoolean();
 1401   
 1402   			int count = ois.readInt();
 1403   			log.trace( "staring deserialization of [" + count + "] entitiesByKey entries" );
 1404   			rtn.entitiesByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1405   			for ( int i = 0; i < count; i++ ) {
 1406   				rtn.entitiesByKey.put( EntityKey.deserialize( ois, session ), ois.readObject() );
 1407   			}
 1408   
 1409   			count = ois.readInt();
 1410   			log.trace( "staring deserialization of [" + count + "] entitiesByUniqueKey entries" );
 1411   			rtn.entitiesByUniqueKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1412   			for ( int i = 0; i < count; i++ ) {
 1413   				rtn.entitiesByUniqueKey.put( EntityUniqueKey.deserialize( ois, session ), ois.readObject() );
 1414   			}
 1415   
 1416   			count = ois.readInt();
 1417   			log.trace( "staring deserialization of [" + count + "] proxiesByKey entries" );
 1418   			rtn.proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK, count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count, .75f );
 1419   			for ( int i = 0; i < count; i++ ) {
 1420   				EntityKey ek = EntityKey.deserialize( ois, session );
 1421   				Object proxy = ois.readObject();
 1422   				if ( proxy instanceof HibernateProxy ) {
 1423   					( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().setSession( session );
 1424   					rtn.proxiesByKey.put( ek, proxy );
 1425   				}
 1426   				else {
 1427   					log.trace( "encountered prunded proxy" );
 1428   				}
 1429   				// otherwise, the proxy was pruned during the serialization process
 1430   			}
 1431   
 1432   			count = ois.readInt();
 1433   			log.trace( "staring deserialization of [" + count + "] entitySnapshotsByKey entries" );
 1434   			rtn.entitySnapshotsByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1435   			for ( int i = 0; i < count; i++ ) {
 1436   				rtn.entitySnapshotsByKey.put( EntityKey.deserialize( ois, session ), ois.readObject() );
 1437   			}
 1438   
 1439   			count = ois.readInt();
 1440   			log.trace( "staring deserialization of [" + count + "] entityEntries entries" );
 1441   			rtn.entityEntries = IdentityMap.instantiateSequenced( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1442   			for ( int i = 0; i < count; i++ ) {
 1443   				Object entity = ois.readObject();
 1444   				EntityEntry entry = EntityEntry.deserialize( ois, session );
 1445   				rtn.entityEntries.put( entity, entry );
 1446   			}
 1447   
 1448   			count = ois.readInt();
 1449   			log.trace( "staring deserialization of [" + count + "] collectionsByKey entries" );
 1450   			rtn.collectionsByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1451   			for ( int i = 0; i < count; i++ ) {
 1452   				rtn.collectionsByKey.put( CollectionKey.deserialize( ois, session ), ois.readObject() );
 1453   			}
 1454   
 1455   			count = ois.readInt();
 1456   			log.trace( "staring deserialization of [" + count + "] collectionEntries entries" );
 1457   			rtn.collectionEntries = IdentityMap.instantiateSequenced( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1458   			for ( int i = 0; i < count; i++ ) {
 1459   				final PersistentCollection pc = ( PersistentCollection ) ois.readObject();
 1460   				final CollectionEntry ce = CollectionEntry.deserialize( ois, session );
 1461   				pc.setCurrentSession( session );
 1462   				rtn.collectionEntries.put( pc, ce );
 1463   			}
 1464   
 1465   			count = ois.readInt();
 1466   			log.trace( "staring deserialization of [" + count + "] arrayHolders entries" );
 1467   			rtn.arrayHolders = IdentityMap.instantiate( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
 1468   			for ( int i = 0; i < count; i++ ) {
 1469   				rtn.arrayHolders.put( ois.readObject(), ois.readObject() );
 1470   			}
 1471   
 1472   			count = ois.readInt();
 1473   			log.trace( "staring deserialization of [" + count + "] nullifiableEntityKeys entries" );
 1474   			rtn.nullifiableEntityKeys = new HashSet();
 1475   			for ( int i = 0; i < count; i++ ) {
 1476   				rtn.nullifiableEntityKeys.add( EntityKey.deserialize( ois, session ) );
 1477   			}
 1478   
 1479   		}
 1480   		catch ( HibernateException he ) {
 1481   			throw new InvalidObjectException( he.getMessage() );
 1482   		}
 1483   
 1484   		return rtn;
 1485   	}
 1486   }

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