HHH-6957 : Throw TransientPropertyValueException if there are unresolved entity insert actions after persist/save/merge listeners execute

This commit is contained in:
Gail Badner 2012-01-27 00:15:38 -08:00
parent bb4738b1e4
commit ebee6b731e
12 changed files with 934 additions and 7 deletions

View File

@ -0,0 +1,99 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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;
import org.hibernate.internal.util.StringHelper;
/**
* Thrown when a property cannot be persisted because it is an association
* with a transient unsaved entity instance.
*
* @author Gail Badner
*/
public class TransientPropertyValueException extends TransientObjectException {
private final String transientEntityName;
private final String propertyOwnerEntityName;
private final String propertyName;
/**
* Constructs an {@link TransientPropertyValueException} instance.
*
* @param message - the exception message;
* @param transientEntityName - the entity name for the transient entity
* @param propertyOwnerEntityName - the entity name for entity that owns
* the association property.
* @param propertyName - the property name
*/
public TransientPropertyValueException(
String message,
String transientEntityName,
String propertyOwnerEntityName,
String propertyName) {
super(message);
this.transientEntityName = transientEntityName;
this.propertyOwnerEntityName = propertyOwnerEntityName;
this.propertyName = propertyName;
}
/**
* Returns the entity name for the transient entity.
* @return the entity name for the transient entity.
*/
public String getTransientEntityName() {
return transientEntityName;
}
/**
* Returns the entity name for entity that owns the association
* property.
* @return the entity name for entity that owns the association
* property
*/
public String getPropertyOwnerEntityName() {
return propertyOwnerEntityName;
}
/**
* Returns the property name.
* @return the property name.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Return the exception message.
* @return the exception message.
*/
@Override
public String getMessage() {
return new StringBuilder( super.getMessage() )
.append( ": " )
.append( StringHelper.qualify( propertyOwnerEntityName, propertyName ) )
.append( " -> " )
.append( transientEntityName )
.toString();
}
}

View File

