[JPA] [10] 객체지향 쿼리 언어 - 기본 문법 (하편)
https://www.inflearn.com/course/ORM-JPA-Basic
인프런 김영한 님의 강의를 듣고 작성한 글입니다.
[4] 페이징
JPA는 DB마다 다른 페이징 문법을 두 가지 API로 추상화했다.
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
- setMaxResults(int maxResult) : 조회할 데이터 수
JpaMain
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getReulstList();
- serFirstResult(1)로 지정해서 index 1부터 시작(DESC라 내림차순임)
- setMaxResults(10)으로 지정해 10개의 결괏값이 조회됨.
[5] 조인
■ 내부조인
SELECT m FROM Member m [INNER] JOIN m.team t
예시]
① Member와 Team 다대일 연관관계 매핑(양방향)
※주의 @ManyToOne에 fetch= FetchType.LAZY로 지연 로딩으로 지정해 주는 걸 잊으면 안 된다.
② JpaMain - query : "select m from Member m inner join m.team t"
inner는 생략 가능
■ 외부조인
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
예시]
① JpaMain - query : "select m from Member m left join m.team t
② 참고] query = "select m from Member m left join m.team t where t.name = :teamName"
=> :로 파라미터 바인딩도 추가
■ 세타조인
select count(m) from Member m, Team t where m.username = t.name
=> 연관성이 없는 두 테이블을 조인하는 방법
예시]
JpaMain - query : "select m from Member m, Team t where m.username = t.name"
■ 조인 - on 절
1. 조인 대상 필터링
예) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
JPQL:
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A';
SQL:
SELECT m.*, t.* FROM
Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A';
2. 연관 관계없는 엔티티 외부 조인(하이버네이트 5.1부터)
예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
JPQL:
SELECT m, t FROM
Member m LEFT JOIN Team t on m.username = t.name;
SQL:
SELECT m.*, t.* FROM
Member m LEFT JOIN Team t ON m.username = t.name;
[6] 서브 쿼리
예시 1] 나이가 평균값보다 많은 회원
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
=> 메인쿼리의 Member m과 서브쿼리의 Member m2를 따로 정의. 이렇게 코드를 짜야 성능이 잘 나온다.
예시 2] 한 건이라도 주문한 고객
select m from Member m
where (select count(o) from Order o where m = o.member) > 0;
■ 서브 쿼리 지원 함수
- [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 서브쿼리의 한계(=> 패치로 변경)
하이버네이트 6부터 FROM절의 서브쿼리를 지원한다.
[7] JPQL 타입 표현과 기타식
■ JPQL 타입 표현
- 문자: ‘HELLO’, ‘She’’s’
=> ' 표현하려면 '' 두 번 넣으면 된다.
- 숫자: 10L(Long), 10D(Double), 10F(Float)
- Boolean: TRUE, FALSE
- ENUM: jpabook.MemberType.Admin (패키지명 포함)
주의점 : @Enumerated(EnumType.STRING) 빼먹지 말자.
=> DB엔 enum이 없어서 꼭 이렇게 지정해줘야 한다.
=> ENUM은 자바 패키지명까지 넣어야 하는 걸 잊지 말자.
- 엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)
■ JPQL 기타
- SQL과 문법이 같은 식
- EXISTS, IN
- AND, OR, NOT
- =, >, >=, <, <=, <>
- BETWEEN, LIKE, IS NULL
예시 1] IS NOT NULL
예시 2] BETWEEN
[8] 조건식(CASE 등등)
■ 기본 CASE 식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
주의 : end를 빼먹지 말자
■ 단순 CASE 식
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
■ COALESCE
하나씩 조회해서 null이 아니면 반환
String query = "select coalesce(m.username, '이름 없는 회원') from Member m";
■ NULLIF: 두 값이 같으면 null 반환, 다르면 첫 번째 값 반환
String query = "select nullif(m.username, '관리자') as username from Member m";
[9] JPQL 함수
• CONCAT
• SUBSTRING
• TRIM : 공백 제거
• LOWER, UPPER : 소문자화, 대문자화
• LENGTH : 길이
• LOCATE
• ABS, SQRT, MOD : 절댓값, 루트(제곱근), 나머지
• SIZE, INDEX(JPA 용도)
String query = "select size(t.members) From Team t";
■ 사용자 정의 함수
- 하이버네이트는 사용 전 방언에 추가해야 한다.
- 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.
MyH2Dialect
persistence.xml
JpaMain
String query = "select group_concat(m.username) From Member m";