Removing an object in a Set not reflected in Hibernate 3
Environment
- Enterprise Application Platform (EAP)
- 5.0
- Hibernate
- 3.3.2
-
Domain Model being Parent 1 <--> * Child (bidirectional association)
Class Parent{ ... @OneToMany(mappedBy="parent", cascade=CascadeType.ALL) private Set<Child> children = new HashSet<Child>(); ... }
-
Process Java code being
Set<Child> children = parent.getChildren(); for (Child child : children) { int i = 1; if (i==1) { parent.getChildren().remove(child); i++; } } em.merge(parent); tx.commit();
Issue
- Removing an element from a collection when objects are detached doesn't work. Calling merge on the entity manager object doesn't reflect the changes in the database.
Resolution
There are many points to be careful with, detachment, owning side of an association and what exact behavior should be triggered when removing an element from a collection.
-
when using a bidirectional association only the owning side is responsible of the persistence aspect. However, in order to have a clean in-memory object tree, the developer must manage both sides of the association. Adding the following line is required:
parent.getChildren().remove(child); child.setParent(null);
-
merge cannot detect everything made during detachment. When only 2 classes are involved, that may look easy, however domain model are generally made of multiple associations between multiple classes. In this particular case, the developer must also call merge on the removed collection element and should not blindly rely on CASCADING.
em.merge(child); em.merge(parent);
However it is advised to avoid detachment state and always try to reattach the entities before changing value or associations
-
removing an element from a collection is the same as breaking the link between the 2 entities. This is reflected in the database by updating the foreign value to null. However, it is possible to set a composition semantic between the 2 entities, meaning that if an element is removed from a colleciton, the element cannot exist anymore and the mapped row should be DELETED. This can be achieved using Hibernate extension CascadeType.DELETE_ORPHAN
@OneToMany(mappedBy="profil", cascade=CascadeType.ALL) @org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN) ...
Root Cause
Misuse of the framework.
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.
Comments