Hibernate: update on parent-child relationship causes duplicate children
- by TimmyJ
I have a parent child relationship in which the parent has a collection of children (a set to be specific). The child collection is setup with cascade="all-delete-orphan". When I initially save the parent element everything works as expected. However, when I update the parent and save again, all the children are re-saved.
This behavior leads me to believe that the parent is losing its reference to the collection of children, and therefore when persisting all the children are re-saved. It seems the only way to fix this is to not use the setter method of this child collection, but unfortunately this setter is called implicitly in my application (Spring MVC is used to bind a multi-select form element to this collection, and the setter is called by spring on the form submission). Overwriting this setter to not lose the reference (ie, do a colleciton.clear() and collection.addAll(newCollection) rather than collection = newCollection) is apparently a hibernate no-no, as is pointed out here: https://forum.hibernate.org/viewtopic.php?t=956859
Does anyone know how to circumvent this problem? I've posted some of my code below.
The parent hibernate configuration:
<hibernate-mapping package="org.fstrf.masterpk.domain">
<class name="ReportCriteriaBean" table="masterPkReportCriteria">
<id name="id" column="id">
<generator class="org.hibernate.id.IncrementGenerator" />
</id>
<set name="treatmentArms" table="masterPkTreatmentArms"
sort="org.fstrf.masterpk.domain.RxCodeComparator" lazy="false" cascade="all-delete-orphan" inverse="true">
<key column="runid"/>
<one-to-many class="TreatmentArm"/>
</set>
</class>
</hibernate-mapping>
The parent object:
public class ReportCriteriaBean{
private Integer id;
private Set<TreatmentArm> treatmentArms;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Set<TreatmentArm> getTreatmentArms() {
return treatmentArms;
}
public void setTreatmentArms(Set<TreatmentArm> treatmentArms) {
this.treatmentArms = treatmentArms;
if(this.treatmentArms != null){
for(TreatmentArm treatmentArm : this.treatmentArms){
treatmentArm.setReportCriteriaBean(this);
}
}
}
The child hibernate configuration:
<hibernate-mapping package="org.fstrf.masterpk.domain">
<class name="TreatmentArm" table="masterPkTreatmentArms">
<id name="id" column="id">
<generator class="org.hibernate.id.IncrementGenerator" />
</id>
<many-to-one name="reportCriteriaBean" class="ReportCriteriaBean" column="runId" not-null="true" />
<property name="rxCode" column="rxCode" not-null="true"/>
</class>
</hibernate-mapping>
The child object:
public class TreatmentArm {
private Integer id;
private ReportCriteriaBean reportCriteriaBean;
private String rxCode;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public ReportCriteriaBean getReportCriteriaBean() {
return reportCriteriaBean;
}
public void setReportCriteriaBean(ReportCriteriaBean reportCriteriaBean) {
this.reportCriteriaBean = reportCriteriaBean;
}
public String getRxCode() {
return rxCode;
}
public void setRxCode(String rxCode) {
this.rxCode = rxCode;
}
}