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(удаление заказа удалит пользователя).