JPA delete가 안된다?

Jinseok Lee·2020년 7월 29일
8
post-thumbnail
post-custom-banner

사연

레터리플라이 서비스 구현 중 사용자(User)가 문의사항(Question)을 쓸 수 있듯이, 삭제하는 부분도 필요했다. 그래서 JpaRepository에서 deleteById 메소드를 호출하여 삭제하려고 하는데 안되는 거임 ㅠㅠ

소스코드

@Entity
public class User {
    ...
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Question> questions = new ArrayList<>();
    ...
}
@Entity
public class Question {
    ...
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id")
    private User user;
    ...
}
...
@Transactional
public void deleteQuestion(Long questionId, Long userId) {
    User user = userService.findById(userId);
    if (user.hasQuestion(questionId)) {
        questionRepository.deleteById(questionId);
    }
}
...

설명

User 엔티티와 Question 엔티티를 설계하고, deleteQuestion이라는 메소드를 사용해서 사용자 정보를 가져온 다음 해당 사용자가 해당 문의사항의 소유자이면 삭제하는 로직을 개발

문제해결

User를 transactional 내부에서 검색한 이후 Question을 삭제할 경우 User의 프로퍼티인 questions의 데이터도 함께 지워줘야 했다. (removeIf)

@Transactional
public void deleteQuestion(Long questionId, Long userId) {
    User user = userService.findById(userId);
    if (user.hasQuestion(questionId)) {
        user.getQuestions().removeIf(question -> question.getId().equals(questionId));
        questionRepository.deleteById(questionId);
    }
}

이후에 아래와 같이 더 간단하게 소스코드를 교정하였다. 연관관계를 가지고 있는 User를 select하지 않았다.

@Transactional
public void deleteQuestion(Long questionId, Long userId) {
    Question question = findById(questionId);
    if (question.belongsToUser(userId)) {
        questionRepository.deleteById(questionId);
    }
}

결론

생각해보면 만약 해당 Question을 소유한 User를 transaction안에서 검색했을 때 삭제하고자 하는 Question을 User의 questions List에서 삭제해주지 않을 경우 실제로는 삭제되어서 존재하지 않는 Question을 User가 가지고 있게 된다. 만약에 그 User 데이터를 가지고 무언가 로직이 진행될 때 문제가 발생할 수 있다. 이러한 이유 때문에 JPA를 설계할 때 한 transaction 안에서 서로 연관관계가 있는 두 Entity를 변경할 때 동기화를 강제하고 있는 것으로 생각된다.

profile
전 위메프, 이직준비중
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 9월 30일

너무 감사합니다. 덕분에 오늘 밤에 잘 수 있을것 같아요 ㅎㅎ

1개의 답글