JPA에서는 update, delete 등의 변경 사항에 대해 따로 쿼리를 작성하지 않아도 JPA가 알아서 DB에 쿼리를 날려주어 해당 내용을 반영시켜준다.
하지만 이같은 기능에만 기댔다가는 성능이 매우 떨어지는 상황이 발생할 수도 있다.
만약 특정 조건을 만족하는 모든 행에 대하여 update연산을 처리하고 싶다면?
where조건을 넣은 JPQL로 List<Entity>를 가져올 것이고.. 반복문을 돌려가며 이들 하나하나에 전부 변경 사항을 적용하는 코드를 짜야 한다.
이럴 경우 JPA가 update에 대한 쿼리를 대신 DB로 보내주는 것은 맞지만 각 row마다 쿼리를 한 번 씩 개별적으로 수행하기 떄문에 대상 List의 크기에 따라 엄청나게 많은 쿼리가 발생할 수도 있게 된다.
이러한 불상사를 막기 위해 JPQL로 update, delete 쿼리를 직접 작성하여 DB에 바로 보내버리는 벌크 연산이 사용된다.
(벌크 연산은 update, delete만 지원한다)
int resultCount = em.createQuery("update Member m set m.age = 20").executeUpdate();
(createQuery는 쿼리가 적용된 row의 수를 반환한다)
주의할 점은, 벌크 연산은 영속성 컨텍스트를 거치지 않고 DB에 쿼리를 바로 적용시킨다는 것이다.
이러한 특성 때문에 데이터의 일관성이 깨지는 일이 발생할 수 있다.
(ex : 영속성 컨텍스트에는 age=10으로 되어있음 -> 벌크연산을 통해 age=20으로 수정 -> 이미 영속성 컨텍스트에 존재하는 엔티티 객체가 있기 때문에 age=10인 객체를 그대로 사용하는 문제 발생)
이러한 문제를 방지하기 위해 아래와 같은 두 가지 해결책이 존재한다.
1. 애플리케이션을 수행하기 전, 아직 영속성 컨텍스트가 비어있을 때, 벌크 연산을 가장 먼저 실행
-> 영속성 컨텍스트엔 아무것도 없기 때문에 이후에 엔티티 객체 필요로 할 시 벌크 연산이 적용된 DB에서 해당 객체 조회하여 사용할 것.
2. 벌크 연산 수행 후 영속성 컨텍스트 초기화
-> 근본적으로는 1과 똑같은 방식. 벌크 연산 수행 후 em.clear()로 영속성 컨텍스트 초기화. 이 후에 DB에서 새롭게 객체 조회하여 사용.
(단, em.clear()로 인해 준영속 상태가 된 이전에 사용하던 객체들은 더이상 사용하면 안 됨)
'김영한님 스프링 강의 정리 > JPA' 카테고리의 다른 글
X to One 성능 최적화 (fetch join) (0) | 2021.04.01 |
---|---|
API 통신에서 DTO의 필요성 (0) | 2021.03.29 |
fetch join의 기능과 한계 (0) | 2021.03.10 |
명시적 조인과 묵시적 조인 (0) | 2021.03.10 |
JPQL - 프로젝션과 페이징 기능 (0) | 2021.03.09 |