본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.
JPQL - 벌크 연산
일반적인 여러 객체에 대한 업데이트나 delete문이라 생각하면 된다.
- 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
- JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL 실행
- 1)재고가 10개 미만인 상품을 리스트로 조회한다.
- 2)상품 엔티티의 가격을 10% 증가한다.
- 3)트랜잭션 커밋 시점에 변경감지가 동작한다.
- 변경된 데이터가 100건이라면 100번의 UPDATE SQL 실행
벌크 연산 예제
- 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
- executeUpdate()의 결과는 영향받은 엔티티 수 반환
- UPDATE, DELETE 지원
- INSERT(insert into .. select, 하이버네이트 지원)
1
2
3
4
5
6
7
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();
벌크 연산 주의
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
- 이로 인해 잘못하면 꼬일수가 있다.
- 해결책은 두 가지가 있다.
- 1)벌크 연산을 먼저 실행(플러시가 자동으로 됨, 쿼리가 나가는거기에)
- 2)벌크 연산 수행 후 영속성 컨텍스트만 초기화(벌크 연산 수행해서 전 직원의 연봉이 1억인데 애플리케이션에는 7000만원으로 되있다? 문제가 되기에 초기화해준다) ~~~ 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.setAge(10); member1.setType(MemberType.ADMIN); member1.changeTeam(teamA); em.persist(member1);
Member member2 = new Member(); member2.setUsername(“회원2”); member2.setAge(10); member2.setType(MemberType.ADMIN); member2.changeTeam(teamA); em.persist(member2);
Member member3 = new Member(); member3.setUsername(“회원3”); member3.setAge(10); member3.setType(MemberType.ADMIN); member3.changeTeam(teamB); em.persist(member3);
em.flush(); em.clear();
int resultCount = em.createQuery(“update Member m set m.age = 20”) .executeUpdate();
// em.clear(); // 그래서 여기서 영속성 컨텍스트를 초기화해줘야한다. 만약 find()로 다시 불러와도 1차 캐시에 있는 데이터를 가져오기에 반영이 안된다.
System.out.println(“resultCount = “ + resultCount);
System.out.println(“age = “ + member1.getAge()); // age = 10 (영속성 컨텍스트에 있는게 그대로 나온다) System.out.println(“age = “ + member2.getAge()); // age = 10 (영속성 컨텍스트에 있는게 그대로 나온다) System.out.println(“age = “ + member3.getAge()); // age = 10 (영속성 컨텍스트에 있는게 그대로 나온다) ~~~
추가적으로
Spring Data JPA에서 @ModifyingQuery
어노테이션을 활용하면 영속성 컨텍스트를 깔끔하게 초기화해준다.