JPA의 CascadeType.ALL과 CascadeType.PERSIST는 둘 다 영속성 전파(cascade persist)를 포함하고 있지만, 용도와 상황에 따라 구분해서 사용해야한다!
이 둘이 헷갈려서 글을 남기면서 이해하려고 한다ㅠㅠ
💡 공통점 : 둘 다 부모 엔티티.persist() 시 연관된 자식 엔티티도 자동으로 persist 된다
ALL로 설정하면 편한데 왜 구분해서 사용해야할까?
요약! : "명확한 책임 분리"와 "예측 가능한 동작" 때문
CascadeType.ALL은 persist, merge, remove, refresh, detach를 모두 포함한다.
이걸 사용하면 생명주기 전체를 부모가 관리하게 되는데,,
생각보다 객체 관계는 항상 그렇게 강결합되지 않는다!
❗ "모든 생명주기를 위임해야 할 만큼 부모-자식 관계가 강해??"
→ 그게 아니라면, ALL은 너무 투머치임
CascadeType.PERSIST로 설정하게 되면
부모를 persist()할 때 자식도 자동으로 저장되지만
자식은 삭제, 병합, 갱신 등의 책임을 부모에게 묻지 않음 ❌
즉
"저장할 땐 편하게, 나머지는 명시적으로!"
→ 유지보수 관점에서 매우매우 유리하다.
@Entity
public class Order {
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items;
}
이렇게 부모:order / 자식 : item일때
누가 orderRepository.delete(order)만 호출하면????
👉 OrderItem도 모두 삭제된다....
사용자의 의도는 주문만 삭제하는건데, 의도치않은 오더아이템까지 싹 삭제가되는것 ]ㅠㅠ
이때 다른 엔티티가 이 오더아이템을 참조하고 있지 않다면 그나마 다행이지만..
다른 곳에서도 이 OrderItem을 참조하고 있었다면?😱
→ 참조 무결성 와장창... + 널포인터 에러까지
➡️ REMOVE는 되도록 명시적으로 delete 하는게 좋다
대형 프로젝트에서 팀원 간 예측 가능한 코드 유지가 가능하다!
persist만 전파되면 다른 팀원들이 “이건 저장만 자동이고 나머진 내가 책임져야 하는구나”라고 인식할 수 있기 때문~!
ALL은 어디까지 영향이 갈지 추적하기 어렵고,, 디버깅도 불편해진다!
| 이유 | 설명 |
|---|---|
| 책임 분리 | 저장만 위임하고 나머지는 명시적으로 관리 가능 |
| 예측 가능성 | "부모 삭제 시 자식 삭제" 같은 예상치 못한 부작용 방지 |
| 유지보수성 | 동작이 명확하므로 디버깅/리팩토링이 쉬움 |
| 안전성 | 실수로 중요한 데이터를 삭제하거나 병합하는 일 방지 |
진짜 부모-자식 생명주기가 완전!! 동일한 경우에 쓴다
단, 이때도 되도록 orphanRemoval = true를 함께 써야 일관성을 유지할 수 있다
부모-자식이 “소유/종속” 관계일 때!
→ 자식이 오직 하나의 부모에만 종속
→ 부모가 자식을 관리 & 자식이 부모 없이는 존재할 수 없을 때
연관관계를 끊으면 자식은 orphan(고아)이므로 자동 삭제해야 맞다!
→ 이때 orphanRemoval = true를 사용하는 것이다~
@OneToMany(mappedBy = "todo", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Manager> managers = new ArrayList<>();
이 코드를 보자
Todo를 persist()하면 Manager도 자동 저장된다
todo.getManagers().remove(m) 하면, 해당 Manager는 DB에서도 삭제된다
➡️ 저장은 편하게 자동(persist)
➡️ 삭제는 명확하게 연관관계 변경으로 처리
➡️ 명시적 entityManager.remove() 없이도 정합성 유지
remove() 해도 자식 엔티티는 DB에 남아 있음
→ 직접 entityManager.remove(manager) 호출해야 함,,,,
➡️ 삭제 누락 가능성, 정합성 문제 발생 위험 ↑