Issue
I have a simple Spring Boot REST service that implements repository pattern with JPA and Hibernate.
When I delete the Parent entity, if an @Async method is still progressing on its Child entities, the Parent entity is duplicated by the end of @Async method.
Let say I have an Parent entity:
@Entity
public class Parent {
... // fields like id etc.
@JsonManagedReference
@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval = true)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> children = new ArrayList<>();
... // getters & setters etc.
}
and Child entity:
@Entity
public class Child {
... // fields like id etc.
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE })
@JoinColumn(name = "parent_id", nullable = false)
private Parent parent;
... // getters & setters etc.
}
I have an @Async method that updates children periodically (ie run every minute) and I've basic delete method on a @Service class.
@Async
public void updateAllChildren() {
List<Parent> parents = findAll();
for (Parent parent: parents) {
...
for (Child child : parent.getChildren()) {
updateChild(child, ...); // <--- time consuming method
}
}
}
@Async
public void updateChild(Child child, ...) {
// do some stuff
this.childRepository.save(child); // save changes
}
public void delete(Parent parent) {
parent.getChildren().clear();
this.parentRepository.delete(parent);
}
When a user requests to delete a Parent entity, and if updateAllChildren() method has not been completed yet;
- The parent entity is deleted.
- When children are updated, because the parent entity no longer exists, clones of parent entity are also created.
How can I avoid this issue?
Solution
Don't cascade MERGE
from the child to the parent. Since a merge will create a new entity if one doesn't exist, the deleted entity will be recreated by the cascading merge from the child entity.
Answered By - Kayaman
Answer Checked By - David Goodson (JavaFixing Volunteer)