Following example is simplified.
I have following database structure:
X
-xid VARCHAR
Y
-yid VARCHAR
-xid VARCHAR
-zid VARCHAR
Z
-zid VARCHAR
I have following Entity structure
@Entity
@Table(name = "X")
public class EntityX {
@Id
@Column(name = "xid")
private String xid;
@OneToMany(... mappedBy = "x", cascadeType.ALL, orphanRemoval=true)
private List<EntityY> yList = new ArrayList();
...
}
@Entity
@Table(name = "Y")
public class EntityY {
@Id
@Column(name = "yid")
private String yid;
@ManyToOne(cascadeType.ALL)
private x;
@ManyToOne(cascadeType.ALL)
private z;
...
}
@Entity
@Table(name = "Z")
public class EntityZ {
@Id
@Column(name = "zid")
private String zid;
}
My save method is:
X x = new X();
x.setXid(1);
Z z = new Z();
z.setZid(1);
Z z2 = new Z();
z2.setZid(2);
Y y = new Y();
y.setYid(1);
y.setX(x);
y.setZ(z);
Y y2 = new Y();
y2.setYid(2);
y2.setX(x);
y2.setZ(z2);
List<Y> yList = new ArrayList<Y>();
yList.add(y);
yList.add(y2);
x.setYList(yList);
entityManager.persist(x);
The save is working perfectly and sqls are:
insert into X (XID) values (1);
insert into Z (ZID) values (1);
insert into Y (YID, XID, ZID) values (1, 1, 1);
insert into Z (ZID) values (2);
insert into Y (YID, XID, ZID) values (2, 1, 2);
My update method is:
X x = new X();
x.setXid(1);
Z z = new Z();
z.setZid(1);
Z z3 = new Z();
z3.setZid(3);
Y y = new Y();
y.setYid(1);
y.setX(x);
y.setZ(z);
Y y3 = new Y();
y3.setYid(3);
y3.setX(x);
y3.setZ(z3);
List<Y> yList = new ArrayList<Y>();
yList.add(y);
yList.add(y3);
x.setYList(yList);
entityManager.merge(x);
The update does following sqls:
insert into Z (ZID) values (3);
insert into Y (YID, XID, ZID) values (3, 1, 3);
delete from Y where XID=1; Why?
delete from Z where XID=1; Why?
delete from Y where XID=2;
delete from Z where XID=2;
insert into Z (ZID) values (1); Why?
insert into Y (YID, XID, ZID) values (1, 1, 1); Why?
Why Y (YID=1) and Z (ZID=1) are deleted and after they are inserted? This is a performance problem if there are for example 100000 times Y and Z which are not changed.
When you are trying to update, do not try to create the entity manually again and perform the merge. You end up having problems when you have cascading options set on your related entities like above.
During the merge, the persistence context retrieves the x(xid=1) entity along with the OneToMany list. You newly created X entity though has a new instance of list. Now persistence provider takes under consideration the orphanRemoval flag and decides that the previous state is not valid any removes all the content of Y with xid=1 (as they are not present in the new list).
Then it inserts the newly added elements in the new List.
I would recommend fetching the X with xid=1 first and then performing the update on a persistence-context managed entity:
X x = session.get(X.class, 1);
...
List<Y> yList = x.getYList();
yList.add(y);
yList.add(y3);
entityManager.merge(x);
all within one transactional method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With