KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > engine > StatefulPersistenceContext


1 // $Id: StatefulPersistenceContext.java,v 1.6 2005/07/20 07:16:18 oneovthafew Exp $
2
package org.hibernate.engine;
3
4 import java.io.IOException JavaDoc;
5 import java.io.InvalidObjectException JavaDoc;
6 import java.io.ObjectInputStream JavaDoc;
7 import java.io.ObjectOutputStream JavaDoc;
8 import java.io.Serializable JavaDoc;
9 import java.util.ArrayList JavaDoc;
10 import java.util.HashMap JavaDoc;
11 import java.util.HashSet JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Map JavaDoc;
15
16 import org.apache.commons.collections.ReferenceMap;
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import org.hibernate.AssertionFailure;
20 import org.hibernate.Hibernate;
21 import org.hibernate.HibernateException;
22 import org.hibernate.LockMode;
23 import org.hibernate.MappingException;
24 import org.hibernate.NonUniqueObjectException;
25 import org.hibernate.PersistentObjectException;
26 import org.hibernate.TransientObjectException;
27 import org.hibernate.collection.PersistentCollection;
28 import org.hibernate.persister.collection.CollectionPersister;
29 import org.hibernate.persister.entity.EntityPersister;
30 import org.hibernate.proxy.HibernateProxy;
31 import org.hibernate.proxy.LazyInitializer;
32 import org.hibernate.tuple.ElementWrapper;
33 import org.hibernate.util.IdentityMap;
34 import org.hibernate.util.MarkerObject;
35
36 /**
37  * A <tt>PersistenceContext</tt> represents the state of persistent "stuff" which
38  * Hibernate is tracking. This includes persistent entities, collections,
39  * as well as proxies generated.
40  * </p>
41  * There is meant to be a one-to-one correspondence between a SessionImpl and
42  * a PersistentContext. The SessionImpl uses the PersistentContext to track
43  * the current state of its context. Event-listeners then use the
44  * PersistentContext to drive their processing.
45  *
46  * @author <a HREF="mailto:steve@hibernate.org">Steve Ebersole</a>
47  */

48 public class StatefulPersistenceContext implements Serializable JavaDoc, PersistenceContext {
49
50     // a possibility for "read-only" sessions would be to use a different
51
// PersistentContext impl that does not track the state of things
52

53     private static final Log log = LogFactory.getLog( StatefulPersistenceContext.class );
54
55     public static final Object JavaDoc NO_ROW = new MarkerObject("NO_ROW");
56
57     private SessionImplementor session;
58     
59     // Loaded entity instances, by EntityKey
60
private final Map JavaDoc entitiesByKey;
61
62     // Loaded entity instances, by EntityUniqueKey
63
private final Map JavaDoc entitiesByUniqueKey;
64     
65     // Identity map of EntityEntry instances, by the entity instance
66
private transient Map JavaDoc entityEntries;
67     
68     // Entity proxies, by EntityKey
69
private transient Map JavaDoc proxiesByKey;
70     
71     // Snapshots of current database state for entities
72
// that have *not* been loaded
73
private final Map JavaDoc entitySnapshotsByKey;
74     
75     // Identity map of array holder ArrayHolder instances, by the array instance
76
private transient Map JavaDoc arrayHolders;
77     
78     // Identity map of CollectionEntry instances, by the collection wrapper
79
private transient Map JavaDoc collectionEntries;
80     
81     // Collection wrappers, by the CollectionKey
82
private final Map JavaDoc collectionsByKey; //key=CollectionKey, value=PersistentCollection
83

84     // Set of EntityKeys of deleted objects
85
private HashSet JavaDoc nullifiableEntityKeys = new HashSet JavaDoc();
86     
87     // EntityKeys that we have tried to load, and not found in the database
88
//private final HashSet nonExistantEntityKeys;
89

90     // EntityUniqueKeys that we have tried to load, and not found in the database
91
//private final HashSet nonExistentEntityUniqueKeys;
92

93     // properties that we have tried to load, and not found in the database
94
private transient HashSet JavaDoc nullAssociations;
95     
96     // A list of collection wrappers that were instantiating during result set
97
// processing, that we will need to initialize at the end of the query
98
private transient List JavaDoc nonlazyCollections;
99     
100     // A container for collections we load up when the owning entity is not
101
// yet loaded ... for now, this is purely transient!
102
private transient Map JavaDoc unownedCollections;
103     
104     private transient int cascading = 0;
105     private transient int loadCounter = 0;
106     private transient boolean flushing = false;
107     
108     private boolean hasNonReadOnlyEntities = false;
109     
110     private transient CollectionLoadContext collectionLoadContext;
111     private transient BatchFetchQueue batchFetchQueue;
112
113     public boolean isStateless() {
114         return false;
115     }
116     
117     public SessionImplementor getSession() {
118         return session;
119     }
120     
121     public CollectionLoadContext getCollectionLoadContext() {
122         if (collectionLoadContext==null) {
123             collectionLoadContext = new CollectionLoadContext(this);
124         }
125         return collectionLoadContext;
126     }
127     
128     public void addUnownedCollection(CollectionKey key, PersistentCollection collection) {
129         if (unownedCollections==null) {
130             unownedCollections = new HashMap JavaDoc(8);
131         }
132         unownedCollections.put(key, collection);
133     }
134     
135     public PersistentCollection useUnownedCollection(CollectionKey key) {
136         if (unownedCollections==null) {
137             return null;
138         }
139         else {
140             return (PersistentCollection) unownedCollections.remove(key);
141         }
142     }
143     
144     /**
145      * Get the <tt>BatchFetchQueue</tt>, instantiating one if
146      * necessary.
147      */

148     public BatchFetchQueue getBatchFetchQueue() {
149         if (batchFetchQueue==null) {
150             batchFetchQueue = new BatchFetchQueue(this);
151         }
152         return batchFetchQueue;
153     }
154     
155     private static final int INIT_MAP_SIZE = 8;
156     
157     /**
158      * Constructs a PersistentContext, bound to the given session.
159      *
160      * @param session The session "owning" this context.
161      */

162     public StatefulPersistenceContext(SessionImplementor session) {
163         this.session = session;
164
165         entitiesByKey = new HashMap JavaDoc(INIT_MAP_SIZE);
166         entitiesByUniqueKey = new HashMap JavaDoc(INIT_MAP_SIZE);
167         proxiesByKey = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
168         entitySnapshotsByKey = new HashMap JavaDoc(INIT_MAP_SIZE);
169         //nonExistantEntityKeys = new HashSet(INIT_MAP_SIZE);
170
//nonExistentEntityUniqueKeys = new HashSet(INIT_MAP_SIZE);
171
entityEntries = IdentityMap.instantiateSequenced(INIT_MAP_SIZE);
172         collectionEntries = IdentityMap.instantiateSequenced(INIT_MAP_SIZE);
173         collectionsByKey = new HashMap JavaDoc(INIT_MAP_SIZE);
174         arrayHolders = IdentityMap.instantiate(INIT_MAP_SIZE);
175
176         initTransientState();
177     }
178
179     private void readObject(ObjectInputStream JavaDoc ois) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
180         log.trace( "deserializing persistent-context" );
181         ois.defaultReadObject();
182
183         entityEntries = IdentityMap.deserialize( ois.readObject() );
184         collectionEntries = IdentityMap.deserialize( ois.readObject() );
185         arrayHolders = IdentityMap.deserialize( ois.readObject() );
186
187         initTransientState();
188
189         proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK );
190         Map JavaDoc map = ( Map JavaDoc ) ois.readObject();
191         proxiesByKey.putAll( map );
192
193         // we need to reconnect all proxies and collections to this session
194
// the association is transient because serialization is used for
195
// different things.
196

