DEV/Spring Data JPA

Update 쿼리를 호출할 때 주의사항 2가지

행운개발자 2023. 9. 24. 20:03
728x90

일반적으로는 Entity에 대한 수정사항은 변경감지를 통해서 진행됩니다. 하지만 여러 Entity에 대해서 update를 한 번에 처리해야하는 경우도 있습니다.

@Repository
public interface MemberJpaRepository extends JpaRepository<Member, Long> {
    int addTokens(@Param("name") String name, @Param("amount") Integer amount);
}

select 쿼리와 달리 update 쿼리는 2가지를 신경써야 합니다.

 

1. @Modifying annotation을 추가해주어야 합니다.

2. Update 쿼리는 DB에 직접 호출됩니다.

 

@Modifying가 없으면 테스트 코드 작성과정에서 검증될 가능성이 높기 때문에 큰 문제가 되지 않습니다. 두 번째 사항은 뒤늦게 발견되고 장애로 이어질 수 있습니다. 

 

Update 쿼리는 DB에 직접 호출됩니다. 그래서 현재 트랜잭션에서 관리하는 영속성 컨텍스트에는 update 사항이 반영되지 않습니다. 따라서 영속성 컨테스트와 데이터베이스에 있는 데이터 사이에 불일치가 발생합니다.

 

이를 피하기 위해서는 EntityManager#flush(), EntityManager#clear()를 update 쿼리 이후에 호출해주어야 합니다.

 @Test
    public void addToken() {
        //given
        String name = "name";
        String email = "email";
        Member member = new Member(name, email, "pwd");
        memberJpaRepository.save(member);

        int updatedCount = memberJpaRepository.addTokens("name", 10);
        Assertions.assertThat(member.getToken()).isEqualTo(10);

        em.flush();
        em.clear();
}

 

 

이것을 보다 편리하게 적용하는 방법은 아래와 같습니다 

 

@Repository
public interface MemberJpaRepository extends JpaRepository<Member, Long> {
	@Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query("update Member m set m.token = m.token + :amount where m.name = :name ")
    int addTokens(@Param("name") String name, @Param("amount") Integer amount);
}
728x90