[JPA] 객체 지향 쿼리 심화

Lee Seung Jae·2021년 8월 1일
0

객체 지향 쿼리 심화

한번에 여러 데이터를 수정할 수 있는 벌크 연산이 있다.

벌크 연산

엔티티를 수정하려면 영속성 컨텍스트 변경 감지 기능이나 병합을 사용하고, 삭제하려면 em.remove() 를 사용한다. 데이터를 하나씩 처리하기엔 너무 오래걸려서 여러개를 동시에 처리할 수 있는 벌크연산이 있다.

벌크 연산은 executeUpdate() 를 사용한다. 이 메소드는 벌크 연산으로 영향을 받은 엔티티 건수를 반환한다.

주의사항

벌크 연산은 영속성 컨텍스트를 통하지 않고 바로 DB에 직접 쿼리를 날린다. 그래서 영속성 컨텍스트에 있는 엔티티와 DB에 있는 테이블의 칼럼 값이 다를 수 있는데, 그래서 이 부분을 주의하여야 한다.

  • em.refresh 사용
    • 엔티티를 사용해야 할 경우엔 DB에서 다시 조회한다.
  • 벌크 연산 먼저 실행
    • 벌크연산을 가장 먼저 실행하여 실행한 후에 조회쿼리를 하면 변경된 것으로 조회가 된다.
  • 벌크연산 수행 후 영속성 컨텍스트 초기화
    • 수행 직후 컨텍스트를 초기화하여 엔티티를 제거했다가 벌크연산이 적용된 DB에서 조회하는 방법

영속성 컨텍스트와 JPQL

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

select m from Member m //엔티티 조회 (영속성 O)
select o.address from Order o //임베디드 타입 조회 (영속성 X)
select m.id, m.name from Member m // 필드 조회 (영속성 X)

엔티티 전체를 조회해야만이 영속성 컨텍스트가 관리한다❗️

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

앞선 포스팅에서 JPQL로 DB에서 조회한 엔티티가 영속성 컨텍스트에 있다면 JPQL로 DB에서 조회한 값은 버리고 영속성 컨텍스트에 있던것을 꺼내온다고 했다.

덮어쓰거나 하게 된다면 컨텍스트안에서 수정 중이었던 데이터가 사라질 수 있어 위험하다.
그래서 영속성 컨텍스트는 엔티티의 동일성을 보장하기 때문에 em.find로 조회를 하던, JPQL을 사용하던 영속성 컨텍스트가 같으면 동일한 엔티티를 반환해준다.

JPQL의 특징

  • JPQL은 항상 DB를 조회한다.
  • JPQL로 조회한 엔티티는 영속 상태이다.
  • 영속성 컨텍스트에 이미 존재하는 엔티티가 있으면 기존 엔티티를 반환한다.

그래서 영속성 컨텍스트의 1차 캐시를 되도록이면 관리하여 많이 이용하는 것이 DB에 부하를 적게 주는 것이고 그게 바람직한 사용방법인것 같다는 나의 견해? 🤔

JPQL과 플러시 모드

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은 SQL을 추상화하여 특정 DB에 의존하지 않는다.
  • QueryDSL은 JPQL을 만드는 빌더 역할만 하므로 JPQL을 잘 알아야 함!
  • QueryDSL을 사용하면 동적 쿼리를 생성하기가 편리하다.
  • QueryDSL은 JPA가 공식 지원하는 것은 아니지만 직관적이고 편리하다.
  • JPA도 네이티브 쿼리를 지원하지만, 종속적인 SQL을 사용하게 되면 특정 DB에만 한정적인게 된다.
    • JPQL을 최대한 활용 해보고 안되면 그 때 네이티브 SQL을 사용하자😊
  • JPQL은 대용량 수정, 삭제를 할 수 있는 벌크 연산을 지원한다.
profile
💻 많이 짜보고 많이 경험해보자 https://lsj8367.tistory.com/ 블로그 주소 옮김

0개의 댓글