본문 바로가기

JPA

[JPA] 벌크성 수정 쿼리

이번에 팀 프로젝트를 진행하면서 findAll() 메서드를 사용하여 리스트를 조회한 뒤 JPA 영속성 컨텍스트의 변경 감지기능을 사용해서 특정 조건의 데이터를 모두 업데이트 해주는 서비스 메서드를 구현했었습니다. 구현이 끝나고 테스트해보니 기능은 잘 동작하였지만 업데이트 되는 데이터 수만큼 쿼리가 나가고 있었습니다. 이런 상황을 해결하기 위해 JPA는 벌크성 수정을 효율적으로 수행할 수 있도록 지원해주고 있습니다.

 

MemberRepository

JPA에서 벌크성 업데이트를 수행하기 위해서는 Repository 클래스에서 @Query 어노테이션을 사용해서 update 쿼리를 작성하여 사용합니다. MemberRepository 클래스에 아래와 같은 메서드를 작성한 후 동일한 테스트를 실행하면 이번에는 업데이트 쿼리가 한번만 나가는 것을 확인할 수 있습니다.

public interface MemberRepository extends JpaRepository<Member, Long> {

    ...

    @Modifying
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
}

 

여기서 주의해야 할 점은 @Modifying 어노테이션의 유무입니다. 

JPA는 업데이트할 때 executeUpdate()라는 메서드를 호출하는데 @Modifying 어노테이션을 사용하지 않으면 getResultList()나 getSingleResult() 등의 메서드를 호출하기 때문에 QueryExcecutionRequestException 에러가 발생합니다.

 

벌크성 업데이트를 사용하기 위해서 또 다른 주의할 점이 있습니다. JPA는 영속성 컨텍스트라는 저장소를 사용해서 데이터를 저장하거나 호출할 때 엔티티를 저장해서 관리해줍니다. 그래서 데이터를 조회해올 때 바로 Select 쿼리를 보내지 않고 먼저 영속성 컨텍스트에서 조회하고자 하는 데이터가 관리되고 있는지 확인한 뒤 있다면 쿼리를 날리지 않고 영속성 컨텍스트에서 데이터를 가져옵니다.

 

하지만 벌크성 수정쿼리는 영속성 컨텍스트를 거치지 않고 바로 데이터 베이스로 쿼리를 보내기 때문에 데이터베이스의 데이터는 변경되었지만 영속성 컨텍스트에서 관리되고 있는 데이터는 갱신되지 않아 데이터베이스와 영속성 컨텍스트의 데이터가 동기화되지 않는 문제가 발생합니다.

 

그래서 벌크성 업데이트를 수행하고 나서는 아래와 같이 EntityManagerclear() 메서드를 사용해서 영속성 컨텍스트에서 관리되고 있는 데이터는 전부 삭제한 뒤 다시 DB에서 조회해 오도록 구현해주어야 합니다.

int i = memberRepository.bulkAgePlus(20);
em.flush();
em.clear();

 

위와 같이 EntityManager의 clear() 메서드를 호출해도 되지만 Spring Data JPA는 위와 같은 문제를 해결하기 위해 더 깔끔한 방법을 제공해줍니다.

 

아래와 같이 @Modifying 어노테이션에 clearAutomatically 속성을 true로 설정해주면 clear() 메서드를 호출하지 않아도 자동으로 업데이트 쿼리를 수정하고 나서 영속성 컨텍스트를 초기화 해줍니다.

public interface MemberRepository extends JpaRepository<Member, Long> {

    ...

    @Modifying(clearAutomatically = true)
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);
}

 

업데이트 쿼리를 실행하고 바로 메서드를 종료하면 위와 같은 문제가 발생하지 않지만 하나의 Transaction 안에서 업데이트 쿼리를 수정한 후 바로 데이터를 조회해서 사용하게 되면 위와 같은 문제가 발생할 수 있으니 꼭 업데이트하고나서 영속성 컨텍스트를 초기화 해주셔야 합니다.