💻문제점
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Modifying
@Query("UPDATE Comment c SET c.member = NULL WHERE c.member.id = :memberId")
void setMemberToNull(Long memberId);
}
@Modifying 애노테이션은 @Query 애노테이션으로 작성된 INSERT, UPDATE, DELETE와 같은 쿼리에 사용됩니다. (SELECT제외)
@Test
@DisplayName("회원이 작성한 댓글의 Member를 null로 변경")
void setMemberToNullTest() {
// given
Member otherMember = MemberFixture.DEFAULT_F.get();
memberRepository.save(otherMember);
Post otherPost = PostFixture.CHALLENGE.get();
postRepository.save(otherPost);
Comment otherComment = CommentFixture.DEFAULT.getTopComment(otherMember, otherPost);
commentRepository.save(otherComment);
// when
commentRepository.setMemberToNull(member.getId());
// then
assertThat(commentRepository.findById(comment.getId()).get().getMember()).isNull();
assertThat(commentRepository.findById(otherComment.getId()).get().getMember()).isNotNull();
}
다음과 같이 테스트 코드를 작성했을 때, assertThat이 실패하는 문제가 있었습니다.
🔍해결
기존 JPA를 통해 조회를 할 때 순서는 다음과 같습니다.
1차 캐시 조회 > 존재하면 반환 > 없으면 DB 접근하여 반환
하지만 @Query로 정의된 JPQL은 JPA처럼 영속성 컨텍스트를 거쳐 쓰기 지연SQL로 동작하는 것이 아닌, 데이터베이스에 바로 질의하게 됩니다.
그렇게 되면, 영속성 컨텍스트의 1차 캐시와 DB의 데이터 싱크가 맞지 않게 됩니다.
테스트 코드에서 findById로 데이터를 가져왔지만 기존 데이터가 사용되는 이유는, 이미 같은 Id로 영속성 컨텍스트에 데이터가 존재하기 때문에 DB에 접근하지 않고, 1차 캐시를 통해 가져오기 때문에 기존 데이터가 사용되는 것입니다.
@Modifying의 속성 중 clearAutomatically 라는 속성이 있습니다. 이 속성을 true로 변경하면 문제를 해결할 수 있습니다.
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Modifying(clearAutomatically = true)
@Query("UPDATE Comment c SET c.member = NULL WHERE c.member.id = :memberId")
void setMemberToNull(Long memberId);
}
clearAutomatically는 @Query로 정의된 JPQL을 실행한 후에 자동으로 영속성 컨텍스트를 비워줍니다.
그래서 findById를 수행하게 되면 1차 캐시에 데이터가 존재하지 않기 때문에 DB 조회 쿼리를 수행하게 됩니다. 그리고 이는 다시 영속성 컨텍스트로 관리되어 최신 상태를 유지할 수 있습니다.
728x90
'TIL' 카테고리의 다른 글
[TIL-20240307] 주간/월간/연간 섭취량 조회 API 성능 개선 (1) | 2024.03.07 |
---|---|
[TIL - 20240110] 컨트롤러 나누기?! (0) | 2024.01.10 |
[TIL - 20231228] Interface의 default 메서드를 활용한 Enum 확장 (0) | 2023.12.28 |
[TIL - 20231226] jacoco 패키지, 클래스 Report에서 제외 (0) | 2023.12.26 |
[TIL - 20231222] AuthenticationEntryPoint를 사용한 JWT 예외 핸들링 (1) | 2023.12.22 |