orphanRemoval
is a boolean property of the @OneToMany
and @OneToOne
annotations in JPA/Hibernate.false
.BlogPost
or FoodItem
) is removed from its parent’s collection (e.g., Category.posts
or Restaurant.foodItems
), orphanRemoval = true
ensures the orphaned entity is deleted from the database.cascade = CascadeType.ALL
(which includes CascadeType.REMOVE
), removing a child entity (e.g., BlogPost
from Category
) using a helper method (e.g., removeBlogPost
) does not delete the child record from the database. Instead, Hibernate nullifies the foreign key (FK).CascadeType.REMOVE
only deletes child entities when the parent entity is deleted, not when a child is removed from the parent’s collection.orphanRemoval = true
instructs Hibernate to delete orphaned child entities (e.g., BlogPost
or FoodItem
) when they are removed from the parent’s relationship (e.g., using removeBlogPost
or removeFoodItem
).Category
↔ BlogPost
Configuration:
@Entity
public class Category extends BaseEntity {
private String categoryName;
@OneToMany(mappedBy = "chosenCategory", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BlogPost> posts = new ArrayList<>();
// Helper method
public void removeBlogPost(BlogPost post) {
posts.remove(post);
post.setChosenCategory(null);
}
// Getters and Setters
}
@Entity
public class BlogPost extends BaseEntity {
private String title;
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private Category chosenCategory;
// Getters and Setters
}
Behavior:
cascade = CascadeType.ALL
: Deleting a Category
deletes all associated BlogPost
entities.orphanRemoval = true
: Removing a BlogPost
from category.posts
(e.g., via removeBlogPost
) deletes the BlogPost
record from the database, not just nullifies the FK (category_id
).Restaurant
↔ FoodItem
Since the previous queries referenced the Food Delivery App, let’s apply orphanRemoval
to Restaurant
↔ FoodItem
:
@Entity
public class Restaurant extends BaseEntity {
private String name;
private String address;
private String city;
private String description;
@OneToMany(mappedBy = "chosenRestaurant", cascade = CascadeType.ALL, orphanRemoval = true)
private List<FoodItem> foodItems = new ArrayList<>();
// Helper methods
public void addFoodItem(FoodItem item) {
foodItems.add(item);
item.setChosenRestaurant(this);
}
public void removeFoodItem(FoodItem item) {
foodItems.remove(item);
item.setChosenRestaurant(null);
}
// Getters and Setters
}
@Entity
public class FoodItem extends BaseEntity {
private String itemName;
private String itemDescription;
private boolean isVeg;
private int price;
@ManyToOne
@JoinColumn(name = "restaurant_id", nullable = false)
private Restaurant chosenRestaurant;
// Getters and Setters
}
Behavior with orphanRemoval = true
:
public String removeFoodItem(Long restaurantId, Long foodItemId) {
Session session = sessionFactory.getCurrentSession();
Restaurant restaurant = session.get(Restaurant.class, restaurantId);
FoodItem item = session.get(FoodItem.class, foodItemId);
if (restaurant != null && item != null) {
restaurant.removeFoodItem(item);
// No need to call session.remove(item); orphanRemoval handles deletion
return "Food item removed successfully";
}
return "Restaurant or Food item not found";
}
restaurant.removeFoodItem(item)
is called, Hibernate deletes the FoodItem
record from the food_items
table, rather than just setting restaurant_id
to null
.CascadeType.REMOVE
vs. orphanRemoval
Restaurant
deletes all its FoodItem
records.restaurant.getFoodItems().remove(item)
).CascadeType.ALL
(including REMOVE
) was insufficient because it doesn’t handle orphaned children. orphanRemoval = true
addresses this specific case.