197         try {
198
199             Iterator JavaDoc iter = collectionEntries.entrySet().iterator();
200             while ( iter.hasNext() ) {
201                     Map.Entry JavaDoc e = ( Map.Entry JavaDoc ) iter.next();
202                     ( ( PersistentCollection ) e.getKey() ).setCurrentSession( session );
203                     CollectionEntry ce = ( CollectionEntry ) e.getValue();
204                     if ( ce.getRole() != null ) {
205                         ce.afterDeserialize( session.getFactory() );
206                     }
207             }
208     
209             iter = proxiesByKey.values().iterator();
210             while ( iter.hasNext() ) {
211                 Object JavaDoc proxy = iter.next();
212                 if ( proxy instanceof HibernateProxy ) {
213                     ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().setSession( session );
214                 }
215                 else {
216                     iter.remove(); //the proxy was pruned during the serialization process
217
}
218             }
219     
220             iter = entityEntries.entrySet().iterator();
221             while ( iter.hasNext() ) {
222                 EntityEntry e = ( EntityEntry ) ( ( Map.Entry JavaDoc ) iter.next() ).getValue();
223                 e.afterDeserialize( session.getFactory() );
224             }
225             
226         }
227         catch (HibernateException he) {
228             throw new InvalidObjectException JavaDoc( he.getMessage() );
229         }
230         
231     }
232
233     private void writeObject(ObjectOutputStream JavaDoc oos) throws IOException JavaDoc {
234         log.trace( "serializing persistent-context" );
235
236         oos.defaultWriteObject();
237
238         oos.writeObject( IdentityMap.serialize(entityEntries) );
239         oos.writeObject( IdentityMap.serialize(collectionEntries) );
240         oos.writeObject( IdentityMap.serialize(arrayHolders) );
241
242         HashMap JavaDoc map = new HashMap JavaDoc(INIT_MAP_SIZE);
243         map.putAll(proxiesByKey);
244         oos.writeObject(map);
245     }
246
247     private void initTransientState() {
248         nullAssociations = new HashSet JavaDoc(INIT_MAP_SIZE);
249         nonlazyCollections = new ArrayList JavaDoc(INIT_MAP_SIZE);
250     }
251
252     public void clear() {
253         arrayHolders.clear();
254         entitiesByKey.clear();
255         entitiesByUniqueKey.clear();
256         entityEntries.clear();
257         entitySnapshotsByKey.clear();
258         collectionsByKey.clear();
259         collectionEntries.clear();
260         proxiesByKey.clear();
261         //nonExistantEntityKeys.clear();
262
nullifiableEntityKeys.clear();
263         //nonExistentEntityUniqueKeys.clear();
264
if (batchFetchQueue!=null) batchFetchQueue.clear();
265         hasNonReadOnlyEntities=false;
266     }
267     
268     public boolean hasNonReadOnlyEntities() {
269         return hasNonReadOnlyEntities;
270     }
271     
272     public void setEntryStatus(EntityEntry entry, Status status) {
273         entry.setStatus(status);
274         setHasNonReadOnlyEnties(status);
275     }
276     
277     private void setHasNonReadOnlyEnties(Status status) {
278         if ( status==Status.DELETED || status==Status.MANAGED || status==Status.SAVING ) {
279             hasNonReadOnlyEntities = true;
280         }
281     }
282
283     public void afterTransactionCompletion() {
284         // Downgrade locks
285
Iterator JavaDoc iter = entityEntries.values().iterator();
286         while ( iter.hasNext() ) {
287             ( (EntityEntry) iter.next() ).setLockMode(LockMode.NONE);
288         }
289     }
290
291     /**
292      * Get the current state of the entity as known to the underlying
293      * database, or null if there is no corresponding row
294      */

