
Cascade
JPA에서는 엔티티끼리 다대일, 일대일 등의 연관관계를 맺어줄 수 있다.
[학생 - 학급] 테이블이 양방향 다대일 관계를 이루고 있다고 가정해보자.
만약, 학급을 만들면서 이 학급에 속하는 학생들을 만들고 싶다면
보통의 경우 아래 순서로 이루어질 것이다.
1. 학급 테이블 생성 및 값 세팅
2. 학생 테이블 생성 및 값 세팅
3. 학급 테이블의 학생 속성에 학생 객체를 리스트로 저장
4. 학급 테이블 저장
5. 학생 테이블의 학급 속성에 학급 객체를 저장
6. 학생 테이블 저장
3번 과정에 주목하자.
여기서 우리는 이미 학급 클래스에 학생 객체를 저장한 상태이다.
이것은 학생의 입장에서 자신에게 학급이 배정되었다고도 말할 수 있다.
하지만 엄밀히 따지면 학생 클래스에는 학급 객체가 저장되지도 않았을 뿐더러 학생 엔티티는 아직 영속화 되지도 않았다.
즉, DB에 저장은 안되었다는 말이다.
그렇기에 5번과 6번 과정을 수행해야 비로소 양방향의 연관관계가 맺어지며 학생 테이블이 DB에 반영된다.
심지어 다대일(학생N:학급1) 관계에서 학생이 DB에 반영되지 않는다면,
학급에 학생이 저장된 3번 과정도 무용지물이 될 것이다.
위 과정을 모두 수행하는 건 세세하게 엔티티를 관리함으로써 설계에 안정성을 더해줄 수 있으나,
경우에 따라서는 단순 반복 작업으로 우리를 귀찮게 할 수 있다.
JAP에서는 Cascade를 지원함으로써 개발자의 수고로움을 덜어준다.
결과부터 말하면 Cascade는 위에서 4번 과정까지만 수행하면 5, 6번 과정이 저절로 수행되어 DB에 저장되고 외래키 또한 반영된다.
public class StudentClass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private List<Student> students = new ArrayList<>();
}
사용 방법은 위와 같다.
엔티티 간 관계를 지정해줄 때 CascadeType으로 지정하면 된다.
보다시피 값을 저장(PERSIST) 할 때를 포함하여,
MERGE, REMOVE 등 여러가지 기능을 제공한다.
ALL은 모든 것을 함께 적용한다는 의미다.
이것들 모두 엔티티 하나에 대해 어떤 "상태"를 적용해주면 그 안에 포함된 다른 엔티티 객체에도 그 상태가 반영된다는 특징이 있다.
이제부터는 주의사항이다.
편한 만큼 아무 때나 사용하면 안될 것 같다는 느낌이 올 것이다.
1. 관리받는 엔티티 클래스의 소유자가 하나일 때에만 사용하자.
무슨말이냐면, [학생 - 학급] 양방향 다대일 관계에서 학생을 학급에서만 관리하면 cascade를 사용해도 된다.
그런데 학급이 아닌 다른 엔티티에서도 학생을 관리하고 있다면 cascade를 사용하면 안된다.
다른 엔티티들의 의사와 상관 없이 예상치 못하게 변할 수 있기 때문이다.
2. 두 엔티티 간 라이프사이클이 유사할 때 사용해야 한다.
한쪽이 쓰일 때 다른쪽도 함께 쓰여야 한다는 의미다.
만약, 저장에 있어서만 라이프사이클이 같다면, CascadeType.PERSIST로 지정하고, 삭제할 때가 같다면 CascadeType.REMOVE와 같이 지정하면 된다.
3. 다대일 관계에서 주의하자.
원래라면 N(다) 쪽의 엔티티가 외래키를 가짐에 따라 N쪽 모든 엔티티가 삭제되고 나서야 1(일)쪽 엔티티가 삭제될수 있다.
하지만 N쪽에서 다른 엔티티 객체에 cascade를 걸어뒀다면?
N 엔티티 중에서 하나만 삭제해도 다대일 관계에 놓인 모~든 엔티티가 삭제될 것이다.
학생 한명이 사라졌는데, 그 반의 모든 학생과 심지어는 학급까지 통째로 사라진 격이다.
반대로 1쪽에서 다른 엔티티 객체에 cascade를 걸어뒀다면?
원래는 외래키를 갖고있는 N 엔티티가 모두 삭제되지 않는 이상 1 엔티티는 삭제할 수 없다.
즉, 학급에 학생이 한명이라도 남아있다면 그 학급은 폐지할 수 없다는 말이다.
그런데 이 상황에서는 학생이 남아있든 말든 강제로 학급과 속해있는 학생들까지 모두 없애버린다.
외래키를 갖고 있지 않아도 해당 테이블은 물론 외래키를 갖는 테이블까지 강제로 모두 삭제해버린다는 뜻이다.
사용하기에 따라 굉장히 위험한 기능이므로 신중하게 잘 사용하자.
'스프링 > JPA' 카테고리의 다른 글
[JPA] dataSourceScriptDatabaseInitializer 빈 생성 오류 (0) | 2022.11.15 |
---|---|
[Spring Data JPA] EntityGraph 사용해서 쿼리 조회 최적화 (0) | 2022.09.26 |
[Spring Data JPA] 사용자 정의 레파지토리 생성 (0) | 2022.09.26 |
[Spring Data JPA] Update 쿼리 사용해서 Bulk로 수정하기. (2) | 2022.09.21 |
[Spring Data JPA] 페이징 메서드 구현 - Page 타입 (2) | 2022.09.20 |