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를 시킨다.