295     public Object JavaDoc[] getDatabaseSnapshot(Serializable JavaDoc id, EntityPersister persister)
296     throws HibernateException {
297         EntityKey key = new EntityKey( id, persister, session.getEntityMode() );
298         Object JavaDoc cached = entitySnapshotsByKey.get(key);
299         if (cached!=null) {
300             return cached==NO_ROW ? null : (Object JavaDoc[]) cached;
301         }
302         else {
303             Object JavaDoc[] snapshot = persister.getDatabaseSnapshot( id, session );
304             entitySnapshotsByKey.put( key, snapshot==null ? NO_ROW : snapshot );
305             return snapshot;
306         }
307     }
308
309     public Object JavaDoc[] getCachedDatabaseSnapshot(EntityKey key) {
310         //TODO: assertion failure if NO_ROW
311
return (Object JavaDoc[]) entitySnapshotsByKey.get(key);
312     }
313
314     /*public void removeDatabaseSnapshot(EntityKey key) {
315         entitySnapshotsByKey.remove(key);
316     }*/

317
318     public void addEntity(EntityKey key, Object JavaDoc entity) {
319         entitiesByKey.put(key, entity);
320         getBatchFetchQueue().removeBatchLoadableEntityKey(key);
321     }
322
323     /**
324      * Get the entity instance associated with the given
325      * <tt>EntityKey</tt>
326      */

327     public Object JavaDoc getEntity(EntityKey key) {
328         return entitiesByKey.get(key);
329     }
330
331     public boolean containsEntity(EntityKey key) {
332         return entitiesByKey.containsKey(key);
333     }
334
335     /**
336      * Remove an entity from the session cache, also clear
337      * up other state associated with the entity, all except
338      * for the <tt>EntityEntry</tt>
339      */

340     public Object JavaDoc removeEntity(EntityKey key) {
341         Object JavaDoc entity = entitiesByKey.remove(key);
342         Iterator JavaDoc iter = entitiesByUniqueKey.values().iterator();
343         while ( iter.hasNext() ) {
344             if ( iter.next()==entity ) iter.remove();
345         }
346         entitySnapshotsByKey.remove(key);
347         nullifiableEntityKeys.remove(key);
348         getBatchFetchQueue().removeBatchLoadableEntityKey(key);
349         getBatchFetchQueue().removeSubselect(key);
350         return entity;
351     }
352
353     /**
354      * Get an entity cached by unique key
355      */

356     public Object JavaDoc getEntity(EntityUniqueKey euk) {
357         return entitiesByUniqueKey.get(euk);
358     }
359
360     /**
361      * Add an entity to the cache by unique key
362      */

363     public void addEntity(EntityUniqueKey euk, Object JavaDoc entity) {
364         entitiesByUniqueKey.put(euk, entity);
365     }
366
367     /**
368      * Retreive the EntityEntry representation of the given entity.
369      *
370      * @param entity The entity for which to locate the EntityEntry.
371      * @return The EntityEntry for the given entity.
372      */

373     public EntityEntry getEntry(Object JavaDoc entity) {
374         return (EntityEntry) entityEntries.get(entity);
375     }
376
377     /**
378      * Remove an entity entry from the session cache
379      */

380     public EntityEntry removeEntry(Object JavaDoc entity) {
381         return (EntityEntry) entityEntries.remove(entity);
382     }
383
384     /**
385      * Is there an EntityEntry for this instance?
386      */

387     public boolean isEntryFor(Object JavaDoc entity) {
388         return entityEntries.containsKey(entity);
389     }
390
391     /**
392      * Get the collection entry for a persistent collection
393      */

