Simple 1:n relationship (1:Parent n:Child) inserts childs twice on addition and merge on parent. This happens only with CascadeType.ALL (CascadeType.ALL merge works). For tests purpose only the Child.hashcode() and Child.equals() method throw an exception, to ensure that the a not invoked during the test.
Note: The tests uses the Spring 3.1.1 TransactionTemplate which only opens/closes/commits the session.
{code} @Test public void twiceInsert() { final Integer parentId = transactionTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus status) { Parent p = new Parent(); // merge with 0 childs, and assign Parent.id p = entityManager.merge(p);
return p.getId(); } });
transactionTemplate.execute(new TransactionCallback<Object>() { @Override public ObjectdoInTransaction(TransactionStatus status) { // reload parent wiht id, within THIS transaction Parent p = entityManager.find(Parent.class, parentId);
final Child A = new Child();
A.setParent(p); p.getChildren().add(A); // Expectation: assign id to the child A p = entityManager.merge(p);
return null; } });
transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { // again, load the Parent by id Parent p = entityManager.find(Parent.class, parentId);
// FAILURE: 1 expected, but 2 childs in List Assert.assertEquals(1, p.getChildren().size());
return 0; } }); }
@Entity public class Parent { @Id @GeneratedValue(strategy = AUTO) Integer id;
@OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL}) List<Child> children = new ArrayList<Child>();
public Integer getId() { return id; }
public List<Child> getChildren() { return children; }
public void setChildren(List<Child> children) { this.children = children; } }
@Entity public class Child { @Id @GeneratedValue(strategy = AUTO) Integer id;
@ManyToOne Parent parent;
public Integer getId() { return id; }
public Parent getParent() { return parent; }
public void setParent(Parent parent) { this.parent = parent; }
@Override public boolean equals(Object o) { throw new UnsupportedOperationException(); }
@Override public int hashCode() { throw new UnsupportedOperationException(); } } {code} |
|