Say I have two entities, Book
and Person
. Book
contains two Person
fields, one for the Author and one for the Editor. The relationships are @ManyToOne
on the Book
side:
@EqualsAndHashCode
@Entity
public class Book {
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Person author;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Person editor;
...
}
@EqualsAndHashCode
@Entity
public class Person {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Book authored;
@OneToMany(mappedBy = "editor", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Book edited;
...
}
If I receive an XML representation of a detached Book
, containing a detached Person
as a sub-entity, I can persist the book using a spring JpaRepository
and the person is also persisted.
However, in the case that one Person
both authors and edits the same Book
;
Book: The Very Hungry Caterpillar
Author: Eric Carle
Editor: Eric Carle
I would get the exception:
java.lang.IllegalStateException: Multiple representations of the same entity [my.project.Person#1] are being merged. Detached: [my.project.Person@4729256a]; Detached: [my.project.Person@56ed25db]
I can 'solve' this by removing CascadeType.PERSIST
from the relationships. However this would result in an EntityNotFoundException
:
EntityNotFoundException: Unable to find my.project.Person with id 759.
This can be furthered by changing the JPA annotation argument CascadeType.MERGE
to a Hibernate annotation:
@OneToMany(fetch=FetchType.LAZY)
@Cascade({CascadeType.SAVE_UPDATE})
private Person author;
...
I believe this will allow Hibernate to effectively choose whether to merge or persist a sub-entity, based on its persistence state. This will however not result in the duplicate Person
objects being persisted. I will then get a JpaObjectRetrievalFailureException:
JpaObjectRetrievalFailureException: Unable to find my.project.Person 759.
This can be worked around by adding another annotation:
@OneToMany(fetch=FetchType.LAZY)
@Cascade({CascadeType.SAVE_UPDATE})
@NotFound(action = NotFoundAction.IGNORE)
private Person author;
...
However this does not solve the issue, as the Person
object will still need to be persisted manually.
Is it at all possible to persist two representations of a detached sub-entity using JPA?
Edit: hashCode() and equals() are implemented, and both Person
objects evaluate to be equal.