ref #10067 adapt AbstractPersistentCollection serialVersionUID
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / collection / internal / AbstractPersistentCollection.java
index e37e53034929c7e339fea7df90b0034a01cf7f34..7f124baf0a9e3dc795ef83103901b6f27d989828 100644 (file)
@@ -1,27 +1,8 @@
-
-
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors.  All third-party contributions are
- * distributed under license by Red Hat Inc.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA  02110-1301  USA
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  */
 package org.hibernate.collection.internal;
 
@@ -34,10 +15,10 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
-
-import javax.naming.NamingException;
+import java.util.Map;
 
 import org.hibernate.AssertionFailure;
+import org.hibernate.FlushMode;
 import org.hibernate.HibernateException;
 import org.hibernate.LazyInitializationException;
 import org.hibernate.Session;
@@ -45,39 +26,52 @@ import org.hibernate.collection.spi.PersistentCollection;
 import org.hibernate.engine.internal.ForeignKeys;
 import org.hibernate.engine.spi.CollectionEntry;
 import org.hibernate.engine.spi.EntityEntry;
+import org.hibernate.engine.spi.PersistenceContext;
 import org.hibernate.engine.spi.SessionFactoryImplementor;
 import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
 import org.hibernate.engine.spi.Status;
 import org.hibernate.engine.spi.TypedValue;
+import org.hibernate.internal.CoreLogging;
+import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.internal.SessionFactoryRegistry;
 import org.hibernate.internal.util.MarkerObject;
-import org.hibernate.internal.util.collections.EmptyIterator;
 import org.hibernate.internal.util.collections.IdentitySet;
 import org.hibernate.persister.collection.CollectionPersister;
 import org.hibernate.persister.entity.EntityPersister;
 import org.hibernate.pretty.MessageHelper;
+import org.hibernate.resource.transaction.spi.TransactionStatus;
+import org.hibernate.type.CompositeType;
+import org.hibernate.type.IntegerType;
+import org.hibernate.type.LongType;
+import org.hibernate.type.PostgresUUIDType;
+import org.hibernate.type.StringType;
 import org.hibernate.type.Type;
-import org.jboss.logging.Logger;
+import org.hibernate.type.UUIDBinaryType;
+import org.hibernate.type.UUIDCharType;
 
 import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
+import eu.etaxonomy.cdm.cache.ProxyUtils;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
-import eu.etaxonomy.taxeditor.remoting.cache.ProxyUtils;
 import eu.etaxonomy.taxeditor.service.ICachedCommonService;
 
 /**
  * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
  *
- * This a extended copy of the original class from hibernate. It has been extended to
+ * This is an extended copy of the original class from hibernate. It has been extended to
  * allow making remote service calls to spring httpinvoker services (see section at the bottom
  * of this class).
  *
+ * NOTE: For updating this class to the latest hibernate version, update the serialVersionUID as
+ *       described above, copy the new class to the old class BUT keep those 5 places marked with
+ *       ##REMOTING-KEEP##
  *
  * @author Gavin King
  * @author Cherian Mathew
  */
 public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
-       private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
+       private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
 
        /**
         * <b>IMPORTANT:</b><br>
@@ -97,9 +91,10 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         * The correct <code>serialVersionUID</code> is the <code>stream classdesc serialVersionUID</code>
         * from the error message.
         */
-       private static final long serialVersionUID = 2742261122392386159L;
+       private static final long serialVersionUID = 6275967693128102740L;
 
-       private transient SessionImplementor session;
+       private transient SharedSessionContractImplementor session;
+       private boolean isTempSession = false;
        private boolean initialized;
        private transient List<DelayedOperation> operationQueue;
        private transient boolean directlyAccessible;
@@ -112,53 +107,76 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
        // collections detect changes made via their public interface and mark
        // themselves as dirty as a performance optimization
        private boolean dirty;
+       protected boolean elementRemoved;
        private Serializable storedSnapshot;
 
        private String sessionFactoryUuid;
-       private boolean specjLazyLoad = false;
+       private boolean allowLoadOutsideTransaction;
+
+       /**
+        * Not called by Hibernate, but used by non-JDK serialization,
+        * eg. SOAP libraries.
+        */
+       public AbstractPersistentCollection() {
+       }
+
+       protected AbstractPersistentCollection(SharedSessionContractImplementor session) {
+               this.session = session;
+       }
+
+       /**
+        * @deprecated {@link #AbstractPersistentCollection(SharedSessionContractImplementor)} should be used instead.
+        */
+       @Deprecated
+       protected AbstractPersistentCollection(SessionImplementor session) {
+               this( (SharedSessionContractImplementor) session );
+       }
 
        @Override
