Programming/Spring

Spring Data JPA - (벌크성 수정 쿼리)

잇(IT) 2023. 7. 26. 11:27

- JPA에서 벌크 연산은 executeUpdate()를 통해 JPQL에 update, delete를 지원하였다.

- 또한 벌크 연산은 DB에 직접 쿼리를 날리기 때문에 영속성 컨텍스트에 영향을 주지 않았다.


- JPA를 이용한 벌크 수정 쿼리

public int bulkAgePlus(int age) {
        int resultCount = em.createQuery(
                "update Member m set m.age = m.age +1" +
                        " where m.age >= :age")
                .setParameter("age", age)
                .executeUpdate();
        return resultCount;
    }

- 벌크 연산과, 파라미터 바인딩을 통해 JPQL에 update 쿼리를 작성하였다.

 

- Test 코드

@Test
    public void bulkUpdate() {
        //given
        memberJpaRepository.save(new Member("member1", 10));
        memberJpaRepository.save(new Member("member2", 19));
        memberJpaRepository.save(new Member("member3", 23));
        memberJpaRepository.save(new Member("member4", 21));
        memberJpaRepository.save(new Member("member5", 34));
        
        //when
        int resultCount = memberJpaRepository.bulkAgePlus(20);
        System.out.println("resultCount = " + resultCount);

        //then
        assertThat(resultCount).isEqualTo(3);
    }

- JPA의 경우 정상적으로 update한 것을 확인 할 수 있다.


- 스프링 데이터 JPA를 통한 벌크 수정 쿼리

 

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

- 스프링 데이터 JPA의 경우 메서드에 @Modifying를 작성해주어야 executeUpdate를 실행한다. 작성해주지 않을 경우 getResultList, getSingleResult를 실행한다.

 

- Test 코드

 @Test
    public void bulkUpdate() {
        //given
        memberRepository.save(new Member("member1", 10));
        memberRepository.save(new Member("member2", 19));
        memberRepository.save(new Member("member3", 23));
        memberRepository.save(new Member("member4", 21));
        memberRepository.save(new Member("member5", 34));

        //when
        int resultCount = memberRepository.bulkAgePlus(20);
        System.out.println("resultCount = " + resultCount);
//        em.flush();
//        em.clear();

        List<Member> result = memberRepository.findListByUsername("member5");
        Member member = result.get(0);
        System.out.println("member = " + member);

        //then
        assertThat(resultCount).isEqualTo(3);
    }

- 벌크 연산의 경우 기본이 DB에 직접 쿼리를 날리는 것이기 때문에 영속성 컨텍스트에 값은 변경하지 않는다.

- 위와 같이 DB에는 정상적으로 쿼리에 의해 값이 증가하였지만 메모리에서 값을 조회하게 되면 영속성 컨텍스트에 있는 값을 조회하기 때문에 변경 사항이 적용되지 않은 값이 출력되는 것을 확인 할 수 있다.

- 이렇게 되면 데이터의 불일치가 발생하므로 clear()를 통해 영속성 컨텍스트를 비워준 다음 다시 DB로 부터 값을 받아오거나, 

@Modifying(clearAutomatically = true)

@Modifying에 옵션을 추가하여 clear가 자동으로 실행되도록 해야한다.

 

- 즉, @Modifying을 통해 영속성 컨텍스트를 비워주거나, em.clear()를 통해 영속성 컨텍스트를 비워줘야 한다.

 

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 김영한(실전! 스프링 데이터 JPA)

728x90