X-Git-Url: https://dev.e-taxonomy.eu/gitweb/taxeditor.git/blobdiff_plain/d637e4bab9be3f8bb6d7192555593a71e3f3a4e3..bdf38d9422d6812e0743798197d089a4516777ef:/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index f2debccfe..7f124baf0 100644 --- a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -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 . */ 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,37 +26,75 @@ 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 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 ); - private static final long serialVersionUID = -7238232378593030571L; + /** + * IMPORTANT:
+ * This serialVersionUID must be kept in sync with the serialVersionUID which is generated + * on the fly for serialized AbstractPersistentCollection objects coming from the httpInvoker + * service. + * This is most probably necessary after updating hibernate to a newer version. In any case + * it the need for updating this serialVersionUID becomes obvious when the attempt + * to connect to the server side fails with an InvalidClassException: + * + *
+	 * java.io.InvalidClassException: org.hibernate.collection.internal.AbstractPersistentCollection;
+	 * local class incompatible:
+	 * stream classdesc serialVersionUID = 2742261122392386159,
+	 * local class serialVersionUID = -7238232378593030571
+	 * 
+ * The correct serialVersionUID is the stream classdesc serialVersionUID + * from the error message. + */ + private static final long serialVersionUID = 6275967693128102740L; - private transient SessionImplementor session; + private transient SharedSessionContractImplementor session; + private boolean isTempSession = false; private boolean initialized; private transient List operationQueue; private transient boolean directlyAccessible; @@ -88,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 @@ -153,93 +195,101 @@ 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() { - @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() { + @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 The java type of the return for this LazyInitializationWork + */ public static interface LazyInitializationWork { + /** + * Do the represented work and return the result. + * + * @return The result + */ public T doWork(); } private T withTemporarySessionIfNeeded(LazyInitializationWork 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 @@ -247,10 +297,10 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers // 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 ); @@ -260,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() { - @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 ); + //keep formatting below to ease update to newer hibernate version + //##REMOTING-KEEP END## + final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this ); + final CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); } - else { - read(); - } - 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; @@ -323,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() { - @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 ); + //keep formatting below to ease update to newer hibernate version + //##REMOTING-KEEP END## + final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this ); + final CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); } - else { - read(); - } - 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; @@ -361,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; @@ -406,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; } /** @@ -426,9 +494,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers */ @SuppressWarnings({"JavaDoc"}) protected boolean isOperationQueueEnabled() { - return !initialized && - isConnectedToSession() && - isInverseCollection(); + return !initialized + && isConnectedToSession() + && isInverseCollection(); } /** @@ -438,9 +506,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers */ @SuppressWarnings({"JavaDoc"}) protected boolean isPutQueueEnabled() { - return !initialized && - isConnectedToSession() && - isInverseOneToManyOrNoOrphanDelete(); + return !initialized + && isConnectedToSession() + && isInverseOneToManyOrNoOrphanDelete(); } /** @@ -450,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(); } @@ -469,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(); } /** @@ -481,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() ); } /** @@ -498,7 +569,22 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers operationQueue = new ArrayList( 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 ); + } + } } /** @@ -509,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; } @@ -593,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() { - @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() { + @Override + public Object doWork() { + session.initializeCollection( AbstractPersistentCollection.this, writing ); + return null; + } + } + ); } } @@ -640,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( "" ); } } + // only include the collection contents if debug logging + if ( LOG.isDebugEnabled() ) { + final String collectionContents = wasInitialized() ? toString() : ""; + 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 ); } } @@ -757,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() ); @@ -828,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; } @@ -865,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 { @@ -890,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 { @@ -949,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 { @@ -1071,6 +1170,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } @Override + @SuppressWarnings("unchecked") public boolean containsAll(Collection c) { return list.containsAll( c ); } @@ -1123,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 ); @@ -1175,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 @@ -1185,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 ) ); } } } @@ -1224,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 ); } } @@ -1234,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; @@ -1257,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; @@ -1288,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) { @@ -1312,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