In Hibernate, an orphan entity refers to a child entity that has been disassociated from its parent. If these orphan entities are not explicitly removed, they remain in the database and result in unwanted data.
The orphanRemoval = true attribute on the mapping annotation on parent entity automatically removes the child entities from the database when they are removed from the collection in the parent entity.
See Also: How to delete an entity in Hibernate?
Do not use cascading removal with @ManyToMany associations. Cascading makes sense only in parent-child relationships. In a @ManyToMany association, generally, both entities are treated as parent.
1. Using ‘orphanRemoval‘ to Remove Orphan Entities
In the following example, Department is the parent entity, and Employee is the child entity. Note that the @ManyToOne annotation (in Item entity) is associated with the Cart class variable. The @JoinColumn annotation references the mapped column.
Setting ‘orphanRemoval = true’ tells Hibernate to automatically remove a child entity if it becomes orphaned.
In the following example, a Cart can have multiple Item entities associated with it.
//Parent entity
@Entity
@Data
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String ownerName;
@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
item.setCart(this); // Set this cart for the item
}
public void removeItem(Item item) {
items.remove(item);
item.setCart(null); // Remove the reference to this cart
}
}
//Child entity
@Entity
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "cart_id")
private Cart cart;
}
2. Demo
The following code demonstrates how to add or remove the child entities from a parent entity such that no orphans are left in the database.
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CartService {
@Autowired
private EntityManager entityManager;
@Transactional
public void removeItemFromCart(Long cartId, Long itemId) {
// Fetch the cart with the given ID
Cart cart = entityManager.find(Cart.class, cartId);
if (cart != null) {
Item itemToRemove = cart.getItems().stream()
.filter(item -> item.getId().equals(itemId))
.findFirst()
.orElse(null);
// Remove the item if it exists in the cart
if (itemToRemove != null) {
cart.removeItem(itemToRemove); // This will also handle orphan removal
entityManager.merge(cart); // Update the cart to reflect changes
System.out.println("Item removed from cart successfully.");
} else {
System.out.println("Item not found in the cart.");
}
} else {
System.out.println("Cart not found.");
}
}
}
The program output:
SELECT * FROM cart WHERE id = :cartId;
SELECT * FROM item WHERE cart_id = :cartId;
DELETE FROM item WHERE id = :itemId;
Note that using entityManager.merge(cart) statement is not necessary as the cart entity is already managed within the current persistence context.
When the cart.removeItem(itemToRemove) statement is executed, the changes to the cart (including any changes to its collection of items) will automatically be synchronized with the database when the transaction commits.
3. Removing Entities in Many-to-Many Association
In a many-to-many relationship, hibernate does not directly support orphan removal because there isn’t a straightforward way to determine when an entity should be considered “orphaned“.
However, we can handle orphan removal manually by removing the relationship between the entities and then deleting the entity if necessary.
For example, we have two entities: Student and Course, which are in a many-to-many relationship. A student can enroll in multiple courses, and a course can have multiple students.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private Set<Course> courses = new HashSet<>();
// Getters, setters, etc.
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
// Getters, setters, etc.
}
If we want to remove a Student from a Course, we should:
- Remove the Course from the Student’s
courses
set. - Also, remove the Student from the Course’s
students
set to ensure that the relationship is completely removed.
EntityManager entityManager = ...;
@Transactional
public void removeCourseFromStudent(Long studentId, Long courseId) {
Student student = entityManager.find(Student.class, studentId);
Course course = entityManager.find(Course.class, courseId);
if (student != null && course != null) {
student.getCourses().remove(course);
course.getStudents().remove(student);
entityManager.merge(student);
entityManager.merge(course);
}
}
4. Conclusion
In this Hibernate orphan removal example, we discussed the following:
- Orphan removal is a cascade operation that deletes child entities that are removed from a parent entity’s collection.
- To enable orphan removal, use the ‘orphanRemoval = true’ option in the relationship annotation (e.g., @OneToMany) on the parent entity.
- Perform orphan removal within a transactional context to ensure that all changes are committed to the database as a single unit of work.
- Be aware of the entity lifecycle in Hibernate. Detached entities must be merged to be updated in the persistence context.
Happy Learning !!
Comments