
JPA는 엔티티를 조회한 뒤, commit 시점에 더티체킹을 통해 수정된 값이 있다면 update 쿼리를 날린다.
즉, 엔티티 하나 하나 더티체킹해서 update를 각각 날린다.
하지만, "모든 직원의 연봉을 10%씩 인상해라"라는 식으로 많은 데이터를 변경해야하는 경우
=> 엔티티를 하나씩 조회해서 더티체킹을 통해 update를 하기보다는 DB에 한 번의 update 쿼리를 날리는 것이 효과적
exeuteUpdate회원 중 특정 나이 이상인 사람의 나이를 모두 +1 하여라
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;
}
int = 반환 타입
@Modifying : JPA의 executeUpdate를 실행
clearAutomatically = true : 벌크 연산 이후에 em.clear()자동으로 해줌
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
// @Modifying: JPA의 executeUpdate를 실행 - 작성 안하면 getResultList, getSingleResult호출함
// clearAutomatically = true: 벌크 연산 이후에 em.clear()자동으로 해줌
JPA는 영속성 컨텍스트라는 것이 있어서 Entity가 모두 관리된다.
벌크 연산은 영속성 컨텍스트를 무시하고 바로 쿼리를 DB에 날려버린다.
⇒ 서로 안맞을 수 있다.
영속성 컨텍스트에 있는 Entity의 정보 != DB에 있는 해당 Entity의 정보
DB에 있는 값은 update된 후의 값이고, 영속성 컨텍스트에는 update되기 전의 값이 있을 것이기 때문이다.
즉, 벌크 연산 이후에는 영속성 컨텍스트를 반드시 초기화(em.flush(), em.clear())시켜야한다.
초기화 시킨 뒤, 다시 엔티티를 DB에서 조회해서 영속성 컨텍스트에 담아야한다.
참고
JPQL은
쿼리 실행 전에영속성 컨텍스트 flush시킨 뒤 실행함.
=> JPQL은 영속성 컨텍스트에 있는 데이터를 고려하지 않고 DB에서 데이터를 조회해오기 때문!
=> 조회하기 전에 영속성 컨텍스트의 정보를 DB와 동기화 시켜준 뒤 값을 가져오기 위해서 JPQL 실행전에 flush를 시킨다.