spring/JPA

[JPA] 객체 지향 쿼리 언어 JPQL 7 (엔티티 직접 사용, 네임드 쿼리, 벌크 연산)

대기업 가고 싶은 공돌이 2024. 8. 7. 01:42

엔티티 직접 사용 - 기본 키 값

  • JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용한다.
  • [JPQL]
    select count(m.id) from Member m // 엔티티의 아이디를 사용
    select count(m) from Member m //엔티티를 직접 사용
  • [SQL]
    select count(m.id) as cnt from Member m
    두 JPQL 다 동일한 SQL이 실행된다.

엔티티를 파라미터로 사용해도 똑같이 엔티티는 기본 키 값으로 처리 된다.

 

엔티티가 데이터베이스에 전달되면 데이터베이스에서는 엔티티를 당연히 기본 식별자로 구분하므로,

엔티티가 기본 키 값으로 처리되는 것이다.

 

Named 쿼리 - 정적 쿼리

  • 미리 정의해서 이름을 부여해두고 사용하는 JPQL
  • 정적 쿼리
  • 어노테이션, XML에 정의
  • 애플리케이션 로딩 시점에 초기화 후 재사용
    • 애플리케이션 로딩 시점에 JPA나 하이버네이트가 SQL로 네임드 쿼리를 파싱한 후 캐싱한다.
    • SQL로 파싱하는 코스트가 거의 들지 않는다는 얘기다.
  • 애플리케이션 로딩 시점에 쿼리를 검증
    • 애플리케이션 로딩 시점에 SQL로 파싱 과정 문법이 맞지 않는다면 에러를 발생시킨다.
    • 이 때문에 대부분의 오류를 다 잡을 수 있다.

다음과 같이 엔티티에 네임드 쿼리를 생성하고 쿼리를 재사용 하는 것이다. 

 

스프링 데이터 jpa에서는 jpaRepository에서 전부 네임드 쿼리로 등록해서 사용되게 된다.

 

벌크 연산

벌크 연산은 여러건의 SQL UPDATE와 DELETE문이라고 생각하면 된다.

 

  • 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
  • JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL이 실행된다.
    1. 재고가 10개 미만인 상품을 리스트로 조회한다.
    2. 상품 엔티티의 가격을 10% 증가한다.
    3. 트랜잭션 커밋 시점에 변경 감지가 동작한다.
  • 변경된 데이터가 100건이라면 100번의 UPDATE SQL이 실행된다. 

벌크 연산 예제

  • 쿼리 한 번으로 여러 테이블 로우 변경(엔티티)
  • executeUpdate()의 결과는 영향받은 엔티티 수 반환
  • UPDATE, DELETE 지원
  • INSERT(insert into ... select, 하이버네이트 지원)

예시) update Member m set m.age = 20
          .executeUpdate();

를 실행하면 반환 값으로 영향받은 엔티티의 수가 반환된다.

벌크 연산 주의할 점

  • 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 날린다.

    해결 방법: 
    1. 벌크 연산을 먼저 실행
      • 영속성 컨텍스트에 아무 것도 없는 상황에서 벌크 연산을 먼저 실행한다.
    2. 벌크 연산 수행 후 영속성 컨텍스트 초기화 
      • 벌크 연산이 수행되면 영속성 컨텍스트는 업데이트가 되지 않고 데이터베이스만 업데이트 되니
        싱크가 맞지 않는 문제가 생긴다. 이를 해결하기 위해 벌크 연산 수행 후 영속성 컨텍스트를 초기화 시켜
        영속성 컨텍스트에서 값을 가져오는 일이 없도록 방지하면 된다.

 

참고: 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편