Source code: org/hibernate/collection/AbstractPersistentCollection.java
1 //$Id: AbstractPersistentCollection.java,v 1.17 2005/04/02 20:33:53 oneovthafew Exp $
2 package org.hibernate.collection;
3
4 import java.io.Serializable;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.ListIterator;
11
12 import org.hibernate.AssertionFailure;
13 import org.hibernate.HibernateException;
14 import org.hibernate.LazyInitializationException;
15 import org.hibernate.engine.CollectionSnapshot;
16 import org.hibernate.engine.ForeignKeys;
17 import org.hibernate.engine.SessionImplementor;
18 import org.hibernate.engine.TypedValue;
19 import org.hibernate.persister.collection.CollectionPersister;
20 import org.hibernate.type.Type;
21 import org.hibernate.util.EmptyIterator;
22
23 /**
24 * Base class implementing <tt>PersistentCollection</tt>
25 * @see PersistentCollection
26 * @author Gavin King
27 */
28 public abstract class AbstractPersistentCollection
29 implements Serializable, PersistentCollection {
30
31 private transient SessionImplementor session;
32 private boolean initialized;
33 private transient List additions;
34 private CollectionSnapshot collectionSnapshot;
35 private transient boolean directlyAccessible;
36 private transient boolean initializing;
37 private Object owner;
38
39 //Careful: these methods do not initialize the collection.
40 /**
41 * Is the initialized collection empty?
42 */
43 public abstract boolean empty();
44 /**
45 * Called by any read-only method of the collection interface
46 */
47 protected final void read() {
48 initialize(false);
49 }
50 /**
51 * Is the collection currently connected to an open session?
52 */
53 private final boolean isConnectedToSession() {
54 return session!=null && session.isOpen();
55 }
56
57 /**
58 * Called by any writer method of the collection interface
59 */
60 protected final void write() {
61 initialize(true);
62 collectionSnapshot.setDirty();
63 }
64 /**
65 * Is this collection in a state that would allow us to
66 * "queue" additions?
67 */
68 private boolean isQueueAdditionEnabled() {
69 return !initialized &&
70 isConnectedToSession() &&
71 session.getPersistenceContext().isInverseCollection(this);
72 }
73 /**
74 * Queue an addition
75 */
76 protected final boolean queueAdd(Object element) {
77 if ( isQueueAdditionEnabled() ) {
78 if (additions==null) additions = new ArrayList(10);
79 additions.add(element);
80 collectionSnapshot.setDirty(); //needed so that we remove this collection from the second-level cache
81 return true;
82 }
83 else {
84 return false;
85 }
86 }
87 /**
88 * Queue additions
89 */
90 protected final boolean queueAddAll(Collection coll) {
91 if ( isQueueAdditionEnabled() ) {
92 if (additions==null) additions = new ArrayList(20);
93 additions.addAll(coll);
94 return true;
95 }
96 else {
97 return false;
98 }
99 }
100
101 /**
102 * After reading all existing elements from the database,
103 * add the queued elements to the underlying collection.
104 */
105 public void delayedAddAll(Collection coll) {
106 throw new AssertionFailure("Collection does not support delayed initialization");
107 }
108
109 /**
110 * After flushing, clear any "queued" additions, since the
111 * database state is now synchronized with the memory state.
112 */
113 public void postFlush() {
114 if (additions!=null) additions=null;
115 }
116
117 /**
118 * Not called by Hibernate, but used by non-JDK serialization,
119 * eg. SOAP libraries.
120 */
121 public AbstractPersistentCollection() {}
122
123 protected AbstractPersistentCollection(SessionImplementor session) {
124 this.session = session;
125 }
126
127 /**
128 * return the user-visible collection (or array) instance
129 */
130 public Object getValue() {
131 return this;
132 }
133
134 /**
135 * Called just before reading any rows from the JDBC result set
136 */
137 public void beginRead() {
138 // override on some subclasses
139 initializing = true;
140 }
141
142 /**
143 * Called after reading all rows from the JDBC result set
144 */
145 public boolean endRead() {
146 //override on some subclasses
147
148 setInitialized();
149 //do this bit after setting initialized to true or it will recurse
150 if (additions!=null) {
151 delayedAddAll(additions);
152 additions=null;
153 return false;
154 }
155 else {
156 return true;
157 }
158 }
159
160 /**
161 * Initialize the collection, if possible, wrapping any exceptions
162 * in a runtime exception
163 * @param writing currently obsolete
164 * @throws LazyInitializationException if we cannot initialize
165 */
166 protected final void initialize(boolean writing) {
167 if (!initialized) {
168 if (initializing) throw new LazyInitializationException("cannot access loading collection");
169 if ( isConnectedToSession() ) {
170 if ( session.isConnected() ) {
171 session.initializeCollection(this, writing);
172 }
173 else {
174 String name = (getCollectionSnapshot()!=null)?"("+getCollectionSnapshot().getRole()+")":"";
175 throw new LazyInitializationException("failed to lazily initialize a collection " + name + " - session is disconnected");
176 }
177 }
178 else {
179 String name = (getCollectionSnapshot()!=null)?"("+getCollectionSnapshot().getRole()+")":"";
180 throw new LazyInitializationException("failed to lazily initialize a collection " + name + " - no session or session was closed");
181 }
182 }
183 }
184
185 protected final void setInitialized() {
186 this.initializing = false;
187 this.initialized = true;
188 }
189
190 protected final void setDirectlyAccessible(boolean directlyAccessible) {
191 this.directlyAccessible = directlyAccessible;
192 }
193
194 /**
195 * Could the application possibly have a direct reference to
196 * the underlying collection implementation?
197 */
198 public boolean isDirectlyAccessible() {
199 return directlyAccessible;
200 }
201
202 /**
203 * Disassociate this collection from the given session.
204 * @return true if this was currently associated with the given session
205 */
206 public final boolean unsetSession(SessionImplementor currentSession) {
207 if (currentSession==this.session) {
208 this.session=null;
209 return true;
210 }
211 else {
212 return false;
213 }
214 }
215
216 /**
217 * Associate the collection with the given session.
218 * @return false if the collection was already associated with the session
219 * @throws HibernateException if the collection was already associated
220 * with another open session
221 */
222 public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
223 if (session==this.session) {
224 return false;
225 }
226 else {
227 if ( isConnectedToSession() ) {
228 throw new HibernateException("Illegal attempt to associate a collection with two open sessions");
229 }
230 else {
231 this.session = session;
232 return true;
233 }
234 }
235 }
236
237 /**
238 * Do we need to completely recreate this collection when it changes?
239 */
240 public boolean needsRecreate(CollectionPersister persister) {
241 return false;
242 }
243 /**
244 * Return a new snapshot of the current state of the collection,
245 * or null if no persister is passed
246 */
247 public final Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
248 return (persister==null) ? null : snapshot(persister);
249 }
250
251 /**
252 * Return a new snapshot of the current state
253 */
254 protected abstract Serializable snapshot(CollectionPersister persister) throws HibernateException;
255
256 /**
257 * To be called internally by the session, forcing
258 * immediate initialization.
259 */
260 public final void forceInitialization() throws HibernateException {
261 if (!initialized) {
262 if (initializing) {
263 throw new AssertionFailure("force initialize loading collection");
264 }
265 if (session==null) {
266 throw new HibernateException("collection is not associated with any session");
267 }
268 if ( !session.isConnected() ) {
269 throw new HibernateException("disconnected session");
270 }
271 session.initializeCollection(this, false);
272 }
273 }
274
275
276 /**
277 * Get the current snapshot from the session
278 */
279 protected final Serializable getSnapshot() {
280 return session.getPersistenceContext().getSnapshot(this);
281 }
282
283 /**
284 * Is this instance initialized?
285 */
286 public final boolean wasInitialized() {
287 return initialized;
288 }
289
290 public boolean isRowUpdatePossible() {
291 return true;
292 }
293
294 /**
295 * Does this instance have any "queued" additions?
296 */
297 public final boolean hasQueuedAdditions() {
298 return additions!=null;
299 }
300 /**
301 * Iterate the "queued" additions
302 */
303 public final Iterator queuedAdditionIterator() {
304 return hasQueuedAdditions() ?
305 additions.iterator() :
306 EmptyIterator.INSTANCE;
307 }
308
309 /**
310 * Returns the collectionSnapshot.
311 * @return CollectionSnapshot
312 */
313 public CollectionSnapshot getCollectionSnapshot() {
314 return collectionSnapshot;
315 }
316
317 /**
318 * Sets the collectionSnapshot.
319 * @param collectionSnapshot The collectionSnapshot to set
320 */
321 public void setCollectionSnapshot(CollectionSnapshot collectionSnapshot) {
322 this.collectionSnapshot = collectionSnapshot;
323 }
324
325 /**
326 * Called before inserting rows, to ensure that any surrogate keys
327 * are fully generated
328 */
329 public void preInsert(CollectionPersister persister) throws HibernateException {}
330 /**
331 * Called after inserting a row, to fetch the natively generated id
332 */
333 public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {}
334 /**
335 * get all "orphaned" elements
336 * @param entityName TODO
337 */
338 public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
339
340 /**
341 * Get the current session
342 */
343 protected final SessionImplementor getSession() {
344 return session;
345 }
346
347 final class IteratorProxy implements Iterator {
348 private final Iterator iter;
349 IteratorProxy(Iterator iter) {
350 this.iter=iter;
351 }
352 public boolean hasNext() {
353 return iter.hasNext();
354 }
355
356 public Object next() {
357 return iter.next();
358 }
359
360 public void remove() {
361 write();
362 iter.remove();
363 }
364
365 }
366
367 final class ListIteratorProxy implements ListIterator {
368 private final ListIterator iter;
369 ListIteratorProxy(ListIterator iter) {
370 this.iter = iter;
371 }
372 public void add(Object o) {
373 write();
374 iter.add(o);
375 }
376
377 public boolean hasNext() {
378 return iter.hasNext();
379 }
380
381 public boolean hasPrevious() {
382 return iter.hasPrevious();
383 }
384
385 public Object next() {
386 return iter.next();
387 }
388
389 public int nextIndex() {
390 return iter.nextIndex();
391 }
392
393 public Object previous() {
394 return iter.previous();
395 }
396
397 public int previousIndex() {
398 return iter.previousIndex();
399 }
400
401 public void remove() {
402 write();
403 iter.remove();
404 }
405
406 public void set(Object o) {
407 write();
408 iter.set(o);
409 }
410
411 }
412
413 class SetProxy implements java.util.Set {
414
415 final Collection set;
416
417 SetProxy(Collection set) {
418 this.set=set;
419 }
420 public boolean add(Object o) {
421 write();
422 return set.add(o);
423 }
424
425 public boolean addAll(Collection c) {
426 write();
427 return set.addAll(c);
428 }
429
430 public void clear() {
431 write();
432 set.clear();
433 }
434
435 public boolean contains(Object o) {
436 return set.contains(o);
437 }
438
439 public boolean containsAll(Collection c) {
440 return set.containsAll(c);
441 }
442
443 public boolean isEmpty() {
444 return set.isEmpty();
445 }
446
447 public Iterator iterator() {
448 return new IteratorProxy( set.iterator() );
449 }
450
451 public boolean remove(Object o) {
452 write();
453 return set.remove(o);
454 }
455
456 public boolean removeAll(Collection c) {
457 write();
458 return set.removeAll(c);
459 }
460
461 public boolean retainAll(Collection c) {
462 write();
463 return set.retainAll(c);
464 }
465
466 public int size() {
467 return set.size();
468 }
469
470 public Object[] toArray() {
471 return set.toArray();
472 }
473
474 public Object[] toArray(Object[] array) {
475 return set.toArray(array);
476 }
477
478 }
479
480 final class ListProxy implements java.util.List {
481
482 private final java.util.List list;
483
484 ListProxy(java.util.List list) {
485 this.list = list;
486 }
487
488 public void add(int index, Object value) {
489 write();
490 list.add(index, value);
491 }
492
493 /**
494 * @see java.util.Collection#add(Object)
495 */
496 public boolean add(Object o) {
497 write();
498 return list.add(o);
499 }
500
501 /**
502 * @see java.util.Collection#addAll(Collection)
503 */
504 public boolean addAll(Collection c) {
505 write();
506 return list.addAll(c);
507 }
508
509 /**
510 * @see java.util.List#addAll(int, Collection)
511 */
512 public boolean addAll(int i, Collection c) {
513 write();
514 return list.addAll(i, c);
515 }
516
517 /**
518 * @see java.util.Collection#clear()
519 */
520 public void clear() {
521 write();
522 list.clear();
523 }
524
525 /**
526 * @see java.util.Collection#contains(Object)
527 */
528 public boolean contains(Object o) {
529 return list.contains(o);
530 }
531
532 /**
533 * @see java.util.Collection#containsAll(Collection)
534 */
535 public boolean containsAll(Collection c) {
536 return list.containsAll(c);
537 }
538
539 /**
540 * @see java.util.List#get(int)
541 */
542 public Object get(int i) {
543 return list.get(i);
544 }
545
546 /**
547 * @see java.util.List#indexOf(Object)
548 */
549 public int indexOf(Object o) {
550 return list.indexOf(o);
551 }
552
553 /**
554 * @see java.util.Collection#isEmpty()
555 */
556 public boolean isEmpty() {
557 return list.isEmpty();
558 }
559
560 /**
561 * @see java.util.Collection#iterator()
562 */
563 public Iterator iterator() {
564 return new IteratorProxy( list.iterator() );
565 }
566
567 /**
568 * @see java.util.List#lastIndexOf(Object)
569 */
570 public int lastIndexOf(Object o) {
571 return list.lastIndexOf(o);
572 }
573
574 /**
575 * @see java.util.List#listIterator()
576 */
577 public ListIterator listIterator() {
578 return new ListIteratorProxy( list.listIterator() );
579 }
580
581 /**
582 * @see java.util.List#listIterator(int)
583 */
584 public ListIterator listIterator(int i) {
585 return new ListIteratorProxy( list.listIterator(i) );
586 }
587
588 /**
589 * @see java.util.List#remove(int)
590 */
591 public Object remove(int i) {
592 write();
593 return list.remove(i);
594 }
595
596 /**
597 * @see java.util.Collection#remove(Object)
598 */
599 public boolean remove(Object o) {
600 write();
601 return list.remove(o);
602 }
603
604 /**
605 * @see java.util.Collection#removeAll(Collection)
606 */
607 public boolean removeAll(Collection c) {
608 write();
609 return list.removeAll(c);
610 }
611
612 /**
613 * @see java.util.Collection#retainAll(Collection)
614 */
615 public boolean retainAll(Collection c) {
616 write();
617 return list.retainAll(c);
618 }
619
620 /**
621 * @see java.util.List#set(int, Object)
622 */
623 public Object set(int i, Object o) {
624 write();
625 return list.set(i, o);
626 }
627
628 /**
629 * @see java.util.Collection#size()
630 */
631 public int size() {
632 return list.size();
633 }
634
635 /**
636 * @see java.util.List#subList(int, int)
637 */
638 public List subList(int i, int j) {
639 return list.subList(i, j);
640 }
641
642 /**
643 * @see java.util.Collection#toArray()
644 */
645 public Object[] toArray() {
646 return list.toArray();
647 }
648
649 /**
650 * @see java.util.Collection#toArray(Object[])
651 */
652 public Object[] toArray(Object[] array) {
653 return list.toArray(array);
654 }
655
656 }
657
658
659 protected static Collection getOrphans(
660 Collection oldElements,
661 Collection currentElements,
662 String entityName,
663 SessionImplementor session)
664 throws HibernateException {
665
666 // short-circuit(s)
667 if ( currentElements.size()==0 ) return oldElements; // no new elements, the old list contains only Orphans
668 if ( oldElements.size()==0) return oldElements; // no old elements, so no Orphans neither
669
670 Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
671
672 // create the collection holding the Orphans
673 Collection res = new ArrayList();
674
675 // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
676 java.util.Set currentIds = new HashSet();
677 for ( Iterator it=currentElements.iterator(); it.hasNext(); ) {
678 Object current = it.next();
679 if ( current!=null && ForeignKeys.isNotTransient(entityName, current, null, session) ) {
680 Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, current, session);
681 currentIds.add( new TypedValue( idType, currentId, session.getEntityMode() ) );
682 }
683 }
684
685 // iterate over the *old* list
686 for ( Iterator it=oldElements.iterator(); it.hasNext(); ) {
687 Object old = it.next();
688 Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, old, session);
689 if ( !currentIds.contains( new TypedValue( idType, oldId, session.getEntityMode() ) ) ) res.add(old);
690 }
691
692 return res;
693 }
694
695 static void identityRemove(
696 Collection list,
697 Object object,
698 String entityName,
699 SessionImplementor session)
700 throws HibernateException {
701
702 if ( object!=null && ForeignKeys.isNotTransient(entityName, object, null, session) ) {
703
704 Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
705
706 Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, object, session);
707 Iterator iter = list.iterator();
708 while ( iter.hasNext() ) {
709 Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, iter.next(), session);
710 if ( idType.isEqual( idOfCurrent, idOfOld, session.getEntityMode(), session.getFactory() ) ) {
711 iter.remove();
712 break;
713 }
714 }
715
716 }
717 }
718
719 public Object getIdentifier(Object entry, int i) {
720 throw new UnsupportedOperationException();
721 }
722
723 public Object getOwner() {
724 return owner;
725 }
726
727 public void setOwner(Object owner) {
728 this.owner = owner;
729 }
730
731
732 }