JPQL(java Persistence Query Language)

  • 엔티티 객체를 조회하는 객체지향 쿼리 (테이블을 대상으로 쿼리하는 것이 아니다.)
  • SQL을 추상화해서 특정 vendor에 의존하지 않는다.
  • JPQL은 일반 Query로 반환된다.
  • 기본 ANSI query랑 구조가 비슷하다.
  • 엔티티 이름이 SQL의 테이블 이름과 동일시 된다.
  • 별칭은 필수로 들어간다.

TypeQuery, Query

  • 쿼리 결과를 어떤 것으로 반환할지를 지정한다. (~.class)로 지정해서
  • 그게 아니면 Object[]를 반환한다.

결과

  • query.getResultList()
  • query.getSingleResult()

파라미터 바인딩

  • NamedParameters 라는 이름으로 구분하는 방법이다. :parameterName으로 매핑한다.
  • PositionParameters 라는 이름으로 위치 값을 주면 된다. ?1이런식으로 바인딩한다.

프로젝션

  • SELECT 절에 조회할 대상을 지정하는 것을 projection이라고 한다.
  • projection에는 TypeQuery 사용이 불가하다. Query를 사용해야 한다.

    엔티티 프로젝션

  • SELECT m FROM Member m
  • SELECT m.team FROM Member m
  • 조회한 엔티티는 영속성 컨텍스트에서 관리된다.

임베디드 타입 프로젝션

  • 엔티티와 거의 비슷하다.
  • 임베디드는 조회의 시작점이 될 수 없다.
  • SELECT o.address FROM Order o
  • 임베디드 타입은 엔티티 타입이 아닌 값 타입이다.
  • 따라서 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.

스칼라 타입 프로젝션

  • 숫자, 문자, 날짜와 같은 기본 데이터 타입들이다.
  • SELECT DISTINCT username FROM member m

Projection Dto 매핑

  • Object[]로 사용해서 일일히 매핑하면 힘들다. ```java TypeQuery query = em.createQuery( """ SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m """ , UserDTO.class);

List resultList = query.getResultList();

- 이렇게 DTO 매핑도 가능하다.

### 페이징
- `setFirstResult`, `setMaxResults`로 페이징 조회를 한다.

### JPQL 조인
- SQL과 기능은 같고 문법은 약간 다르다.

#### Inner/Outer
```java
"""
SELECT m
FROM Member m
INNER JOIN m.team t
WHERE t.name = :teamName
"""
//JPQL

"""
SELECT
    M.ID AS ID,
    M.AGE AS AGE,
    M.TEAM_ID AS TEAM_ID,
    M.NAME AS NAME
FROM
    MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
WHERE
    T.NAME = ?
"""
  • 위와 같이 거의 비슷하다. 특이한 점은 연관 관계를 가지기 위해서 연관 필드를 이용한다는 점이다.

FetchJoin

  • SQL 관점의 Join은 아니고 JPQL에서 성능 최적화를 위해서 제공하는 기능이다.
  • 엔티티나 컬렉션을 한 번에 같이 조회하는 기능이다. join fetch로 사용할 수 있다.
  • 지연 로딩 대상을 대상으로 join fetch를 하면 프록시가 아닌 실제 엔티티이므로 메인 엔티티가 영속성 컨텍스트에서 분리되어 준영속이 되더라도 같이 조회한 엔티티를 영속성 컨텍스트에서 조회할 수 있다.
  • 만약 안쓴다면?
    • JPQL은 결과를 반환할 때 연관관계를 고려하지 않으므로 연관 관계는 프록시 상태로 있는다.
  • 페치 조인은 한 번에 연관된 관계를 함께 조회할 수 있어서 SQL 호출 횟수를 줄일 수 있다.
  • 글로벌 로딩 전략을 지연 로딩으로 설정해도 JPQL에서 페치 조인을 사용하면 페치 조인을 적용해서 함께 조회한다.
    • 최적화를 위해서 EAGER로 설정하면 즉시 로딩이 벌어져서 성능에 악영향을 미칠 수 있다.
  • 그러나 join fetch에도 단점이 있다.
    1. fetch join 대상에 별칭을 줄 수 없다.
    2. 둘 이상의 컬렉션을 fetch 할 수 없다. 구현체에 따라 되기도 하는데 카테시안 곱이 만들어지므로 주의해야 한다.
    3. 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.
      1. 1:1이라면 상관 없다.
      2. 만약 페이징 API를 쓰라고 하면 메모리에서 페이징 처리를 한다. 성능 이슈, 메모리 초과 예외가 발생할 수도 있다.

서브 쿼리

  • WHERE, HAVING에만 사용할 수 있다.
  • SELECT, FROM은 사용 불가능하다.
  • EXISTS, ALL ANY SOME, IN 을 서브 쿼리에서 사용할 수 있다.

Named 쿼리: 정적쿼리

  • 동적 쿼리: em.createQuery("SELECT ... ") 처럼 JPQL을 문자로 완성해서 직접 넘기는 것
  • 정적 쿼리: 미리 정의한 쿼리에 이름을 부여해서 필요할 떄 사용하는 것 @NamedQuery