Spring

- CascadeType.REMOVE vs @OnDelete

상황은 이러하다.

카드 엔티티는 1:N 양방향관계로 댓글set과 할일set을 가지고 있었다.

카드 삭제 시 연관된 댓글들과 할일들을 모두 삭제하려고 카드 엔티티의 댓글set, 할일 set 필드에 cascade = CascadeType.REMOVE 로 선언을 해준 후 카드를 삭제하려니 

'JDBC exception executing SQL [delete from card where id=?] [Cannot delete or update a parent row: a foreign key constraint fails'

이런 에러가 뜨는 것이 아닌가.

이 말은 외래키의 주인(댓글, 할일)이 참조하고 있는 카드 엔티티를 삭제하려고 하니, 외래키 제약조건이 위배되어 부모 엔티티를 삭제할 수 없다는 의미이다.

아니 cascade 했잖아 근데 왜 뜨는데?? 한참을 고민하다가 다른 방법으로 일단 돌아가게는 했다

그 방법은 바로 @OnDelete이다.

 

@JoinColumn(name = "card_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Card card;

이 어노테이션은 자식 엔티티에 선언해주면 되는데 이렇게 하니까 해결되었다.

 

그럼 조금은 생소한 @OnDelete는 무엇이며 왜 cascade 옵션이 적용되지 않았던 것일까??

 

1. CascadeType.REMOVE 와 @OnDelete의 차이

일단 CascadeType.REMOVE는 부모 엔티티가 삭제될 때 부모 엔티티의 삭제되는 상태를 자식 엔티티에게 전파시켜 자동으로 삭제가 되게 해준다. 여기서 핵심은 'JPA 레벨에서 삭제를 진행한다.' 라는 것이다. 

 

@OnDelete 는 데이터베이스 ddl 자체에 ON DELETE 옵션을 추가하여 테이블 레벨에서 card가 삭제되면 자식 엔티티들도 삭제되게 하는 것이다.

 

2. 그럼 왜 내 Cascade는 먹히지 않았을까?

일단 CascadeType.REMOVE가 JPA 레벨에서 동작하는 것을 잘 알지 못한 채, 삭제를 진행할 때 QueryDSL로 디비에 직접 쿼리를 날려서 삭제를 시켜주었다. 만약 CardRepository.delete(지우려는 카드 엔티티) 를 사용하여 삭제를 진행했다면. Jpa가 알아서 자식 엔티티도 삭제시켜줬을 텐데 계속 디비에 직접 쿼리를 날리니 될 리가 없는 것이다.

@OnDelete를 사용해주면 테이블 자체 스키마가 변경되어서 무조건 삭제가 되었던 것이다.

 

추후 다른 팀원들과의 통일성을 위해 JPARepository의 delete 메소드를 사용해주긴 했지만 연관관계에 있는 엔티티를 같이 삭제하는 또 다른 방법과 디비에 대한 이해력이 늘어나는 좋은 트러블슈티이었다.

 

 

알고리즘

- BOJ : 달팽이 3(Gold 3, 수학, 많은 분기 처리)

느낀 점

새해가 밝았다. 취업은 바로 힘들겠지만 화이팅이다..

 

'TIL' 카테고리의 다른 글

[24.01.04]  (1) 2024.01.05
[24.01.03]  (2) 2024.01.04
[23.12.28]  (0) 2023.12.29
[23.12.27]  (2) 2023.12.27
[23.12.26]  (0) 2023.12.26

+ Recent posts