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 2bb52f27bc..b7531a8c70 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 @@ -47,6 +47,8 @@ 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.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.internal.util.MarkerObject; import org.hibernate.internal.util.collections.EmptyIterator; @@ -56,7 +58,6 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.ComponentType; import org.hibernate.type.Type; -import org.jboss.logging.Logger; /** * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection} @@ -64,7 +65,7 @@ import org.jboss.logging.Logger; * @author Gavin King */ 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 transient SessionImplementor session; private boolean initialized; @@ -269,7 +270,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers ( (Session) session ).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; } @@ -601,6 +602,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers return true; } else { + if ( this.session != null ) { + LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) ); + } return false; } } @@ -620,28 +624,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } - @Override public final boolean setCurrentSession(SessionImplementor session) throws HibernateException { if ( session == this.session ) { return false; } else { - if ( isConnectedToSession() ) { - final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); - if ( ce == null ) { + if ( this.session != null ) { + final String msg = generateUnexpectedSessionStateMessage( session ); + if ( isConnectedToSession() ) { throw new HibernateException( - "Illegal attempt to associate a collection with two open sessions" + "Illegal attempt to associate a collection with two open sessions. " + msg ); } else { - throw new HibernateException( - "Illegal attempt to associate a collection with two open sessions: " + - MessageHelper.collectionInfoString( - ce.getLoadedPersister(), this, - ce.getLoadedKey(), session - ) - ); + LOG.logUnexpectedSessionInCollectionNotConnected( msg ); + this.session = session; + return true; } } else { @@ -651,6 +650,46 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } + private String generateUnexpectedSessionStateMessage(SessionImplementor 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.getPersistenceContext().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(); + } + @Override public boolean needsRecreate(CollectionPersister persister) { // Workaround for situations like HHH-7072. If the collection element is a component that consists entirely diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 9b8fd801fb..4721dbda32 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1680,4 +1680,12 @@ public interface CoreMessageLogger extends BasicLogger { @LogMessage(level = DEBUG) @Message(value = "Creating pooled optimizer (lo) with [incrementSize=%s; returnClass=%s]", id = 467) void creatingPooledLoOptimizer(int incrementSize, String name); + + @LogMessage(level = WARN) + @Message(value = "An unexpected session is defined for a collection, but the collection is not connected to that session. A persistent collection may only be associated with one session at a time. Overwriting session. %s", id = 470) + void logUnexpectedSessionInCollectionNotConnected(String msg); + + @LogMessage(level = WARN) + @Message(value = "Cannot unset session in a collection because an unexpected session is defined. A persistent collection may only be associated with one session at a time. %s", id = 471 ) + void logCannotUnsetUnexpectedSessionInCollection(String msg); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionTest.java new file mode 100644 index 0000000000..5ffa0d4bdf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/collection/multisession/MultipleSessionCollectionTest.java @@ -0,0 +1,694 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, 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 + */ +package org.hibernate.test.collection.multisession; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.jboss.logging.Logger; +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.collection.internal.AbstractPersistentCollection; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +/** + * @author Gail Badner + */ +public class MultipleSessionCollectionTest extends BaseCoreFunctionalTestCase { + private static final Logger log = Logger.getLogger( MultipleSessionCollectionTest.class ); + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testSaveOrUpdateOwnerWithCollectionInNewSessionBeforeFlush() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.saveOrUpdate( p ); + + // try to save the same entity in a new session before flushing the first session + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testSaveOrUpdateOwnerWithCollectionInNewSessionAfterFlush() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.saveOrUpdate( p ); + s1.flush(); + + // try to save the same entity in a new session after flushing the first session + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testSaveOrUpdateOwnerWithUninitializedCollectionInNewSession() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + + Session s1 = openSession(); + s1.getTransaction().begin(); + p = (Parent) s1.get( Parent.class, p.id ); + assertFalse( Hibernate.isInitialized( p.children ) ); + + // try to save the same entity (with an uninitialized collection) in a new session + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to initialize collection, modify and commit in first session + assertFalse( Hibernate.isInitialized( p.children ) ); + Hibernate.initialize( p.children ); + p.children.add( new Child() ); + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( 2, pGet.children.size()); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testSaveOrUpdateOwnerWithInitializedCollectionInNewSession() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + + Session s1 = openSession(); + s1.getTransaction().begin(); + p = (Parent) s1.get( Parent.class, p.id ); + Hibernate.initialize( p.children ); + + // try to save the same entity (with an initialized collection) in a new session + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + // + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testCopyPersistentCollectionReferenceBeforeFlush() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.persist( p ); + + // Copy p.children into a different Parent before flush and try to save in new session. + + Parent pWithSameChildren = new Parent(); + pWithSameChildren.children = p.children; + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( pWithSameChildren ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testCopyPersistentCollectionReferenceAfterFlush() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.persist( p ); + s1.flush(); + + // Copy p.children into a different Parent after flush and try to save in new session. + + Parent pWithSameChildren = new Parent(); + pWithSameChildren.children = p.children; + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( pWithSameChildren ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testCopyUninitializedCollectionReferenceAfterGet() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + + Session s1 = openSession(); + s1.getTransaction().begin(); + p = (Parent) s1.get( Parent.class, p.id ); + assertFalse( Hibernate.isInitialized( p.children ) ); + + // Copy p.children (uninitialized) into a different Parent and try to save in new session. + + Parent pWithSameChildren = new Parent(); + pWithSameChildren.children = p.children; + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( pWithSameChildren ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testCopyInitializedCollectionReferenceAfterGet() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + + Session s1 = openSession(); + s1.getTransaction().begin(); + p = (Parent) s1.get( Parent.class, p.id ); + Hibernate.initialize( p.children ); + + // Copy p.children (initialized) into a different Parent.children and try to save in new session. + + Parent pWithSameChildren = new Parent(); + pWithSameChildren.children = p.children; + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( pWithSameChildren ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testCopyInitializedCollectionReferenceToNewEntityCollectionRoleAfterGet() { + Parent p = new Parent(); + Child c = new Child(); + p.children.add( c ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + + Session s1 = openSession(); + s1.getTransaction().begin(); + p = (Parent) s1.get( Parent.class, p.id ); + Hibernate.initialize( p.children ); + + // Copy p.children (initialized) into a different Parent.oldChildren (note different collection role) + // and try to save in new session. + + Parent pWithSameChildren = new Parent(); + pWithSameChildren.oldChildren = p.children; + + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( pWithSameChildren ); + s2.getTransaction().commit(); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit in first session + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + Parent pGet = (Parent) s1.get( Parent.class, p.id ); + assertEquals( c.id, pGet.children.iterator().next().id ); + session.delete( pGet ); + s1.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testDeleteCommitCopyToNewOwnerInNewSession() { + Parent p1 = new Parent(); + p1.nickNames.add( "nick" ); + Parent p2 = new Parent(); + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.save( p1 ); + s1.save( p2 ); + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + s1.delete( p1 ); + s1.flush(); + s1.getTransaction().commit(); + + // need to commit after flushing; otherwise, will get lock failure when try to move the collection below + + assertNull( ( (SessionImplementor) s1 ).getPersistenceContext().getEntry( p1 ) ); + CollectionEntry ceChildren = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.children ); + CollectionEntry ceNickNames = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.nickNames ); + assertNull( ceChildren ); + assertNull( ceNickNames ); + assertNull( ( ( AbstractPersistentCollection) p1.children ).getSession() ); + assertNull( ( ( AbstractPersistentCollection) p1.oldChildren ).getSession() ); + assertNull( ( ( AbstractPersistentCollection) p1.nickNames ).getSession() ); + assertNull( ( (AbstractPersistentCollection) p1.oldNickNames ).getSession() ); + + // Assign the deleted collection to a different entity with same collection role (p2.nickNames) + + p2.nickNames = p1.nickNames; + Session s2 = openSession(); + s2.getTransaction().begin(); + s2.saveOrUpdate( p2 ); + s2.getTransaction().commit(); + s2.close(); + + s1.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testDeleteCommitCopyToNewOwnerNewCollectionRoleInNewSession() { + Parent p1 = new Parent(); + p1.nickNames.add( "nick" ); + Parent p2 = new Parent(); + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.save( p1 ); + s1.save( p2 ); + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + s1.delete( p1 ); + s1.flush(); + s1.getTransaction().commit(); + + // need to commit after flushing; otherwise, will get lock failure when try to move the collection below + + assertNull( ( (SessionImplementor) s1 ).getPersistenceContext().getEntry( p1 ) ); + CollectionEntry ceChildren = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.children ); + CollectionEntry ceNickNames = ( (SessionImplementor) s1 ).getPersistenceContext().getCollectionEntry( (PersistentCollection) p1.nickNames ); + assertNull( ceChildren ); + assertNull( ceNickNames ); + assertNull( ( ( AbstractPersistentCollection) p1.children ).getSession() ); + assertNull( ( ( AbstractPersistentCollection) p1.oldChildren ).getSession() ); + assertNull( ( ( AbstractPersistentCollection) p1.nickNames ).getSession() ); + assertNull( ( (AbstractPersistentCollection) p1.oldNickNames ).getSession() ); + + // Assign the deleted collection to a different entity with different collection role (p2.oldNickNames) + + p2.oldNickNames = p1.nickNames; + Session s2 = openSession(); + s2.getTransaction().begin(); + s2.saveOrUpdate( p2 ); + s2.getTransaction().commit(); + s2.close(); + + s1.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testDeleteCopyToNewOwnerInNewSessionBeforeFlush() { + Parent p1 = new Parent(); + p1.nickNames.add( "nick" ); + Parent p2 = new Parent(); + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.save( p1 ); + s1.save( p2 ); + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + s1.delete( p1 ); + + // Assign the deleted collection to a different entity with same collection role (p2.nickNames) + // before committing delete. + + p2.nickNames = p1.nickNames; + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p2 ); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit the original delete + s1.getTransaction().commit(); + s1.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9518" ) + public void testDeleteCopyToNewOwnerNewCollectionRoleInNewSessionBeforeFlush() { + Parent p1 = new Parent(); + p1.nickNames.add( "nick" ); + Parent p2 = new Parent(); + Session s1 = openSession(); + s1.getTransaction().begin(); + s1.save( p1 ); + s1.save( p2 ); + s1.getTransaction().commit(); + s1.close(); + + s1 = openSession(); + s1.getTransaction().begin(); + s1.delete( p1 ); + + // Assign the deleted collection to a different entity with different collection role (p2.oldNickNames) + // before committing delete. + + p2.oldNickNames = p1.nickNames; + Session s2 = openSession(); + s2.getTransaction().begin(); + try { + s2.saveOrUpdate( p2 ); + fail( "should have thrown HibernateException" ); + } + catch (HibernateException ex) { + log.error( ex ); + s2.getTransaction().rollback(); + } + finally { + s2.close(); + } + + // should still be able to commit the original delete + s1.getTransaction().commit(); + s1.close(); + } + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Parent.class, + Child.class + }; + } + + @Entity + @Table(name="Parent") + public static class Parent { + @Id + @GeneratedValue + private Long id; + + @ElementCollection + private Set nickNames = new HashSet(); + + @ElementCollection + private Set oldNickNames = new HashSet(); + + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn + private Set children = new HashSet(); + + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn + private Set oldChildren = new HashSet(); + + } + + @Entity + @Table(name="Child") + public static class Child { + @Id + @GeneratedValue + private Long id; + + } +}