JPA는 더티체킹이 가능하지만 만약 모든 회원 데이터에 같은 값으로 수정이 된다면 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을 이용한 bulk 수정쿼리이다.
일정 age이상이면 1살을 더하는 쿼리이다.
@Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
일반 JPA 쿼리문 만드는것과 동일하다. 하지만 하나 다른점은 @Modifying
이라는 어노테이션을 사용했다는것이다.
벌크성 수정, 삭제 쿼리에 꼭 적용해야하는 어노테이션이다. 만약 사용하지않는다면
org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations
라는 에러가 발생하게 됨.
JPA에서 벌크성 update는 조심해야할 부분이있다.
JPA는 1차 캐시라고 불리는 영속성 컨텍스트에서 엔티티를 관리한다. 하지만 벌크성 쿼리문은 이를 다 무시하고 바로 DB에 때려박는 수정문이다.
만약 내가 save를 통해 저장하면 영속성 컨텍스트에 반영되지만 DB에는 반영이 되지 않기때문에 저장과 벌크성 쿼리문을 동시에 날리게 되면 업데이트가 되지않는 치명적인 단점이 존재한다.
그래서 벌크성 수정 쿼리 이후에는 무조건 영속석 컨텍스트를 날려야한다.
@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);
em.flush();
em.clear(); //영속성 컨텍스트 날리기
//then
assertThat(resultCount).isEqualTo(3);
}
flush와 clear를 이용해 영속성 컨텍스트를 날리는 방법이 존재한다.
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
기본적으로 clearAutomatically
는 false로 초기화 되어있다. 때문에 이렇게 true값으로 바꾸여 자동으로 벌크성 쿼리문 이후 영속성 컨텍스트를 날려줄 수도 있다.