394     public CollectionEntry getCollectionEntry(PersistentCollection coll) {
395         return (CollectionEntry) collectionEntries.get(coll);
396     }
397
398     /**
399      * Adds an entity to the internal caches.
400      */

401     public EntityEntry addEntity(
402             final Object JavaDoc entity,
403             final Status status,
404             final Object JavaDoc[] loadedState,
405             final EntityKey entityKey,
406             final Object JavaDoc version,
407             final LockMode lockMode,
408             final boolean existsInDatabase,
409             final EntityPersister persister,
410             final boolean disableVersionIncrement,
411             boolean lazyPropertiesAreUnfetched
412     ) {
413         
414         addEntity( entityKey, entity );
415         
416         return addEntry(
417                 entity,
418                 status,
419                 loadedState,
420                 null,
421                 entityKey.getIdentifier(),
422                 version,
423                 lockMode,
424                 existsInDatabase,
425                 persister,
426                 disableVersionIncrement,
427                 lazyPropertiesAreUnfetched
428             );
429     }
430
431
432     /**
433      * Generates an appropriate EntityEntry instance and adds it
434      * to the event source's internal caches.
435      */

436     public EntityEntry addEntry(
437             final Object JavaDoc entity,
438             final Status status,
439             final Object JavaDoc[] loadedState,
440             final Object JavaDoc rowId,
441             final Serializable JavaDoc id,
442             final Object JavaDoc version,
443             final LockMode lockMode,
444             final boolean existsInDatabase,
445             final EntityPersister persister,
446             final boolean disableVersionIncrement,
447             boolean lazyPropertiesAreUnfetched) {
448         
449         EntityEntry e = new EntityEntry(
450                 status,
451                 loadedState,
452                 rowId,
453                 id,
454                 version,
455                 lockMode,
456                 existsInDatabase,
457                 persister,
458                 session.getEntityMode(),
459                 disableVersionIncrement,
460                 lazyPropertiesAreUnfetched
461             );
462         entityEntries.put(entity, e);
463         
464         setHasNonReadOnlyEnties(status);
465         return e;
466     }
467
468     public boolean containsProxy(Object JavaDoc entity) {
469         return proxiesByKey.values().contains( entity );
470     }
471
472     /**
473      * Takes the given object and, if it represents a proxy, reassociates it with this event source.
474      *
475      * @param value The possible proxy to be reassociated.
476      * @return Whether the passed value represented an actual proxy which got initialized.
477      * @throws MappingException
478      */

479     public boolean reassociateIfUninitializedProxy(Object JavaDoc value) throws MappingException {
480         if ( value instanceof ElementWrapper ) {
481             value = ( (ElementWrapper) value ).getElement();
482         }
483         
484         if ( !Hibernate.isInitialized(value) ) {
485             HibernateProxy proxy = (HibernateProxy) value;
486             LazyInitializer li = proxy.getHibernateLazyInitializer();
487             reassociateProxy(li, proxy);
488             return true;
489         }
490         else {
491             return false;
492         }
493     }
494
495     /**
496      * If a deleted entity instance is re-saved, and it has a proxy, we need to
497      * reset the identifier of the proxy
498      */

499     public void reassociateProxy(Object JavaDoc value, Serializable JavaDoc id) throws MappingException {
500         if ( value instanceof ElementWrapper ) {
501             value = ( (ElementWrapper) value ).getElement();
502         }
503         
504         if ( value instanceof HibernateProxy ) {
505             if ( log.isDebugEnabled() ) log.debug("setting proxy identifier: " + id);
506             HibernateProxy proxy = (HibernateProxy) value;
507             LazyInitializer li = proxy.getHibernateLazyInitializer();
508             li.setIdentifier(id);
509             reassociateProxy(li, proxy);
510         }
511     }
512
513     /**
514      * Associate a proxy that was instantiated by another session with this session
515      */

516     private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) throws HibernateException {
517         if ( li.getSession() != this ) {
518             EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() );
519             EntityKey key = new EntityKey( li.getIdentifier(), persister, session.getEntityMode() );
520             if ( !proxiesByKey.containsKey(key) ) proxiesByKey.put(key, proxy); // any earlier proxy takes precedence
521
proxy.getHibernateLazyInitializer().setSession(session);
522         }
523     }
524
525     /**
526      * Get the entity instance underlying the given proxy, throwing
527      * an exception if the proxy is uninitialized. If the given object
528      * is not a proxy, simply return the argument.
529      */

530     public Object JavaDoc unproxy(Object JavaDoc maybeProxy) throws HibernateException {
531         if ( maybeProxy instanceof ElementWrapper ) {
532             maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
533         }
534         
535         if ( maybeProxy instanceof HibernateProxy ) {
536             HibernateProxy proxy = (HibernateProxy) maybeProxy;
537             LazyInitializer li = proxy.getHibernateLazyInitializer();
538             if ( li.isUninitialized() ) {
539                 throw new PersistentObjectException(
540                         "object was an uninitialized proxy for " +
541                         li.getEntityName()
542                 );
543             }
544             return li.getImplementation(); //unwrap the object
545
}
546         else {
547             return maybeProxy;
548         }
549     }
550
551     /**
552      * Possibly unproxy the given reference and reassociate it with the current session.
553      *
554      * @param maybeProxy The reference to be unproxied if it currently represents a proxy.
555      * @return The unproxied instance.
556      * @throws HibernateException
557      */

