JPA Hibernate one to many batch insert
conside 开发者_运维知识库this scenario:
class Parent {
private Integer id;
private String name;
@oneToMany(mappedBy="father")
private List<Child> children;
... ...
}
class Child {
private Integer id;
private String name;
@ManyToOne (optional="false")
@JoinColumn(name="id")
private Parent father;
........
}
Class MyParentService{
public Parent parentService(List<Child> childList){
em.getTransaction.begin();
Parent parent = new Parent();
parent.setChildren(childList);
em.persist(parent);
em.getTransaction.commit();
}
}
I will get an Exception of org.hibernate.PropertyValueException: not-null property references a null or transient
because the attribute father is "optional=false"
so I have to replace parent.setChildren(childList);
and do a loop like this in parentService() :
for(Child c: childList){
c.setFather(parent.getId());
parent.getChildrent().add(c);
}
is this correct? is there a better way to do without looping childList again?
When working with Hibernate, it's your responsibility to maintain consistent state of both sides of bidirectional relationship. Moreover, when persisting a relationship, Hibernate looks at the owning side (the side without mappedBy
), so you code wouldn't be correct even without optional=false
.
You can use the following approach to ensure consistensy:
class Parent {
...
public void addChild(Child c) {
children.add(c);
c.setParent(this);
}
}
public Parent parentService(List<Child> childList) {
...
for (Child c: childList) {
parent.addChild(c);
}
...
}
In this case visibility of setChildren()
can be restricted to avoid erroneous calls.
Aslo, it looks strange that you don't have cascading and don't persist children inside the same transaction with the parent.
I think it's good thing to do - e.g. in unit tests you may not use hibernate which automagically could set "father", but code should still work.
精彩评论