FK 제약조건으로 인해 객체를 삭제할 때는 제거하려는 객체를 참조하고 있는 객체들부터 먼저 제거해줘야 한다.
Question 객체를 삭제하고자 하지만 Notification에서 FK로 Question의 id를 참조하고 있는 상황이다. 그래서 Question 객체를 삭제하려고 할 때 연관된 Notification 객체들부터 전부 제거해줘야 했다.
jpa의 orpahnRemoval=true를 이용하고 Question 객체의 필드인 Notification 배열을 clear 해주는 방식으로 구현을 했었다. 그런데 FK 관계가 한두개가 아니다보니 이렇게 하는 건 중간에 실수가 발생하기 쉽겠다는 생각이 들었다.
@Transactional
public void delete(Question question) {
// FK 제약 조건 때문에 관련 Notification 먼저 제거 필요
question.getNotifications().clear();
questionRepository.delete(question);
}
설계한 엔티티의 일부인데 Project를 FK로 참조하고 있는 테이블이 3개나 된다. 3개 전부 getXXX를 한 뒤 clear() 해주는 방식은 깔끔하지 않다. 언젠가 몇개 clear 하는 걸 깜빡하고 원인을 모른 채 오랜 시간 디버깅을 하게 되는 원인이 될 수 있다.
JPA의 CascadeType.REMOVE를 이용하면 메소드를 호출할 필요가 없이 연관된 객체까지 깔끔하게 제거가 가능하다. 그러므로 당연히 FK 제약 때문에 제거를 못 할 일은 없다. 물론, 이 방법은 연관된 객체까지 레코드에서 전부 제거하는 걸 원할 때만 유효하다.
CascadeType.REMOVE는 이해가 어려운 개념이었다.
지금 이해한 바로는 아래와 같은 코드일 때 Question 타입의 객체가 제거되면 children들(즉, 제거될 객체를 parent로 참조하고 있는 객체들)을 모두 제거한다고 이해했다.
몇 번의 Question 타입의 객체를 제거하는 시도의 결과 이해한 바가 맞다는 확신이 어느 정도 들고 있다.
@Entity
public class Question {
...
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Question> children = new ArrayList<>();
}
일반화 시킨 예시를 보면 이해가 더 잘 될 거라 생각한다.
cascade = CascadeType.REMOVE 때문에 A 타입의 객체를 제거하면 해당 객체를 FK로 참조하고 있던 B 타입의 객체들 역시 모두 제거된다. JPA가 FK 제약에 안 걸리게 순서대로 레코드들을 테이블에서 잘 제거해준다.
@Entity
public class A {
...
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private List<B> children = new ArrayList<>();
}
이제 풀고자 했던 문제로 돌아가보면, Question 테이블에서 레코드를 하나 제거하려 할 때에 해당 레코드를 FK로 참조하고 있던 레코드들 역시 Notification 테이블에서 모두 제거돼야 한다. 순서가 중요하다. Notification 테이블에서 먼저 제거하고나서 Question 테이블에서 제거해야 한다. 그래야 FK 제약조건에 위배되지 않는다.
cascade = CascadeType.ALL은 cascade = CascadeType.REMOVE 를 포함한다.
그러므로 아래와 같이 cascade를 사용해주면 직접 getNotifications().clear()를 해줄 필요가 없다.
굉장히 코드가 깔끔해진다.
결론은 cascade 애용합시다.
'프로젝트 > 크루트' 카테고리의 다른 글
테스트 케이스를 private으로 작성하면 test를 못 읽어요 (0) | 2022.03.20 |
---|---|
vscode에서 template을 사용해서 귀찮음을 줄이자 (0) | 2022.03.18 |
textarea에 엔터를 입력했는데 렌더링해보니 줄 바꿈이 안 된다? (0) | 2022.03.10 |
NuxtLink의 to에 path를 사용하면 params는 못 써요 (0) | 2022.03.09 |
spring boot + aws s3 (0) | 2022.03.09 |