슬기로운 개발생활

[JPA] 벌크 연산(Bulk Operation)이란?

by coco3o
반응형

시작하기 앞서,
DB에 Member라는 테이블이 있고, salary라는 컬럼이 존재하며 이는 연봉을 나타낸다고 가정해보겠다.

연봉 3000만원 미만의 Member의 salary를 10% 만큼 인상한다면 다음과 같은 SQL문을 짤 수 있겠다.

UPDATE MEMBER SET SALARY = SALARY * 1.1 WHERE SALARY < 30000000

이제 JPA의 관점에서 생각해보자.

JPA에서 특정 엔티티의 값을 변경하기 위해 다음과 같은 순서로 진행될 것이다.

1. em.find() OR select 쿼리를 날려 영속성 컨텍스트에 엔티티 저장 후 반환
2. 반환 받은 엔티티의 값을 변경한다. -> 영속성 컨텍스트에 반영된다.
3. Commit 시점에 변경 감지(Dirty Checking)가 일어나며 Update 쿼리를 날려 DB에 반영한다.

만약 Member가 80만 명 존재하고, 연봉이 3000만 원 미만인 Member가 60만 명이라면?

총 60만 번의 더티 체킹이 일어날 것이며, 60만 번의 UPDATE 쿼리가 날아갈 것이다. (상상만 해도 끔찍하다.)

이러한 문제에 대해 나온 해결책이 바로 벌크 연산이다.


벌크 연산(Bulk Operation)

벌크 연산은 UPDATE, DELETE 문을 지원하며, Hibernate는 INSERT 문도 지원한다.
executeUpdate() 메서드를 통해 벌크 연산을 수행한다.

(여러 건 한 번에 수정) 연봉 3000만 원 미만의 Member의 연봉을 10%만큼 상승

String sql = 
    "update Member m" +
    "set m.salary = m.salary * 1.1" +
    "where m.salary < :salary";
 
int resultCount = em.createQuery(sql)
                    .setParameter("salary", 30000000)
                    .executeUpdate();


(여러 건 한 번에 삭제) 나이가 70 초과인 Member를 삭제

String sql = 
    "delete from Member m" +
    "where m.age > :age";
 
int resultCount = em.createQuery(sql)
                    .setParameter("age", 70)
                    .executeUpdate();

벌크 연산을 사용할 경우 주의사항

※ 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 직접 쿼리 한다는 점을 주의해야 한다.

즉, DB에 반영된 변경이 영속성 컨텍스트에는 반영되지 않는다는 말이다.

만약 salary 데이터를 조회 후 벌크 연산을 수행 한다면,
영속성 컨텍스트에 있는 salary와 DB에 있는 salary의 값이 다를 수 있다.
(영속성 컨텍스트와 DB 간에 데이터 차이 발생)


해결 방법

  1. em.refresh() 사용
    • 벌크 연산 수행 직후 정확한 salary 엔티티를 사용해야 한다면, em.refresh(salary)를 사용하여 DB에서 salary를 다시 조회한다.
  2. 벌크 연산 먼저 실행
    • 벌크 연산 먼저 실행하고 조회하면 된다. 가장 실용적인 해결책이며, JPA와 JDBC를 함께 사용할 때도 유용하다.
  3. 벌크 연산 수행 후 영속성 컨텍스트 초기화
    • 영속성 컨텍스트에 남아 있는 엔티티를 제거하는 방법이다.

정리하면

JPA에서 단 건의 데이터의 경우 Dirty Checking을 통해 UPDATE를 수행 하고,
여러 건(대량의 데이터)의 데이터의 경우 벌크 연산(Bulk Operation)을 통해 한 번에 수정하거나 삭제 한다.


참고 자료 :
https://data-make.tistory.com/617
https://dev-gorany.tistory.com/327

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기