Simplified version of my problem is this: I have three entities (annotations also simplified):
@Entity
public class A
{
@Id
private int id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
private Collection<B> bs;
}
@Entity
public class B
{
@Id
private int id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
private Collection<C> cs;
}
@Entity
public class C
{
@Id
private short id;
private Object someProperty;
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && getClass() == obj.getClass())
{
final C other = (C) obj;
return id == other.id;
}
return false;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = 31 + id;
return result;
}
}
Now I need to persist these entities (to an empty DB). The entities are created as a result of parsing XML data, so it is not as simple as the following code, but in reality it is done like that (that is why I'm not reusing c1):
A a = new A;
a.setId(1);
Collection<B> bs = new ArrayList<B>();
B b1 = new B();
b1.setId(21);
Collection<C> cs1 = new ArrayList<C>();
C c1 = new C();
c1.setId(31);
c1.setOtherProperty(null);
cs1.add(c1);
b1.setCs(cs1);
B b2 = new B();
b2.setId(22);
Collection<C> cs2 = new ArrayList<C>();
C c2 = new C();
c2.setId(31); // notice this is the same id as c1
c2.setOtherProperty(null);
cs2.add(c2);
b2.setCs(cs2);
bs.add(b1);
bs.add(b2);
a.setBs(bs);
Now when I try to persist the newly created entity A via
entityManager.merge(a);
I get an exception
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.C(ID)"; SQL statement:
INSERT INTO PUBLIC.C (ID, SOMEPROPERTY) VALUES (?, ?) ...
In the SQL log, I can see that OpenJPA (which is the provider I'm using) is trying to insert a row with id 31 to table C two times. However, I would expect that c1 and c2 objects are treated as equal, and just one row is inserted. How can I possibly achieve that?