558     public Object JavaDoc unproxyAndReassociate(Object JavaDoc maybeProxy) throws HibernateException {
559         if ( maybeProxy instanceof ElementWrapper ) {
560             maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
561         }
562         
563         if ( maybeProxy instanceof HibernateProxy ) {
564             HibernateProxy proxy = (HibernateProxy) maybeProxy;
565             LazyInitializer li = proxy.getHibernateLazyInitializer();
566             reassociateProxy(li, proxy);
567             return li.getImplementation(); //initialize + unwrap the object
568
}
569         else {
570             return maybeProxy;
571         }
572     }
573
574     /**
575      * Attempts to check whether the given key represents an entity already loaded within the
576      * current session.
577      * @param id The key to be checked.
578      * @param object The entity reference against which to perform the uniqueness check.
579      * @throws HibernateException
580      */

581     public void checkUniqueness(EntityKey key, Object JavaDoc object) throws HibernateException {
582         Object JavaDoc entity = getEntity(key);
583         if ( entity == object ) {
584             throw new AssertionFailure( "object already associated, but no entry was found" );
585         }
586         if ( entity != null ) {
587             throw new NonUniqueObjectException( key.getIdentifier(), key.getEntityName() );
588         }
589     }
590
591     /**
592      * If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
593      * and overwrite the registration of the old one. This breaks == and occurs only for
594      * "class" proxies rather than "interface" proxies. Also init the proxy to point to
595      * the given target implementation if necessary.
596      *
597      * @param proxy The proxy instance to be narrowed.
598      * @param persister The persister for the proxied entity.
599      * @param key The internal cache key for the proxied entity.
600      * @param object (optional) the actual proxied entity instance.
601      * @return An appropriately narrowed instance.
602      * @throws HibernateException
603      */

604     public Object JavaDoc narrowProxy(Object JavaDoc proxy, EntityPersister persister, EntityKey key, Object JavaDoc object)
605     throws HibernateException {
606         
607         boolean alreadyNarrow = persister.getConcreteProxyClass( session.getEntityMode() )
608                 .isAssignableFrom( proxy.getClass() );
609         
610         if ( !alreadyNarrow ) {
611             
612             if ( log.isWarnEnabled() )
613                 log.warn(
614                         "Narrowing proxy to " +
615                         persister.getConcreteProxyClass( session.getEntityMode() ) +
616                         " - this operation breaks =="
617                 );
618
619             if ( object != null ) {
620                 proxiesByKey.remove(key);
621                 return object; //return the proxied object
622
}
623             else {
624                 proxy = persister.createProxy( key.getIdentifier(), session );
625                 proxiesByKey.put(key, proxy); //overwrite old proxy
626
return proxy;
627             }
628             
629         }
630         else {
631             
632             if ( object != null ) {
633                 LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
634                 li.setImplementation(object);
635             }
636             
637             return proxy;
638             
639         }
640         
641     }
642
643     /**
644      * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
645      * third argument (the entity associated with the key) if no proxy exists. Init
646      * the proxy to the target implementation, if necessary.
647      */

648     public Object JavaDoc proxyFor(EntityPersister persister, EntityKey key, Object JavaDoc impl)
649     throws HibernateException {
650         if ( !persister.hasProxy() ) return impl;
651         Object JavaDoc proxy = proxiesByKey.get(key);
652         if ( proxy != null ) {
653             return narrowProxy(proxy, persister, key, impl);
654         }
655         else {
656             return impl;
657         }
658     }
659
660     /**
661      * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
662      * argument (the entity associated with the key) if no proxy exists.
663      * (slower than the form above)
664      */

665     public Object JavaDoc proxyFor(Object JavaDoc impl) throws HibernateException {
666         EntityEntry e = getEntry(impl);
667         EntityPersister p = e.getPersister();
668         return proxyFor( p, new EntityKey( e.getId(), p, session.getEntityMode() ), impl );
669     }
670
671     /**
672      * Get the entity that owns this persistent collection
673      */

674     public Object JavaDoc getCollectionOwner(Serializable JavaDoc key, CollectionPersister collectionPersister) throws MappingException {
675         return getEntity( new EntityKey( key, collectionPersister.getOwnerEntityPersister(), session.getEntityMode() ) );
676     }
677
678     /**
679      * add a collection we just loaded up (still needs initializing)
680      */

681     public void addUninitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable JavaDoc id) {
682         CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
683         addCollection(collection, ce, id);
684     }
685
686     /**
687      * add a detached uninitialized collection
688      */

