In a Spring MVC application using hibernate and jpa over a MySQL database, I am getting the following error message about a child entity whenever I try to save a parent enti
First of all there are some things to clear out:
You have a bidirectional association between HL7GeneralCode(the parent) and HL7Address (the child). If the HL7GeneralCode.addresses is the "inverse" side (mappedBy) then why the owning side HL7Address.use has insertable/updatable false? The owning side should control this association so you should remove the insertable/updatable=false flags.
It always makes sense to cascade from the Parent to the Child, not the other way around. But in your use case, you try to persist the Child and automatically persist the Parent too. That's why the CASCADE.ALL on the many to one end doesn't make sense.
When using bidirectional associations, both sides are mandatory to be set:
HL7Address addr = new HL7Address();
HL7GeneralCode code = new HL7GeneralCode();
...
code.getAddresses().add(addr);
addr.setUse(code);
The persist operation is meant to INSERT transient entities, never to merge them or reattach entities. This implies that both the HL7Address and the HL7GeneralCode are new entities when you call your service method. If you have already saved a HL7GeneralCode with the same ID, you will get the primary key constraint violation exception.
If the HL7GeneralCode is possible to exist, then you should fetch it from db.
HL7GeneralCode code = em.find(HL7GeneralCode, pk);
HL7Address addr = new HL7Address();
if(code != null) {
code = new HL7GeneralCode();
em.persist(code);
}
code.getAddresses().add(addr);
addr.setUse(code);
em.persist(addr);
UPDATE
The HL7Address address doesn't override equals/hashCode so the default object same reference check rule applies. This will ensure we can add/remove addresses from the code.addresses List. In case you change your mind later, make sure you implement equals and hashCode properly.
Although not related to your issue, you might want to use getter/setter instead of making your fields public. This provides better encapsulation and you will avoid mixing setters with public field access.
The savehl7Address method:
@Override
public void savehl7Address(HL7Address addr) {
HL7GeneralCode code = addr.use();
if(code != null && code.getId()==null){
//HL7GeneralCode is not persistent. We don't support that
throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
//In case you'd want to support it
//code = em.find(HL7GeneralCode, code.getId());
}
//Merge the code without any address info
//This will ensure we only reattach the code without triggering the address
//transitive persistence by reachability
addr.setUse(null);
code.getAddresses().remove(addr);
code = em.merge(code);
//Now set the code to the address and vice-versa
addr.setUse(code);
code.getAddresses().add(addr);
if ((Integer)addr.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
em.persist(addr);
}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
addr = em.merge(addr);
}
}