Gymterview
middle

Как работает каскадирование (CascadeType)?

Каскадирование определяет, какие операции с родительской сущностью автоматически распространяются на связанные (дочерние) сущности.

Аналогия из жизни: каскад — как удаление папки на диске. Удалили папку (родитель) — удалились все файлы внутри (дочерние). Но удаление файла из папки не удаляет саму папку.

Пример
@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "order",
               cascade = CascadeType.ALL,     // каскад всех операций
               orphanRemoval = true)           // удалять «осиротевших» детей
    private List<OrderItem> items = new ArrayList<>();
}

Типы каскадирования

CascadeType Операция Описание
PERSIST persist/save Новые дочерние сущности сохраняются вместе с родителем
MERGE merge Detached дочерние сущности сливаются вместе с родителем
REMOVE remove/delete Дочерние сущности удаляются при удалении родителя
REFRESH refresh Дочерние сущности перезагружаются из БД
DETACH detach Дочерние сущности отсоединяются от Persistence Context
ALL все вышеперечисленные Все операции каскадируются

orphanRemoval

orphanRemoval = true — удаляет дочернюю сущность при удалении из коллекции родителя:

Пример
@Transactional
public void removeItem(Long orderId, Long itemId) {
    Order order = orderRepository.findById(orderId).orElseThrow();
    order.getItems().removeIf(item -> item.getId().equals(itemId));
    // orphanRemoval=true → Hibernate выполнит DELETE FROM order_items WHERE id = ?
}

Важное

  • CascadeType.ALL = PERSIST + MERGE + REMOVE + REFRESH + DETACH
  • orphanRemoval — удаляет дочернюю сущность, когда она убирается из коллекции родителя
  • Каскад распространяется только от родителя к ребёнку, не наоборот
  • Используйте каскад только для связей «родитель владеет ребёнком» (Order → OrderItem), не для ссылочных связей (Order → User)

Частые ошибки

  • CascadeType.ALL для @ManyToOne — удаление Order не должно удалять User; каскад здесь опасен
  • CascadeType.REMOVE без orphanRemoval — REMOVE срабатывает при remove(order), но не при order.getItems().remove(item)
  • Каскад на ассоциациях с общими сущностями — если Department каскадно удаляет Users, удаление отдела удалит всех пользователей
  • orphanRemoval на @ManyToMany — концептуально бессмысленно; удаление из связи не должно удалять сущность

Как используется в 2026

  • CascadeType.ALL + orphanRemoval — стандарт для parent-child отношений (Order-OrderItem, Post-Comment)
  • Для слабых связей (User-Department) — каскад не используется
  • В DDD: каскад только внутри Aggregate Root → его Entity

На собеседовании: объясните разницу между CascadeType.REMOVE и orphanRemoval. REMOVE — при удалении родителя. orphanRemoval — при удалении из коллекции. Используйте CascadeType.ALL только для parent-child связей. Главная ошибка — каскад на @ManyToOne (удаление заказа удалит пользователя).