689     public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection collection) {
690         CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
691         addCollection( collection, ce, collection.getKey() );
692     }
693
694     /**
695      * Add a new collection (ie. a newly created one, just instantiated by the
696      * application, with no database state or snapshot)
697      * @param collection The collection to be associated with the persistence context
698      */

699     public void addNewCollection(CollectionPersister persister, PersistentCollection collection)
700     throws HibernateException {
701         addCollection(collection, persister);
702     }
703     
704     /**
705      * Add an collection to the cache, with a given collection entry
706      */

707     private void addCollection(PersistentCollection coll, CollectionEntry entry, Serializable JavaDoc key) {
708         collectionEntries.put(coll, entry);
709         CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key, session.getEntityMode() );
710         PersistentCollection old = (PersistentCollection) collectionsByKey.put(collectionKey, coll);
711         if ( old != null ) {
712             if (old==coll) throw new AssertionFailure("bug adding collection twice");
713             // or should it actually throw an exception?
714
old.unsetSession(session);
715             collectionEntries.remove(old);
716             // watch out for a case where old is still referenced
717
// somewhere in the object graph! (which is a user error)
718
}
719     }
720
721     /**
722      * Add a collection to the cache, creating a new collection entry for it
723      */

724     private void addCollection(PersistentCollection collection, CollectionPersister persister)
725     throws HibernateException {
726         CollectionEntry ce = new CollectionEntry(persister, collection);
727         collectionEntries.put(collection, ce);
728     }
729
730     /**
731      * add an (initialized) collection that was created by another session and passed
732      * into update() (ie. one with a snapshot and existing state on the database)
733      */

734     public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection collection)
735     throws HibernateException {
736         if ( collection.isUnreferenced() ) {
737             //treat it just like a new collection
738
addCollection( collection, collectionPersister );
739         }
740         else {
741             CollectionEntry ce = new CollectionEntry( collection, session.getFactory() );
742             addCollection( collection, ce, collection.getKey() );
743         }
744     }
745
746     /**
747      * add a collection we just pulled out of the cache (does not need initializing)
748      */

749     public CollectionEntry addInitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable JavaDoc id)
750     throws HibernateException {
751         CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
752         ce.postInitialize(collection);
753         addCollection(collection, ce, id);
754         return ce;
755     }
756     
757     /**
758      * Get the collection instance associated with the <tt>CollectionKey</tt>
759      */

760     public PersistentCollection getCollection(CollectionKey collectionKey) {
761         return (PersistentCollection) collectionsByKey.get(collectionKey);
762     }
763     
764     /**
765      * Register a collection for non-lazy loading at the end of the
766      * two-phase load
767      */

768     public void addNonLazyCollection(PersistentCollection collection) {
769         nonlazyCollections.add(collection);
770     }
771
772     /**
773      * Force initialization of all non-lazy collections encountered during
774      * the current two-phase load (actually, this is a no-op, unless this
775      * is the "outermost" load)
776      */

777     public void initializeNonLazyCollections() throws HibernateException {
778         if ( loadCounter == 0 ) {
779             log.debug( "initializing non-lazy collections" );
780             //do this work only at the very highest level of the load
781
loadCounter++; //don't let this method be called recursively
782
try {
783                 int size;
784                 while ( ( size = nonlazyCollections.size() ) > 0 ) {
785                     //note that each iteration of the loop may add new elements
786
( (PersistentCollection) nonlazyCollections.remove( size - 1 ) ).forceInitialization();
787                 }
788             }
789             finally {
790                 loadCounter--;
791                 clearNullProperties();
792             }
793         }
794     }
795
796
797     /**
798      * Get the <tt>PersistentCollection</tt> object for an array
799      */

800     public PersistentCollection getCollectionHolder(Object JavaDoc array) {
801         return (PersistentCollection) arrayHolders.get(array);
802     }
803
804     /**
805      * Register a <tt>PersistentCollection</tt> object for an array.
806      * Associates a holder with an array - MUST be called after loading
807      * array, since the array instance is not created until endLoad().
808      */

809     public void addCollectionHolder(PersistentCollection holder) {
810         //TODO:refactor + make this method private
811
arrayHolders.put( holder.getValue(), holder );
812     }
813
814     public PersistentCollection removeCollectionHolder(Object JavaDoc array) {
815         return (PersistentCollection) arrayHolders.remove(array);
816     }
817
818     /**
819      * Get the snapshot of the pre-flush collection state
820      */

821     public Serializable JavaDoc getSnapshot(PersistentCollection coll) {
822         return getCollectionEntry(coll).getSnapshot();
823     }
824
825     /**
826      * Is this the "inverse" end of a bidirectional association?
827      */

828     public boolean isInverseCollection(PersistentCollection collection) {
829         CollectionEntry ce = getCollectionEntry(collection);
830         return ce != null && ce.getLoadedPersister().isInverse();
831     }
832
833     /**
834      * Get the collection entry for a collection passed to filter,
835      * which might be a collection wrapper, an array, or an unwrapped
836      * collection. Return null if there is no entry.
837      */

