[JPA] JPQL

hi·2022년 10월 5일

JPQL

  • 객체지향 쿼리 언어
  • 테이블이 아닌 엔티티 객체를 대상으로 쿼리
  • 특정 데이터 베이스 SQL에 의존하지 않음
  • JPQL은 결국 SQL로 변환된다
  • 영속성 컨텍스트를 거치지 않고 바로 DB로 감
  • 실행 전 flush 호출
    단, 실행될 JPQL과 관련된 엔티티만 부분적으로 flush

문법

select m from Member as m where m.age > 18
  • 엔티티, 속성은 대소문자 구분 O
    Member, age
  • JPQL 키워드는 대소문자 구분 X
    SELECT, FROM, where
  • 테이블 명이 아닌, 엔티티 이름 사용
    Member
  • 별칭 필수, as 생략 가능
    m
  • 집합, 정렬
    GROUP BY, HAVING, ORDER BY

TypeQuery, Query

TypeQuery : 반환 타입 명확 (거의 Entity 타입)
Query : 반환 타입 불명확

TypedQuery<Member> query1 = 
	em.createQuery("select m from Member m", Member.class);

TypedQuery<String> query2 = 
	em.createQuery("select m.username from Member m", String.class); // m.username : string
Query query3 = 
	em.createQuery("select m.username, m.age from Member m"); // m.uesrname : string , m.age : int

결과 조회 API

query.getResultList()

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

query.getSingleResult()

  • 결과가 정확히 하나
  • 단일 객체 반환
  • 결과가 없음 : javax.persistence.NoResultException
    둘 이상 : javax.persistence.NonUniqueResultException

파라미터 바인딩

이름 기준
SELECT m FROM Member m where m.username=:username

query.setParameter("username", usernameParam);


위치 기준
SELECT m FROM Member m where m.username=?1

query.setParameter(1, usernameParam);


프로젝션

  • SELECT절에 조회할 대상을 지정하는 것
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입 (숫자, 문자 등 기본 데이터 타입)

엔티티 프로젝션 1
select m from Member m

엔티티 프로젝션 2
select m.team from Member m

다른 테이블인 경우 JOIN 쿼리가 실행되는데 위 쿼리로는 예측이 불가능
따라서 아래처럼 작성

select t from Member m join m.team t

임베디드 타입 프로젝션
select m.address from Member m

소속되어있기 때문에 아래처럼 작성 불가
임베디드 타입만으로는 안되고 엔티티를 정해줘야함

select address from Address o

스칼라 타입 프로젝션
select m.username, m.age from Member m


프로젝션 - 여러 값 조회

  • Query 타입으로 조회

  • Object[] 타입으로 조회

  • new 명령어로 조회

    • 단순 값을 DTO로 바로 조회
    • 패키지 명을 포함한 전체 클래스 명 입력
    • 순서와 타입이 일치하는 생성자 필요

페이징 API

setFirstResult (int startPosition)

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

setMaxResults (int maxResult)

  • 조회할 데이터 수
ex)

String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
	.setFirstResult(10)
	.setMaxResults(20)
	.getResultList();

  • MySQL 방언
SELECT
	M.ID AS ID,
	M.AGE AS AGE,
	M.TEAM_ID AS TEAM_ID,
	M.NAME AS NAME 
FROM
	MEMBER M 
ORDER BY
	M.NAME DESC LIMIT ?, ?

  • Oracle 방언
SELECT * FROM
	( SELECT ROW_.*, ROWNUM ROWNUM_ 
	FROM
		( SELECT
			M.ID AS ID,
			M.AGE AS AGE,
			M.TEAM_ID AS TEAM_ID,
			M.NAME AS NAME 
		FROM MEMBER M 
		ORDER BY M.NAME 
		) ROW_ 
	WHERE ROWNUM <= ?
	) 
WHERE ROWNUM_ > ?

JOIN

  • inner join
  • outer join
  • 세타 조인 (cross join)

ON절 활용

  • 조인 대상 필터링
  • 연관관계 없는 엔티티 외부 조인 (hibernate 5.1~)

서브쿼리

지원 함수

  • [NOT] EXIST (subquery) : 서브쿼리에 결과가 존재하면 참

    • { ALL | ANY | SOME } (subquery)
      ALL : 모두 만족하면 참
      ANY, SOME : 조건을 하나라도 만족하면 참
  • [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참


한계

  • JPA는 where, having 절에서만 서브 쿼리 사용 가능
  • select 절도 가능 (hibernate 지원)
  • from 절의 서브 쿼리는 현재 JPQL 에서 불가
    조인으로 풀어서 해결

JPQL 타입 표현

문자
'HELLO'
'She''s'

숫자
10L (Long)
10D (Double)
10F (Float)

Boolean
TRUE
FALSE

ENUM
jpabook.MemberType.Admin (패키지명 포함)

엔티티 타입
TYPE(m) = Member (상속 관계에서 사용)


조건식

case ~ end

coalesce
하나씩 조회해서 null이 아니면 반환

nullif
두 값이 같으면 null 반환, 다르면 첫 번째 값 반환


JPQL 기본 함수

concat, substring, trim, lower, upper, length, locate, abs, sqrt, mod
size, index

0개의 댓글