한번에 여러 데이터를 수정할 수 있는 벌크 연산이 있다.
엔티티를 수정하려면 영속성 컨텍스트 변경 감지 기능이나 병합을 사용하고, 삭제하려면 em.remove()
를 사용한다. 데이터를 하나씩 처리하기엔 너무 오래걸려서 여러개를 동시에 처리할 수 있는 벌크연산이 있다.
벌크 연산은 executeUpdate()
를 사용한다. 이 메소드는 벌크 연산으로 영향을 받은 엔티티 건수를 반환한다.
주의사항
벌크 연산은 영속성 컨텍스트를 통하지 않고 바로 DB에 직접 쿼리를 날린다. 그래서 영속성 컨텍스트에 있는 엔티티와 DB에 있는 테이블의 칼럼 값이 다를 수 있는데, 그래서 이 부분을 주의하여야 한다.
em.refresh
사용select m from Member m //엔티티 조회 (영속성 O)
select o.address from Order o //임베디드 타입 조회 (영속성 X)
select m.id, m.name from Member m // 필드 조회 (영속성 X)
엔티티 전체를 조회해야만이 영속성 컨텍스트가 관리한다❗️
앞선 포스팅에서 JPQL로 DB에서 조회한 엔티티가 영속성 컨텍스트에 있다면 JPQL로 DB에서 조회한 값은 버리고 영속성 컨텍스트에 있던것을 꺼내온다고 했다.
덮어쓰거나 하게 된다면 컨텍스트안에서 수정 중이었던 데이터가 사라질 수 있어 위험하다.
그래서 영속성 컨텍스트는 엔티티의 동일성을 보장하기 때문에 em.find
로 조회를 하던, JPQL을 사용하던 영속성 컨텍스트가 같으면 동일한 엔티티를 반환해준다.
JPQL의 특징
그래서 영속성 컨텍스트의 1차 캐시를 되도록이면 관리하여 많이 이용하는 것이 DB에 부하를 적게 주는 것이고 그게 바람직한 사용방법인것 같다는 나의 견해? 🤔
flush는 영속성 컨텍스트의 변경 내역을 DB에 동기화 해주는 것이다.
그래서 JPA는 flush가 발생했을 때 쓰기지연 SQL 저장소 라고 했던 저장소에 있던 쿼리들을 쭉 만들어 DB에 반영해준다. flush를 호출하려면 em.flush()
를 하거나 flush 모드에 따라 커밋 직전이나 쿼리 실행 직전에 자동 호출된다.
JPQL은 영속성 컨텍스트 데이터를 고려하지 않고 DB에서 조회하기 때문에 사용할때는 반드시 영속성 컨텍스트의 변경사항을 flush해주어야 한다. 그렇지 않으면 데이터가 섞일 수 있다.
@Test
@DisplayName("쿼리와 플러시 모드")
void queryAndFlushTest() {
em.setFlushMode(FlushModeType.COMMIT);
Item item = em.find(Item.class, 1L);
item.setPrice(2000);
Object item2 = em.createQuery("select i from Item i where i.price = 2000").getSingleResult();
System.out.println(item2.toString());
}
flush모드를 commit시에만 플러시로 설정해놓으면
이러한 select쿼리문 후에 에러를 발생한다.
맞는 엔티티를 찾을 수 없다고 나오게 된다.
빌더
역할만 하므로 JPQL을 잘 알아야 함!