-    public final String getRole() {
+       public final String getRole() {
                return role;
        }
 
        @Override
-    public final Serializable getKey() {
+       public final Serializable getKey() {
                return key;
        }
 
        @Override
-    public final boolean isUnreferenced() {
+       public final boolean isUnreferenced() {
                return role == null;
        }
 
        @Override
-    public final boolean isDirty() {
+       public final boolean isDirty() {
                return dirty;
        }
 
        @Override
-    public final void clearDirty() {
+       public boolean isElementRemoved() {
+               return elementRemoved;
+       }
+
+       @Override
+       public final void clearDirty() {
                dirty = false;
+               elementRemoved = false;
        }
 
        @Override
-    public final void dirty() {
+       public final void dirty() {
                dirty = true;
        }
 
        @Override
-    public final Serializable getStoredSnapshot() {
+       public final Serializable getStoredSnapshot() {
                return storedSnapshot;
        }
 
        //Careful: these methods do not initialize the collection.
 
-       /**
-        * Is the initialized collection empty?
-        */
        @Override
-    public abstract boolean empty();
+       public abstract boolean empty();
 
        /**
         * Called by any read-only method of the collection interface
@@ -177,104 +195,112 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                                return true;
                        }
                        else {
+                               //##REMOTING-KEEP##
                                // In remoting we are sure that session is null
                                // both when using property paths and switching off conversations
                                if(session == null && remoting) {
-                                       log.info("--> readSize, of " + getRole() + " with key " + getKey());
+                                       LOG.info("--> readSize, of " + getRole() + " with key " + getKey());
                                        read();
                                } else {
-                                       boolean isExtraLazy = withTemporarySessionIfNeeded(
-                                                       new LazyInitializationWork<Boolean>() {
-                                                               @Override
-                                                               public Boolean doWork() {
-                                                                       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
-
-                                                                       if ( entry != null ) {
-                                                                               CollectionPersister persister = entry.getLoadedPersister();
-                                                                               if ( persister.isExtraLazy() ) {
-                                                                                       if ( hasQueuedOperations() ) {
-                                                                                               session.flush();
-                                                                                       }
-                                                                                       cachedSize = persister.getSize( entry.getLoadedKey(), session );
-                                                                                       return true;
-                                                                               }
-                                                                               else {
-                                                                                       read();
+                           //keep formatting below to ease update to newer hibernate version
+                               //##REMOTING-KEEP END##
+                               final boolean isExtraLazy = withTemporarySessionIfNeeded(
+                                               new LazyInitializationWork<Boolean>() {
+                                                       @Override
+                                                       public Boolean doWork() {
+                                                               final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
+
+                                                               if ( entry != null ) {
+                                                                       final CollectionPersister persister = entry.getLoadedPersister();
+                                                                       if ( persister.isExtraLazy() ) {
+                                                                               if ( hasQueuedOperations() ) {
+                                                                                       session.flush();
                                                                                }
+                                                                               cachedSize = persister.getSize( entry.getLoadedKey(), session );
+                                                                               return true;
                                                                        }
-                                                                       else{
-                                                                               throwLazyInitializationExceptionIfNotConnected();
+                                                                       else {
+                                                                               read();
                                                                        }
-                                                                       return false;
                                                                }
+                                                               else{
+                                                                       throwLazyInitializationExceptionIfNotConnected();
+                                                               }
+                                                               return false;
                                                        }
-                                                       );
-                                       if ( isExtraLazy ) {
-                                               return true;
-                                       }
+                                               }
+                               );
+                               if ( isExtraLazy ) {
+                                       return true;
+                               }
                                }
                        }
                }
                return false;
        }
 
+       /**
+        * TBH not sure why this is public
+        *
+        * @param <T> The java type of the return for this LazyInitializationWork
+        */
        public static interface LazyInitializationWork<T> {
+               /**
+                * Do the represented work and return the result.
+                *
+                * @return The result
+                */
                public T doWork();
        }
 
        private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
-               SessionImplementor originalSession = null;
-               boolean isTempSession = false;
-               boolean isJTA = false;
+               SharedSessionContractImplementor tempSession = null;
 
                if ( session == null ) {
-                       if ( specjLazyLoad ) {
-                               session = openTemporarySessionForLoading();
-                               isTempSession = true;
+                       if ( allowLoadOutsideTransaction ) {
+                               tempSession = openTemporarySessionForLoading();
                        }
                        else {
                                throwLazyInitializationException( "could not initialize proxy - no Session" );
                        }
                }
-               else if ( !session.isOpen() ) {
-                       if ( specjLazyLoad ) {
-                               originalSession = session;
-                               session = openTemporarySessionForLoading();
-                               isTempSession = true;
+               else if ( !session.isOpenOrWaitingForAutoClose() ) {
+                       if ( allowLoadOutsideTransaction ) {
+                               tempSession = openTemporarySessionForLoading();
                        }
                        else {
                                throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
                        }
                }
                else if ( !session.isConnected() ) {
-                       if ( specjLazyLoad ) {
-                               originalSession = session;
-                               session = openTemporarySessionForLoading();
-                               isTempSession = true;
+                       if ( allowLoadOutsideTransaction ) {
+                               tempSession = openTemporarySessionForLoading();
                        }
                        else {
                                throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
                        }
                }
 
-               if ( isTempSession ) {
-                       // TODO: On the next major release, add an
-                       // 'isJTA' or 'getTransactionFactory' method to Session.
-                       /*isJTA = session.getTransactionCoordinator()
-                                       .getTransactionContext().getTransactionEnvironment()
-                                       .getTransactionFactory()
-                                       .compatibleWithJtaSynchronization();*/
+               SharedSessionContractImplementor originalSession = null;
+               boolean isJTA = false;
+
+               if ( tempSession != null ) {
+                       isTempSession = true;
+                       originalSession = session;
+                       session = tempSession;
+
                        isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
+
                        if ( !isJTA ) {
                                // Explicitly handle the transactions only if we're not in
                                // a JTA environment.  A lazy loading temporary session can
                                // be created even if a current session and transaction are
                                // open (ex: session.clear() was used).  We must prevent
                                // multiple transactions.
-                               ( ( Session) session ).beginTransaction();
+                               ( (Session) session ).beginTransaction();
                        }
 
-                       session.getPersistenceContext().addUninitializedDetachedCollection(
+                       session.getPersistenceContextInternal().addUninitializedDetachedCollection(
                                        session.getFactory().getCollectionPersister( getRole() ),
                                        this
                        );
@@ -284,62 +310,70 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                        return lazyInitializationWork.doWork();
                }
                finally {
-                       if ( isTempSession ) {
+                       if ( tempSession != null ) {
                                // make sure the just opened temp session gets closed!
+                               isTempSession = false;
+                               session = originalSession;
+
                                try {
                                        if ( !isJTA ) {
-                                               ( ( Session) session ).getTransaction().commit();
+                                               ( (Session) tempSession ).getTransaction().commit();
                                        }
-                                       ( (Session) session ).close();
+                                       ( (Session) tempSession ).close();
                                }
                                catch (Exception e) {
-                                       log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
+                                       LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
                                }
-                               session = originalSession;
                        }
                }
        }
 
-       private SessionImplementor openTemporarySessionForLoading() {
+       private SharedSessionContractImplementor openTemporarySessionForLoading() {
                if ( sessionFactoryUuid == null ) {
                        throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
                }
 
-               SessionFactoryImplementor sf = (SessionFactoryImplementor)
+               final SessionFactoryImplementor sf = (SessionFactoryImplementor)
                                SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
-               return (SessionImplementor) sf.openSession();
+               final SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();
+               session.getPersistenceContextInternal().setDefaultReadOnly( true );
+               session.setFlushMode( FlushMode.MANUAL );
+               return session;
        }
 
        protected Boolean readIndexExistence(final Object index) {
                if ( !initialized ) {
+                       //##REMOTING-KEEP##
                        // In remoting we are sure that session is null
                        // both when using property paths and switching off conversations
                        if(session == null && remoting) {
-                               log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
+                               LOG.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
                                read();
                        } else {
-                               Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
-                                               new LazyInitializationWork<Boolean>() {
-                                                       @Override
-                                                       public Boolean doWork() {
-                                                               CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
-                                                               CollectionPersister persister = entry.getLoadedPersister();
-                                                               if ( persister.isExtraLazy() ) {
-                                                                       if ( hasQueuedOperations() ) {
-                                                                               session.flush();
-                                                                       }
-                                                                       return persister.indexExists( entry.getLoadedKey(), index, session );
-                                                               }
-                                                               else {
-                                                                       read();
+                   //keep formatting below to ease update to newer hibernate version
+                       //##REMOTING-KEEP END##
+                       final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+                                       new LazyInitializationWork<Boolean>() {
+                                               @Override
+                                               public Boolean doWork() {
+                                                       final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
+                                                       final CollectionPersister persister = entry.getLoadedPersister();
+                                                       if ( persister.isExtraLazy() ) {
+                                                               if ( hasQueuedOperations() ) {
+                                                                       session.flush();
                                                                }
-                                                               return null;
+                                                               return persister.indexExists( entry.getLoadedKey(), index, session );
                                                        }
+                                                       else {
+                                                               read();
+                                                       }
+                                                       return null;
                                                }
-                                               );
-                               if ( extraLazyExistenceCheck != null ) {
-                                       return extraLazyExistenceCheck;
-                               }
+                                       }
+                       );
+                       if ( extraLazyExistenceCheck != null ) {
+                               return extraLazyExistenceCheck;
+                       }
                        }
                }
                return null;
@@ -347,35 +381,38 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
 
        protected Boolean readElementExistence(final Object element) {
                if ( !initialized ) {
+                       //##REMOTING-KEEP##
                        // In remoting we are sure that session is null
                        // both when using property paths and switching off conversations
                        if(session == null && remoting) {
-                               log.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
+                               LOG.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
                                read();
 
                        } else {
-                               Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
-                                               new LazyInitializationWork<Boolean>() {
-                                                       @Override
-                                                       public Boolean doWork() {
-                                                               CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
-                                                               CollectionPersister persister = entry.getLoadedPersister();
-                                                               if ( persister.isExtraLazy() ) {
-                                                                       if ( hasQueuedOperations() ) {
-                                                                               session.flush();
-                                                                       }
-                                                                       return persister.elementExists( entry.getLoadedKey(), element, session );
-                                                               }
-                                                               else {
-                                                                       read();
+                   //keep formatting below to ease update to newer hibernate version
+                       //##REMOTING-KEEP END##
+                       final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+                                       new LazyInitializationWork<Boolean>() {
+                                               @Override
+                                               public Boolean doWork() {
+                                                       final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
+                                                       final CollectionPersister persister = entry.getLoadedPersister();
+                                                       if ( persister.isExtraLazy() ) {
+                                                               if ( hasQueuedOperations() ) {
+                                                                       session.flush();
                                                                }
-                                                               return null;
+                                                               return persister.elementExists( entry.getLoadedKey(), element, session );
                                                        }
+                                                       else {
+                                                               read();
+                                                       }
+                                                       return null;
                                                }
-                                               );
-                               if ( extraLazyExistenceCheck != null ) {
-                                       return extraLazyExistenceCheck;
-                               }
+                                       }
+                       );
+                       if ( extraLazyExistenceCheck != null ) {
+                               return extraLazyExistenceCheck;
+                       }
                        }
                }
                return null;
@@ -385,41 +422,44 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
 
        protected Object readElementByIndex(final Object index) {
                if ( !initialized ) {
+                       //##REMOTING-KEEP##
                        // In remoting we are sure that session is null
                        // both when using property paths and switching off conversations
                        if(session == null && remoting) {
-                               log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
+                               LOG.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
                                read();
 
                        } else {
-                               class ExtraLazyElementByIndexReader implements LazyInitializationWork {
-                                       private boolean isExtraLazy;
-                                       private Object element;
+                   //keep formatting below to ease update to newer hibernate version
+                       //##REMOTING-KEEP END##
+                       class ExtraLazyElementByIndexReader implements LazyInitializationWork {
+                               private boolean isExtraLazy;
+                               private Object element;
 
-                                       @Override
-                                       public Object doWork() {
-                                               CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
-                                               CollectionPersister persister = entry.getLoadedPersister();
-                                               isExtraLazy = persister.isExtraLazy();
-                                               if ( isExtraLazy ) {
-                                                       if ( hasQueuedOperations() ) {
-                                                               session.flush();
-                                                       }
-                                                       element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
-                                               }
-                                               else {
-                                                       read();
+                               @Override
+                               public Object doWork() {
+                                       final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
+                                       final CollectionPersister persister = entry.getLoadedPersister();
+                                       isExtraLazy = persister.isExtraLazy();
+                                       if ( isExtraLazy ) {
+                                               if ( hasQueuedOperations() ) {
+                                                       session.flush();
                                                }
-                                               return null;
+                                               element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
+                                       }
+                                       else {
+                                               read();
                                        }
+                                       return null;
                                }
+                       }
 
-                               ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
-                               //noinspection unchecked
-                               withTemporarySessionIfNeeded( reader );
-                               if ( reader.isExtraLazy ) {
-                                       return reader.element;
-                               }
+                       final ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
+                       //noinspection unchecked
+                       withTemporarySessionIfNeeded( reader );
+                       if ( reader.isExtraLazy ) {
+                               return reader.element;
+                       }
                        }
                }
                return UNKNOWN;
@@ -430,10 +470,14 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                return cachedSize;
        }
 
-       private boolean isConnectedToSession() {
-               return session != null &&
-                               session.isOpen() &&
-                               session.getPersistenceContext().containsCollection( this );
+       protected boolean isConnectedToSession() {
+               return session != null
+                               && session.isOpen()
+                               && session.getPersistenceContextInternal().containsCollection( this );
+       }
+
+       protected boolean isInitialized() {
+               return initialized;
        }
 
        /**
@@ -450,9 +494,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         */
        @SuppressWarnings({"JavaDoc"})
        protected boolean isOperationQueueEnabled() {
-               return !initialized &&
-                               isConnectedToSession() &&
-                               isInverseCollection();
+               return !initialized
+                               && isConnectedToSession()
+                               && isInverseCollection();
        }
 
        /**
@@ -462,9 +506,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         */
        @SuppressWarnings({"JavaDoc"})
        protected boolean isPutQueueEnabled() {
-               return !initialized &&
-                               isConnectedToSession() &&
-                               isInverseOneToManyOrNoOrphanDelete();
+               return !initialized
+                               && isConnectedToSession()
+                               && isInverseOneToManyOrNoOrphanDelete();
        }
 
        /**
@@ -474,17 +518,17 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         */
        @SuppressWarnings({"JavaDoc"})
        protected boolean isClearQueueEnabled() {
-               return !initialized &&
-                               isConnectedToSession() &&
-                               isInverseCollectionNoOrphanDelete();
+               return !initialized
+                               && isConnectedToSession()
+                               && isInverseCollectionNoOrphanDelete();
        }
 
        /**
         * Is this the "inverse" end of a bidirectional association?
         */
        @SuppressWarnings({"JavaDoc"})
-       private boolean isInverseCollection() {
-               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+       protected boolean isInverseCollection() {
+               final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( this );
                return ce != null && ce.getLoadedPersister().isInverse();
        }
 
@@ -493,11 +537,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         * no orphan delete enabled?
         */
        @SuppressWarnings({"JavaDoc"})
-       private boolean isInverseCollectionNoOrphanDelete() {
-               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
-               return ce != null &&
-                               ce.getLoadedPersister().isInverse() &&
-                               !ce.getLoadedPersister().hasOrphanDelete();
+       protected boolean isInverseCollectionNoOrphanDelete() {
+               final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( this );
+               if ( ce == null ) {
+                       return false;
+               }
+               final CollectionPersister loadedPersister = ce.getLoadedPersister();
+               return loadedPersister.isInverse() && !loadedPersister.hasOrphanDelete();
        }
 
        /**
@@ -505,12 +551,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         * of a collection with no orphan delete?
         */
        @SuppressWarnings({"JavaDoc"})
-       private boolean isInverseOneToManyOrNoOrphanDelete() {
-               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
-               return ce != null && ce.getLoadedPersister().isInverse() && (
-                               ce.getLoadedPersister().isOneToMany() ||
-                                               !ce.getLoadedPersister().hasOrphanDelete()
-               );
+       protected boolean isInverseOneToManyOrNoOrphanDelete() {
+               final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( this );
+               if ( ce == null ) {
+                       return false;
+               }
+               final CollectionPersister loadedPersister = ce.getLoadedPersister();
+               return loadedPersister.isInverse() && ( loadedPersister.isOneToMany() || !loadedPersister.hasOrphanDelete() );
        }
 
        /**
@@ -522,7 +569,22 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                        operationQueue = new ArrayList<DelayedOperation>( 10 );
                }
                operationQueue.add( operation );
-               dirty = true; //needed so that we remove this collection from the second-level cache
+               //needed so that we remove this collection from the second-level cache
+               dirty = true;
+       }
+
+       /**
+        * Replace entity instances with copy in {@code copyCache}/.
+        *
+        * @param copyCache - mapping from entity in the process of being
+        *                    merged to managed copy.
+        */
+       public final void replaceQueuedOperationValues(CollectionPersister persister, Map copyCache) {
+               for ( DelayedOperation operation : operationQueue ) {
+                       if ( ValueDelayedOperation.class.isInstance( operation ) ) {
+                               ( (ValueDelayedOperation) operation ).replace( persister, copyCache );
+                       }
+               }
        }
 
        /**
@@ -533,73 +595,50 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                for ( DelayedOperation operation : operationQueue ) {
                        operation.operate();
                }
+               clearOperationQueue();
        }
 
-       /**
-        * After flushing, re-init snapshot state.
-        */
        @Override
-    public void setSnapshot(Serializable key, String role, Serializable snapshot) {
+       public void setSnapshot(Serializable key, String role, Serializable snapshot) {
                this.key = key;
                this.role = role;
                this.storedSnapshot = snapshot;
        }
 
-       /**
-        * After flushing, clear any "queued" additions, since the
-        * database state is now synchronized with the memory state.
-        */
        @Override
-    public void postAction() {
-               operationQueue = null;
+       public void postAction() {
+               clearOperationQueue();
                cachedSize = -1;
                clearDirty();
        }
 
-       /**
-        * Not called by Hibernate, but used by non-JDK serialization,
-        * eg. SOAP libraries.
-        */
-       public AbstractPersistentCollection() {
-       }
-
-       protected AbstractPersistentCollection(SessionImplementor session) {
-               this.session = session;
+       public final void clearOperationQueue() {
+               operationQueue = null;
        }
 
-       /**
-        * return the user-visible collection (or array) instance
-        */
        @Override
-    public Object getValue() {
+       public Object getValue() {
                return this;
        }
 
-       /**
-        * Called just before reading any rows from the JDBC result set
-        */
        @Override
-    public void beginRead() {
+       public void beginRead() {
                // override on some subclasses
                initializing = true;
        }
 
-       /**
-        * Called after reading all rows from the JDBC result set
-        */
        @Override
-    public boolean endRead() {
+       public boolean endRead() {
                //override on some subclasses
                return afterInitialize();
        }
 
        @Override
-    public boolean afterInitialize() {
+       public boolean afterInitialize() {
                setInitialized();
                //do this bit after setting initialized to true or it will recurse
-               if ( operationQueue != null ) {
+               if ( hasQueuedOperations() ) {
                        performQueuedOperations();
-                       operationQueue = null;
                        cachedSize = -1;
                        return false;
                }
@@ -617,24 +656,26 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
         * @throws LazyInitializationException if we cannot initialize
         */
        protected final void initialize(final boolean writing) {
-           if ( initialized ) {
-               return;
-           }
-
+               if ( initialized ) {
+                       return;
+               }
+               //##REMOTING-KEEP##
            // In remoting we are sure that session is null
            // both when using property paths and switching off conversations
            if(session == null && remoting) {
                remoteInitialize();
            } else {
-               withTemporarySessionIfNeeded(
-                       new LazyInitializationWork<Object>() {
-                           @Override
-                           public Object doWork() {
-                               session.initializeCollection( AbstractPersistentCollection.this, writing );
-                               return null;
-                           }
-                       }
-                       );
+           //keep formatting below to ease update to newer hibernate version
+               //##REMOTING-KEEP END##
+           withTemporarySessionIfNeeded(
+                               new LazyInitializationWork<Object>() {
+                                       @Override
+                                       public Object doWork() {
+                                               session.initializeCollection( AbstractPersistentCollection.this, writing );
+                                               return null;
+                                       }
+                               }
+               );
            }
        }
 
@@ -664,111 +705,166 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                this.directlyAccessible = directlyAccessible;
        }
 
-       /**
-        * Could the application possibly have a direct reference to
-        * the underlying collection implementation?
-        */
        @Override
-    public boolean isDirectlyAccessible() {
+       public boolean isDirectlyAccessible() {
                return directlyAccessible;
        }
 
-       /**
-        * Disassociate this collection from the given session.
-        *
-        * @return true if this was currently associated with the given session
-        */
        @Override
-    public final boolean unsetSession(SessionImplementor currentSession) {
-               prepareForPossibleSpecialSpecjInitialization();
+       public final boolean unsetSession(SharedSessionContractImplementor currentSession) {
+               prepareForPossibleLoadingOutsideTransaction();
                if ( currentSession == this.session ) {
-                       this.session = null;
+                       if ( !isTempSession ) {
+                               if ( hasQueuedOperations() ) {
+                                       final String collectionInfoString = MessageHelper.collectionInfoString( getRole(), getKey() );
+                                       try {
+                                               final TransactionStatus transactionStatus =
+                                                               session.getTransactionCoordinator().getTransactionDriverControl().getStatus();
+                                               if ( transactionStatus.isOneOf(
+                                                               TransactionStatus.ROLLED_BACK,
+                                                               TransactionStatus.MARKED_ROLLBACK,
+                                                               TransactionStatus.FAILED_COMMIT,
+                                                               TransactionStatus.FAILED_ROLLBACK,
+                                                               TransactionStatus.ROLLING_BACK
+                                               ) ) {
+                                                       // It was due to a rollback.
+                                                       LOG.queuedOperationWhenDetachFromSessionOnRollback( collectionInfoString );
+                                               }
+                                               else {
+                                                       // We don't know why the collection is being detached.
+                                                       // Just log the info.
+                                                       LOG.queuedOperationWhenDetachFromSession( collectionInfoString );
+                                               }
+                                       }
+                                       catch (Exception e) {
+                                               // We don't know why the collection is being detached.
+                                               // Just log the info.
+                                               LOG.queuedOperationWhenDetachFromSession( collectionInfoString );
+                                       }
+                               }
+                               if ( allowLoadOutsideTransaction && !initialized && session.getLoadQueryInfluencers().hasEnabledFilters() ) {
+                                       final String collectionInfoString = MessageHelper.collectionInfoString( getRole(), getKey() );
+                                       LOG.enabledFiltersWhenDetachFromSession( collectionInfoString );
+                               }
+                               this.session = null;
+                       }
                        return true;
                }
                else {
+                       if ( this.session != null ) {
+                               LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) );
+                       }
                        return false;
                }
        }
 
-       protected void prepareForPossibleSpecialSpecjInitialization() {
+       protected void prepareForPossibleLoadingOutsideTransaction() {
                if ( session != null ) {
-                       specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
+                       allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
 
-                       if ( specjLazyLoad && sessionFactoryUuid == null ) {
-                               try {
-                                       sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
-                               }
-                               catch (NamingException e) {
-                                       //not much we can do if this fails...
-                               }
+                       if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {
+                               sessionFactoryUuid = session.getFactory().getUuid();
                        }
                }
        }
 
-
-       /**
-        * Associate the collection with the given session.
-        *
-        * @return false if the collection was already associated with the session
-        *
-        * @throws HibernateException if the collection was already associated
-        * with another open session
-        */
        @Override
-    public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
+       public final boolean setCurrentSession(SharedSessionContractImplementor session) throws HibernateException {
                if ( session == this.session ) {
                        return false;
                }
-               else {
+               else if ( this.session != null ) {
+                       final String msg = generateUnexpectedSessionStateMessage( session );
                        if ( isConnectedToSession() ) {
-                               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
-                               if ( ce == null ) {
-                                       throw new HibernateException(
-                                                       "Illegal attempt to associate a collection with two open sessions"
-                                       );
-                               }
-                               else {
-                                       throw new HibernateException(
-                                                       "Illegal attempt to associate a collection with two open sessions: " +
-                                                                       MessageHelper.collectionInfoString(
-                                                                                       ce.getLoadedPersister(), this,
-                                                                                       ce.getLoadedKey(), session
-                                                                       )
-                                       );
-                               }
+                               throw new HibernateException(
+                                               "Illegal attempt to associate a collection with two open sessions. " + msg
+                               );
                        }
                        else {
-                               this.session = session;
-                               return true;
+                               LOG.logUnexpectedSessionInCollectionNotConnected( msg );
                        }
                }
+               if ( hasQueuedOperations() ) {
+                       LOG.queuedOperationWhenAttachToSession( MessageHelper.collectionInfoString( getRole(), getKey() ) );
+               }
+               this.session = session;
+               return true;
+       }
+
+       private String generateUnexpectedSessionStateMessage(SharedSessionContractImplementor session) {
+               // NOTE: If this.session != null, this.session may be operating on this collection
+               // (e.g., by changing this.role, this.key, or even this.session) in a different thread.
+
+               // Grab the current role and key (it can still get changed by this.session...)
+               // If this collection is connected to this.session, then this.role and this.key should
+               // be consistent with the CollectionEntry in this.session (as long as this.session doesn't
+               // change it). Don't access the CollectionEntry in this.session because that could result
+               // in multi-threaded access to this.session.
+               final String roleCurrent = role;
+               final Serializable keyCurrent = key;
+
+               final StringBuilder sb = new StringBuilder( "Collection : " );
+               if ( roleCurrent != null ) {
+                       sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) );
+               }
+               else {
+                       final CollectionEntry ce = session.getPersistenceContextInternal().getCollectionEntry( this );
+                       if ( ce != null ) {
+                               sb.append(
+                                               MessageHelper.collectionInfoString(
+                                                               ce.getLoadedPersister(),
+                                                               this,
+                                                               ce.getLoadedKey(),
+                                                               session
+                                               )
+                               );
+                       }
+                       else {
+                               sb.append( "<unknown>" );
+                       }
+               }
+               // only include the collection contents if debug logging
+               if ( LOG.isDebugEnabled() ) {
+                       final String collectionContents = wasInitialized() ? toString() : "<uninitialized>";
+                       sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" );
+               }
+               return sb.toString();
        }
 
-       /**
-        * Do we need to completely recreate this collection when it changes?
-        */
        @Override
-    public boolean needsRecreate(CollectionPersister persister) {
+       public boolean needsRecreate(CollectionPersister persister) {
+               // Workaround for situations like HHH-7072.  If the collection element is a component that consists entirely
+               // of nullable properties, we currently have to forcefully recreate the entire collection.  See the use
+               // of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info.  In order to delete
+               // row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
+               // the current "WHERE COL = ?" (fails for null for most DBs).  Note that
+               // the param would have to be bound twice.  Until we eventually add "parameter bind points" concepts to the
+               // AST in ORM 5+, handling this type of condition is either extremely difficult or impossible.  Forcing
+               // recreation isn't ideal, but not really any other option in ORM 4.
+               // Selecting a type used in where part of update statement
+               // (must match condition in org.hibernate.persister.collection.BasicCollectionPersister#doUpdateRows).
+               // See HHH-9474
+               Type whereType;
+               if ( persister.hasIndex() ) {
+                       whereType = persister.getIndexType();
+               }
+               else {
+                       whereType = persister.getElementType();
+               }
+               if ( whereType instanceof CompositeType ) {
+                       CompositeType componentIndexType = (CompositeType) whereType;
+                       return !componentIndexType.hasNotNullProperty();
+               }
                return false;
        }
 
-       /**
-        * To be called internally by the session, forcing
-        * immediate initialization.
-        */
        @Override
-    public final void forceInitialization() throws HibernateException {
+       public final void forceInitialization() throws HibernateException {
                if ( !initialized ) {
                        if ( initializing ) {
                                throw new AssertionFailure( "force initialize loading collection" );
                        }
-                       if ( session == null ) {
-                               throw new HibernateException( "collection is not associated with any session" );
-                       }
-                       if ( !session.isConnected() ) {
-                               throw new HibernateException( "disconnected session" );
-                       }
-                       session.initializeCollection( this, false );
+                       initialize( false );
                }
        }
 
@@ -781,66 +877,54 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                return session.getPersistenceContext().getSnapshot( this );
        }
 
-       /**
-        * Is this instance initialized?
-        */
        @Override
-    public final boolean wasInitialized() {
+       public final boolean wasInitialized() {
                return initialized;
        }
 
        @Override
-    public boolean isRowUpdatePossible() {
+       public boolean isRowUpdatePossible() {
                return true;
        }
 
-       /**
-        * Does this instance have any "queued" additions?
-        */
        @Override
-    public final boolean hasQueuedOperations() {
+       public final boolean hasQueuedOperations() {
                return operationQueue != null;
        }
 
-       /**
-        * Iterate the "queued" additions
-        */
        @Override
-    public final Iterator queuedAdditionIterator() {
+       public final Iterator queuedAdditionIterator() {
                if ( hasQueuedOperations() ) {
                        return new Iterator() {
-                               int i = 0;
+                               private int index;
 
                                @Override
-                public Object next() {
-                                       return operationQueue.get( i++ ).getAddedInstance();
+                               public Object next() {
+                                       return operationQueue.get( index++ ).getAddedInstance();
                                }
 
                                @Override
-                public boolean hasNext() {
-                                       return i < operationQueue.size();
+                               public boolean hasNext() {
+                                       return index < operationQueue.size();
                                }
 
                                @Override
-                public void remove() {
+                               public void remove() {
                                        throw new UnsupportedOperationException();
                                }
                        };
                }
                else {
-                       return EmptyIterator.INSTANCE;
+                       return Collections.emptyIterator();
                }
        }
 
-       /**
-        * Iterate the "queued" additions
-        */
        @Override
-    @SuppressWarnings({"unchecked"})
+       @SuppressWarnings({"unchecked"})
        public final Collection getQueuedOrphans(String entityName) {
                if ( hasQueuedOperations() ) {
-                       Collection additions = new ArrayList( operationQueue.size() );
-                       Collection removals = new ArrayList( operationQueue.size() );
+                       final Collection additions = new ArrayList( operationQueue.size() );
+                       final Collection removals = new ArrayList( operationQueue.size() );
                        for ( DelayedOperation operation : operationQueue ) {
                                additions.add( operation.getAddedInstance() );
                                removals.add( operation.getOrphan() );
@@ -852,32 +936,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
        }
 
-       /**
-        * Called before inserting rows, to ensure that any surrogate keys
-        * are fully generated
-        */
        @Override
-    public void preInsert(CollectionPersister persister) throws HibernateException {
+       public void preInsert(CollectionPersister persister) throws HibernateException {
        }
 
-       /**
-        * Called after inserting a row, to fetch the natively generated id
-        */
        @Override
-    public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
+       public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
        }
 
-       /**
-        * get all "orphaned" elements
-        */
        @Override
-    public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
+       public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
 
        /**
-        * Get the current session
+        * Get the session currently associated with this collection.
+        *
+        * @return The session
         */
-       @SuppressWarnings({"JavaDoc"})
-       public final SessionImplementor getSession() {
+       public final SharedSessionContractImplementor getSession() {
                return session;
        }
 
@@ -889,21 +964,20 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
 
                @Override
-        public boolean hasNext() {
+               public boolean hasNext() {
                        return itr.hasNext();
                }
 
                @Override
-        public Object next() {
+               public Object next() {
                        return itr.next();
                }
 
                @Override
-        public void remove() {
+               public void remove() {
                        write();
                        itr.remove();
                }
-
        }
 
        protected final class ListIteratorProxy implements ListIterator {
@@ -914,55 +988,54 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
 
                @Override
-        @SuppressWarnings({"unchecked"})
+               @SuppressWarnings({"unchecked"})
                public void add(Object o) {
                        write();
                        itr.add( o );
                }
 
                @Override
-        public boolean hasNext() {
+               public boolean hasNext() {
                        return itr.hasNext();
                }
 
                @Override
-        public boolean hasPrevious() {
+               public boolean hasPrevious() {
                        return itr.hasPrevious();
                }
 
                @Override
-        public Object next() {
+               public Object next() {
                        return itr.next();
                }
 
                @Override
-        public int nextIndex() {
+               public int nextIndex() {
                        return itr.nextIndex();
                }
 
                @Override
-        public Object previous() {
+               public Object previous() {
                        return itr.previous();
                }
 
                @Override
-        public int previousIndex() {
+               public int previousIndex() {
                        return itr.previousIndex();
                }
 
                @Override
-        public void remove() {
+               public void remove() {
                        write();
                        itr.remove();
                }
 
                @Override
-        @SuppressWarnings({"unchecked"})
+               @SuppressWarnings({"unchecked"})
                public void set(Object o) {
                        write();
                        itr.set( o );
                }
-
        }
 
        protected class SetProxy implements java.util.Set {
@@ -973,79 +1046,81 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
 
                @Override
-        @SuppressWarnings({"unchecked"})
+               @SuppressWarnings({"unchecked"})
                public boolean add(Object o) {
                        write();
                        return set.add( o );
                }
 
                @Override
-        @SuppressWarnings({"unchecked"})
+               @SuppressWarnings({"unchecked"})
                public boolean addAll(Collection c) {
                        write();
                        return set.addAll( c );
                }
 
                @Override
-        public void clear() {
+               public void clear() {
                        write();
                        set.clear();
                }
 
                @Override
-        public boolean contains(Object o) {
+               public boolean contains(Object o) {
                        return set.contains( o );
                }
 
                @Override
-        public boolean containsAll(Collection c) {
+               @SuppressWarnings("unchecked")
+               public boolean containsAll(Collection c) {
                        return set.containsAll( c );
                }
 
                @Override
-        public boolean isEmpty() {
+               public boolean isEmpty() {
                        return set.isEmpty();
                }
 
                @Override
-        public Iterator iterator() {
+               public Iterator iterator() {
                        return new IteratorProxy( set.iterator() );
                }
 
                @Override
-        public boolean remove(Object o) {
+               public boolean remove(Object o) {
                        write();
                        return set.remove( o );
                }
 
                @Override
-        public boolean removeAll(Collection c) {
+               @SuppressWarnings("unchecked")
+               public boolean removeAll(Collection c) {
                        write();
                        return set.removeAll( c );
                }
 
                @Override
-        public boolean retainAll(Collection c) {
+               @SuppressWarnings("unchecked")
+               public boolean retainAll(Collection c) {
                        write();
                        return set.retainAll( c );
                }
 
                @Override
-        public int size() {
+               public int size() {
                        return set.size();
                }
 
                @Override
-        public Object[] toArray() {
+               public Object[] toArray() {
                        return set.toArray();
                }
 
                @Override
-        @SuppressWarnings({"unchecked"})
+               @SuppressWarnings({"unchecked"})
                public Object[] toArray(Object[] array) {
                        return set.toArray( array );
                }
-
        }
 
        protected final class ListProxy implements java.util.List {
@@ -1095,6 +1170,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
 
                @Override
+               @SuppressWarnings("unchecked")
                public boolean containsAll(Collection c) {
                        return list.containsAll( c );
                }
@@ -1147,12 +1223,14 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
 
                @Override
+               @SuppressWarnings("unchecked")
                public boolean removeAll(Collection c) {
                        write();
                        return list.removeAll( c );
                }
 
                @Override
+               @SuppressWarnings("unchecked")
                public boolean retainAll(Collection c) {
                        write();
                        return list.retainAll( c );
@@ -1199,6 +1277,41 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                public Object getOrphan();
        }
 
+       protected interface ValueDelayedOperation extends DelayedOperation {
+               void replace(CollectionPersister collectionPersister, Map copyCache);
+       }
+
+       protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
+               private Object addedValue;
+               private Object orphan;
+
+               protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
+                       this.addedValue = addedValue;
+                       this.orphan = orphan;
+               }
+
+               @Override
+        public void replace(CollectionPersister persister, Map copyCache) {
+                       if ( addedValue != null ) {
+                               addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
+                       }
+               }
+
+               protected final Object getReplacement(Type type, Object current, Map copyCache) {
+                       return type.replace( current, null, session, owner, copyCache );
+               }
+
+               @Override
+               public final Object getAddedInstance() {
+                       return addedValue;
+               }
+
+               @Override
+               public final Object getOrphan() {
+                       return orphan;
+               }
+       }
+
        /**
         * Given a collection of entity instances that used to
         * belong to the collection, and a collection of instances
@@ -1209,38 +1322,42 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                        Collection oldElements,
                        Collection currentElements,
                        String entityName,
-                       SessionImplementor session) throws HibernateException {
+                       SharedSessionContractImplementor session) throws HibernateException {
 
                // short-circuit(s)
                if ( currentElements.size() == 0 ) {
-                       return oldElements; // no new elements, the old list contains only Orphans
+                       // no new elements, the old list contains only Orphans
+                       return oldElements;
                }
                if ( oldElements.size() == 0 ) {
-                       return oldElements; // no old elements, so no Orphans neither
+                       // no old elements, so no Orphans neither
+                       return oldElements;
                }
 
                final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
                final Type idType = entityPersister.getIdentifierType();
+               final boolean useIdDirect = mayUseIdDirect( idType );
 
                // create the collection holding the Orphans
-               Collection res = new ArrayList();
+               final Collection res = new ArrayList();
 
                // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
-               java.util.Set currentIds = new HashSet();
-               java.util.Set currentSaving = new IdentitySet();
+               final java.util.Set currentIds = new HashSet();
+               final java.util.Set currentSaving = new IdentitySet();
+               final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
                for ( Object current : currentElements ) {
                        if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
-                               EntityEntry ee = session.getPersistenceContext().getEntry( current );
+                               final EntityEntry ee = persistenceContext.getEntry( current );
                                if ( ee != null && ee.getStatus() == Status.SAVING ) {
                                        currentSaving.add( current );
                                }
                                else {
-                                       Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+                                       final Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
                                                        entityName,
                                                        current,
                                                        session
                                        );
-                                       currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
+                                       currentIds.add( useIdDirect ? currentId : new TypedValue( idType, currentId ) );
                                }
                        }
                }
@@ -1248,8 +1365,8 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                // iterate over the *old* list
                for ( Object old : oldElements ) {
                        if ( !currentSaving.contains( old ) ) {
-                               Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
-                               if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
+                               final Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
+                               if ( !currentIds.contains( useIdDirect ? oldId : new TypedValue( idType, oldId ) ) ) {
                                        res.add( old );
                                }
                        }
@@ -1258,20 +1375,37 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                return res;
        }
 
+       private static boolean mayUseIdDirect(Type idType) {
+               return idType == StringType.INSTANCE
+                       || idType == IntegerType.INSTANCE
+                       || idType == LongType.INSTANCE
+                       || idType == UUIDBinaryType.INSTANCE
+                       || idType == UUIDCharType.INSTANCE
+                       || idType == PostgresUUIDType.INSTANCE;
+       }
+
+       /**
+        * Removes entity entries that have an equal identifier with the incoming entity instance
+        *
+        * @param list The list containing the entity instances
+        * @param entityInstance The entity instance to match elements.
+        * @param entityName The entity name
+        * @param session The session
+        */
        public static void identityRemove(
                        Collection list,
-                       Object object,
+                       Object entityInstance,
                        String entityName,
-                       SessionImplementor session) throws HibernateException {
+                       SharedSessionContractImplementor session) {
 
-               if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
+               if ( entityInstance != null && ForeignKeys.isNotTransient( entityName, entityInstance, null, session ) ) {
                        final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
-                       Type idType = entityPersister.getIdentifierType();
+                       final Type idType = entityPersister.getIdentifierType();
 
-                       Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
-                       Iterator itr = list.iterator();
+                       final Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, entityInstance, session );
+                       final Iterator itr = list.iterator();
                        while ( itr.hasNext() ) {
-                               Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
+                               final Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
                                if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
                                        itr.remove();
                                        break;
@@ -1281,28 +1415,55 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                }
        }
 
+       /**
+        * Removes entity entries that have an equal identifier with the incoming entity instance
+        *
+        * @param list The list containing the entity instances
+        * @param entityInstance The entity instance to match elements.
+        * @param entityName The entity name
+        * @param session The session
+        *
+        * @deprecated {@link #identityRemove(Collection, Object, String, SharedSessionContractImplementor)}
+        *             should be used instead.
+        */
+       @Deprecated
+       public static void identityRemove(
+                       Collection list,
+                       Object entityInstance,
+                       String entityName,
+                       SessionImplementor session) {
+               identityRemove( list, entityInstance, entityName, (SharedSessionContractImplementor) session );
+       }
+
        @Override
-    public Object getIdentifier(Object entry, int i) {
+       public Object getIdentifier(Object entry, int i) {
                throw new UnsupportedOperationException();
        }
 
        @Override
-    public Object getOwner() {
+       public Object getOwner() {
                return owner;
        }
 
        @Override
-    public void setOwner(Object owner) {
+       public void setOwner(Object owner) {
                this.owner = owner;
        }
 
-       /** ------ Below is section of code which makes remote service calls ----- */
-       // The affected methods are :
-       // initialize(final boolean writing)
-       // readSize()
-       // readIndexExistence(final Object index)
-       // readElementExistence(final Object element)
-       // readElementByIndex(final Object index)
+
+/** ##REMOTING-KEEP## #######################################################
+
+    ADDED PART: Below is the section of code which makes remote service calls.
+    Keeps this code when upgrading to newer hibernate version. Also keep
+    other code marked with ##REMOTING-KEEP## in the following 5 methods:
+
+    {@link #initialize(boolean)}
+       {@link #readSize()}
+       {@link #readIndexExistence(Object)}
+       {@link #readElementExistence(Object)}
+       {@link #readElementByIndex(Object)}
+
+    ######################################################################### */
 
        private static CdmApplicationRemoteConfiguration configuration;
        private static boolean remoting = false;
@@ -1312,15 +1473,16 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                configuration = conf;
        }
 
-
        private void remoteInitialize() {
 
                if (getOwner() != null && !initialized) {
-
+                   Object collectionType = null;
+                   Field field = null;
+                   Class<?> clazz = null;
                        try {
                                String role = getRole();
                                String fieldName = role.substring(role.lastIndexOf(".") + 1);
-                               log.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
+                               LOG.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
                                Object owner = getOwner();
                                CdmBase cdmBase;
                                if(owner instanceof CdmBase) {
@@ -1336,31 +1498,28 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
                                        throw new HibernateException("commonService not initialized (null)");
                                }
 
-                               //Object obj = ProxyUtils.deproxy(cachedCommonService.initializeCollection(this));
-                               Object obj = ProxyUtils.deproxy(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
-                               if(ProxyUtils.isProxy(obj)) {
+                               //Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
+                               Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
+                               if(ProxyUtils.isUninitializedProxy(obj)) {
                                    throw new HibernateException("Persistent Collection initialized but is still a proxy");
                                }
                                afterInitialize();
 
-                               Class<?> clazz = getClass();
+                               clazz = getClass();
                                if (clazz != null) {
-                                       //CollectionField cf = cachedCommonService.getCollectionField(col);
-                                       //cachedCommonService.updatePersistentCollection(cf);
-                                   Object collectionType = ProxyUtils.getCollectionType(obj);
-                                       Field field = clazz.getDeclaredField(collectionType.toString());
+                                       collectionType = ProxyUtils.getCollectionType(obj, clazz);
+                                   field = clazz.getDeclaredField(collectionType.toString());
                                        field.setAccessible(true);
                                        field.set(this, obj);
                                        ProxyUtils.setRoleValueInOwner(owner, role, obj);
 
                                }
                        } catch (Exception ex) {
-                               throw new CdmEagerLoadingException(ex);
+                           String originalMessage = ex.getMessage();
+                           String message = originalMessage + "clazz: " + (clazz == null? "" :clazz.getSimpleName())+ "- field: " + field + " - collectionType: " + collectionType;
+                           throw new CdmEagerLoadingException(message);
                        }
                }
        }
-
-
-
-}
-
+    // ##REMOTING-KEEP END##
+}
\ No newline at end of file