Dev/Java | Spring

연관관계 맵핑되어 있는 객체 삭제 시, 에러가 발생하는 경우

vanss 2022. 9. 20. 00:43
해당 글은 경험했던 트러블 슈팅 내용을 정리하기 위한 글입니다. 어떤 피드백이든 환영이에요! 😍

상황

- 회원 삭제 API 구현 후, Postman으로 호출을 해보니 아래와 같이 DataIntegrityViolationException 에러가 발생하였다.


원인

MemberService 클래스

@Transactional
public void deleteCrew(Long crewId, CrewDeleteRequest crewRequest) {
      Member member = validMemberIsExist(crewRequest.getMemberId());
      validCaptainAuthority(crewId, member, member.getCrew().getId());
      Crew crew = validIsCrewExist(crewId);

      crewRepository.delete(crew);
}

Member, Crew Entity 클래스

@Getter
@Entity
@NoArgsConstructor
public class Member extends BaseTime {
    ```
    @ManyToOne(fetch = LAZY)
    private Crew crew;

    ```
}
@Getter
@Entity
@NoArgsConstructor
public class Crew extends BaseTime {
    ```
    @OneToMany(mappedBy = "crew")
    private List<Member> members = new ArrayList<>();
    ```
}

- Member와 Crew의 Entity는 서로 연관관계를 맺고 있으며, Member 쪽에서 Crew와의 연관관계에 대한 FK(ex. crew_id)를 갖고 있다.

 

- 이런 상황에서 Crew가 제거된다면, Member 족에서 갖고 있는 FK(crew_id)가 참조하는 Member를 더 이상 찾을 수 없어 참조 무결성 제약 조건을 위반 (Referential integrity constraint violation) 하게된다. 즉, 부모인 Crew가 제거되어 자식인 Member가 혼자 남아 고아 상태가 된 것.


해결

- 보통, 부모가 삭제될 때 자식도 같이 삭제될 수 있도록 cascade = CascadeType.REMOVE을 연관관계에 설정하여 부모의 영속성 상태가 자식에게 전이되게 함으로써 해결할 수 있지만, 원하는 요구사항은 부모(Crew)가 삭제되어도 (Member)는 삭제되지 않아야 되므로 다르게 진행해야 한다.

 

- Member(자식)의 Crew_Id를 Null 값으로 바꿔준 뒤, 참조를 없애고 Crew(부모)를 삭제해야 한다. 하지만 이 방법은 나중에 Crew에 Member가 많아질 경우 벌크 연산을 통해 모든 Member를 조회하고 Null 값을 바꿔줘야 하므로 비추한다.

 

- 하지만 @Modifying 애노테이션을 추가해주면, 벌크 연산 후 영속성 콘텍스트 clear를 자동으로 해주므로 데이터 정합성 문제는 해결할 수 있다. 하기와 같이 MemberRepository에 벌크 연산 쿼리 메서드를 만들어주고, Member의 Crew_id를 null값으로 수정해주면 정상적으로 Crew는 delete가 완료된다.

@Modifying(clearAutomatically = true)
@Query("update Member m set m.crew.id = null where m.crew.id = ?1")
void updateCrewIdOfMembers(Long crewId);
@Transactional
public void deleteCrew(Long crewId, CrewDeleteRequest crewRequest) {
     ```
     memberRepository.updateCrewIdOfMembers(crewId);
     crewRepository.delete(crew);
     ```
}