838     public CollectionEntry getCollectionEntryOrNull(Object JavaDoc collection) {
839         PersistentCollection coll;
840         if ( collection instanceof PersistentCollection ) {
841             coll = (PersistentCollection) collection;
842             //if (collection==null) throw new TransientObjectException("Collection was not yet persistent");
843
}
844         else {
845             coll = getCollectionHolder(collection);
846             if ( coll == null ) {
847                 //it might be an unwrapped collection reference!
848
//try to find a wrapper (slowish)
849
Iterator JavaDoc wrappers = IdentityMap.keyIterator(collectionEntries);
850                 while ( wrappers.hasNext() ) {
851                     PersistentCollection pc = (PersistentCollection) wrappers.next();
852                     if ( pc.isWrapper(collection) ) {
853                         coll = pc;
854                         break;
855                     }
856                 }
857             }
858         }
859
860         return (coll == null) ? null : getCollectionEntry(coll);
861     }
862
863     /**
864      * Get an existing proxy by key
865      */

866     public Object JavaDoc getProxy(EntityKey key) {
867         return proxiesByKey.get(key);
868     }
869
870     /**
871      * Add a proxy to the session cache
872      */

873     public void addProxy(EntityKey key, Object JavaDoc proxy) {
874         proxiesByKey.put(key, proxy);
875     }
876
877     /**
878      * Remove a proxy from the session cache
879      */

880     public Object JavaDoc removeProxy(EntityKey key) {
881         return proxiesByKey.remove(key);
882     }
883
884     /**
885      * Record the fact that an entity does not exist in the database
886      *
887      * @param key the primary key of the entity
888      */

889     /*public void addNonExistantEntityKey(EntityKey key) {
890         nonExistantEntityKeys.add(key);
891     }*/

892
893     /**
894      * Record the fact that an entity does not exist in the database
895      *
896      * @param key a unique key of the entity
897      */

898     /*public void addNonExistantEntityUniqueKey(EntityUniqueKey key) {
899         nonExistentEntityUniqueKeys.add(key);
900     }*/

901
902     /*public void removeNonExist(EntityKey key) {
903         nonExistantEntityKeys.remove(key);
904     }*/

905
906     /**
907      * Retrieve the set of EntityKeys representing nullifiable references
908      */

909     public HashSet JavaDoc getNullifiableEntityKeys() {
910         return nullifiableEntityKeys;
911     }
912
913     public Map JavaDoc getEntitiesByKey() {
914         return entitiesByKey;
915     }
916
917     public Map JavaDoc getEntityEntries() {
918         return entityEntries;
919     }
920
921     public Map JavaDoc getCollectionEntries() {
922         return collectionEntries;
923     }
924
925     public Map JavaDoc getCollectionsByKey() {
926         return collectionsByKey;
927     }
928
929     /**
930      * Do we already know that the entity does not exist in the
931      * database?
932      */

933     /*public boolean isNonExistant(EntityKey key) {
934         return nonExistantEntityKeys.contains(key);
935     }*/

936
937     /**
938      * Do we already know that the entity does not exist in the
939      * database?
940      */

941     /*public boolean isNonExistant(EntityUniqueKey key) {
942         return nonExistentEntityUniqueKeys.contains(key);
943     }*/

944
945     public int getCascadeLevel() {
946         return cascading;
947     }
948
949     public int incrementCascadeLevel() {
950         return ++cascading;
951     }
952
953     public int decrementCascadeLevel() {
954         return --cascading;
955     }
956
957     public boolean isFlushing() {
958         return flushing;
959     }
960
961     public void setFlushing(boolean flushing) {
962         this.flushing = flushing;
963     }
964
965     /**
966      * Call this before begining a two-phase load
967      */

968     public void beforeLoad() {
969         loadCounter++;
970     }
971
972     /**
973      * Call this after finishing a two-phase load
974      */

975     public void afterLoad() {
976         loadCounter--;
977     }
978
979     /**
980      * Returns a string representation of the object.
981      *
982      * @return a string representation of the object.
983      */

984     public String JavaDoc toString() {
985         return new StringBuffer JavaDoc()
986                 .append("PersistenceContext[entityKeys=")
987                 .append(entitiesByKey.keySet())
988                 .append(",collectionKeys=")
989                 .append(collectionsByKey.keySet())
990                 .append("]")
991                 .toString();
992     }
993     
994     /**
995      * Search the persistence context for an owner for the child object,
996      * given a collection role. If <tt>mergeMap</tt> is non-null, also
997      * check the detached graph being merged for a parent.
998      */

