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 mSELECT 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
- 이렇게 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에도 단점이 있다.- fetch join 대상에 별칭을 줄 수 없다.
- 둘 이상의 컬렉션을 fetch 할 수 없다. 구현체에 따라 되기도 하는데 카테시안 곱이 만들어지므로 주의해야 한다.
- 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.
- 1:1이라면 상관 없다.
- 만약 페이징 API를 쓰라고 하면 메모리에서 페이징 처리를 한다. 성능 이슈, 메모리 초과 예외가 발생할 수도 있다.
서브 쿼리
- WHERE, HAVING에만 사용할 수 있다.
- SELECT, FROM은 사용 불가능하다.
-
EXISTS, ALL ANY SOME, IN 을 서브 쿼리에서 사용할 수 있다.
Named 쿼리: 정적쿼리
- 동적 쿼리:
em.createQuery("SELECT ... ")처럼 JPQL을 문자로 완성해서 직접 넘기는 것 - 정적 쿼리: 미리 정의한 쿼리에 이름을 부여해서 필요할 떄 사용하는 것
@NamedQuery