ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 벌크성 수정 쿼리
    JPA/스프링 데이터 JPA 2023. 8. 11. 23:21

    데이터 하나를 수정할 때에는 변경 감지가 좋지만 모든 데이터를 수정할 때, 예를 들어 모든 상품의 가격을 10% 인상을 할 때에는 벌크 연산이 좋다.

     

     

    @Modifying // JPA에서 query.executeUpdate()과 같은 기능
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);

    스프링 데이터 JPA에서는 @Modifying을 넣어준 다음 update 쿼리를 날려주면 된다. 반환은 update 되는 데이터의 개수이므로 int로 반환해주면 된다. @Modifying을 넣어주지 않으면 에러가 난다.

     

    @Test
    public void bulkUpdate() throws Exception {
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 19));
        memberRepository.save(new Member("member3", 20));
        memberRepository.save(new Member("member4", 21));
        memberRepository.save(new Member("member5", 40));
        
        //when
        int resultCount = memberRepository.bulkAgePlus(20);
        
        //then
        assertThat(resultCount).isEqualTo(3);
    }

    테스트에서 실행을 해보니 resultCount 값은 3개이고, member3, 4, 5의 나이가 1씩 증가한 것을 확인할 수 있다.

     

     

     

     

    벌크 연산 시 주의점

    이전 벌크 연산 포스팅에서도 설명했지만 벌크 연산은 영속성 컨텍스트를 무시하고 바로 db의 데이터를 수정하기 때문에 영속성 컨텍스트의 데이터들은 반영이 안되어서 서로 안 맞을 수가 있다.

    Member member = memberRepository.findByUsername("member5");
    System.out.println("age = " + member.getAge());

    이렇게 출력을 해보면 db에는 41살이라 반영되었지만 영속성 컨텍스트를 무시하고 db에 반영한 것이기 때문에 콘솔에 찍었을 때 40살이라 나온다.

     

     

     

    벌크 연산 이후에는 영속성 컨텍스트를 무조건 초기화 해줘야 한다.

    int resultCount = memberRepository.bulkAgePlus(20);
    
    em.flush(); // 변경되지 않은 내용이 db에 반영
    em.clear(); // 엔티티 매니저 초기화
    
    Member member = memberRepository.findByUsername("member5");
    System.out.println("age = " + member.getAge());

    벌크 연산 이후에 em.flush(), em.clear()로 초기화 해주면 41살이라고 출력된다. 벌크 연산 이후에 로직이 아예 끝나면 상관없지만 추가 로직이 있을 경우에는 무조건 영속성 컨텍스트를 초기화 해주는 것이 좋다.

     

    위의 내용이 저번 JPA 포스팅에서의 해결법이다.

     

     

     

     

    스프링 데이터 JPA에서 제공하는 해결법 - clearAutomatically = true

    @Modifying(clearAutomatically = true) // JPA에서 query.executeUpdate()과 같은 기능
    @Query("update Member m set m.age = m.age + 1 where m.age >= :age")
    int bulkAgePlus(@Param("age") int age);

    스프링 데이터 JPA에서는 @Modifying(clearAutomatically = true)라고 쓸 수 있는데 clearAutomatically = true는 이 쿼리가 나간 다음에 자동으로 영속성 컨텍스트를 초기화 한다는 기능이다. em.flush(), em.clear()를 사용할 필요 없이 이 기능으로 해결이 가능하다.

Designed by Tistory.