728x90
반응형
벌크 연산 설명과 사용법
벌크 연산은 말 그대로 여러 데이터를 한꺼번에 변경하는 것을 의미한다.
예를 들어 재고가 10개 미만인 모든 상품의 가격을 10% 상승시키려고 한다면 JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL을 실행시켜야 한다.
즉, 아래와 같은 과정으로 실행이 될 것이다.
- 재고가 10개 미만인 상품을 리스트로 조회
- 상품 엔티티의 가격을 10% 증가
- 트랙재션 커밋 시점에 변경감지가 동작
변경된 데이터가 100건, 1000건이라면 100번, 1000번의 UPDATE SQL을 실행시킨다.
벌크 연산은...
- 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
- executeUpdate()의 결과는 영향받은 엔티티 수를 반환한다.
- UPDATE, DELETE 지원한다.
- INSERT(insert into .. select, 하이버네이트 지원)
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();
벌크 연산 주의
벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 하기 때문에 (바로 쿼리가 들어가는 것) 잘못하면 데이터가 꼬이게 되고, 정합성이 안맞을 수 있다.
하나 예시로 들자면 아래와 같이 Team과 Member를 저장하고 벌크 연산으로 Member의 나이를 다 20으로 변경을 하고 있다.
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team teamA = new Team();
teamA.setName("teamA");
em.persist(teamA);
Team teamB = new Team();
teamB.setName("teamB");
em.persist(teamB);
Member member1 = new Member();
member1.setUsername("회원1");
member1.setTeam(teamA);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("회원2");
member2.setTeam(teamB);
em.persist(member2);
Member member3 = new Member();
member3.setUsername("회원3");
member3.setTeam(teamA);
em.persist(member3);
Member member4 = new Member();
member4.setUsername("회원4");
em.persist(member4);
int resultCount = em.createQuery("update Member m set m.age = 20").executeUpdate();
System.out.println("resultCount = " + resultCount);
Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember.getAge() = " + findMember.getAge());
tx.commit();
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
} finally {
em.close();
}
emf.close();
}
}
위의 코드를 실행시키면 아래와 같이 결과가 나오게 된다. member1의 나이가 20이 나올거 같지만 0으로 나온다.

위와 같은 문제점을 해결하기 위해서는
1) 영속성 컨텍스트에 값을 넣지말고 벌크 연산을 먼저 실행하거나
2) 벌크 연산 수행 후 영속성 컨텍스트를 초기화를 시킨다.
2번의 방법으로 해결해보자면 벌크 연산 수행 후 em.clear();을 넣어주면 결과가 20으로 나오는 것을 확인할 수 있다.

728x90
반응형
'TIL > JPA' 카테고리의 다른 글
[TIL/JPA] 기본개념 : Named 쿼리 (0) | 2024.09.17 |
---|---|
[TIL/JPA] 기본개념 : JPQL에서 엔티티 직접 사용하기 (0) | 2024.09.17 |
[TIL/JPA] 기본개념 : 다형성 쿼리 (0) | 2024.09.15 |
[TIL/JPA] 기본개념 : 페치 조인(fetch join) (0) | 2024.09.11 |
[TIL/JPA] 기본개념 : 경로 표현식 (0) | 2024.09.10 |