/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.cascade.multicircle;
import javax.persistence.EntityManager;
import javax.persistence.RollbackException;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.FailureExpected;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import junit.framework.Assert;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* This test uses a complicated model that requires Hibernate to delay
* inserts until non-nullable transient entity dependencies are resolved.
*
* All IDs are generated from a sequence.
*
* JPA cascade types are used (javax.persistence.CascadeType)..
*
* This test uses the following model:
*
* <code>
* ------------------------------ N G
* |
* | 1
* | |
* | |
* | N
* |
* | E N--------------0,1 * F
* |
* | 1 N
* | | |
* | | |
* 1 N |
* * |
* B * N---1 D * 1------------------
* *
* N N
* | |
* | |
* 1 |
* |
* C * 1-----
*</code>
*
* In the diagram, all associations are bidirectional;
* assocations marked with '*' cascade persist, save, merge operations to the
* associated entities (e.g., B cascades persist to D, but D does not cascade
* persist to B);
*
* b, c, d, e, f, and g are all transient unsaved that are associated with each other.
*
* When saving b, the entities are added to the ActionQueue in the following order:
* c, d (depends on e), f (depends on d, g), e, b, g.
*
* Entities are inserted in the following order:
* c, e, d, b, g, f.
*/
public class MultiCircleJpaCascadeTest extends BaseEntityManagerFunctionalTestCase {
private B b;
private C c;
private D d;
private E e;
private F f;
private G g;
private boolean skipCleanup;
@Before
public void setup() {
b = new B();
c = new C();
d = new D();
e = new E();
f = new F();
g = new G();
b.getGCollection().add( g );
b.setC( c );
b.setD( d );
c.getBCollection().add( b );
c.getDCollection().add( d );
d.getBCollection().add( b );
d.setC( c );
d.setE( e );
d.getFCollection().add( f );
e.getDCollection().add( d );
e.setF( f );
f.getECollection().add( e );
f.setD( d );
f.setG( g );
g.setB( b );
g.getFCollection().add( f );
skipCleanup = false;
}
@After
public void cleanup() {
if ( ! skipCleanup ) {
b.setC( null );
b.setD( null );
b.getGCollection().remove( g );
c.getBCollection().remove( b );
c.getDCollection().remove( d );
d.getBCollection().remove( b );
d.setC( null );
d.setE( null );
d.getFCollection().remove( f );
e.getDCollection().remove( d );
e.setF( null );
f.setD( null );
f.getECollection().remove( e );
f.setG( null );
g.setB( null );
g.getFCollection().remove( f );
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
b = em.merge( b );
c = em.merge( c );
d = em.merge( d );
e = em.merge( e );
f = em.merge( f );
g = em.merge( g );
em.remove( f );
em.remove( g );
em.remove( b );
em.remove( d );
em.remove( e );
em.remove( c );
em.getTransaction().commit();
em.close();
}
}
@Test
public void testPersist() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
em.persist( b );
em.getTransaction().commit();
em.close();
check();
}
@Test
public void testPersistNoCascadeToTransient() {
skipCleanup = true;
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
try {
em.persist( c );
fail( "should have failed." );
}
catch( IllegalStateException ex ) {
assertTrue( TransientPropertyValueException.class.isInstance( ex.getCause() ) );
TransientPropertyValueException pve = (TransientPropertyValueException) ex.getCause();
assertEquals( G.class.getName(), pve.getTransientEntityName() );
assertEquals( F.class.getName(), pve.getPropertyOwnerEntityName() );
assertEquals( "g", pve.getPropertyName() );
}
em.getTransaction().rollback();
em.close();
}
@Test
@FailureExpected( jiraKey = "HHH-6999" )
// fails on d.e; should pass
public void testPersistThenUpdate() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
em.persist( b );
// remove old e from associations
e.getDCollection().remove( d );
d.setE( null );
f.getECollection().remove( e );
e.setF( null );
// add new e to associations
e = new E();
e.getDCollection().add( d );
f.getECollection().add( e );
d.setE( e );
e.setF( f );
em.getTransaction().commit();
em.close();
check();
}
@Test
public void testPersistThenUpdateNoCascadeToTransient() {
// expected to fail, so nothing will be persisted.
skipCleanup = true;
// remove elements from collections and persist
c.getBCollection().clear();
c.getDCollection().clear();
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
em.persist( c );
// now add the elements back
c.getBCollection().add( b );
c.getDCollection().add( d );
try {
em.getTransaction().commit();
fail( "should have thrown IllegalStateException");
}
catch ( RollbackException ex ) {
assertTrue( ex.getCause() instanceof IllegalStateException );
IllegalStateException ise = ( IllegalStateException ) ex.getCause();
// should fail on entity g (due to no cascade to f.g);
// instead it fails on entity e ( due to no cascade to d.e)
// because e is not in the process of being saved yet.
// when HHH-6999 is fixed, this test should be changed to
// check for g and f.g
//noinspection ThrowableResultOfMethodCallIgnored
TransientPropertyValueException tpve = assertTyping( TransientPropertyValueException.class, ise.getCause() );
assertEquals( E.class.getName(), tpve.getTransientEntityName() );
assertEquals( D.class.getName(), tpve.getPropertyOwnerEntityName() );
assertEquals( "e", tpve.getPropertyName() );
}
em.close();
}
@Test
public void testMerge() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
b = em.merge( b );
c = b.getC();
d = b.getD();
e = d.getE();
f = e.getF();
g = f.getG();
em.getTransaction().commit();
em.close();
check();
}
private void check() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
B bRead = em.find( B.class, b.getId() );
Assert.assertEquals( b, bRead );
G gRead = bRead.getGCollection().iterator().next();
Assert.assertEquals( g, gRead );
C cRead = bRead.getC();
Assert.assertEquals( c, cRead );
D dRead = bRead.getD();
Assert.assertEquals( d, dRead );
Assert.assertSame( bRead, cRead.getBCollection().iterator().next() );
Assert.assertSame( dRead, cRead.getDCollection().iterator().next() );
Assert.assertSame( bRead, dRead.getBCollection().iterator().next() );
Assert.assertEquals( cRead, dRead.getC() );
E eRead = dRead.getE();
Assert.assertEquals( e, eRead );
F fRead = dRead.getFCollection().iterator().next();
Assert.assertEquals( f, fRead );
Assert.assertSame( dRead, eRead.getDCollection().iterator().next() );
Assert.assertSame( fRead, eRead.getF() );
Assert.assertSame( eRead, fRead.getECollection().iterator().next() );
Assert.assertSame( dRead, fRead.getD() );
Assert.assertSame( gRead, fRead.getG());
Assert.assertSame( bRead, gRead.getB() );
Assert.assertSame( fRead, gRead.getFCollection().iterator().next() );
em.getTransaction().commit();
em.close();
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[]{
B.class,
C.class,
D.class,
E.class,
F.class,
G.class
};
}
}