When trying to persist an entity that references another new entity via a one2many AND a many2one relation a not-null property references a null or transient value is thrown. This happens even if cascading options are specified.
Hibernate version:
Hibernate 3.2CR2, Hibernate Annotations 3.1CR1, Hibernate Entity Manager 3.1 CR1
Full stack trace of any exception that occurs:
Code:
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: test.ManyToOneAndOneToManyTest$Address.contact
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:567)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:192)
at test.ManyToOneAndOneToManyTest.persist(ManyToOneAndOneToManyTest.java:62)
at test.ManyToOneAndOneToManyTest.testManyToOneAndOneToMany2(ManyToOneAndOneToManyTest.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:457)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:670)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: test.ManyToOneAndOneToManyTest$Address.contact
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:284)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:611)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:603)
at org.hibernate.engine.CascadingAction$8.cascade(CascadingAction.java:202)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:213)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:157)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:108)
at org.hibernate.engine.Cascade.cascade(Cascade.java:248)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:412)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:261)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:38)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:620)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:594)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:598)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:186)
... 20 more
TestCaseCode:
package test;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
import junit.framework.TestCase;
import org.hibernate.annotations.GenericGenerator;
public class ManyToOneAndOneToManyTest extends TestCase
{
@javax.persistence.Entity
public static class Address
{
@ManyToOne(optional = false, fetch = FetchType.EAGER)
Contact contact;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
String id;
}
@javax.persistence.Entity
public static class Contact
{
@OneToMany(mappedBy = "contact", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
@org.hibernate.annotations.OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
List<Address> addresses = new ArrayList<Address>();
@ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, optional = true)
Address defaultDeliveryAddress;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
String id;
}
private EntityManagerFactory emf;
private void persist(Object e) throws RuntimeException
{
EntityManager em = emf.createEntityManager();
EntityTransaction tx = null;
try
{
tx = em.getTransaction();
tx.begin();
em.persist(e);
tx.commit();
}
catch (RuntimeException ex)
{
if (tx != null && tx.isActive()) tx.rollback();
throw ex;
}
finally
{
em.close();
}
}
protected void setUp() throws Exception
{
emf = Persistence.createEntityManagerFactory("manager1");
}
protected void tearDown() throws Exception
{
emf.close();
}
/**
* WORKS
* persisting contact first and address separately
*/
public void testManyToOneAndOneToMany1()
{
Contact contact = new Contact();
persist(contact);
Address address = new Address();
address.contact = contact;
contact.addresses.add(address);
contact.defaultDeliveryAddress = address;
persist(address);
}
/**
* DOES NOT WORK
* persisting contact and cascade to address
*/
public void testManyToOneAndOneToMany2()
{
Contact contact = new Contact();
Address address = new Address();
address.contact = contact;
contact.addresses.add(address);
contact.defaultDeliveryAddress = address;
// exception is thrown here
persist(contact);
}
}
Any help, comments and suggestions are appreciated.
Johan