cancel
Showing results for 
Search instead for 
Did you mean: 

JPA: trouble removing a 1:M cascade ALL relationship from entity

0 Kudos

I have an entity A, with a OneToMany relationship to entity B, and cascade set to ALL. Entity B has a ManyToOne relationship to A. In my scenario I create a new instance of B to a detached instance of A, and I add it to the 1:M of A and set the M:1 on B.

Then I'd like to merge the changes on A using EntityManager.merge(A). However, before I do that I need to remove a different (old) instance of B from A, and then merge my changes so that my new instance of B is persisted.

I've tried a couple different scenarios and get different results (none favorable):

1. First lookup the instance of B I want to remove via EntityManager.find(id), and then call EM.remove(B). And then I call EM.merge(A) on the instance of A with my new B. I get the following IllegalArgumentException during the merge:

java.lang.IllegalArgumentException: Cannot fulfill merge operation: Relationship >>outputElementInstances<< of the target entity {com.sap.eventus.wire.entity.EvtWireInstance(id=85e83543-45d1-49cd-bb90-8688699ca0d4)} has been modified

The relationship it's talking about is actually a different (but related) one that was affected when the old instance of B was removed. It seems reasonable that JPA has determined that my instance of A with the new B is no longer in sync with the database and is complaining that a relationship has been changed.

Any ways around that?

2. I also tried simply removing B from A's 1:M, and setting B's M:1 to null, and then merging in hopes that would trigger a delete on the old B. However the old B is not removed.

I was expecting this to trigger a delete, however, I realize that there are other relationships from B to other objects (such as a 1:M cascade ALL) that I did not remove which I suspect might cause JPA to not delete the old B. Is this true?

3. And finally I tried 1 & 2 together, first calling EM.remove(old B), and then removing the old B from A and then merging, which yields the following IllegalStateException:

java.lang.IllegalStateException: Relationship >>wireInstance<< of entity {com.sap.eventus.wire.entity.EvtElementInstance(id=08ca6253-1385-45dd-9173-978ee298b55f)} contains the new entity {com.sap.eventus.wire.entity.EvtWireInstance(id=c9fbd4e7-5575-4cc9-9b7d-2a8549c43574)}

Which is another different (but related) relationship off of object B.

Basically, after all this, my question is: what is the correct way to remove a 1:M relationship and add a new one? Simple enough, however I do not want to remove the old 1:M, re-query to get the new updated parent object and then add the new 1:M, as it's a fairly involved in creating and adding the new 1:M and it must be done BEFORE removing the old 1:M.

Thanks!

Accepted Solutions (0)

Answers (2)

Answers (2)

0 Kudos

Okay, I'll try out your suggestions.

Thanks.

adrian_goerler
Active Participant
0 Kudos

Hi Derek,

in order to better understand your issue and to match it with the error messages you observed, it would be helpful to know the names to the entities A and B and the names of the relationship fields. Could you provide us with the the relevant snippets from your entities?

-Adrian

0 Kudos

Sure.

A is EvtWireDefinition B is EvtWireInstance

Relationships:

EvtWireDefinition 1:M EvtWireInstance - wireInstances EvtWireInstance M:1 EvtWireDefinition - wireDefinition

There are also the following relationships:

EvtWireInstance 1:M EvtOutputElementInstance - outputElementInstances EvtOutputElementInstance M:1 EvtWireInstance - wireInstance EvtWireInstance 1:1 EvtInputElementInstance - inputElementInstance EvtInputElementInstance 1:1 EvtWireInstance - wireInstance

EvtElementInstance is a base class for EvtOutputElementInstance and EvtInputElementInstance

There are a few other relationships I left out that are not mentioned in the stack traces. Let me know if you need any more info.

adrian_goerler
Active Participant
0 Kudos

Hi Derek,

I understand your task as follows:

You are having three entities A, B_old and B_new. Within one transaction you want to do the following:

- remove B_old from the database

- remove B_old from the 1:n relationship A.bs

- persist the B_new

- add B_new to the 1:n relationship A.bs

- set the n:1 relationship B_new.a to refer to A

well, I suggest that you do this without a merge:

- em.remove(B_old) // you have to do this explicitly as JPA 1 does not have automatic orphan removal

- A.bs.remove(B_old)

- em.persist(B_new) // also, you have to make sure that the entities related to B_new are managed upon commit

- A.bs.add(B_new)

- B_new.a = A

I hope this works.

Regarding your observations:

1. The error message is not self-explaining. We have got to improve it ;-).

2. You have to remove the B_old explicitly as JPA 1 does not have automatic orphan removal.

3. This message should be self-explaining. Maybe there is one of the relationships EvtOutputElementInstance.wireInstance or EvtInputElementInstance.wireInstance still referring B_new. You'll surely find out.

Regards,

Adrian