오봉이와 함께하는 개발 블로그

JPA - 벌크 연산 본문

BE/JPA

JPA - 벌크 연산

오봉봉이 2022. 7. 6. 03:02
728x90

벌크 연산

SQL에서 PK를 집어서 하는 Update, Delete를 제외한 모든 Update, Delete문이라고 생각하면 좋다.

  • 재고 10개 미만인 모든 상품의 가격을 10% 상승시키려면?
  • JPA 변경 감지 기능으로 실행하면 너무 많은 SQL을 실행한다.
    • 재고가 10개 미만인 상품을 리스트로 조회
    • 상품의 엔티티 가격을 10% 증가
    • 트랜잭션 커밋 시점에 변경감지 작동
    • 변경된 데이터가 100건이라면 100번의 Update SQL이 실행된다.

예제

  • 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
  • executeUpdate()의 결과는 영향받은 엔티티 수 반환
  • UPDATE, DELETE 지원
  • INSERT(insert into .. select(select한 값을 insert), 하이버네이트에서 지원)지원
String qlString = "update Product p " +
                  "set p.price = p.price * 1.1 " +
                  "where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString)
                    .setParameter("stockAmount", 10)
                    .executeUpdate();

주의

  • 벌크 연산은 영속성 컨텍스트를 무시하고 DB에 직접 쿼리한다.
    • 벌크 연산을 먼저 실행
      • 영속성 컨텍스트에 작업을 하지 않고 벌크 연산을 실행한다.
    • 벌크 연산 수행 후 영속성 컨텍스트를 초기화
      • 벌크 연산을 수행하면 어쨌든 쿼리가 나가기 때문에 벌크 연산 전에 강제적으로 flush()가 된다.
      • flush()가 됐기 때문에 영속성 컨텍스트를 초기화를 해주면 된다.

만약 초기화를 하지 않으면 Member의 이름을 바꿨는데 영속성 컨텍스트에는 바꾸기 전 값이 있고, DB에는 바꾼 뒤 값이 있을 수 있다.

// 모든 Member의 나이는 1로 세팅.

// flush()
int resultCount = em.createQuery("update Member m set m.age = 20").executeUpdate();

System.out.println("member1.getAge() = " + member1.getAge());
System.out.println("member2.getAge() = " + member2.getAge());
System.out.println("member3.getAge() = " + member3.getAge());

// 결과
member1.getAge() = 1
member2.getAge() = 1
member3.getAge() = 1

// 하지만 DB에는 모두 20이라고 바뀜.

초기화를 하면 결과가 달라진다.

// 모든 Member의 나이는 1로 세팅.

// flush()
int resultCount = em.createQuery("update Member m set m.age = 20").executeUpdate();

em.clear();

Member findMember = em.find(Member.class, member1.getId());

System.out.println("findMember = " + findMember.getAge());

// 결과
findMember = 20

참고

Spring Date JPA에서는 Modifying Quries를 통해 벌크 연산을 지원한다.

출처 : 인프런 김영한 지식공유자님의 스프링 부트와 JPA 실무 완전 정복 로드맵 강의
728x90
Comments