JPQL
테이블 대상이 아닌 엔티티 객체를 대상으로 쿼리한다. 따라서 SQL을 추상화하여 특정 데이터베이스 SQL에 의존하지 않는다.
문법
select m from **Member** as m where **m.age** > 18
- 엔티티는 대소문자 구분 O, 키워드 구분 X
- 테이블 이름이 아닌 엔티티 이름 사용
- 별칭 필수!
- 일반적으로 SQL 문법과 비슷함
TypeQuery, Query
반환 타입이 명확할 경우 , TypeQuery
반환 타입이 불명확 할경우 , Query
결과 조회 api
try {
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);
System.out.println("asda");
TypedQuery<Member> query1= em.createQuery("select m from Member as m" , Member.class);
Member result = query1.getSingleResult();
System.out.println("asd" + result);
transaction.commit();
}
getResultList() : 결과가 하나 이상일 경우, 리스트를 반환한다.
getSingleResult은 오직 결과가 1개 있을 때 , 단일 객체이어야 한다. 아닐 경우 no unique exception이 발생한다.
파라미터 바인딩
- 이름 기반
TypedQuery<Member> query1= em.createQuery("select m from Member as m where m.username = :username" , Member.class);
query1.setParameter("username", "member1");
위치 기반으로도 가능하지만 유지 보수, 수정 시에 순서가 밀린다거나 장애가 생길 수 있으니 가급적 이름 기반 파라미터 바인딩을 사용하자.
프로젝션
- select 절에 조회할 대상을 지정하는 것.
엔티티 프로젝션 , 임베디드 타입 프로젝션 , 스칼라 타입 플로젝션을 대상으로 조회 할 수 있다.
- select로 가져온 값들은 모두 영속성 컨텍스트에서 관리된다.
여러값 조회
- Query 타입으로 조회한다.
- Object[] 타입으로 조회한다.
- new 명령어(DTO)로 조회하는 방법이 있다.
- 패키지 명을 포함한 전체 클래스 명을 입력해야한다.
- 순서와 타입이 일치하는 생성자가 필요하다.
Query query1= em.createQuery("select new jpql.MemberDTO(m,username, m.age) from Member as m" , Member.class);
페이징 API
Query query1= em.createQuery("select m from Member as m" , Member.class)
.setFirstResult(0)
.setMaxResults(3);
.setFirstResult(0) : 조회 위치 시작
.setMaxResults(3) : 조회할 데이터 수
조인
- 전반적으로 sql join과 유사함.
//내부 조인
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](http://t.name/)
ON 절
//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'
SQL 문에서는 id가 같다는 것을 명시적으로 지정해주는데 JPQL은 ON을 적용하는 것으로 바로 id를 기준으로 조인해준다.
- 따로 id로 on을 적용해도 문제 없음.
서브 쿼리
sql과 함수 기능이 동일함.
예1) 나이가 평균보다 많은 회원
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
예2) 한 건이라도 주문한 고객
select m from Member m
where (select count(o) from Order o where m = o.member) > 0
예3) 팀 A 소속인 회원
select m from Member m
where exists (select t from m.team t where t.name = ‘팀A')
예4) 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL (select p.stockAmount from Product p)
예5) 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY (select t from Team t)
- FROM절의 서브 쿼리는 하이버네이트6부터 지원하지만, 서브 쿼리를 join으로 풀어서 해결하는게 좋다.
JPQL 타입 표현
- ENUM (패키지명을 포함해야한다.)
String query = "SELECT m FROM Member m WHERE m.memberType = jpabook.MemberType.Admin";
- 엔티티 (상속관계에서 사용한다.)
String query = "SELECT m FROM Member m WHERE TYPE(m) = AdminMember";
가급적 하드코딩으로 복잡하게 하기보다, setParameter에 지정해서 사용하자.
QueryDSL을 사용하면 어렵지 않게 각 클래스를 import해서 사용할 수 있다.
조건식 - CASE
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
- COALESCE : 하나씩 조회해서 null이면 원하는 값 반환
select coalesce(m.username,'이름 없는 회원') from Member m
- NULLIF : 두 값이 같으면 null, 다르면 첫번째 값 반환
이 표준 함수들은 sql과 함수가 모두 동일하고 어느 데이터베이스에서 사용하더라고 사용이 가능하다.