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.collection;
26
27 import java.io.Serializable;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.ListIterator;
34
35 import org.hibernate.AssertionFailure;
36 import org.hibernate.HibernateException;
37 import org.hibernate.LazyInitializationException;
38 import org.hibernate.engine.CollectionEntry;
39 import org.hibernate.engine.ForeignKeys;
40 import org.hibernate.engine.SessionImplementor;
41 import org.hibernate.engine.TypedValue;
42 import org.hibernate.persister.collection.CollectionPersister;
43 import org.hibernate.pretty.MessageHelper;
44 import org.hibernate.type.Type;
45 import org.hibernate.util.CollectionHelper;
46 import org.hibernate.util.EmptyIterator;
47 import org.hibernate.util.MarkerObject;
48
49 /**
50 * Base class implementing {@link PersistentCollection}
51 *
52 * @author Gavin King
53 */
54 public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
55
56 private transient SessionImplementor session;
57 private boolean initialized;
58 private transient List operationQueue;
59 private transient boolean directlyAccessible;
60 private transient boolean initializing;
61 private Object owner;
62 private int cachedSize = -1;
63
64 private String role;
65 private Serializable key;
66 // collections detect changes made via their public interface and mark
67 // themselves as dirty as a performance optimization
68 private boolean dirty;
69 private Serializable storedSnapshot;
70
71 public final String getRole() {
72 return role;
73 }
74
75 public final Serializable getKey() {
76 return key;
77 }
78
79 public final boolean isUnreferenced() {
80 return role==null;
81 }
82
83 public final boolean isDirty() {
84 return dirty;
85 }
86
87 public final void clearDirty() {
88 dirty = false;
89 }
90
91 public final void dirty() {
92 dirty = true;
93 }
94
95 public final Serializable getStoredSnapshot() {
96 return storedSnapshot;
97 }
98
99 //Careful: these methods do not initialize the collection.
100 /**
101 * Is the initialized collection empty?
102 */
103 public abstract boolean empty();
104 /**
105 * Called by any read-only method of the collection interface
106 */
107 protected final void read() {
108 initialize(false);
109 }
110 /**
111 * Called by the <tt>size()</tt> method
112 */
113 protected boolean readSize() {
114 if (!initialized) {
115 if ( cachedSize!=-1 && !hasQueuedOperations() ) {
116 return true;
117 }
118 else {
119 throwLazyInitializationExceptionIfNotConnected();
120 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
121 CollectionPersister persister = entry.getLoadedPersister();
122 if ( persister.isExtraLazy() ) {
123 if ( hasQueuedOperations() ) {
124 session.flush();
125 }
126 cachedSize = persister.getSize( entry.getLoadedKey(), session );
127 return true;
128 }
129 }
130 }
131 read();
132 return false;
133 }
134
135 protected Boolean readIndexExistence(Object index) {
136 if (!initialized) {
137 throwLazyInitializationExceptionIfNotConnected();
138 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
139 CollectionPersister persister = entry.getLoadedPersister();
140 if ( persister.isExtraLazy() ) {
141 if ( hasQueuedOperations() ) {
142 session.flush();
143 }
144 return new Boolean( persister.indexExists( entry.getLoadedKey(), index, session ) );
145 }
146 }
147 read();
148 return null;
149
150 }
151
152 protected Boolean readElementExistence(Object element) {
153 if (!initialized) {
154 throwLazyInitializationExceptionIfNotConnected();
155 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
156 CollectionPersister persister = entry.getLoadedPersister();
157 if ( persister.isExtraLazy() ) {
158 if ( hasQueuedOperations() ) {
159 session.flush();
160 }
161 return new Boolean( persister.elementExists( entry.getLoadedKey(), element, session ) );
162 }
163 }
164 read();
165 return null;
166
167 }
168
169 protected static final Object UNKNOWN = new MarkerObject("UNKNOWN");
170
171 protected Object readElementByIndex(Object index) {
172 if (!initialized) {
173 throwLazyInitializationExceptionIfNotConnected();
174 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
175 CollectionPersister persister = entry.getLoadedPersister();
176 if ( persister.isExtraLazy() ) {
177 if ( hasQueuedOperations() ) {
178 session.flush();
179 }
180 return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
181 }
182 }
183 read();
184 return UNKNOWN;
185
186 }
187
188 protected int getCachedSize() {
189 return cachedSize;
190 }
191
192 /**
193 * Is the collection currently connected to an open session?
194 */
195 private final boolean isConnectedToSession() {
196 return session!=null &&
197 session.isOpen() &&
198 session.getPersistenceContext().containsCollection(this);
199 }
200
201 /**
202 * Called by any writer method of the collection interface
203 */
204 protected final void write() {
205 initialize(true);
206 dirty();
207 }
208 /**
209 * Is this collection in a state that would allow us to
210 * "queue" operations?
211 */
212 protected boolean isOperationQueueEnabled() {
213 return !initialized &&
214 isConnectedToSession() &&
215 isInverseCollection();
216 }
217 /**
218 * Is this collection in a state that would allow us to
219 * "queue" puts? This is a special case, because of orphan
220 * delete.
221 */
222 protected boolean isPutQueueEnabled() {
223 return !initialized &&
224 isConnectedToSession() &&
225 isInverseOneToManyOrNoOrphanDelete();
226 }
227 /**
228 * Is this collection in a state that would allow us to
229 * "queue" clear? This is a special case, because of orphan
230 * delete.
231 */
232 protected boolean isClearQueueEnabled() {
233 return !initialized &&
234 isConnectedToSession() &&
235 isInverseCollectionNoOrphanDelete();
236 }
237
238 /**
239 * Is this the "inverse" end of a bidirectional association?
240 */
241 private boolean isInverseCollection() {
242 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
243 return ce != null && ce.getLoadedPersister().isInverse();
244 }
245
246 /**
247 * Is this the "inverse" end of a bidirectional association with
248 * no orphan delete enabled?
249 */
250 private boolean isInverseCollectionNoOrphanDelete() {
251 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
252 return ce != null &&
253 ce.getLoadedPersister().isInverse() &&
254 !ce.getLoadedPersister().hasOrphanDelete();
255 }
256
257 /**
258 * Is this the "inverse" end of a bidirectional one-to-many, or
259 * of a collection with no orphan delete?
260 */
261 private boolean isInverseOneToManyOrNoOrphanDelete() {
262 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
263 return ce != null && ce.getLoadedPersister().isInverse() && (
264 ce.getLoadedPersister().isOneToMany() ||
265 !ce.getLoadedPersister().hasOrphanDelete()
266 );
267 }
268
269 /**
270 * Queue an addition
271 */
272 protected final void queueOperation(Object element) {
273 if (operationQueue==null) operationQueue = new ArrayList(10);
274 operationQueue.add(element);
275 dirty = true; //needed so that we remove this collection from the second-level cache
276 }
277
278 /**
279 * After reading all existing elements from the database,
280 * add the queued elements to the underlying collection.
281 */
282 protected final void performQueuedOperations() {
283 for ( int i=0; i<operationQueue.size(); i++ ) {
284 ( (DelayedOperation) operationQueue.get(i) ).operate();
285 }
286 }
287
288 /**
289 * After flushing, re-init snapshot state.
290 */
291 public void setSnapshot(Serializable key, String role, Serializable snapshot) {
292 this.key = key;
293 this.role = role;
294 this.storedSnapshot = snapshot;
295 }
296
297 /**
298 * After flushing, clear any "queued" additions, since the
299 * database state is now synchronized with the memory state.
300 */
301 public void postAction() {
302 operationQueue=null;
303 cachedSize = -1;
304 clearDirty();
305 }
306
307 /**
308 * Not called by Hibernate, but used by non-JDK serialization,
309 * eg. SOAP libraries.
310 */
311 public AbstractPersistentCollection() {}
312
313 protected AbstractPersistentCollection(SessionImplementor session) {
314 this.session = session;
315 }
316
317 /**
318 * return the user-visible collection (or array) instance
319 */
320 public Object getValue() {
321 return this;
322 }
323
324 /**
325 * Called just before reading any rows from the JDBC result set
326 */
327 public void beginRead() {
328 // override on some subclasses
329 initializing = true;
330 }
331
332 /**
333 * Called after reading all rows from the JDBC result set
334 */
335 public boolean endRead() {
336 //override on some subclasses
337 return afterInitialize();
338 }
339
340 public boolean afterInitialize() {
341 setInitialized();
342 //do this bit after setting initialized to true or it will recurse
343 if (operationQueue!=null) {
344 performQueuedOperations();
345 operationQueue=null;
346 cachedSize = -1;
347 return false;
348 }
349 else {
350 return true;
351 }
352 }
353
354 /**
355 * Initialize the collection, if possible, wrapping any exceptions
356 * in a runtime exception
357 * @param writing currently obsolete
358 * @throws LazyInitializationException if we cannot initialize
359 */
360 protected final void initialize(boolean writing) {
361 if (!initialized) {
362 if (initializing) {
363 throw new LazyInitializationException("illegal access to loading collection");
364 }
365 throwLazyInitializationExceptionIfNotConnected();
366 session.initializeCollection(this, writing);
367 }
368 }
369
370 private void throwLazyInitializationExceptionIfNotConnected() {
371 if ( !isConnectedToSession() ) {
372 throwLazyInitializationException("no session or session was closed");
373 }
374 if ( !session.isConnected() ) {
375 throwLazyInitializationException("session is disconnected");
376 }
377 }
378
379 private void throwLazyInitializationException(String message) {
380 throw new LazyInitializationException(
381 "failed to lazily initialize a collection" +
382 ( role==null ? "" : " of role: " + role ) +
383 ", " + message
384 );
385 }
386
387 protected final void setInitialized() {
388 this.initializing = false;
389 this.initialized = true;
390 }
391
392 protected final void setDirectlyAccessible(boolean directlyAccessible) {
393 this.directlyAccessible = directlyAccessible;
394 }
395
396 /**
397 * Could the application possibly have a direct reference to
398 * the underlying collection implementation?
399 */
400 public boolean isDirectlyAccessible() {
401 return directlyAccessible;
402 }
403
404 /**
405 * Disassociate this collection from the given session.
406 * @return true if this was currently associated with the given session
407 */
408 public final boolean unsetSession(SessionImplementor currentSession) {
409 if (currentSession==this.session) {
410 this.session=null;
411 return true;
412 }
413 else {
414 return false;
415 }
416 }
417
418 /**
419 * Associate the collection with the given session.
420 * @return false if the collection was already associated with the session
421 * @throws HibernateException if the collection was already associated
422 * with another open session
423 */
424 public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
425 if (session==this.session) {
426 return false;
427 }
428 else {
429 if ( isConnectedToSession() ) {
430 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
431 if (ce==null) {
432 throw new HibernateException(
433 "Illegal attempt to associate a collection with two open sessions"
434 );
435 }
436 else {
437 throw new HibernateException(
438 "Illegal attempt to associate a collection with two open sessions: " +
439 MessageHelper.collectionInfoString(
440 ce.getLoadedPersister(),
441 ce.getLoadedKey(),
442 session.getFactory()
443 )
444 );
445 }
446 }
447 else {
448 this.session = session;
449 return true;
450 }
451 }
452 }
453
454 /**
455 * Do we need to completely recreate this collection when it changes?
456 */
457 public boolean needsRecreate(CollectionPersister persister) {
458 return false;
459 }
460
461 /**
462 * To be called internally by the session, forcing
463 * immediate initialization.
464 */
465 public final void forceInitialization() throws HibernateException {
466 if (!initialized) {
467 if (initializing) {
468 throw new AssertionFailure("force initialize loading collection");
469 }
470 if (session==null) {
471 throw new HibernateException("collection is not associated with any session");
472 }
473 if ( !session.isConnected() ) {
474 throw new HibernateException("disconnected session");
475 }
476 session.initializeCollection(this, false);
477 }
478 }
479
480
481 /**
482 * Get the current snapshot from the session
483 */
484 protected final Serializable getSnapshot() {
485 return session.getPersistenceContext().getSnapshot(this);
486 }
487
488 /**
489 * Is this instance initialized?
490 */
491 public final boolean wasInitialized() {
492 return initialized;
493 }
494
495 public boolean isRowUpdatePossible() {
496 return true;
497 }
498
499 /**
500 * Does this instance have any "queued" additions?
501 */
502 public final boolean hasQueuedOperations() {
503 return operationQueue!=null;
504 }
505 /**
506 * Iterate the "queued" additions
507 */
508 public final Iterator queuedAdditionIterator() {
509 if ( hasQueuedOperations() ) {
510 return new Iterator() {
511 int i = 0;
512 public Object next() {
513 return ( (DelayedOperation) operationQueue.get(i++) ).getAddedInstance();
514 }
515 public boolean hasNext() {
516 return i<operationQueue.size();
517 }
518 public void remove() {
519 throw new UnsupportedOperationException();
520 }
521 };
522 }
523 else {
524 return EmptyIterator.INSTANCE;
525 }
526 }
527 /**
528 * Iterate the "queued" additions
529 */
530 public final Collection getQueuedOrphans(String entityName) {
531 if ( hasQueuedOperations() ) {
532 Collection additions = new ArrayList( operationQueue.size() );
533 Collection removals = new ArrayList( operationQueue.size() );
534 for ( int i = 0; i < operationQueue.size(); i++ ) {
535 DelayedOperation op = (DelayedOperation) operationQueue.get(i);
536 additions.add( op.getAddedInstance() );
537 removals.add( op.getOrphan() );
538 }
539 return getOrphans(removals, additions, entityName, session);
540 }
541 else {
542 return CollectionHelper.EMPTY_COLLECTION;
543 }
544 }
545
546 /**
547 * Called before inserting rows, to ensure that any surrogate keys
548 * are fully generated
549 */
550 public void preInsert(CollectionPersister persister) throws HibernateException {}
551 /**
552 * Called after inserting a row, to fetch the natively generated id
553 */
554 public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {}
555 /**
556 * get all "orphaned" elements
557 */
558 public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
559
560 /**
561 * Get the current session
562 */
563 public final SessionImplementor getSession() {
564 return session;
565 }
566
567 final class IteratorProxy implements Iterator {
568 private final Iterator iter;
569 IteratorProxy(Iterator iter) {
570 this.iter=iter;
571 }
572 public boolean hasNext() {
573 return iter.hasNext();
574 }
575
576 public Object next() {
577 return iter.next();
578 }
579
580 public void remove() {
581 write();
582 iter.remove();
583 }
584
585 }
586
587 final class ListIteratorProxy implements ListIterator {
588 private final ListIterator iter;
589 ListIteratorProxy(ListIterator iter) {
590 this.iter = iter;
591 }
592 public void add(Object o) {
593 write();
594 iter.add(o);
595 }
596
597 public boolean hasNext() {
598 return iter.hasNext();
599 }
600
601 public boolean hasPrevious() {
602 return iter.hasPrevious();
603 }
604
605 public Object next() {
606 return iter.next();
607 }
608
609 public int nextIndex() {
610 return iter.nextIndex();
611 }
612
613 public Object previous() {
614 return iter.previous();
615 }
616
617 public int previousIndex() {
618 return iter.previousIndex();
619 }
620
621 public void remove() {
622 write();
623 iter.remove();
624 }
625
626 public void set(Object o) {
627 write();
628 iter.set(o);
629 }
630
631 }
632
633 class SetProxy implements java.util.Set {
634
635 final Collection set;
636
637 SetProxy(Collection set) {
638 this.set=set;
639 }
640 public boolean add(Object o) {
641 write();
642 return set.add(o);
643 }
644
645 public boolean addAll(Collection c) {
646 write();
647 return set.addAll(c);
648 }
649
650 public void clear() {
651 write();
652 set.clear();
653 }
654
655 public boolean contains(Object o) {
656 return set.contains(o);
657 }
658
659 public boolean containsAll(Collection c) {
660 return set.containsAll(c);
661 }
662
663 public boolean isEmpty() {
664 return set.isEmpty();
665 }
666
667 public Iterator iterator() {
668 return new IteratorProxy( set.iterator() );
669 }
670
671 public boolean remove(Object o) {
672 write();
673 return set.remove(o);
674 }
675
676 public boolean removeAll(Collection c) {
677 write();
678 return set.removeAll(c);
679 }
680
681 public boolean retainAll(Collection c) {
682 write();
683 return set.retainAll(c);
684 }
685
686 public int size() {
687 return set.size();
688 }
689
690 public Object[] toArray() {
691 return set.toArray();
692 }
693
694 public Object[] toArray(Object[] array) {
695 return set.toArray(array);
696 }
697
698 }
699
700 final class ListProxy implements java.util.List {
701
702 private final java.util.List list;
703
704 ListProxy(java.util.List list) {
705 this.list = list;
706 }
707
708 public void add(int index, Object value) {
709 write();
710 list.add(index, value);
711 }
712
713 /**
714 * @see java.util.Collection#add(Object)
715 */
716 public boolean add(Object o) {
717 write();
718 return list.add(o);
719 }
720
721 /**
722 * @see java.util.Collection#addAll(Collection)
723 */
724 public boolean addAll(Collection c) {
725 write();
726 return list.addAll(c);
727 }
728
729 /**
730 * @see java.util.List#addAll(int, Collection)
731 */
732 public boolean addAll(int i, Collection c) {
733 write();
734 return list.addAll(i, c);
735 }
736
737 /**
738 * @see java.util.Collection#clear()
739 */
740 public void clear() {
741 write();
742 list.clear();
743 }
744
745 /**
746 * @see java.util.Collection#contains(Object)
747 */
748 public boolean contains(Object o) {
749 return list.contains(o);
750 }
751
752 /**
753 * @see java.util.Collection#containsAll(Collection)
754 */
755 public boolean containsAll(Collection c) {
756 return list.containsAll(c);
757 }
758
759 /**
760 * @see java.util.List#get(int)
761 */
762 public Object get(int i) {
763 return list.get(i);
764 }
765
766 /**
767 * @see java.util.List#indexOf(Object)
768 */
769 public int indexOf(Object o) {
770 return list.indexOf(o);
771 }
772
773 /**
774 * @see java.util.Collection#isEmpty()
775 */
776 public boolean isEmpty() {
777 return list.isEmpty();
778 }
779
780 /**
781 * @see java.util.Collection#iterator()
782 */
783 public Iterator iterator() {
784 return new IteratorProxy( list.iterator() );
785 }
786
787 /**
788 * @see java.util.List#lastIndexOf(Object)
789 */
790 public int lastIndexOf(Object o) {
791 return list.lastIndexOf(o);
792 }
793
794 /**
795 * @see java.util.List#listIterator()
796 */
797 public ListIterator listIterator() {
798 return new ListIteratorProxy( list.listIterator() );
799 }
800
801 /**
802 * @see java.util.List#listIterator(int)
803 */
804 public ListIterator listIterator(int i) {
805 return new ListIteratorProxy( list.listIterator(i) );
806 }
807
808 /**
809 * @see java.util.List#remove(int)
810 */
811 public Object remove(int i) {
812 write();
813 return list.remove(i);
814 }
815
816 /**
817 * @see java.util.Collection#remove(Object)
818 */
819 public boolean remove(Object o) {
820 write();
821 return list.remove(o);
822 }
823
824 /**
825 * @see java.util.Collection#removeAll(Collection)
826 */
827 public boolean removeAll(Collection c) {
828 write();
829 return list.removeAll(c);
830 }
831
832 /**
833 * @see java.util.Collection#retainAll(Collection)
834 */
835 public boolean retainAll(Collection c) {
836 write();
837 return list.retainAll(c);
838 }
839
840 /**
841 * @see java.util.List#set(int, Object)
842 */
843 public Object set(int i, Object o) {
844 write();
845 return list.set(i, o);
846 }
847
848 /**
849 * @see java.util.Collection#size()
850 */
851 public int size() {
852 return list.size();
853 }
854
855 /**
856 * @see java.util.List#subList(int, int)
857 */
858 public List subList(int i, int j) {
859 return list.subList(i, j);
860 }
861
862 /**
863 * @see java.util.Collection#toArray()
864 */
865 public Object[] toArray() {
866 return list.toArray();
867 }
868
869 /**
870 * @see java.util.Collection#toArray(Object[])
871 */
872 public Object[] toArray(Object[] array) {
873 return list.toArray(array);
874 }
875
876 }
877
878
879 protected interface DelayedOperation {
880 public void operate();
881 public Object getAddedInstance();
882 public Object getOrphan();
883 }
884
885 /**
886 * Given a collection of entity instances that used to
887 * belong to the collection, and a collection of instances
888 * that currently belong, return a collection of orphans
889 */
890 protected static Collection getOrphans(
891 Collection oldElements,
892 Collection currentElements,
893 String entityName,
894 SessionImplementor session)
895 throws HibernateException {
896
897 // short-circuit(s)
898 if ( currentElements.size()==0 ) return oldElements; // no new elements, the old list contains only Orphans
899 if ( oldElements.size()==0) return oldElements; // no old elements, so no Orphans neither
900
901 Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
902
903 // create the collection holding the Orphans
904 Collection res = new ArrayList();
905
906 // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
907 java.util.Set currentIds = new HashSet();
908 for ( Iterator it=currentElements.iterator(); it.hasNext(); ) {
909 Object current = it.next();
910 if ( current!=null && ForeignKeys.isNotTransient(entityName, current, null, session) ) {
911 Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, current, session);
912 currentIds.add( new TypedValue( idType, currentId, session.getEntityMode() ) );
913 }
914 }
915
916 // iterate over the *old* list
917 for ( Iterator it=oldElements.iterator(); it.hasNext(); ) {
918 Object old = it.next();
919 Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, old, session);
920 if ( !currentIds.contains( new TypedValue( idType, oldId, session.getEntityMode() ) ) ) {
921 res.add(old);
922 }
923 }
924
925 return res;
926 }
927
928 static void identityRemove(
929 Collection list,
930 Object object,
931 String entityName,
932 SessionImplementor session)
933 throws HibernateException {
934
935 if ( object!=null && ForeignKeys.isNotTransient(entityName, object, null, session) ) {
936
937 Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
938
939 Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, object, session);
940 Iterator iter = list.iterator();
941 while ( iter.hasNext() ) {
942 Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, iter.next(), session);
943 if ( idType.isEqual( idOfCurrent, idOfOld, session.getEntityMode(), session.getFactory() ) ) {
944 iter.remove();
945 break;
946 }
947 }
948
949 }
950 }
951
952 public Object getIdentifier(Object entry, int i) {
953 throw new UnsupportedOperationException();
954 }
955
956 public Object getOwner() {
957 return owner;
958 }
959
960 public void setOwner(Object owner) {
961 this.owner = owner;
962 }
963
964 }
965