/*
* 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.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 org.junit.Test;
import org.jboss.logging.Logger;
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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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<String> nickNames = new HashSet<String>();
@ElementCollection
private Set<String> oldNickNames = new HashSet<String>();
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn
private Set<Child> children = new HashSet<Child>();
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn
private Set<Child> oldChildren = new HashSet<Child>();
}
@Entity
@Table(name="Child")
public static class Child {
@Id
@GeneratedValue
private Long id;
}
}