[main-project] 연관 관계 삭제

박채은·2023년 3월 24일

Project

목록 보기
21/21

발단

게시글을 삭제하려고 하는데 삭제가 안 되는 문제가 발생했다.

이는 게시글에 여러 연관 관계가 존재하기 때문이고 연관 관계를 맺고 있는 다른 데이터가 삭제되지 않은 상태이기 때문이다.
이때 게시글을 삭제하려면 연관 관계를 맺고 있는 테이블은 어떻게 해야하는가?!

1. 게시글을 삭제할 때, 같이 삭제한다.

  • @OneToMany(cascade = CascadeType.REMOVE)를 사용
  • CascadeType.REMOVE는 부모 엔티티가 삭제되면 자식 엔티티도 삭제된다. 즉, 부모가 자식의 삭제 생명 주기를 관리한다.
    • 부모 엔티티인 게시글이 삭제될 때 삭제되어야 할 자식 엔티티로는 urls, prf_post_comment, prf_post_like가 있다.
    • urls은 부모 엔티티가 prf_post 밖에 없기 때문에 CascadeType.REMOVE로 설정해도 문제가 없다.
    • prf_post_comment, prf_post_like은 부모 엔티티가 member와 prf_post가 있는데 member는 삭제되는 경우가 없기 때문에 CascadeType.REMOVE로 설정해도 문제가 없다.(탈퇴는 MemberStatus = "QUIT"으로 상태값만 바꾸기 때문에)

❗️ cascade는 @OneToMany에 거는 것이 좋다.(@ManyToOne 이 안된다는 것은 아님!)

2. 게시글을 삭제할 때, 삭제되지 않아야 하는 것

  • 모집글은 게시글이 삭제되어도 삭제되면 안된다.
  • 게시글을 삭제하려고 하면 다음과 같은 에러가 발생한다.

    Cannot delete or update a parent row: a foreign key constraint fails Persist

FK로 연결되어 있는 자식 테이블이 지워지지 않아서, 부모 테이블인 게시글도 지워지지 않는 것이다.


해결 방법

Cannot delete or update a parent row: a foreign key constraint fails Persist


1. 외래키를 해제하여 부모-자식 관계를 끊는다.

외래키 체크 설정을 해제하면 부모 엔티티와 자식 엔티티 간의 관계가 끊겨서 부모 엔티티를 삭제할 수 있다. 그 후에 다시 자식 엔티티에 외래키 설정을 해둔다.
하지만 이 방법을 사용하면 문제가 발생할 수 있다고 한다.
데이터가 깨지거나 테이블 간의 관계가 이상해지는 등 여러 문제가 있으니 로컬이나 테스트 서버에서 테스트 시에만 사용하는 걸 추천한다고 한다.

=> 보편적인 방법이나 이 방법은 위험하니 사용하지 않겠다!

https://stackoverflow.com/questions/1905470/cannot-delete-or-update-a-parent-row-a-foreign-key-constraint-fails

2. 자식 엔티티를 삭제하고, 다시 테이블에 넣어주는 방법

이 방법은 현재 DB 상에 알맞지 않은 것 같다.

자식 엔티티의 데이터를 삭제하고 나서 부모 엔티티의 데이터를 삭제한다. 그러고 나서 자식 엔티티의 값을 다시 넣어주는 방법이다.
prf_post의 자식 엔티티인 recruit_post를 삭제하려면 recruit_post의 자식 엔티티인 recruit_post_comment, recruit_post_like, participation 엔티티를 또 먼저 삭제해줘야 한다.
연속적으로 연관 관계가 존재하기 때문에 좋지 않은 방법이다.

3. ON DELETE SET NULL

  • 부모 테이블의 값이 삭제되면 해당 PK를 참조하는 자식 테이블의 FK값들을 NULL값으로 설정해주는 옵션이다.
  • JPA로 설정할 순 없고, DB에서 직접 FK의 설정을 바꿔야 한다.

만약 성능이 고민된다면, 배치 update JPQL을 사용해서 한번에 해당 부모와 관련있는 모든 자식 엔티티의 부모 FK 값을 null로 변경하는 방법도 있다.

부모 엔티티 삭제 시, 자식 엔티티의 FK를 NULL으로 변경하는 방법
https://velog.io/@seulki412/CASCADE-ON-DELETE
https://wrkbr.tistory.com/691

4. FK에 NULL 값을 직접 넣어준다.

3번과 동일한 방법이지만, 직접 비즈니스 로직을 작성하는 방법이다.
오히려 명확한 방법이고 이슈 발생도 적을 것 같다.

5. 외래키를 사용하지 않는다.

꼭 외래키를 사용하지 않아도 된다면 아예 끊어버리는 것도 나쁘지 않을 것 같다.
외래키로 사용되는 prf_post_id는 prfpost로의 url이 필요해서 넣은 것인데 굳이 외래키로 설정할 필요는 없지 않을까?라는 생각이 들었다.

6. prfpost를 삭제하지 않고, status로 관리한다.

이력 관리 테이블의 역할도 하도록 prfpost를 삭제하지 않고 상태값만 바꿔서 관리한다. 이 방법을 사용하면 테이블도 변경해야하고 코드도 수정해야 해서 번거로움이 있을 것 같다. 또한, 검색이나 리스트를 GET 해올 때 성능이 느려질 것 같아서 지금 상황에서는 좋지 않은 방법인 것 같다.


결과

4번인 FK에 NULL 값을 넣어주는 방법을 선택했고 잘 삭제된다!


[참고]
JPA CASCADE
JPA CascadeType.REMOVE vs orphanRemoval = true
CascadeType.PERSIST
https://ojt90902.tistory.com/639
https://yeon-kr.tistory.com/196
https://stackoverflow.com/questions/1571581/how-to-add-on-delete-cascade-in-alter-table-statement
https://great-woman-hoseung.tistory.com/56

0개의 댓글