SpringData에서의 여러 데이터들의 값들을 한 번에 수정하는 벌크 수정을 보도록 하겠습니다.
public int bulkAgePlus(int age) {
String query = "update Member m set m.age = m.age + 1 where m.age >= :age";
return em.createQuery(query)
.setParameter("age", age)
.executeUpdate();
}
@Modifying(clearAutomatically = true) // == executeUpdate(); 벌크성 수정 & 삭제 쿼리는 해당 애노테이션이 없으면 오류 발생
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
SpringData에서 벌크 수정을 하는 것은 지금까지 @Query를 이용해서 쿼리를 실행하는 방식과 매우 비슷합니다. 다만, 벌크 수정 & 삭제 시에는 @Modifying 애노테이션이 필요합니다. 해당 애노테이션에선, JPA에서 executeUpdate()와 비슷한 개념입니다.
여기서 주의할 점은, clearAutomatically = true로 설정하시는 것이 권장됩니다.
@Modifying에서 DEFAULT 값은 clearAutomatically = false 입니다. 하지만, true로 변경해서 벌크 수정 쿼리가 실행 후, 자동으로 영속성 컨텍스트가 초기화 되도록 설정해주는 것이 권장됩니다.
왜냐하면, 벌크 연산을 실행하면 영속성 컨텍스트를 확인하지 않고, 바로 DB를 접근해서 데이터를 변경하기 때문에 영속성 컨텍스트와 DB의 값이 불일치하기 때문입니다.
변경 감지를 통해 하나의 데이터의 값만 변경하면, 영속성 컨텍스트를 확인한 후 DB를 접근하기 때문에 이러한 현상이 발생하지 않습니다.
다음 예제를 통해 불일치 하는 것을 보도록 하겠습니다.@Test void bulkUpdate() { //given memberDataRepository.save(new Member("member0", 10)); memberDataRepository.save(new Member("member1", 19)); memberDataRepository.save(new Member("member2", 20)); memberDataRepository.save(new Member("member3", 21)); memberDataRepository.save(new Member("member4", 40)); //when int resultCount = memberDataRepository.bulkAgePlus(20); List<Member> member4 = memberDataRepository.findByUsername("member4"); System.out.println("member4.age = " + member4.get(0).getAge()); Assertions.assertThat(resultCount).isEqualTo(3); }
해당 Test를 실행해보면, 벌크 연산 후 member4의 age는 41이 되어야 합니다. 그러나, clearAutomatically = false로 실행한 후 member4의 age를 출력해 보면 40이 출력됩니다. 하지만, DB에서의 값은 목표대로 41로 저장되어 있습니다. 왜냐하면, 현재 영속성 컨테스트에는 member4가 존재하는데, 벌크 연산을 하면 영속성 컨테스트를 거치지 않고 바로 DB에 접근 해서 값을 변경하기 때문에, DB에는 변경된 값이 저장되어 있지만, 영속성 컨텍스트에 있는 member4의 값은 변경이 되지 않기 때문입니다. 그러므로, 영속성 컨텍스트와 DB의 값이 불일치 합니다.
clearAutomatically = true로 실행 하면, 벌크 연산 후 영속성 컨텍스트가 초기화 되기 때문에 영속성 컨텍스트에 member4가 존재하지 않습니다. 그렇기 때문에 member4의 age를 출력하기 위해서는 DB에서 member4를 영속성 컨텍스트로 가져옵니다. 그러므로, 둘의 불일치가 존재하지 않게 됩니다.