웹/Spring Data
[스프링 데이터 JPA] 4. 벌크성 수정 쿼리
두잇베스트
2020. 12. 17. 14:09
벌크성 수정 쿼리
- JPA에서는 영속성 컨텍스트를 거쳐서 DB로 쿼리가 flush 되고 커밋된다. 그러나 벌크성 수정쿼리는 다르다. 벌크성 수정 쿼리는 영속성 컨텍스트를 거치지 않고 곧바로 DB로 update 쿼리가 날라간다.
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;
}
스프링 데이터 JPA에서의 벌크성 수정 쿼리
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
JPA에서는 executeUpdate()
를 사용하지만, 스프링 데이터 JPA에서는 @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);
List<Member> member5 = memberRepository.findByUsername("member5");
Member member = member5.get(0);
System.out.println("member = " + member.getAge());
//then
assertThat(resultCount).isEqualTo(3);
}
위의 member.getAge()
를 실행해보면 member5의 나이는 몇살로 출력될까?
40살일까 , 41살일까?
결론부터 말하자면 40살이다. 그 이유는 바로 벌크성 수정 쿼리 bulkAgePlus()
은 영속성 컨텍스트를 거치지 않고 바로 DB로 수정 쿼리를 보낸다. 그래서 DB에는 20살 이상의 member 들이 모두 1살씩 더해진 상태이다. 근데 이때 영속성 컨텍스트에는 여전히 수정되지 않는 나이로 영속되어 있고, 여기서 findByUsername()
의 메소드를 이용해 멤버를 조회하게 된다면, DB에서 바로 조회하는 것이 아니라 우선적으로 영속성 컨텍스트의 1차 캐시를 검색하게 되고 , 여기에서의 엔티티 "member5" 가져오게 된다. 그래서 member.getAge()
를 했을시 40살이 출력되어진다.
JPQL 쿼리 실행시, 커밋 시 위의 코드들이 자동으로 플러시됨 ( clear() 는 아님)
해결
2가지 방법
- 영속성 컨텍스트에 엔티티가 없는 상태에서 벌크 연산을 먼저 실행한다.
- 영속성 컨텍스트에 엔티티가 있으면서 벌크성 수정 쿼리를 사용했을시 , 영속성 컨텍스트를 초기화한다. 스프링 데이터 JPA에서는
@Modifying(clearAutomatically = true)