Питання 22 · Розділ 16

Що таке orphan removal

Orphan removal — механізм автоматичного видалення пов'язаних сутностей, коли вони видаляються з колекції або зв'язок з ними розривається. Це потужний інструмент для підтримки ці...

Мовні версії: English Russian Ukrainian

Огляд

Orphan removal — механізм автоматичного видалення пов’язаних сутностей, коли вони видаляються з колекції або зв’язок з ними розривається. Це потужний інструмент для підтримки цілісності даних в композиціях.


🟢 Junior Level

Що таке orphan removal

Orphan removal — автоматично видаляє пов’язані сутності з БД, коли вони видаляються з колекції.

@Entity
public class Order {
    @OneToMany(mappedBy = "order", orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();
}

Order order = entityManager.find(Order.class, 1L);
order.getItems().remove(0);  // видалений item автоматично видалиться з БД!

Cascade vs orphanRemoval

// Cascade — propagates операції
@OneToMany(cascade = CascadeType.REMOVE)
// entityManager.remove(order) → remove для всіх items

// orphanRemoval — видаляє orphan entities
@OneToMany(orphanRemoval = true)
// order.getItems().remove(item) → DELETE для item з БД

// Разом — повний контроль
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
// persist, merge, remove + automatic orphan deletion

🟡 Middle Level

Cascade + orphanRemoval разом

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items;

order.getItems().add(newItem);   // persist через cascade
order.getItems().remove(oldItem);  // delete через orphanRemoval
order.getItems().clear();         // delete ALL через orphanRemoval

Коли orphanRemoval працює

✅ @OneToMany
✅ @OneToOne
❌ @ManyToOne — немає сенсу
❌ @ManyToMany — немає сенсу

Типові помилки

// ❌ orphanRemoval для shared entities
@ManyToOne(orphanRemoval = true)  // ❌ не можна для @ManyToOne
private User user;

// ❌ orphanRemoval для @ManyToMany
@ManyToMany(orphanRemoval = true)  // ❌ не можна для @ManyToMany
private List<Tag> tags;

🔴 Senior Level

Внутрішня реалізація

orphanRemoval mechanism:
1. При flush: Hibernate перевіряє всі колекції з orphanRemoval
2. Порівнює поточний стан з snapshot
3. Entities які були в snapshot, але немає в поточному → orphan
4. Для кожного orphan → schedule DELETE
5. При flush → DELETE з БД

Важливо: orphanRemoval спрацьовує при flush, не одразу!

Production патерни

// Pattern 1: Helper method
@Entity
public class Order {
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();

    public void addItem(OrderItem item) {
        items.add(item);
        item.setOrder(this);
    }

    public void removeItem(OrderItem item) {
        items.remove(item);
        item.setOrder(null);  // важливо для консистентності
    }
}

Best Practices

✅ orphanRemoval для composite children
✅ З cascade = ALL
✅ Helper methods для модифікації колекції
✅ Тільки для private ownership

❌ Для shared entities
❌ Без cascade
❌ Для @ManyToOne або @ManyToMany

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • orphanRemoval — автоматично видаляє з БД сутності видалені з колекції
  • Cascade.REMOVE — тільки при видаленні батька, orphanRemoval — при видаленні з колекції
  • Працює тільки для @OneToMany та @OneToOne, НЕ для @ManyToOne/@ManyToMany
  • Спрацьовує при flush, не одразу — важливо для розуміння таймінгу

Пов’язані теми:

  • [[20. Як працюють каскадні операції (Cascade)]]
  • [[21. Які типи Cascade існують]]
  • [[23. Як правильно використовувати @OneToMany і @ManyToOne]]
  • [[24. У чому особливості bidirectional relationships]]