문법
- 예) select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자 구분 (Member, age)
- JPQL 키워드는 대소문자 구분 X (select, from, where)
- 엔티티 이름 사용 (테이블명 X)
- 별칭 필수 (m), as 생략 가능
집합과 정렬
TypeQuery, Query
- TypeQuery: 반환 타입 명확할 때
- Query: 반환 타입 명확하지 않을 때
TypedQuery<Member> q1 = em.createQuery("select m from Member as m where m.age > 18", Member.class);
Query q2 = em.createQuery("select m.username, m.age from Member as m where m.age > 18");
결과 조회 API
- query.getResultList(): 결과가 하나 이상, 리스트 반환
- 결과 없다면 빈 리스트
List<Member> result = em.createQuery("select m from Member as m where m.username = :username", Member.class)
.getResultList();
- query.getSingleResult(): 결과가 딱 하나, 단일 객체 반환
- 결과 없다면 javax.persistence.NoResultException
- 결과가 둘 이상이면 javax.persistence.NonUniqueResultException
- 참고: iter 단축키
- 파라미터 바인딩: 이름 기준으로 할 것!
// 이름 기준
Member result = em.createQuery("select m from Member as m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult();
// 위치 기준 - 지양할 것
Member result = em.createQuery("select m from Member as m where m.username = ?1", Member.class)
.setParameter(1, "member1")
.getSingleResult();
프로젝션
- SELECT 절에서 조회 대상 지정하는 것
- 엔티티, 임베디드 타입, 기본 값 타입
- DISTINCT로 중복 제거
- 이렇게 조회한 엔티티는 영속성 컨텍스트가 관리해줌
- member와 연관관계가 맺어진 team의 경우 명시적인 구문을 사용하는 게 좋다
- 임베디드 타입인 address는 주인인 엔티티(Member)로부터 조회해야함
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
List<Team> result = em.createQuery("select m.team from Member as m", Team.class)
.getResultList();
List<Address> result = em.createQuery("select o.address from Order as o", Address.class)
.getResultList();
em.createQuery("select m.age, m.username from Member as m")
.getResultList();
- 여러 값 조회
- select m.username, m.age from Member as m
- 1. Query 타입으로 조회
- 2. Object[] 타입으로 조회
- 3. new 로 조회
- 엔티티의 dto 생성 후 조회
- dto에 순서와 타입 일치하는 생성자 필요
- 패키지 명을 포함한 전체 클래스명 입력(jpql.MembetDto)
List<MemberDto> resultList = em.createQuery("select new jpql.MemberDto(m.username, m.age) from Member as m", MemberDto.class)
.getResultList();
페이징 API
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터)
- setMaxResults(int maxResult) : 조회할 데이터 수
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
조인
- 세타 조인: 관계 없는 엔티티끼리 조인
- ON 절:
- 조인 대상 필터링
- 예) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
- SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
- 연관관계 없는 엔티티 외부 조인 (전에는 내부 조인만 됐음)
- 회원의 이름과 팀의 이름이 같은 대상 외부 조인
- SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
- 조인 대상 필터링
서브 쿼리
쿼리안의 쿼리
- SQL과 마찬가지로 위 예시처럼 서브쿼리에서 m 대신 m2로 사용하여 쿼리끼리 관계없게 해야 성능 더 좋음
지원 함수
- [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
- {ALL | ANY | SOME} (subquery)
- ALL 모두 만족하면 참
- ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참
- [NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
예제
- 팀A 소속인 회원
select m from Member m
where exists (select t from m.team t where t.name = ‘팀A') - 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p) - 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY (select t from Team t)
한계
- JPA 표준은 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
- 하이버네이트가 SELECT절에서도 사용 가능하게 해줌
- FROM 절의 서브 쿼리는 현재 JPQL에서 불가능 -> 조인으로 풀 수 있다면 풀어서 해결
- 그래도 안되면 쿼리 두번 실행, 혹은 네이티브 SQL 사용
JPQL 타입 표현과 기타식
타입 표현
- 문자: 'HELLO', 'She''s'
- 숫자: 10L, 10D, 10F
- Boolean: TRUE, FALSE
- ENUM: jpabook.MemberType.Admin (패키지명 포함) -> 파라미터 바인딩
- 엔티티: TYPE(m) = Member (상속관계에서 사용)
기타
- EXISTS, IN
- AND, OR, NOT
- =, >, >=, <, <=, <>
- BETWEEN, LIKE, IS NULL
조건식 - CASE 식
- 모두 JPA 표준 함수이기 때문에 모든 DB에서 사용 가능
// 기본 Case 식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
// 단순 Case 식
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
- COALESCE: 왼쪽 값이 null이 아니면 반환, null이면 오른쪽 값 반환
select coalesce(m.username, '이름 없는 회원') from Member m
- NULLIF: 두 값이 같으면 null, 다르면 왼쪽 값 반환
select NULLIF(m.username, '관리자') from Member m
JPQL 기본 함수
- JPA 표준
- CONCAT: 문자 합치기
- SUBSTRING: 문자 자르기
- TRIM: 공백 제거
- LOWER, UPPER: 대소문자 변경
- LENGTH: 문자 길이
- LOCATE: 문자 위치 확인
- ABS, SQRT, MOD
- SIZE: 연관관계가 맺어진 컬렉션 필드의 크기
- INDEX(JPA 용도)
- 사용자 정의 함수
- hibernate의 방언 클래스에 이미 많은 함수들이 등록되어있어 그냥 사용하면 됨 -> 여기에도 없다면 직접 등록
- 사용하는 DB 방언을 상속받고, 사용자 정의함수 등록 -> 설정파일에 반영 후 사용
- hibernate가 각 방언에 함수 등록한 코드 참고
// 사용자 정의 함수 등록 (강의에서는 이미 있는 함수 재사용)
public class MyH2Dialect extends H2Dialect {
public MyH2Dialect() {
registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
// jpql로 사용
select function('group_concat', i.name) from Item i
* Un-inject Language/Reference로 ide가 제공하는 기능 끄거나 hql(hibernate query)로 inject해야 하는 경우 있음!
'course > inflearn' 카테고리의 다른 글
[실전! Querydsl] 기본 문법 (1) | 2022.11.22 |
---|---|
[JPA 프로그래밍 기본편] JPQL - 중급 문법 (0) | 2022.11.10 |
[JPA 프로그래밍 기본편] 객체지향 쿼리 언어 소개 (0) | 2022.09.19 |
[JPA 프로그래밍 기본편] 값 타입 (0) | 2022.09.18 |
[JPA 프로그래밍 기본편] 프록시와 연관관계 관리 (0) | 2022.09.09 |