@ -36,6 +36,7 @@ import java.util.TreeSet;
import org.jboss.logging.Logger;
import org.hibernate.PropertyValueException;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.engine.internal.NonNullableTransientDependencies;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionImplementor;
@ -131,8 +132,9 @@ public class UnresolvedEntityInsertActions {
nonNullableTransientDependencies.getNonNullableTransientEntities().iterator().next();
String firstPropertyPath =
nonNullableTransientDependencies.getNonNullableTransientPropertyPaths( firstTransientDependency ).iterator().next();
throw new PropertyValueException(
throw new TransientPropertyValueException(
"Not-null property references a transient value - transient instance must be saved before current operation",
firstDependentAction.getSession().guessEntityName( firstTransientDependency ),
firstDependentAction.getEntityName(),
firstPropertyPath
);

View File

@ -34,6 +34,7 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.ReplicationMode;
import org.hibernate.TransientObjectException;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.event.spi.EventSource;
@ -377,10 +378,11 @@ public abstract class CascadingAction {
&& ForeignKeys.isTransient( childEntityName, child, null, session ) ) {
String parentEntiytName = persister.getEntityName();
String propertyName = persister.getPropertyNames()[propertyIndex];
throw new TransientObjectException(
"object references an unsaved transient instance - " +
"save the transient instance before flushing: " +
parentEntiytName + "." + propertyName + " -> " + childEntityName
throw new TransientPropertyValueException(
"object references an unsaved transient instance - save the transient instance before flushing",
childEntityName,
parentEntiytName,
propertyName
);
}
@ -393,7 +395,7 @@ public abstract class CascadingAction {
private boolean isInManagedState(Object child, EventSource session) {
EntityEntry entry = session.getPersistenceContext().getEntry( child );
return entry != null &&
return entry != null &&
(
entry.getStatus() == Status.MANAGED ||
entry.getStatus() == Status.READ_ONLY ||

View File

@ -31,6 +31,7 @@ import org.hibernate.JDBCException;
import org.hibernate.PropertyValueException;
import org.hibernate.Session;
import org.hibernate.TransientObjectException;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionImplementor;
@ -682,7 +683,7 @@ public class MultiPathCircleCascadeTest extends BaseCoreFunctionalTestCase {
}
}
else {
assertTrue( ex instanceof PropertyValueException );
assertTrue( ex instanceof TransientPropertyValueException );
}
}

View File

@ -0,0 +1,94 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
import java.io.Serializable;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
@Id
@GeneratedValue
private Long id;
@Basic
@Column(unique = true, updatable = false, length = 36, columnDefinition = "char(36)")
private String uuid;
@Column(updatable = false)
private Date created;
public AbstractEntity() {
super();
uuid = UUID.randomUUID().toString();
created = new Date();
}
public Long getId() {
return id;
}
public String getUuid() {
return uuid;
}
public Date getCreated() {
return created;
}
@Override
public int hashCode() {
return uuid == null ? 0 : uuid.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AbstractEntity ))
return false;
final AbstractEntity other = (AbstractEntity) obj;
if (uuid == null) {
if (other.uuid != null)
return false;
} else if (!uuid.equals(other.uuid))
return false;
return true;
}
public String toString() {
if (id != null) {
return "id: '" + id + "' uuid: '" + uuid + "'";
} else {
return "id: 'transient entity' " + " uuid: '" + uuid + "'";
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
/**
* No Documentation
*/
@javax.persistence.Entity
public class B extends AbstractEntity {
private static final long serialVersionUID = 325417243L;
/**
* No documentation
*/
@javax.persistence.OneToMany(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}
, mappedBy = "b")
private java.util.Set<G> gCollection = new java.util.HashSet<G>();
/**
* No documentation
*/
@javax.persistence.ManyToOne(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}
, optional = false)
private C c;
/**
* No documentation
*/
@javax.persistence.ManyToOne(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}
, optional = false)
private D d;
public java.util.Set<G> getGCollection() {
return gCollection;
}
public void setGCollection(
java.util.Set<G> parameter) {
this.gCollection = parameter;
}
public C getC() {
return c;
}
public void setC(C parameter) {
this.c = parameter;
}
public D getD() {
return d;
}
public void setD(D parameter) {
this.d = parameter;
}
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
import java.util.Set;
/**
* No Documentation
*/
@javax.persistence.Entity
public class C extends AbstractEntity {
private static final long serialVersionUID = 1226955752L;
@javax.persistence.OneToMany(mappedBy = "c")
private Set<B> bCollection = new java.util.HashSet<B>();
@javax.persistence.OneToMany(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}
, mappedBy = "c")
private Set<D> dCollection = new java.util.HashSet<D>();
public Set<B> getBCollection() {
return bCollection;
}
public void setBCollection(Set<B> bCollection) {
this.bCollection = bCollection;
}
public Set<D> getDCollection() {
return dCollection;
}
public void setDCollection(Set<D> dCollection) {
this.dCollection = dCollection;
}
}

View File

@ -0,0 +1,78 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
/**
* No Documentation
*/
@javax.persistence.Entity
public class D extends AbstractEntity {
private static final long serialVersionUID = 2417176961L;
@javax.persistence.OneToMany(mappedBy = "d")
private java.util.Set<B> bCollection = new java.util.HashSet<B>();
@javax.persistence.ManyToOne(optional = false)
private C c;
@javax.persistence.ManyToOne(optional = false)
private E e;
@javax.persistence.OneToMany(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH},
mappedBy = "d"
)
private java.util.Set<F> fCollection = new java.util.HashSet<F>();
public java.util.Set<B> getBCollection() {
return bCollection;
}
public void setBCollection(
java.util.Set<B> parameter) {
this.bCollection = parameter;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
public java.util.Set<F> getFCollection() {
return fCollection;
}
public void setFCollection(
java.util.Set<F> parameter) {
this.fCollection = parameter;
}
}

View File

@ -0,0 +1,52 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
/**
* No Documentation
*/
@javax.persistence.Entity
public class E extends AbstractEntity {
private static final long serialVersionUID = 1226955558L;
@javax.persistence.OneToMany(mappedBy = "e")
private java.util.Set<D> dCollection = new java.util.HashSet<D>();
@javax.persistence.ManyToOne(optional = true)
private F f;
public java.util.Set<D> getDCollection() {
return dCollection;
}
public void setDCollection(java.util.Set<D> dCollection) {
this.dCollection = dCollection;
}
public F getF() {
return f;
}
public void setF(F parameter) {
this.f = parameter;
}
}

View File

@ -0,0 +1,69 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
/**
* No Documentation
*/
@javax.persistence.Entity
public class F extends AbstractEntity {
private static final long serialVersionUID = 1471534025L;
/**
* No documentation
*/
@javax.persistence.OneToMany(cascade = {
javax.persistence.CascadeType.MERGE, javax.persistence.CascadeType.PERSIST, javax.persistence.CascadeType.REFRESH}
, mappedBy = "f")
private java.util.Set<E> eCollection = new java.util.HashSet<E>();
@javax.persistence.ManyToOne(optional = false)
private D d;
@javax.persistence.ManyToOne(optional = false)
private G g;
public java.util.Set<E> getECollection() {
return eCollection;
}
public void setECollection(
java.util.Set<E> parameter) {
this.eCollection = parameter;
}
public D getD() {
return d;
}
public void setD(D parameter) {
this.d = parameter;
}
public G getG() {
return g;
}
public void setG(G parameter) {
this.g = parameter;
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
/**
* No Documentation
*/
@javax.persistence.Entity
public class G extends AbstractEntity {
private static final long serialVersionUID = 325417437L;
@javax.persistence.ManyToOne(optional = false)
private B b;
@javax.persistence.OneToMany(mappedBy = "g")
private java.util.Set<F> fCollection = new java.util.HashSet<F>();
public B getB() {
return b;
}
public void setB(B parameter){
this.b = parameter;
}
public java.util.Set<F> getFCollection() {
return fCollection;
}
public void setFCollection(
java.util.Set<F> parameter) {
this.fCollection = parameter;
}
}

View File

@ -0,0 +1,333 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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.ejb.test.cascade.multicircle;
import javax.persistence.EntityManager;
import javax.persistence.RollbackException;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.FailureExpected;
/**
* 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
assertTrue( ise.getCause() instanceof TransientPropertyValueException );
TransientPropertyValueException tpve = ( TransientPropertyValueException ) 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
};
}
}