Каскадные операции JPA

Cascade в JPA распространяет операцию EntityManager от одной entity к связанной. Это не то же самое, что database ON DELETE CASCADE, и не означает автоматическое сохранение любой reachable entity.

Типы cascade

CascadeTypeРаспространяет
PERSISTpersist
MERGEmerge
REMOVEremove
REFRESHrefresh
DETACHdetach
ALLвсе перечисленные

CascadeType.ALL удобно, только если жизненный цикл child действительно принадлежит parent. Для shared reference, например справочника, REMOVE опасен.

Пример aggregate

@Entity
public class Topic {
    @Id
    @GeneratedValue
    private Long id;
 
    @OneToMany(
            mappedBy = "topic",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private final List<Comment> comments = new ArrayList<>();
 
    public void addComment(Comment comment) {
        comments.add(comment);
        comment.setTopic(this);
    }
 
    public void removeComment(Comment comment) {
        comments.remove(comment);
        comment.setTopic(null);
    }
}

В двунаправленной связи обе стороны синхронизирую в памяти, но SQL foreign key определяет owning side — здесь Comment.topic. mappedBy указывает inverse side.

orphanRemoval

Если child удалён из owning aggregate collection, orphanRemoval=true планирует удаление строки orphan при flush. CascadeType.REMOVE распространяет явное удаление parent. Механизмы связаны, но срабатывают по разным событиям.

JPA cascade и DB cascade

JPA cascade: EntityManager → ORM action graph → SQL
DB cascade: DELETE/UPDATE SQL → foreign key action inside DB

DB cascade работает даже для SQL вне ORM. Но persistence context может сохранить stale objects, если БД удалила строки без ведома Hibernate. Стратегию выбираю одну и согласую с cache и bulk operations.

Производительность

Удаление большого aggregate через JPA может загрузить children и отправить много statements. Database cascade или bulk delete быстрее, но обходят callbacks/version checks и требуют очистки persistence context. Измеряю SQL и проверяю бизнес-инварианты.

Типичные сбои

  • Поставить ALL на @ManyToMany и удалить shared entity.
  • Изменить только inverse side и не получить ожидаемый foreign key update.
  • Путать orphanRemoval с удалением parent.
  • Смешать DB cascade и ORM cache без синхронизации.
  • Сериализовать двунаправленную связь и получить рекурсию.
  • Удалить большую коллекцию по одной строке без оценки SQL.

Вопросы для собеседования

Что делает cascade persist? Передаёт persist связанным entities по настроенной association.

Чем orphanRemoval отличается от REMOVE? Первый реагирует на исключение child из отношения, второй — на удаление parent.

Что такое owning side? Сторона relation, изменение которой определяет обновление foreign key/join table.

JPA cascade равен ON DELETE CASCADE? Нет: один выполняет ORM, другой — сама БД.

Почему CascadeType.ALL опасен в many-to-many? Жизненный цикл shared child обычно не принадлежит одному parent.

См. Hibernate, Session, Анализ производительности ORM систем.