From 467258cffa105943877596b58c48beece070a29d Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 7 Aug 2012 14:51:13 -0500 Subject: [PATCH] HHH-7457 - Enable loading of collections thats been detached from session --- .../AbstractPersistentCollection.java | 408 ++++++++++-------- .../proxy/AbstractLazyInitializer.java | 67 +-- 2 files changed, 262 insertions(+), 213 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index cd9c37a259..4053a8d4cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -23,20 +23,30 @@ */ package org.hibernate.collection.internal; +import javax.naming.NamingException; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.cfg.Configuration; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ForeignKeys; -import org.hibernate.engine.spi.*; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.Status; +import org.hibernate.engine.spi.TypedValue; import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.internal.util.MarkerObject; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.EmptyIterator; import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.persister.collection.CollectionPersister; @@ -44,8 +54,6 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; -import javax.naming.NamingException; - /** * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection} * @@ -60,7 +68,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers private transient boolean initializing; private Object owner; private int cachedSize = -1; - + private String role; private Serializable key; // collections detect changes made via their public interface and mark @@ -74,60 +82,63 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers public final String getRole() { return role; } - + public final Serializable getKey() { return key; } - + public final boolean isUnreferenced() { - return role==null; + return role == null; } - + public final boolean isDirty() { return dirty; } - + public final void clearDirty() { dirty = false; } - + public final void dirty() { dirty = true; } - + public final Serializable getStoredSnapshot() { return storedSnapshot; } - + //Careful: these methods do not initialize the collection. + /** * Is the initialized collection empty? */ public abstract boolean empty(); + /** * Called by any read-only method of the collection interface */ protected final void read() { - initialize(false); + initialize( false ); } /** * Called by the {@link Collection#size} method */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean readSize() { - if (!initialized) { - if ( cachedSize!=-1 && !hasQueuedOperations() ) { + if ( !initialized ) { + if ( cachedSize != -1 && !hasQueuedOperations() ) { return true; } else { try { - if(specjLazyLoad) { - specialSpecjInitialization(false); + if ( specjLazyLoad ) { + specialSpecjInitialization( false ); } - else + else { throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + } + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); CollectionPersister persister = entry.getLoadedPersister(); if ( persister.isExtraLazy() ) { if ( hasQueuedOperations() ) { @@ -138,8 +149,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } finally { - if(specjLazyLoad) + if ( specjLazyLoad ) { session = null; + } } } } @@ -148,14 +160,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } protected Boolean readIndexExistence(Object index) { - if (!initialized) { + if ( !initialized ) { try { - if(specjLazyLoad) { - specialSpecjInitialization(false); + if ( specjLazyLoad ) { + specialSpecjInitialization( false ); } - else + else { throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + } + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); CollectionPersister persister = entry.getLoadedPersister(); if ( persister.isExtraLazy() ) { if ( hasQueuedOperations() ) { @@ -165,8 +178,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } finally { - if(specjLazyLoad) + if ( specjLazyLoad ) { session = null; + } } } read(); @@ -174,14 +188,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } protected Boolean readElementExistence(Object element) { - if (!initialized) { + if ( !initialized ) { try { - if(specjLazyLoad) { - specialSpecjInitialization(false); + if ( specjLazyLoad ) { + specialSpecjInitialization( false ); } - else + else { throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + } + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); CollectionPersister persister = entry.getLoadedPersister(); if ( persister.isExtraLazy() ) { if ( hasQueuedOperations() ) { @@ -191,48 +206,49 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } finally { - if(specjLazyLoad) + if ( specjLazyLoad ) { session = null; + } } } read(); return null; } - - protected static final Object UNKNOWN = new MarkerObject("UNKNOWN"); - + + protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" ); + protected Object readElementByIndex(Object index) { - if (!initialized) { + if ( !initialized ) { throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( this ); CollectionPersister persister = entry.getLoadedPersister(); if ( persister.isExtraLazy() ) { if ( hasQueuedOperations() ) { session.flush(); } - return persister.getElementByIndex(entry.getLoadedKey(), index, session, owner); + return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); } } read(); return UNKNOWN; - + } - + protected int getCachedSize() { return cachedSize; } - + private boolean isConnectedToSession() { - return session!=null && + return session != null && session.isOpen() && - session.getPersistenceContext().containsCollection(this); + session.getPersistenceContext().containsCollection( this ); } /** * Called by any writer method of the collection interface */ protected final void write() { - initialize(true); + initialize( true ); dirty(); } @@ -240,29 +256,31 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Is this collection in a state that would allow us to * "queue" operations? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isOperationQueueEnabled() { return !initialized && isConnectedToSession() && isInverseCollection(); } + /** * Is this collection in a state that would allow us to * "queue" puts? This is a special case, because of orphan * delete. */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isPutQueueEnabled() { return !initialized && isConnectedToSession() && isInverseOneToManyOrNoOrphanDelete(); } + /** * Is this collection in a state that would allow us to * "queue" clear? This is a special case, because of orphan * delete. */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isClearQueueEnabled() { return !initialized && isConnectedToSession() && @@ -272,9 +290,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Is this the "inverse" end of a bidirectional association? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseCollection() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); return ce != null && ce.getLoadedPersister().isInverse(); } @@ -282,34 +300,34 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Is this the "inverse" end of a bidirectional association with * no orphan delete enabled? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseCollectionNoOrphanDelete() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); - return ce != null && + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); + return ce != null && ce.getLoadedPersister().isInverse() && !ce.getLoadedPersister().hasOrphanDelete(); } /** - * Is this the "inverse" end of a bidirectional one-to-many, or + * Is this the "inverse" end of a bidirectional one-to-many, or * of a collection with no orphan delete? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseOneToManyOrNoOrphanDelete() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); return ce != null && ce.getLoadedPersister().isInverse() && ( - ce.getLoadedPersister().isOneToMany() || - !ce.getLoadedPersister().hasOrphanDelete() - ); + ce.getLoadedPersister().isOneToMany() || + !ce.getLoadedPersister().hasOrphanDelete() + ); } /** * Queue an addition */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected final void queueOperation(DelayedOperation operation) { - if (operationQueue==null) { - operationQueue = new ArrayList(10); + if ( operationQueue == null ) { + operationQueue = new ArrayList( 10 ); } operationQueue.add( operation ); dirty = true; //needed so that we remove this collection from the second-level cache @@ -339,16 +357,17 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * database state is now synchronized with the memory state. */ public void postAction() { - operationQueue=null; + operationQueue = null; cachedSize = -1; clearDirty(); } - + /** * Not called by Hibernate, but used by non-JDK serialization, * eg. SOAP libraries. */ - public AbstractPersistentCollection() {} + public AbstractPersistentCollection() { + } protected AbstractPersistentCollection(SessionImplementor session) { this.session = session; @@ -376,13 +395,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers //override on some subclasses return afterInitialize(); } - + public boolean afterInitialize() { setInitialized(); //do this bit after setting initialized to true or it will recurse - if (operationQueue!=null) { + if ( operationQueue != null ) { performQueuedOperations(); - operationQueue=null; + operationQueue = null; cachedSize = -1; return false; } @@ -394,40 +413,42 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Initialize the collection, if possible, wrapping any exceptions * in a runtime exception + * * @param writing currently obsolete + * * @throws LazyInitializationException if we cannot initialize */ protected final void initialize(boolean writing) { - if (!initialized) { - if (initializing) { - throw new LazyInitializationException("illegal access to loading collection"); + if ( !initialized ) { + if ( initializing ) { + throw new LazyInitializationException( "illegal access to loading collection" ); } else if ( specjLazyLoad ) { - specialSpecjInitialization(writing); + specialSpecjInitialization( writing ); } - else if ( session==null ) { - throw new LazyInitializationException("could not initialize proxy - no Session"); + else if ( session == null ) { + throw new LazyInitializationException( "could not initialize proxy - no Session" ); } else if ( !session.isOpen() ) { - throw new LazyInitializationException("could not initialize proxy - the owning Session was closed"); + throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" ); } else if ( !session.isConnected() ) { - throw new LazyInitializationException("could not initialize proxy - the owning Session is disconnected"); + throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); } else { throwLazyInitializationExceptionIfNotConnected(); - session.initializeCollection(this, writing); + session.initializeCollection( this, writing ); } } } private void throwLazyInitializationExceptionIfNotConnected() { - if ( !isConnectedToSession() ) { - throwLazyInitializationException("no session or session was closed"); + if ( !isConnectedToSession() ) { + throwLazyInitializationException( "no session or session was closed" ); } if ( !session.isConnected() ) { - throwLazyInitializationException("session is disconnected"); - } + throwLazyInitializationException( "session is disconnected" ); + } } protected void specialSpecjInitialization(boolean writing) { @@ -437,15 +458,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers throwLazyInitializationExceptionIfNotConnected(); } try { - SessionFactoryImplementor sf = (SessionFactoryImplementor) + SessionFactoryImplementor sf = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); - session = (SessionImplementor) sf.openSession(); + session = (SessionImplementor) sf.openSession(); CollectionPersister collectionPersister = session.getFactory().getCollectionPersister( this.getRole() ); session.getPersistenceContext() .addUninitializedDetachedCollection( collectionPersister, this ); - session.initializeCollection(this, writing); + session.initializeCollection( this, writing ); //CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); //CollectionPersister persister = entry.getLoadedPersister(); @@ -453,23 +474,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers //session = null; } - catch( Exception e ) { + catch (Exception e) { e.printStackTrace(); - throw new LazyInitializationException(e.getMessage()); + throw new LazyInitializationException( e.getMessage() ); } } else { - session.initializeCollection(this, writing); + session.initializeCollection( this, writing ); } } - + private void throwLazyInitializationException(String message) { throw new LazyInitializationException( - "failed to lazily initialize a collection" + - ( role==null ? "" : " of role: " + role ) + - ", " + message - ); + "failed to lazily initialize a collection" + + (role == null ? "" : " of role: " + role) + + ", " + message + ); } protected final void setInitialized() { @@ -491,12 +512,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Disassociate this collection from the given session. + * * @return true if this was currently associated with the given session */ public final boolean unsetSession(SessionImplementor currentSession) { - prepareForPossibleSpecialSpecjInitialization(); - if (currentSession==this.session) { - this.session=null; + prepareForPossibleSpecialSpecjInitialization(); + if ( currentSession == this.session ) { + this.session = null; return true; } else { @@ -506,11 +528,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers protected void prepareForPossibleSpecialSpecjInitialization() { if ( session != null ) { - specjLazyLoad = Boolean.parseBoolean( session.getFactory().getProperties().getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS)); + specjLazyLoad = Boolean.parseBoolean( + session.getFactory() + .getProperties() + .getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS ) + ); - if ( specjLazyLoad && sessionFactoryUuid == null) { + if ( specjLazyLoad && sessionFactoryUuid == null ) { try { - sessionFactoryUuid = (String) session.getFactory().getReference().get("uuid").getContent(); + sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); } catch (NamingException e) { //not much we can do if this fails... @@ -522,31 +548,33 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * 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 */ public final boolean setCurrentSession(SessionImplementor session) throws HibernateException { - if (session==this.session) { + if ( session == this.session ) { return false; } else { if ( isConnectedToSession() ) { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); - if (ce==null) { + 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(), - ce.getLoadedKey(), - session.getFactory() - ) - ); + MessageHelper.collectionInfoString( + ce.getLoadedPersister(), + ce.getLoadedKey(), + session.getFactory() + ) + ); } } else { @@ -562,23 +590,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers public boolean needsRecreate(CollectionPersister persister) { return false; } - + /** * To be called internally by the session, forcing * immediate initialization. */ public final void forceInitialization() throws HibernateException { - if (!initialized) { - if (initializing) { - throw new AssertionFailure("force initialize loading collection"); + 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 == null ) { + throw new HibernateException( "collection is not associated with any session" ); } if ( !session.isConnected() ) { - throw new HibernateException("disconnected session"); + throw new HibernateException( "disconnected session" ); } - session.initializeCollection(this, false); + session.initializeCollection( this, false ); } } @@ -586,9 +614,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Get the current snapshot from the session */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected final Serializable getSnapshot() { - return session.getPersistenceContext().getSnapshot(this); + return session.getPersistenceContext().getSnapshot( this ); } /** @@ -597,7 +625,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers public final boolean wasInitialized() { return initialized; } - + public boolean isRowUpdatePossible() { return true; } @@ -606,8 +634,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Does this instance have any "queued" additions? */ public final boolean hasQueuedOperations() { - return operationQueue!=null; + return operationQueue != null; } + /** * Iterate the "queued" additions */ @@ -615,12 +644,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers if ( hasQueuedOperations() ) { return new Iterator() { int i = 0; + public Object next() { - return operationQueue.get(i++).getAddedInstance(); + return operationQueue.get( i++ ).getAddedInstance(); } + public boolean hasNext() { - return i * This method should only be called during serialization when read-only/modifiable setting * is not available (i.e., isReadOnlySettingAvailable() == false) * * @return null, if the default setting should be used; - * true, for read-only; - * false, for modifiable + * true, for read-only; + * false, for modifiable + * * @throws IllegalStateException if isReadOnlySettingAvailable() == true */ protected final Boolean isReadOnlyBeforeAttachedToSession() { @@ -343,12 +351,13 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { /** * Set the read-only/modifiable setting that should be put in affect when it is * attached to a session. - * + *

* This method should only be called during deserialization, before associating * the proxy with a session. * * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when * associated with a session; null indicates that the default should be used. + * * @throws IllegalStateException if isReadOnlySettingAvailable() == true */ /* package-private */