Hi all,
I'm quite new to (Open)JPA and am experiencing unexpected behavior of the
merge() operation. I'm dealing with a parent and child entity that are
defined as follows:
//Parent:
@Entity
@Table(name="TEST_PARENT")
public class TestParent {
@Id
@Column(name="ID", nullable=false)
private String id = new String();
@Column(name="name")
private String name = new String();
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER,
cascade={CascadeType.ALL})
private Set<TestChild> children = new HashSet<TestChild>();
@Override
public boolean equals(Object other) {
if (other != null && other instanceof TestParent) {
return ((TestParent)other).getId().equals(this.id);
}
return false;
}
@Override
public int hashCode() {
return id.hashCode();
}
// skipping getters/setters
public void addChild(TestChild c) {
this.children.add(c);
c.setParent(this);
}
}
//Child:
@Entity
@Table(name="TEST_CHILD")
public class TestChild {
@Id
@Column(name="ID", nullable=false)
private String id = new String();
@Column(name="NAME")
private String name = new String();
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="PARENT_ID", nullable=false)
private TestParent parent = new TestParent();
@Override
public boolean equals(Object other) {
if (other != null && other instanceof TestChild) {
return ((TestChild)other).getId().equals(this.id);
}
return false;
}
@Override
public int hashCode() {
return id.hashCode();
}
//Skipping getters/setters
}
The class testing the merge() behavior looks as follows:
public class PCTester {
private EntityManager em = null;
public PCTester() {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(
"testFactory");
em = emf.createEntityManager();
}
public void mergeParent(TestParent p) {
em.getTransaction().begin();
p = em.merge(p);
em.getTransaction().commit();
}
public static void main(String[] args) {
TestParent p = new TestParent();
p.setId("1");
p.setName("parent-1-update");
TestChild c = new TestChild();
c.setId("1");
c.setName("child-1-update");
p.addChild(c);
PCTester t = new PCTester();
t.mergeParent(p);
}
}
Finally, the entity manager is configured as follows in persistence.xml:
<persistence-unit name="testFactory">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>com.lmco.jsf.alis.obphm.afrs.session.model.TestParent</class>
<class>com.lmco.jsf.alis.obphm.afrs.session.model.TestChild</class>
<properties>
<property name="openjpa.ConnectionURL"
value="jdbc:oracle:thin:@PROPRIETARY"/>
<property name="openjpa.ConnectionDriverName"
value="oracle.jdbc.OracleDriver"/>
<property name="openjpa.ConnectionUserName"
value="PROPRIETARY"/>
<property name="openjpa.ConnectionPassword"
value="PROPRIETARY"/>
<property name="openjpa.Log" value="SQL=TRACE"/>
<property name="openjpa.ConnectionFactoryProperties"
value="PrettyPrint=true, PrettyPrintLineLength=72"/>
<property name="openjpa.jdbc.SchemaFactory"
value="native(foreignKeys=true)" />
</properties>
</persistence-unit>
In case the database already contains parent and child records with id "1",
the test runs without problems and both parent and child names are correctly
updated. The SQL trace shows the following statements being executed:
SELECT t0.NAME, t1.PARENT_ID, t1.ID, t1.NAME
FROM AFRS_USER.TEST_PARENT t0, AFRS_USER.TEST_CHILD t1
WHERE t0.ID = ? AND t0.ID = t1.PARENT_ID(+)
ORDER BY t1.PARENT_ID ASC
[params=(String) 1]
UPDATE AFRS_USER.TEST_PARENT
SET NAME = ?
WHERE ID = ?
[params=(String) parent-1-update, (String) 1]
UPDATE AFRS_USER.TEST_CHILD
SET PARENT_ID = ?, NAME = ?
WHERE ID = ?
[params=(String) 1, (String) child-1-update, (String) 1]
However, when parent and child do not exist in the database, the merge()
operation throws an exception because the database tries to insert NULL into
the parent table for reasons unknown to me:
SELECT t0.NAME, t1.PARENT_ID, t1.ID, t1.NAME
FROM AFRS_USER.TEST_PARENT t0, AFRS_USER.TEST_CHILD t1
WHERE t0.ID = ? AND t0.ID = t1.PARENT_ID(+)
ORDER BY t1.PARENT_ID ASC
[params=(String) 1]
SELECT t0.NAME, t1.ID, t1.NAME
FROM AFRS_USER.TEST_CHILD t0, AFRS_USER.TEST_PARENT t1
WHERE t0.ID = ? AND t0.PARENT_ID = t1.ID(+)
[params=(String) 1]
// Unexpected!!
SELECT t0.NAME, t1.PARENT_ID, t1.ID, t1.NAME
FROM AFRS_USER.TEST_PARENT t0, AFRS_USER.TEST_CHILD t1
WHERE t0.ID = ? AND t0.ID = t1.PARENT_ID(+)
ORDER BY t1.PARENT_ID ASC
[params=(String) ]
INSERT INTO AFRS_USER.TEST_PARENT (ID, NAME)
VALUES (?, ?)
[params=(String) 1, (String) parent-1-update]
INSERT INTO AFRS_USER.TEST_CHILD (ID, PARENT_ID, NAME)
VALUES (?, ?, ?)
[params=(String) 1, (String) 1, (String) child-1-update]
// Unexpected!!
INSERT INTO AFRS_USER.TEST_PARENT (ID, NAME)
VALUES (?, ?)
[params=(String) , (String) ]
Leading to
Exception in thread "main" <openjpa-1.0.0-r420667:568756 fatal store error>
org.apache.openjpa.persistence.RollbackException: The transaction has been
rolled back. See the nested exceptions for details on the errors that
occurred.
Etc...
When creating a new parent only (i.e. without children) in the test method,
it is correctly persisted into the database with the merge() operation, so
it seems that something goes wrong with cascading the merge to the child
object.
Does anyone know what's the reason for the unexpected SQL statements?
Any help is highly appreciated.
Thanks,
Jonatan Samoocha
Using:
OpenJPA 1.0.0
JDK 1.5.0_14
Oracle 10G
--
View this message in context:
http://n2.nabble.com/Unexpected-merge-cascade-behavior-tp1668355p1668355.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.