Issue: While removing an entry from collection which was loaded initially and adding back to collection with same key. We see only "insert" statement and no "delete" statement which results in primary key constraint violation.
Hibernate version: 3.2.5
Oracle version: 10
Table structure:
------------------
Table Name: PARENT
Column PK(primary key) Data Type
------------------------------------------------------
PID Yes(1) number not null
NAME varchar2
version number
Table Name: CHILD (composite primary key: CID and PID)
Column PK(primary key) Data Type
------------------------------------------------------
CID Yes(1) number not null
PID Yes(2) number not null
NAME varchar2
version number
Hibernate mapping
----------------------
parent.hbm.xml
------------------
<hibernate-mapping package="com.xxx">
<class name="ParentImpl" table="PARENT" optimistic-lock="version">
<id name="pid" column="PID" type="long">
<generator class="foreign">
<param name="property">parent</param>
</generator>
</id>
<version name="version" column="VERSION" type="integer" unsaved-value="null" />
......
......
<bag name="childList" cascade="all-delete-orphan" lazy="false" inverse="true">
<key column="PID" />
<one-to-many class="com.xxx.ChildImpl" />
</bag>
......
......
</class>
</hibernate>
child.hbm.xml
----------------
<hibernate-mapping package="com.xxx">
<class name="ChildImpl" table="CHILD" optimistic-lock="version">
<composite-id>
<key-many-to-one name="parent" column="PID" class="com.xxx.ParentImpl" />
<key-property name="cid" column="CID type="string" />
</composite-id>
<version name="version" column="VERSION" type="integer" unsaved-value="null" />
......
......
</class>
</hibernate>
Java Class
-----------
public class ParentImpl {
private List<ChildImpl> childList;
public List<ChildImpl> getChildList() {
return childList;
}
public void setChildList(List<ChildImpl> childList) {
if (childList != null && childList.size() > 0) {
for (ChildImpl child: childList) {
if(child != null){ ((ChildImpl)child).setParent(this);
}
}
}
this.fedDeliveryList = fedDeliveryList;
}
public void setChild(int index, ChildImpl child) {
if (childList != null && XUtil.isIndexValid(index, childList.size())) {
childList.set(index, fedDelivery);
} else {
// index is not valid or list is null
childList = XUtil.addElement(childList, child);
}
if (child != null){
((ChildImpl)child).setParent(this);
}
}
}
ChildImpl.java
-----------------
public class ChildImpl {
.....
....
public boolean equals(Object other) {
if (!(other instanceof ChildImpl)) return false;
ChildImpl castOther = (ChildImpl)other;
return new EqualsBuilder()
.append(this.getPid(), castOther.getPid())
.append(this.getCid(), castOther.getCid())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(getPid())
.append(etCid())
.toHashCode();
}
}
Client Code
--------------
public class TestClient {
public static void main(String[] args) {
// 0. loading parent object from database
ParentService service = (ParentService) ServiceLocator.getParentService();
final ParentImpl parent = service.getParent(232L);
// 1. modifying child collection in parent object
modifyChildList(parent);
// 2. save the object
service.saveParent(parent);
}
private static void modifyChildList(ParentImpl parent) {
List<ChildImpl> childList = parent.getChildList();
if (childList != null) {
//.....
// for testing always removing 2nd index.. and also code is takes care the second index is present..
childList.remove(2);
}
ChildImpl newChild = new ChildImpl();
newChild.setPid(232L);
newChild.setCid("3");
parent.setChild(childList.size(), newChild);
}
}
Sample data for error scenario
-----------------------------------
child data in database(TABLE: CHILD)
CID PID VERSION
1 232 6
2 232 8
3 232 2
After loading the parent object using hibernate, the initial list collection has above 3 entries. The child CID: 3(index 2) is removed from collection as shown in client code and added back with new child object with same
details which was deleted earlier.
I was expecting hibernate to issue "delete" and "insert".But instead it fires "insert" and results in primary key constraint violation.
Note: It works well when i add different key element which is different to deleted prior. Hibernate issues "delete" and "insert".
The child collection object has equals and hashCode methods overrides default as shown in ChildImpl.java
I would really appreciate your help on this.
Thanks
Prem
|