
변경감지 vs Bulk업데이트
JPA에는 변경감지 기능이 있다.
영속성 컨텍스트에서 불러온 엔티티의 값을 Setter 등의 메서드로 수정해주면 그 값이 영속성 컨텍스트에 반영되는 것을 말한다.
그런데 만약 엔티티들에 일괄적으로 동일한 값을 추가하거나 삭제하는 등의 수정이 필요할 때, 변경감지로 인한 값 수정은 최선이 아닐 수 있다.
해당 엔티티들을 개별적으로 전부 영속성 컨텍스트에서 꺼내와서 하나씩 변경된 값을 반영해야 하기 때문이다.
그렇기에 우리는 직접 쿼리를 작성해서 한번에 값을 수정해볼 것이다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgeUpdate(@Param("age") int age);
}
방법은 간단하다.
레파지토리에서 @Query 어노테이션 선언 후 그 안에 JPQL로 업데이트 쿼리를 작성하면 된다.
쿼리 내용을 보면, 특성 age 이상인 Member 엔티티의 age 값을 일괄적으로 +1씩 더해주는 업데이트다.
이런 경우에 이 방식을 사용하면 변경감지보다 성능상 최적화 될 수 있다.
또한, 비즈니스 로직에 들어가는 코드를 줄여줄 수도 있을 것이다.
업데이트 쿼리 작성 시 @Modifying 어노테이션이 필수로 들어가야 하는데, 그 안에 clearAutomatically 부분은 선택적 요소이다.
이 부분은 아래 주의사항에서 설명드리겠다.
주의사항
하지만 이방법 역시 주의해야 할 게 있다.
직접 쿼리를 이용해서 벌크 업데이트를 하게 되면 영속성 컨텍스트에 변경된 값이 반영되지 않고 DB에만 그 값이 반영된다.
업데이트를 하고 동일 트랜잭션 내에서 해당 엔티티를 이용한 추가 로직이 없다면 크게 문제될 게 없다.
그런데 만약, 업데이트 한 뒤 해당 엔티티 값을 조회한다면?
DB가 아닌, 영속성 컨텍스트에서 값을 꺼내오기 때문에 변경 이전의 값을 가져올 것이다.
이 심각한 문제를 해결할 방법은 두 가지다.
첫 째는, 업데이트 메서드를 사용한 뒤 직접 엔티티매니저에 clear를 통해 영속성 컨텍스트를 싹 비우는 것이다.
그렇게 하면 값을 조회하더라도 DB에서 가져와 새로운 값으로 영속성 컨텍스트를 채워넣기 때문에 해결된다.
하지만 이 글이 Spring Data JPA 를 기반으로 작성하는 만큼 더 좋은 방법이 있다.
위에서 말씀드렸던 @Modifying(clearAutomatically = true) 부분이다.
Modifying 어노테이션을 선언 후 그 안에 clearAutomatically를 true로 설정하면 레파지토리 안에서 모든게 간단하게 해결된다.
설령, clearAutomatically를 설정하지 않더라도 업데이트 쿼리 작성 시 Modifying 어노테이션은 필수로 선언해야 하는 점에 주의하자.
'스프링 > JPA' 카테고리의 다른 글
[Spring Data JPA] EntityGraph 사용해서 쿼리 조회 최적화 (0) | 2022.09.26 |
---|---|
[Spring Data JPA] 사용자 정의 레파지토리 생성 (0) | 2022.09.26 |
[Spring Data JPA] 페이징 메서드 구현 - Page 타입 (2) | 2022.09.20 |
[Spring Data JPA] @NamedQuery 없이 레파지토리에 직접 쿼리 작성하기 (2) | 2022.09.19 |
[Spring Data JPA] NamedQuery 사용하기 (0) | 2022.09.19 |