객체지향 쿼리 심화

원종서·2022년 2월 12일
1

JPA

목록 보기
12/13

벌크 연산

엔티티를 수정하려면 연속성 컨텍스트의 변경 감지 기능이나 병합을 사용하고 삭제하라면 em.remove를 사용한다.
하지만 이 방법은 대량의 데이터를 하나씩 처리하기엔 너무 많은 시간이 걸린다.
이런 여러건의 데이터를 한번에 수정 삭제 처리할 때는 벌크연산을 사용한다.

// 수정
String updateJpql = "update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount"l

int upResultCnt = em.createQuery(updateJpql)
	.setParameter("stockAmount", 10)
    .executeUpdate();
    
//삭제

String deleteJpql = "delete from Product p where p.stockAmount < :stockAmount";

int delResultCnt = em.createQuery(deleteJpql)
	.setParameter("stockAmount", 10)
    .executeDelete();

주의점

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

해결방법

1.em.refresh()

벌크 연산을 한 직후 강제로 em.refresh() 해서 데베에서 엔티티를 다시 조회한다.

2.벌크 연산 먼저 실행

3. 벌크 연산 수행 후 영속성 컨텍스트 초기화

영속성 컨텍스느와 JPQL

쿼리 후 영속 상태인 것과 아닌것.

JPQL로 엔티티를 조회하면 영속성 컨텍스트에서 관리되지만 임베디드 타입이나, 값 타입은 영속성 컨텍스트에서 관리되지 않는다.

JPQL로 조회한 엔티티와 영속성 컨텍스트

영속성 컨텍스트에 회원1이 있는데 JPQL로 회원1을 다시 조회하면 ,
두번째로 찾은 JPQL로 회원1은 날려버리고 기존의 영속성 컨텍스트의 회원1을 반환한다.

이유는 영속성 컨텍스트는 영속 상태인 엔티티의 동일성을 보장해야하기 때문이다.

JPQL는 다이렉트로 데베를 뒤져서 원하는 식별자에 관한 엔티티를 찾고 이를 영속성 컨텍스트에 저장 후 반환한다.

JPQL과 플러시 모드

FlushMode.AUTO 보다는 FlushMode.COMMIT 이 성능이 좋다. 그래서 만약 플러시 모드를 커밋으로 설정해 놓고, JPQL을 사용하다보면 원하는 값이 나오지 않을 수 있다.

만약 영속성 컨텍스트의 엔티티의 값을 변경하고 , 해당 엔티티를 JPQL 로 검색했을 시 ( 플러시 모드가 커밋임으로 JPQL 을 실행하기 전에 flush되지 않음) 변경되기 전의 값을 받는다.

이런 상황에서는

product.setPrice(2000);

//em.flush(); 를 하던가

Product product2 = em.createQuery("select p from Product p where p.price = 2000" ,Product.class)
	.setFlushMode(FlushModeTpye.AUTO) // 를 해야한다.
    .getSingleResult();

FlushMode.COMMIT 모드는 트랜잭션을 커밋할 때만 플러시하고 쿼리를 실행할 떄는 플러시 하지 않는다.
따랗서 JPA 쿼리를 사용할 때 영속성 컨텍스트에 있지만 아직 데베에 반영되지 않는 데이터를 조회할 수 없다.
이런 상황은 데이터 무결성에 심각한 피해를 줄 수 있다.
그럼에도 커밋모드를 사용하면 쿼리를 발생하는 플러시 횟수를 줄여서 성능을 최적화 할 수 있따.

0개의 댓글