8

yeoro·2021년 9월 30일
0

JPQL(Java Persistence Query Language) 기본

개요

JPA는 다양한 쿼리 방법을 지원한다.

  • JPQL
  • JPA Criteria
  • QueryDSL
  • 네이티브 SQL
  • JDBC API 직접 사용
  • MyBatis, SpringJdbcTemplate과 같이 사용

등장 배경

JPA를 사용하면 엔티티 객체를 중심으로 개발한다.
이 때 문제는, 테이블이 아닌 엔티티 객체를 대상으로 검색해야 하는데, 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하다.

애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL 필요 (where, group by 등으로 필터링)

따라서 JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.

엔티티 객체를 대상으로 생성된 쿼리(JPQL)를 DB 테이블을 대상으로하는 SQL 쿼리로 변환시켜준다.

기본 문법과 쿼리 API

문법

  • select m from Member as m where m.age > 18
  • 엔티티와 속성은 대소문자를 구분해야 한다. (객체의 변수명과 똑같이)
  • JPQL 키워드는 대소문자 구분X(select, SELECT)
  • 테이블 이름이 아닌 엔티티 이름 사용
  • 별칭이 필수

TypedQuery, Query

TypedQuery

반환 타입이 명확할 경우 사용, 2번 째 인자로 클래스 명시

QUery

반환 타입이 명확하지 않을 경우 사용

결과 조회 API

query.getResultList()

  • 결과가 하나 이상일 때 리스트 반환
  • 결과가 없으면 빈 리스트 반환

query.getSingleResult()

  • 결과가 정확히 하나이며 단일 객체 반환
  • 결과가 없으면 NoResultException
  • 둘 이상이면 NonUniqueResultException
  • 결과가 무조건 하나인 경우만 사용
  • Spring Data JPA는 두 예외를 모두 처리해놨다.

파라미터 바인딩

이름

'where m.username = :username'
query.setParameter("username", 찾을 값)

위치

'where m.username = ?1'
query.setParameter(1, 찾을 값)

  • 순서 중간에 값이 바뀌면 장애가 일어나므로 사용X

프로젝션

  • SELECT 절에 조회할 대상을 지정하는 것
  • 프로젝션 대상
    - 엔티티 : 영속성 컨텍스트에서 관리
    • 연관관계 엔티티 : inner join으로 조회, jpql 작성시 join을 명시적으로 작성하는게 좋음
    • 임베디드 타입 : 소속 클래스를 기준으로 조회
    • 스칼라 타입(숫자, 문자 등 기본 데이터 타입)

여러 값 조회

1. Query 타입 조회

2. Query[] 타입 조회

3. new 명령어 조회

select new jpql.MemberDTO(m.username, m.age) from Member m

  • 단순 값을 DTO로 바로 조회

  • 패키지 명을 포함한 전체 클래스 명 입력

  • 순서와 타입이 일치하는 생성자 필요

페이징 API

  • JPA는 페이징을 두 API로 추상화 함

setFirstResult(int startPosition)

  • 조회 시작 위치(0부터 시작)

setMaxResults(int maxResult)

  • 조회할 데이터 수

조인

내부 조인

select m from member m (inner) join m.team t

외부 조인

select m from member m left (outer) join m.team t

세타 조인

select count(m) from member m, team t where m.username = t.name

ON 절 (JPA 2.1 부터 지원)

조인 대상 필터링

ex) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'

연관관계 없는 엔티티 외부 조인

ex) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name


서브 쿼리

지원 함수

(NOT) EXISTS

서브 쿼리에 결과가 존재하면 참

ALL/ANY/SOME

  • ALL : 서브 쿼리의 모든 결과와 비교했을 때 만족하는 경우
  • ANY, SOME : 둘은 같은 의미이며, 조건을 하나라도 만족하는 경우

(NOT) IN

서브 쿼리의 결과 중 하나라도 같은 것이 있으면 참

JPA 서브쿼리 한계

WHERE, HAVING 절에서만 서브쿼리 사용 가능

하이버네이트에서는 SELECT 절 사용도 지원함

FROM 절의 서브쿼리(인라인 뷰)는 현재 JPQL에서 불가능

  • 조인으로 해결할 수 있으면 해결

타입 표현과 기타식

문자

  • 'HELLO' -> HELLO
  • 'She''s' -> She's

숫자

  • Long : 10L
  • Double : 10D
  • Float : 10F

논리

  • TRUE
  • FALSE

Enum

  • jpql.MemberType.ADMIN (패키지명 포함)

상속 관계의 엔티티

SELECT i FROm Item i WHERE TYPE(i) = Book

조건식

기본 CASE

여러 컬럼에 대한 조건을 세우고 그에 따른 결과값 명시

단순 CASE

특정 컬럼에 대한 조건을 세우고 그에 따른 결과값 명시

COALESCE

하나씩 조회해서 null이 아니면 반환
select coalesce(m.username, '이름 없는 회원') from Member m

NULLIF

두 값이 같으면 null, 다르면 첫번째 값
select NULLIF(m.username, '관리자') from Member m

JPQL 함수

기본 함수

  • CONCAT('a', 'b') == 'a' || 'b'
  • SUBSTRING
  • TRIM(L, R)
  • LOWER, UPPER
  • LENGTH
  • LOCATE
  • ABS, SQRT, MOD
  • SIZE, INDEX(JPA 용도)

사용자 정의 함수

  • 하이버네이트는 함수 사용전 방언에 추가해야 한다.
  • 사용하는 DB 방언을 상속받고, 사용자 정의 함수 등록
  • 각 DB 방언에는 아래와 같이 함수가 미리 등록되어 있다.


0개의 댓글