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.
@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();
} }
}
|