2 * Hibernate, Relational Persistence for Idiomatic Java
4 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
7 package org
.hibernate
.collection
.internal
;
9 import java
.io
.Serializable
;
10 import java
.lang
.reflect
.Field
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Collection
;
13 import java
.util
.Collections
;
14 import java
.util
.HashSet
;
15 import java
.util
.Iterator
;
16 import java
.util
.List
;
17 import java
.util
.ListIterator
;
20 import org
.hibernate
.AssertionFailure
;
21 import org
.hibernate
.FlushMode
;
22 import org
.hibernate
.HibernateException
;
23 import org
.hibernate
.LazyInitializationException
;
24 import org
.hibernate
.Session
;
25 import org
.hibernate
.collection
.spi
.PersistentCollection
;
26 import org
.hibernate
.engine
.internal
.ForeignKeys
;
27 import org
.hibernate
.engine
.spi
.CollectionEntry
;
28 import org
.hibernate
.engine
.spi
.EntityEntry
;
29 import org
.hibernate
.engine
.spi
.PersistenceContext
;
30 import org
.hibernate
.engine
.spi
.SessionFactoryImplementor
;
31 import org
.hibernate
.engine
.spi
.SessionImplementor
;
32 import org
.hibernate
.engine
.spi
.SharedSessionContractImplementor
;
33 import org
.hibernate
.engine
.spi
.Status
;
34 import org
.hibernate
.engine
.spi
.TypedValue
;
35 import org
.hibernate
.internal
.CoreLogging
;
36 import org
.hibernate
.internal
.CoreMessageLogger
;
37 import org
.hibernate
.internal
.SessionFactoryRegistry
;
38 import org
.hibernate
.internal
.util
.MarkerObject
;
39 import org
.hibernate
.internal
.util
.collections
.IdentitySet
;
40 import org
.hibernate
.persister
.collection
.CollectionPersister
;
41 import org
.hibernate
.persister
.entity
.EntityPersister
;
42 import org
.hibernate
.pretty
.MessageHelper
;
43 import org
.hibernate
.resource
.transaction
.spi
.TransactionStatus
;
44 import org
.hibernate
.type
.CompositeType
;
45 import org
.hibernate
.type
.IntegerType
;
46 import org
.hibernate
.type
.LongType
;
47 import org
.hibernate
.type
.PostgresUUIDType
;
48 import org
.hibernate
.type
.StringType
;
49 import org
.hibernate
.type
.Type
;
50 import org
.hibernate
.type
.UUIDBinaryType
;
51 import org
.hibernate
.type
.UUIDCharType
;
53 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationRemoteConfiguration
;
54 import eu
.etaxonomy
.cdm
.cache
.ProxyUtils
;
55 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
56 import eu
.etaxonomy
.taxeditor
.remoting
.CdmEagerLoadingException
;
57 import eu
.etaxonomy
.taxeditor
.service
.ICachedCommonService
;
60 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
62 * This is an extended copy of the original class from hibernate. It has been extended to
63 * allow making remote service calls to spring httpinvoker services (see section at the bottom
66 * NOTE: For updating this class to the latest hibernate version, update the serialVersionUID as
67 * described above, copy the new class to the old class BUT keep those 5 places marked with
71 * @author Cherian Mathew
73 public abstract class AbstractPersistentCollection
implements Serializable
, PersistentCollection
{
74 private static final CoreMessageLogger LOG
= CoreLogging
.messageLogger( AbstractPersistentCollection
.class );
77 * <b>IMPORTANT:</b><br>
78 * This serialVersionUID must be kept in sync with the serialVersionUID which is generated
79 * on the fly for serialized AbstractPersistentCollection objects coming from the httpInvoker
81 * This is most probably necessary after updating hibernate to a newer version. In any case
82 * it the need for updating this <code>serialVersionUID</code> becomes obvious when the attempt
83 * to connect to the server side fails with an <code>InvalidClassException</code>:
86 * java.io.InvalidClassException: org.hibernate.collection.internal.AbstractPersistentCollection;
87 * local class incompatible:
88 * stream classdesc serialVersionUID = 2742261122392386159,
89 * local class serialVersionUID = -7238232378593030571
91 * The correct <code>serialVersionUID</code> is the <code>stream classdesc serialVersionUID</code>
92 * from the error message.
94 private static final long serialVersionUID
= 7094296207968006972L;
96 private transient SharedSessionContractImplementor session
;
97 private boolean isTempSession
= false;
98 private boolean initialized
;
99 private transient List
<DelayedOperation
> operationQueue
;
100 private transient boolean directlyAccessible
;
101 private transient boolean initializing
;
102 private Object owner
;
103 private int cachedSize
= -1;
106 private Serializable key
;
107 // collections detect changes made via their public interface and mark
108 // themselves as dirty as a performance optimization
109 private boolean dirty
;
110 protected boolean elementRemoved
;
111 private Serializable storedSnapshot
;
113 private String sessionFactoryUuid
;
114 private boolean allowLoadOutsideTransaction
;
117 * Not called by Hibernate, but used by non-JDK serialization,
118 * eg. SOAP libraries.
120 public AbstractPersistentCollection() {
123 protected AbstractPersistentCollection(SharedSessionContractImplementor session
) {
124 this.session
= session
;
128 * @deprecated {@link #AbstractPersistentCollection(SharedSessionContractImplementor)} should be used instead.
131 protected AbstractPersistentCollection(SessionImplementor session
) {
132 this( (SharedSessionContractImplementor
) session
);
136 public final String
getRole() {
141 public final Serializable
getKey() {
146 public final boolean isUnreferenced() {
151 public final boolean isDirty() {
156 public boolean isElementRemoved() {
157 return elementRemoved
;
161 public final void clearDirty() {
163 elementRemoved
= false;
167 public final void dirty() {
172 public final Serializable
getStoredSnapshot() {
173 return storedSnapshot
;
176 //Careful: these methods do not initialize the collection.
179 public abstract boolean empty();
182 * Called by any read-only method of the collection interface
184 protected final void read() {
189 * Called by the {@link Collection#size} method
191 @SuppressWarnings({"JavaDoc"})
192 protected boolean readSize() {
193 if ( !initialized
) {
194 if ( cachedSize
!= -1 && !hasQueuedOperations() ) {
199 // In remoting we are sure that session is null
200 // both when using property paths and switching off conversations
201 if(session
== null && remoting
) {
202 LOG
.info("--> readSize, of " + getRole() + " with key " + getKey());
205 //keep formatting below to ease update to newer hibernate version
206 //##REMOTING-KEEP END##
207 final boolean isExtraLazy
= withTemporarySessionIfNeeded(
208 new LazyInitializationWork
<Boolean
>() {
210 public Boolean
doWork() {
211 final CollectionEntry entry
= session
.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection
.this );
213 if ( entry
!= null ) {
214 final CollectionPersister persister
= entry
.getLoadedPersister();
215 if ( persister
.isExtraLazy() ) {
216 if ( hasQueuedOperations() ) {
219 cachedSize
= persister
.getSize( entry
.getLoadedKey(), session
);
227 throwLazyInitializationExceptionIfNotConnected();
243 * TBH not sure why this is public
245 * @param <T> The java type of the return for this LazyInitializationWork
247 public static interface LazyInitializationWork
<T
> {
249 * Do the represented work and return the result.
256 private <T
> T
withTemporarySessionIfNeeded(LazyInitializationWork
<T
> lazyInitializationWork
) {
257 SharedSessionContractImplementor tempSession
= null;
259 if ( session
== null ) {
260 if ( allowLoadOutsideTransaction
) {
261 tempSession
= openTemporarySessionForLoading();
264 throwLazyInitializationException( "could not initialize proxy - no Session" );
267 else if ( !session
.isOpenOrWaitingForAutoClose() ) {
268 if ( allowLoadOutsideTransaction
) {
269 tempSession
= openTemporarySessionForLoading();
272 throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
275 else if ( !session
.isConnected() ) {
276 if ( allowLoadOutsideTransaction
) {
277 tempSession
= openTemporarySessionForLoading();
280 throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
284 SharedSessionContractImplementor originalSession
= null;
285 boolean isJTA
= false;
287 if ( tempSession
!= null ) {
288 isTempSession
= true;
289 originalSession
= session
;
290 session
= tempSession
;
292 isJTA
= session
.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
295 // Explicitly handle the transactions only if we're not in
296 // a JTA environment. A lazy loading temporary session can
297 // be created even if a current session and transaction are
298 // open (ex: session.clear() was used). We must prevent
299 // multiple transactions.
300 ( (Session
) session
).beginTransaction();
303 session
.getPersistenceContextInternal().addUninitializedDetachedCollection(
304 session
.getFactory().getCollectionPersister( getRole() ),
310 return lazyInitializationWork
.doWork();
313 if ( tempSession
!= null ) {
314 // make sure the just opened temp session gets closed!
315 isTempSession
= false;
316 session
= originalSession
;
320 ( (Session
) tempSession
).getTransaction().commit();
322 ( (Session
) tempSession
).close();
324 catch (Exception e
) {
325 LOG
.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
331 private SharedSessionContractImplementor
openTemporarySessionForLoading() {
332 if ( sessionFactoryUuid
== null ) {
333 throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
336 final SessionFactoryImplementor sf
= (SessionFactoryImplementor
)
337 SessionFactoryRegistry
.INSTANCE
.getSessionFactory( sessionFactoryUuid
);
338 final SharedSessionContractImplementor session
= (SharedSessionContractImplementor
) sf
.openSession();
339 session
.getPersistenceContextInternal().setDefaultReadOnly( true );
340 session
.setFlushMode( FlushMode
.MANUAL
);
344 protected Boolean
readIndexExistence(final Object index
) {
345 if ( !initialized
) {
347 // In remoting we are sure that session is null
348 // both when using property paths and switching off conversations
349 if(session
== null && remoting
) {
350 LOG
.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
353 //keep formatting below to ease update to newer hibernate version
354 //##REMOTING-KEEP END##
355 final Boolean extraLazyExistenceCheck
= withTemporarySessionIfNeeded(
356 new LazyInitializationWork
<Boolean
>() {
358 public Boolean
doWork() {
359 final CollectionEntry entry
= session
.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection
.this );
360 final CollectionPersister persister
= entry
.getLoadedPersister();
361 if ( persister
.isExtraLazy() ) {
362 if ( hasQueuedOperations() ) {
365 return persister
.indexExists( entry
.getLoadedKey(), index
, session
);
374 if ( extraLazyExistenceCheck
!= null ) {
375 return extraLazyExistenceCheck
;
382 protected Boolean
readElementExistence(final Object element
) {
383 if ( !initialized
) {
385 // In remoting we are sure that session is null
386 // both when using property paths and switching off conversations
387 if(session
== null && remoting
) {
388 LOG
.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
392 //keep formatting below to ease update to newer hibernate version
393 //##REMOTING-KEEP END##
394 final Boolean extraLazyExistenceCheck
= withTemporarySessionIfNeeded(
395 new LazyInitializationWork
<Boolean
>() {
397 public Boolean
doWork() {
398 final CollectionEntry entry
= session
.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection
.this );
399 final CollectionPersister persister
= entry
.getLoadedPersister();
400 if ( persister
.isExtraLazy() ) {
401 if ( hasQueuedOperations() ) {
404 return persister
.elementExists( entry
.getLoadedKey(), element
, session
);
413 if ( extraLazyExistenceCheck
!= null ) {
414 return extraLazyExistenceCheck
;
421 protected static final Object UNKNOWN
= new MarkerObject( "UNKNOWN" );
423 protected Object
readElementByIndex(final Object index
) {
424 if ( !initialized
) {
426 // In remoting we are sure that session is null
427 // both when using property paths and switching off conversations
428 if(session
== null && remoting
) {
429 LOG
.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
433 //keep formatting below to ease update to newer hibernate version
434 //##REMOTING-KEEP END##
435 class ExtraLazyElementByIndexReader
implements LazyInitializationWork
{
436 private boolean isExtraLazy
;
437 private Object element
;
440 public Object
doWork() {
441 final CollectionEntry entry
= session
.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection
.this );
442 final CollectionPersister persister
= entry
.getLoadedPersister();
443 isExtraLazy
= persister
.isExtraLazy();
445 if ( hasQueuedOperations() ) {
448 element
= persister
.getElementByIndex( entry
.getLoadedKey(), index
, session
, owner
);
457 final ExtraLazyElementByIndexReader reader
= new ExtraLazyElementByIndexReader();
458 //noinspection unchecked
459 withTemporarySessionIfNeeded( reader
);
460 if ( reader
.isExtraLazy
) {
461 return reader
.element
;
469 protected int getCachedSize() {
473 protected boolean isConnectedToSession() {
474 return session
!= null
476 && session
.getPersistenceContextInternal().containsCollection( this );
479 protected boolean isInitialized() {
484 * Called by any writer method of the collection interface
486 protected final void write() {
492 * Is this collection in a state that would allow us to
493 * "queue" operations?
495 @SuppressWarnings({"JavaDoc"})
496 protected boolean isOperationQueueEnabled() {
498 && isConnectedToSession()
499 && isInverseCollection();
503 * Is this collection in a state that would allow us to
504 * "queue" puts? This is a special case, because of orphan
507 @SuppressWarnings({"JavaDoc"})
508 protected boolean isPutQueueEnabled() {
510 && isConnectedToSession()
511 && isInverseOneToManyOrNoOrphanDelete();
515 * Is this collection in a state that would allow us to
516 * "queue" clear? This is a special case, because of orphan
519 @SuppressWarnings({"JavaDoc"})
520 protected boolean isClearQueueEnabled() {
522 && isConnectedToSession()
523 && isInverseCollectionNoOrphanDelete();
527 * Is this the "inverse" end of a bidirectional association?
529 @SuppressWarnings({"JavaDoc"})
530 protected boolean isInverseCollection() {
531 final CollectionEntry ce
= session
.getPersistenceContextInternal().getCollectionEntry( this );
532 return ce
!= null && ce
.getLoadedPersister().isInverse();
536 * Is this the "inverse" end of a bidirectional association with
537 * no orphan delete enabled?
539 @SuppressWarnings({"JavaDoc"})
540 protected boolean isInverseCollectionNoOrphanDelete() {
541 final CollectionEntry ce
= session
.getPersistenceContextInternal().getCollectionEntry( this );
545 final CollectionPersister loadedPersister
= ce
.getLoadedPersister();
546 return loadedPersister
.isInverse() && !loadedPersister
.hasOrphanDelete();
550 * Is this the "inverse" end of a bidirectional one-to-many, or
551 * of a collection with no orphan delete?
553 @SuppressWarnings({"JavaDoc"})
554 protected boolean isInverseOneToManyOrNoOrphanDelete() {
555 final CollectionEntry ce
= session
.getPersistenceContextInternal().getCollectionEntry( this );
559 final CollectionPersister loadedPersister
= ce
.getLoadedPersister();
560 return loadedPersister
.isInverse() && ( loadedPersister
.isOneToMany() || !loadedPersister
.hasOrphanDelete() );
566 @SuppressWarnings({"JavaDoc"})
567 protected final void queueOperation(DelayedOperation operation
) {
568 if ( operationQueue
== null ) {
569 operationQueue
= new ArrayList
<DelayedOperation
>( 10 );
571 operationQueue
.add( operation
);
572 //needed so that we remove this collection from the second-level cache
577 * Replace entity instances with copy in {@code copyCache}/.
579 * @param copyCache - mapping from entity in the process of being
580 * merged to managed copy.
582 public final void replaceQueuedOperationValues(CollectionPersister persister
, Map copyCache
) {
583 for ( DelayedOperation operation
: operationQueue
) {
584 if ( ValueDelayedOperation
.class.isInstance( operation
) ) {
585 ( (ValueDelayedOperation
) operation
).replace( persister
, copyCache
);
591 * After reading all existing elements from the database,
592 * add the queued elements to the underlying collection.
594 protected final void performQueuedOperations() {
595 for ( DelayedOperation operation
: operationQueue
) {
598 clearOperationQueue();
602 public void setSnapshot(Serializable key
, String role
, Serializable snapshot
) {
605 this.storedSnapshot
= snapshot
;
609 public void postAction() {
610 clearOperationQueue();
615 public final void clearOperationQueue() {
616 operationQueue
= null;
620 public Object
getValue() {
625 public void beginRead() {
626 // override on some subclasses
631 public boolean endRead() {
632 //override on some subclasses
633 return afterInitialize();
637 public boolean afterInitialize() {
639 //do this bit after setting initialized to true or it will recurse
640 if ( hasQueuedOperations() ) {
641 performQueuedOperations();
651 * Initialize the collection, if possible, wrapping any exceptions
652 * in a runtime exception
654 * @param writing currently obsolete
656 * @throws LazyInitializationException if we cannot initialize
658 protected final void initialize(final boolean writing
) {
663 // In remoting we are sure that session is null
664 // both when using property paths and switching off conversations
665 if(session
== null && remoting
) {
668 //keep formatting below to ease update to newer hibernate version
669 //##REMOTING-KEEP END##
670 withTemporarySessionIfNeeded(
671 new LazyInitializationWork
<Object
>() {
673 public Object
doWork() {
674 session
.initializeCollection( AbstractPersistentCollection
.this, writing
);
682 private void throwLazyInitializationExceptionIfNotConnected() {
683 if ( !isConnectedToSession() ) {
684 throwLazyInitializationException( "no session or session was closed" );
686 if ( !session
.isConnected() ) {
687 throwLazyInitializationException( "session is disconnected" );
691 private void throwLazyInitializationException(String message
) {
692 throw new LazyInitializationException(
693 "failed to lazily initialize a collection" +
694 (role
== null ?
"" : " of role: " + role
) +
699 protected final void setInitialized() {
700 this.initializing
= false;
701 this.initialized
= true;
704 protected final void setDirectlyAccessible(boolean directlyAccessible
) {
705 this.directlyAccessible
= directlyAccessible
;
709 public boolean isDirectlyAccessible() {
710 return directlyAccessible
;
714 public final boolean unsetSession(SharedSessionContractImplementor currentSession
) {
715 prepareForPossibleLoadingOutsideTransaction();
716 if ( currentSession
== this.session
) {
717 if ( !isTempSession
) {
718 if ( hasQueuedOperations() ) {
719 final String collectionInfoString
= MessageHelper
.collectionInfoString( getRole(), getKey() );
721 final TransactionStatus transactionStatus
=
722 session
.getTransactionCoordinator().getTransactionDriverControl().getStatus();
723 if ( transactionStatus
.isOneOf(
724 TransactionStatus
.ROLLED_BACK
,
725 TransactionStatus
.MARKED_ROLLBACK
,
726 TransactionStatus
.FAILED_COMMIT
,
727 TransactionStatus
.FAILED_ROLLBACK
,
728 TransactionStatus
.ROLLING_BACK
730 // It was due to a rollback.
731 LOG
.queuedOperationWhenDetachFromSessionOnRollback( collectionInfoString
);
734 // We don't know why the collection is being detached.
735 // Just log the info.
736 LOG
.queuedOperationWhenDetachFromSession( collectionInfoString
);
739 catch (Exception e
) {
740 // We don't know why the collection is being detached.
741 // Just log the info.
742 LOG
.queuedOperationWhenDetachFromSession( collectionInfoString
);
745 if ( allowLoadOutsideTransaction
&& !initialized
&& session
.getLoadQueryInfluencers().hasEnabledFilters() ) {
746 final String collectionInfoString
= MessageHelper
.collectionInfoString( getRole(), getKey() );
747 LOG
.enabledFiltersWhenDetachFromSession( collectionInfoString
);
754 if ( this.session
!= null ) {
755 LOG
.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession
) );
761 protected void prepareForPossibleLoadingOutsideTransaction() {
762 if ( session
!= null ) {
763 allowLoadOutsideTransaction
= session
.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
765 if ( allowLoadOutsideTransaction
&& sessionFactoryUuid
== null ) {
766 sessionFactoryUuid
= session
.getFactory().getUuid();
772 public final boolean setCurrentSession(SharedSessionContractImplementor session
) throws HibernateException
{
773 if ( session
== this.session
) {
776 else if ( this.session
!= null ) {
777 final String msg
= generateUnexpectedSessionStateMessage( session
);
778 if ( isConnectedToSession() ) {
779 throw new HibernateException(
780 "Illegal attempt to associate a collection with two open sessions. " + msg
784 LOG
.logUnexpectedSessionInCollectionNotConnected( msg
);
787 if ( hasQueuedOperations() ) {
788 LOG
.queuedOperationWhenAttachToSession( MessageHelper
.collectionInfoString( getRole(), getKey() ) );
790 this.session
= session
;
794 private String
generateUnexpectedSessionStateMessage(SharedSessionContractImplementor session
) {
795 // NOTE: If this.session != null, this.session may be operating on this collection
796 // (e.g., by changing this.role, this.key, or even this.session) in a different thread.
798 // Grab the current role and key (it can still get changed by this.session...)
799 // If this collection is connected to this.session, then this.role and this.key should
800 // be consistent with the CollectionEntry in this.session (as long as this.session doesn't
801 // change it). Don't access the CollectionEntry in this.session because that could result
802 // in multi-threaded access to this.session.
803 final String roleCurrent
= role
;
804 final Serializable keyCurrent
= key
;
806 final StringBuilder sb
= new StringBuilder( "Collection : " );
807 if ( roleCurrent
!= null ) {
808 sb
.append( MessageHelper
.collectionInfoString( roleCurrent
, keyCurrent
) );
811 final CollectionEntry ce
= session
.getPersistenceContextInternal().getCollectionEntry( this );
814 MessageHelper
.collectionInfoString(
815 ce
.getLoadedPersister(),
823 sb
.append( "<unknown>" );
826 // only include the collection contents if debug logging
827 if ( LOG
.isDebugEnabled() ) {
828 final String collectionContents
= wasInitialized() ?
toString() : "<uninitialized>";
829 sb
.append( "\nCollection contents: [" ).append( collectionContents
).append( "]" );
831 return sb
.toString();
835 public boolean needsRecreate(CollectionPersister persister
) {
836 // Workaround for situations like HHH-7072. If the collection element is a component that consists entirely
837 // of nullable properties, we currently have to forcefully recreate the entire collection. See the use
838 // of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info. In order to delete
839 // row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
840 // the current "WHERE COL = ?" (fails for null for most DBs). Note that
841 // the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the
842 // AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing
843 // recreation isn't ideal, but not really any other option in ORM 4.
844 // Selecting a type used in where part of update statement
845 // (must match condition in org.hibernate.persister.collection.BasicCollectionPersister#doUpdateRows).
848 if ( persister
.hasIndex() ) {
849 whereType
= persister
.getIndexType();
852 whereType
= persister
.getElementType();
854 if ( whereType
instanceof CompositeType
) {
855 CompositeType componentIndexType
= (CompositeType
) whereType
;
856 return !componentIndexType
.hasNotNullProperty();
862 public final void forceInitialization() throws HibernateException
{
863 if ( !initialized
) {
864 if ( initializing
) {
865 throw new AssertionFailure( "force initialize loading collection" );
873 * Get the current snapshot from the session
875 @SuppressWarnings({"JavaDoc"})
876 protected final Serializable
getSnapshot() {
877 return session
.getPersistenceContext().getSnapshot( this );
881 public final boolean wasInitialized() {
886 public boolean isRowUpdatePossible() {
891 public final boolean hasQueuedOperations() {
892 return operationQueue
!= null;
896 public final Iterator
queuedAdditionIterator() {
897 if ( hasQueuedOperations() ) {
898 return new Iterator() {
902 public Object
next() {
903 return operationQueue
.get( index
++ ).getAddedInstance();
907 public boolean hasNext() {
908 return index
< operationQueue
.size();
912 public void remove() {
913 throw new UnsupportedOperationException();
918 return Collections
.emptyIterator();
923 @SuppressWarnings({"unchecked"})
924 public final Collection
getQueuedOrphans(String entityName
) {
925 if ( hasQueuedOperations() ) {
926 final Collection additions
= new ArrayList( operationQueue
.size() );
927 final Collection removals
= new ArrayList( operationQueue
.size() );
928 for ( DelayedOperation operation
: operationQueue
) {
929 additions
.add( operation
.getAddedInstance() );
930 removals
.add( operation
.getOrphan() );
932 return getOrphans( removals
, additions
, entityName
, session
);
935 return Collections
.EMPTY_LIST
;
940 public void preInsert(CollectionPersister persister
) throws HibernateException
{
944 public void afterRowInsert(CollectionPersister persister
, Object entry
, int i
) throws HibernateException
{
948 public abstract Collection
getOrphans(Serializable snapshot
, String entityName
) throws HibernateException
;
951 * Get the session currently associated with this collection.
953 * @return The session
955 public final SharedSessionContractImplementor
getSession() {
959 protected final class IteratorProxy
implements Iterator
{
960 protected final Iterator itr
;
962 public IteratorProxy(Iterator itr
) {
967 public boolean hasNext() {
968 return itr
.hasNext();
972 public Object
next() {
977 public void remove() {
983 protected final class ListIteratorProxy
implements ListIterator
{
984 protected final ListIterator itr
;
986 public ListIteratorProxy(ListIterator itr
) {
991 @SuppressWarnings({"unchecked"})
992 public void add(Object o
) {
998 public boolean hasNext() {
999 return itr
.hasNext();
1003 public boolean hasPrevious() {
1004 return itr
.hasPrevious();
1008 public Object
next() {
1013 public int nextIndex() {
1014 return itr
.nextIndex();
1018 public Object
previous() {
1019 return itr
.previous();
1023 public int previousIndex() {
1024 return itr
.previousIndex();
1028 public void remove() {
1034 @SuppressWarnings({"unchecked"})
1035 public void set(Object o
) {
1041 protected class SetProxy
implements java
.util
.Set
{
1042 protected final Collection set
;
1044 public SetProxy(Collection set
) {
1049 @SuppressWarnings({"unchecked"})
1050 public boolean add(Object o
) {
1052 return set
.add( o
);
1056 @SuppressWarnings({"unchecked"})
1057 public boolean addAll(Collection c
) {
1059 return set
.addAll( c
);
1063 public void clear() {
1069 public boolean contains(Object o
) {
1070 return set
.contains( o
);
1074 @SuppressWarnings("unchecked")
1075 public boolean containsAll(Collection c
) {
1076 return set
.containsAll( c
);
1080 public boolean isEmpty() {
1081 return set
.isEmpty();
1085 public Iterator
iterator() {
1086 return new IteratorProxy( set
.iterator() );
1090 public boolean remove(Object o
) {
1092 return set
.remove( o
);
1096 @SuppressWarnings("unchecked")
1097 public boolean removeAll(Collection c
) {
1099 return set
.removeAll( c
);
1103 @SuppressWarnings("unchecked")
1104 public boolean retainAll(Collection c
) {
1106 return set
.retainAll( c
);
1115 public Object
[] toArray() {
1116 return set
.toArray();
1120 @SuppressWarnings({"unchecked"})
1121 public Object
[] toArray(Object
[] array
) {
1122 return set
.toArray( array
);
1126 protected final class ListProxy
implements java
.util
.List
{
1127 protected final List list
;
1129 public ListProxy(List list
) {
1134 @SuppressWarnings({"unchecked"})
1135 public void add(int index
, Object value
) {
1137 list
.add( index
, value
);
1141 @SuppressWarnings({"unchecked"})
1142 public boolean add(Object o
) {
1144 return list
.add( o
);
1148 @SuppressWarnings({"unchecked"})
1149 public boolean addAll(Collection c
) {
1151 return list
.addAll( c
);
1155 @SuppressWarnings({"unchecked"})
1156 public boolean addAll(int i
, Collection c
) {
1158 return list
.addAll( i
, c
);
1162 public void clear() {
1168 public boolean contains(Object o
) {
1169 return list
.contains( o
);
1173 @SuppressWarnings("unchecked")
1174 public boolean containsAll(Collection c
) {
1175 return list
.containsAll( c
);
1179 public Object
get(int i
) {
1180 return list
.get( i
);
1184 public int indexOf(Object o
) {
1185 return list
.indexOf( o
);
1189 public boolean isEmpty() {
1190 return list
.isEmpty();
1194 public Iterator
iterator() {
1195 return new IteratorProxy( list
.iterator() );
1199 public int lastIndexOf(Object o
) {
1200 return list
.lastIndexOf( o
);
1204 public ListIterator
listIterator() {
1205 return new ListIteratorProxy( list
.listIterator() );
1209 public ListIterator
listIterator(int i
) {
1210 return new ListIteratorProxy( list
.listIterator( i
) );
1214 public Object
remove(int i
) {
1216 return list
.remove( i
);
1220 public boolean remove(Object o
) {
1222 return list
.remove( o
);
1226 @SuppressWarnings("unchecked")
1227 public boolean removeAll(Collection c
) {
1229 return list
.removeAll( c
);
1233 @SuppressWarnings("unchecked")
1234 public boolean retainAll(Collection c
) {
1236 return list
.retainAll( c
);
1240 @SuppressWarnings({"unchecked"})
1241 public Object
set(int i
, Object o
) {
1243 return list
.set( i
, o
);
1252 public List
subList(int i
, int j
) {
1253 return list
.subList( i
, j
);
1257 public Object
[] toArray() {
1258 return list
.toArray();
1262 @SuppressWarnings({"unchecked"})
1263 public Object
[] toArray(Object
[] array
) {
1264 return list
.toArray( array
);
1270 * Contract for operations which are part of a collection's operation queue.
1272 protected interface DelayedOperation
{
1273 public void operate();
1275 public Object
getAddedInstance();
1277 public Object
getOrphan();
1280 protected interface ValueDelayedOperation
extends DelayedOperation
{
1281 void replace(CollectionPersister collectionPersister
, Map copyCache
);
1284 protected abstract class AbstractValueDelayedOperation
implements ValueDelayedOperation
{
1285 private Object addedValue
;
1286 private Object orphan
;
1288 protected AbstractValueDelayedOperation(Object addedValue
, Object orphan
) {
1289 this.addedValue
= addedValue
;
1290 this.orphan
= orphan
;
1294 public void replace(CollectionPersister persister
, Map copyCache
) {
1295 if ( addedValue
!= null ) {
1296 addedValue
= getReplacement( persister
.getElementType(), addedValue
, copyCache
);
1300 protected final Object
getReplacement(Type type
, Object current
, Map copyCache
) {
1301 return type
.replace( current
, null, session
, owner
, copyCache
);
1305 public final Object
getAddedInstance() {
1310 public final Object
getOrphan() {
1316 * Given a collection of entity instances that used to
1317 * belong to the collection, and a collection of instances
1318 * that currently belong, return a collection of orphans
1320 @SuppressWarnings({"JavaDoc", "unchecked"})
1321 protected static Collection
getOrphans(
1322 Collection oldElements
,
1323 Collection currentElements
,
1325 SharedSessionContractImplementor session
) throws HibernateException
{
1328 if ( currentElements
.size() == 0 ) {
1329 // no new elements, the old list contains only Orphans
1332 if ( oldElements
.size() == 0 ) {
1333 // no old elements, so no Orphans neither
1337 final EntityPersister entityPersister
= session
.getFactory().getEntityPersister( entityName
);
1338 final Type idType
= entityPersister
.getIdentifierType();
1339 final boolean useIdDirect
= mayUseIdDirect( idType
);
1341 // create the collection holding the Orphans
1342 final Collection res
= new ArrayList();
1344 // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1345 final java
.util
.Set currentIds
= new HashSet();
1346 final java
.util
.Set currentSaving
= new IdentitySet();
1347 final PersistenceContext persistenceContext
= session
.getPersistenceContextInternal();
1348 for ( Object current
: currentElements
) {
1349 if ( current
!= null && ForeignKeys
.isNotTransient( entityName
, current
, null, session
) ) {
1350 final EntityEntry ee
= persistenceContext
.getEntry( current
);
1351 if ( ee
!= null && ee
.getStatus() == Status
.SAVING
) {
1352 currentSaving
.add( current
);
1355 final Serializable currentId
= ForeignKeys
.getEntityIdentifierIfNotUnsaved(
1360 currentIds
.add( useIdDirect ? currentId
: new TypedValue( idType
, currentId
) );
1365 // iterate over the *old* list
1366 for ( Object old
: oldElements
) {
1367 if ( !currentSaving
.contains( old
) ) {
1368 final Serializable oldId
= ForeignKeys
.getEntityIdentifierIfNotUnsaved( entityName
, old
, session
);
1369 if ( !currentIds
.contains( useIdDirect ? oldId
: new TypedValue( idType
, oldId
) ) ) {
1378 private static boolean mayUseIdDirect(Type idType
) {
1379 return idType
== StringType
.INSTANCE
1380 || idType
== IntegerType
.INSTANCE
1381 || idType
== LongType
.INSTANCE
1382 || idType
== UUIDBinaryType
.INSTANCE
1383 || idType
== UUIDCharType
.INSTANCE
1384 || idType
== PostgresUUIDType
.INSTANCE
;
1388 * Removes entity entries that have an equal identifier with the incoming entity instance
1390 * @param list The list containing the entity instances
1391 * @param entityInstance The entity instance to match elements.
1392 * @param entityName The entity name
1393 * @param session The session
1395 public static void identityRemove(
1397 Object entityInstance
,
1399 SharedSessionContractImplementor session
) {
1401 if ( entityInstance
!= null && ForeignKeys
.isNotTransient( entityName
, entityInstance
, null, session
) ) {
1402 final EntityPersister entityPersister
= session
.getFactory().getEntityPersister( entityName
);
1403 final Type idType
= entityPersister
.getIdentifierType();
1405 final Serializable idOfCurrent
= ForeignKeys
.getEntityIdentifierIfNotUnsaved( entityName
, entityInstance
, session
);
1406 final Iterator itr
= list
.iterator();
1407 while ( itr
.hasNext() ) {
1408 final Serializable idOfOld
= ForeignKeys
.getEntityIdentifierIfNotUnsaved( entityName
, itr
.next(), session
);
1409 if ( idType
.isEqual( idOfCurrent
, idOfOld
, session
.getFactory() ) ) {
1419 * Removes entity entries that have an equal identifier with the incoming entity instance
1421 * @param list The list containing the entity instances
1422 * @param entityInstance The entity instance to match elements.
1423 * @param entityName The entity name
1424 * @param session The session
1426 * @deprecated {@link #identityRemove(Collection, Object, String, SharedSessionContractImplementor)}
1427 * should be used instead.
1430 public static void identityRemove(
1432 Object entityInstance
,
1434 SessionImplementor session
) {
1435 identityRemove( list
, entityInstance
, entityName
, (SharedSessionContractImplementor
) session
);
1439 public Object
getIdentifier(Object entry
, int i
) {
1440 throw new UnsupportedOperationException();
1444 public Object
getOwner() {
1449 public void setOwner(Object owner
) {
1454 /** ##REMOTING-KEEP## #######################################################
1456 ADDED PART: Below is the section of code which makes remote service calls.
1457 Keeps this code when upgrading to newer hibernate version. Also keep
1458 other code marked with ##REMOTING-KEEP## in the following 5 methods:
1460 {@link #initialize(boolean)}
1462 {@link #readIndexExistence(Object)}
1463 {@link #readElementExistence(Object)}
1464 {@link #readElementByIndex(Object)}
1466 ######################################################################### */
1468 private static CdmApplicationRemoteConfiguration configuration
;
1469 private static boolean remoting
= false;
1471 public static void setConfiguration(CdmApplicationRemoteConfiguration conf
) {
1473 configuration
= conf
;
1476 private void remoteInitialize() {
1478 if (getOwner() != null && !initialized
) {
1479 Object collectionType
= null;
1481 Class
<?
> clazz
= null;
1483 String role
= getRole();
1484 String fieldName
= role
.substring(role
.lastIndexOf(".") + 1);
1485 LOG
.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName
);
1486 Object owner
= getOwner();
1488 if(owner
instanceof CdmBase
) {
1489 cdmBase
= (CdmBase
)owner
;
1491 throw new HibernateException("Owner of persistent collection is not a cdm entity");
1493 if(configuration
== null) {
1494 throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1496 ICachedCommonService cachedCommonService
= configuration
.getCachedCommonService();
1497 if(cachedCommonService
== null) {
1498 throw new HibernateException("commonService not initialized (null)");
1501 //Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
1502 Object obj
= ProxyUtils
.deproxyIfInitialized(cachedCommonService
.initializeCollection(cdmBase
.getUuid(), fieldName
));
1503 if(ProxyUtils
.isUninitializedProxy(obj
)) {
1504 throw new HibernateException("Persistent Collection initialized but is still a proxy");
1509 if (clazz
!= null) {
1510 collectionType
= ProxyUtils
.getCollectionType(obj
, clazz
);
1511 field
= clazz
.getDeclaredField(collectionType
.toString());
1512 field
.setAccessible(true);
1513 field
.set(this, obj
);
1514 ProxyUtils
.setRoleValueInOwner(owner
, role
, obj
);
1517 } catch (Exception ex
) {
1518 String originalMessage
= ex
.getMessage();
1519 String message
= originalMessage
+ "clazz: " + (clazz
== null?
"" :clazz
.getSimpleName())+ "- field: " + field
+ " - collectionType: " + collectionType
;
1520 throw new CdmEagerLoadingException(message
);
1524 // ##REMOTING-KEEP END##