问题
TL;DR: Is it possible to configure Hibernate to delete all child objects using a single delete query?
Full Question: I have the following parent/child association defined in Hibernate 5.1:
public class Parent {
@OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<Child> children;
}
public class Child {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "parent_id", nullable = false)
private Parent parent;
}
When I delete the parent object, all of the child objects are deleted, as expected, but each one is deleted individually. In my application a parent could have many thousands of children, so for performance reasons I need to use a single query to delete them all at once.
Possible Workarounds
- Manually execute my own HQL query,
DELETE FROM child WHERE parent_id = ?
, prior to deleting the parent. The downside here is that I (and any other developers) have to remember to call the method. Plus, it essentially circumvents the cascade delete. - Allow the cascade delete to happen at the database level. Since data is changing behind the scenes, I assume I would need to remember to manually
.clear()
the child collection to prevent disparity between Hibernate and the database.
Edit: I see older versions of Hibernate used to have the concept of a one-shot delete but I cannot find anything similar in the documentation for the latest version. Has that functionality been removed?
回答1:
- Say you delete a Parent.
- If some other table, say GrandChild, has a foreign key constraint with Child, with each Child having multiple GrandChild records, they need to be removed too.
- The reason for multiple generated delete queries is, Hibernate can not know whether there are any foreign key constraints from other tables, to the "Child" table. Also there might be some call back methods that needs to executed such as @PreRemove. This is the reason why each Child reference is loaded from the database and removed individually.
- In short, if you depend on Hibernate to manage your entities, this is the default behavior.
Either set the ON DELETE CASCADE in the data base explicitly, or mark the required Child entity with the annotation @org.hibernate.annotations.OnDelete, which automatically adds the "on delete" to schema during the schema generation. For your example it would be,
`@OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", cascade = CascadeType.REMOVE) @org.hibernate.annotations.OnDelete( action = @org.hibernate.annotations.OnDeleteAction.CASCADE) private List<Child> children;
回答2:
This may seem overly simple, so perhaps I'm not understanding your question correctly, but:
Parent parent = parentRepository.findByName("dad");
parent.getChildren().clear();
entityManager.save(parent);
If only we could .clear() our children, just like that. Perhaps better to soft delete them incase we change our minds :)
来源:https://stackoverflow.com/questions/35583403/hibernate-delete-all-children-with-one-query