999     public Serializable JavaDoc getOwnerId(String JavaDoc entity, String JavaDoc property, Object JavaDoc childEntity, Map JavaDoc mergeMap) {
1000        
1001        EntityPersister persister = session.getFactory()
1002                .getEntityPersister(entity);
1003        final CollectionPersister collectionPersister = session.getFactory()
1004                .getCollectionPersister(entity + '.' + property);
1005        
1006        Iterator JavaDoc entities = entityEntries.entrySet().iterator();
1007        while ( entities.hasNext() ) {
1008            Map.Entry JavaDoc me = (Map.Entry JavaDoc) entities.next();
1009            EntityEntry ee = (EntityEntry) me.getValue();
1010            if ( persister.isSubclassEntityName( ee.getEntityName() ) ) {
1011                Object JavaDoc instance = me.getKey();
1012
1013                //check if the managed object is the parent
1014
boolean found = isFoundInParent(
1015                        property,
1016                        childEntity,
1017                        persister,
1018                        collectionPersister,
1019                        instance
1020                    );
1021
1022                if (!found && mergeMap!=null) {
1023                    //check if the detached object being merged is the parent
1024
Object JavaDoc unmergedInstance = mergeMap.get(instance);
1025                    Object JavaDoc unmergedChild = mergeMap.get(childEntity);
1026                    if ( unmergedInstance!=null && unmergedChild!=null ) {
1027                        found = isFoundInParent(
1028                                property,
1029                                unmergedChild,
1030                                persister,
1031                                collectionPersister,
1032                                unmergedInstance
1033                            );
1034                    }
1035                }
1036                
1037                if ( found ) {
1038                    return ee.getId();
1039                }
1040                
1041            }
1042        }
1043        return null;
1044    }
1045
1046    private boolean isFoundInParent(
1047            String JavaDoc property,
1048            Object JavaDoc childEntity,
1049            EntityPersister persister,
1050            CollectionPersister collectionPersister,
1051            Object JavaDoc potentialParent
1052    ) {
1053        Object JavaDoc collection = persister.getPropertyValue(
1054                potentialParent,
1055                property,
1056                session.getEntityMode()
1057            );
1058        return collection!=null && Hibernate.isInitialized(collection) &&
1059                collectionPersister.getCollectionType()
1060                        .contains(collection, childEntity, collectionPersister, session);
1061    }
1062
1063    /**
1064     * Search the persistence context for an index of the child object,
1065     * given a collection role
1066     */

1067    public Object JavaDoc getIndexInOwner(String JavaDoc entity, String JavaDoc property, Object JavaDoc childEntity, Map JavaDoc mergeMap) {
1068
1069        EntityPersister persister = session.getFactory()
1070                .getEntityPersister(entity);
1071        CollectionPersister cp = session.getFactory()
1072                .getCollectionPersister(entity + '.' + property);
1073        Iterator JavaDoc entities = entityEntries.entrySet().iterator();
1074        while ( entities.hasNext() ) {
1075            Map.Entry JavaDoc me = (Map.Entry JavaDoc) entities.next();
1076            EntityEntry ee = (EntityEntry) me.getValue();
1077            if ( persister.isSubclassEntityName( ee.getEntityName() ) ) {
1078                Object JavaDoc instance = me.getKey();
1079                
1080                Object JavaDoc index = getIndexInParent(property, childEntity, persister, cp, instance);
1081                
1082                if (index==null && mergeMap!=null) {
1083                    Object JavaDoc unmergedInstance = mergeMap.get(instance);
1084                    Object JavaDoc unmergedChild = mergeMap.get(childEntity);
1085                    if ( unmergedInstance!=null && unmergedChild!=null ) {
1086                        index = getIndexInParent(property, unmergedChild, persister, cp, unmergedInstance);
1087                    }
1088                }
1089                
1090                if (index!=null) return index;
1091            }
1092        }
1093        return null;
1094    }
1095    
1096    private Object JavaDoc getIndexInParent(
1097            String JavaDoc property,
1098            Object JavaDoc childEntity,
1099            EntityPersister persister,
1100            CollectionPersister collectionPersister,
1101            Object JavaDoc potentialParent
1102    ){
1103        Object JavaDoc collection = persister.getPropertyValue( potentialParent, property, session.getEntityMode() );
1104        if ( collection!=null && Hibernate.isInitialized(collection) ) {
1105            return collectionPersister.getCollectionType().indexOf(collection, childEntity);
1106        }
1107        else {
1108            return null;
1109        }
1110    }
1111    
1112    /**
1113     * Record the fact that the association belonging to the keyed
1114     * entity is null.
1115     */

1116    public void addNullProperty(EntityKey ownerKey, String JavaDoc propertyName) {
1117        nullAssociations.add( new AssociationKey(ownerKey, propertyName) );
1118    }
1119    
1120    /**
1121     * Is the association property belonging to the keyed entity null?
1122     */

1123    public boolean isPropertyNull(EntityKey ownerKey, String JavaDoc propertyName) {
1124        return nullAssociations.contains( new AssociationKey(ownerKey, propertyName) );
1125    }
1126    
1127    private void clearNullProperties() {
1128        nullAssociations.clear();
1129    }
1130
1131    public void setReadOnly(Object JavaDoc entity, boolean readOnly) {
1132        EntityEntry entry = getEntry(entity);
1133        if (entry==null) {
1134            throw new TransientObjectException("Instance was not associated with the session");
1135        }
1136        entry.setReadOnly(readOnly, entity);
1137        hasNonReadOnlyEntities = hasNonReadOnlyEntities || !readOnly;
1138    }
1139}
1140
Popular Tags