~raginggoblin/infolog/infolog

« back to all changes in this revision

Viewing changes to InfologServer/lib/hibernate-distribution-3.3.2.GA/project/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java

  • Committer: Raging Goblin
  • Date: 2013-11-16 16:51:32 UTC
  • Revision ID: raging_goblin-20131116165132-weujnptzc88uy4ah
Mavenized the project, now using shared project InfologSync

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
}