2

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.

Chris
  • 409
  • 3
  • 17
  • Have you assigned both person objects the same ID, and implemented both `Person.equals()` and `Person.hashCode()`? – Thomas Timbul Jun 22 '18 at 09:37
  • Sorry, yes they are identical objects (this is garaunteed from the source of the XML), and I have implemented hashCode() and equals() using project lombok's `@EqualsAndHashCode' annotation. – Chris Jun 22 '18 at 09:43
  • were you able to find the solution to your problem? – mia Jan 10 '22 at 23:02

0 Answers0