JPA는 다양한 쿼리 방법을 지원한다
1. JPQL
2. JPA Criteria (JPQL 사용)
3. QueryDsl (JPQL 사용)
4. 네이티브 SQL
5. JDBC API
JPA를 사용하면 엔티티를 중심으로 개발한다
검색 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색할 수 있어야한다
모든 DB 데이터를 객체로 변환하는 것은 불가능
결국 애플리케이션이 필요한 데이터만 불러오려면 검색 조건이 포함된 SQL이 필요함
JPA는 SQL을 추상화한 객체 지향 쿼리 언어 JPQL을 제공한다
JPQL은 엔티티를 대상으로 쿼리한다
// JPQL
SELECT m
FROM Member m
WHERE m.username LIKE '%kim'
문법이 거의 sql과 동일하다
JPQL문도 결국 문자열이기 때문에 동적 쿼리가 어렵고, 실수하기 쉽다
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
Root<Member> m = query.from(Member.class);
CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), "kim"));
List<Member> resultList = em.createQuery(cq.getResultList();
JPAFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list =
query.selectFrom(m)
.where(m.age.gt(18))
.orderBy(m.name.desc())
.fetch();
String sql = "SELECT id, age, team_id, name FROM member WHERE nme = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();
쌩 SQL은 너무 관리하기 힘들다
JDBC를 사용하면 JPA(정확히는 영속성 컨텍스트)와 아무 상관 없음에 주의하자
영속성 컨텍스트를 적절한 시점에 강제로 플러시 해줘야하지만, JDBC 커넥션이나 마이바티스등을 JPA와 쓸 수도 있다
Java Persistence-layer Query Language
JPQL은 엔티티를 대상으로 쿼리한다. 추상화된 쿼리 언어로 특정 DB SQL에 의존하지 않는다
SELECT m FROM Member as m where m.age > 18
SELECT
COUNT(m),
SUM(m.age)
AVG(m.age),
MAX(m.age),
MIN(m.age)
FROM Member m
엔티티와 속성은 대소문자 구분
JPQL 키워드는 대소문자 구분X
테이블명이 아닌 엔티티명 사용
별칭이 필수
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m", Member.class);
Query query =
em.createQuery("SELECT m.username, m.age FROM Member m");
TypeQuery : 반환 타입이 명확할 때 사용
Query : 반환 타입이 명확히지 않을 때 사용
query.getResultList() : 결과 값이 하나 이상일때 사용. 리스트 반환
query.getSingleResult() : 단일 객체 반환
// 이름 기준
TypedQuery<Member> query = em.createQuert(SELECT m FROM Member m WHERE m.username=:username);
query.setParameter("username", usernameParam);
// 위치 기준
TypedQuery<Member> query = em.createQuert(SELECT m FROM Member m WHERE m.username=?1);
query.setParameter(1, usernameParam);
SELECT 절에 조회할 대상을 지정
엔티티, 임베디드 타입, 스칼라 타입이 대상이 될 수 있다
SELECT m FROM Member m // 엔티티 프로젝션
SELECT m.address FROM Member m // 임베디드 프로젝션
SELECT m.username FROM Member m // 스칼라 타입 프로젝션
jpql을 통해 엔티티를 가져와도 영속성 컨텍스트에 의해 관리 된다
여러 값을 조회할 수도 있다
setFirstResult
와 setMaxResults
API를 사용해 벤더에 종속적이지 않게 페이징을 구현할 수 있다 em.createQuery("SELECT m FROM Member m order by m.age desc", Member.class)
.setFirstResult(0) // page
.setMaxResults(10) // size
.getResultList();
SELECT m FROM Member m JOIN m.team t
SELECT m FROM Member m LEFT JOIN m.team t
SELECT COUNT(m) FROM Member m, Team t WHERE m.username = t.name
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
SELECT m
FROM Member m
WHERE m.age > (SELECT AVG(m2.age) FROM Member m2)
SELECT m
FROM Member m
WHERE EXISTS (SELECT t FROM m.team t WHERE t.name = '팀A')
상위 쿼리의 객체를 서브쿼리에서 사용하지 않아야 성능이 좋다
EXISTS, ALL, ANY 같은 함수를 지원한다
JPA는 HWERE, HAVING 절에서만 서브 쿼리 사용 가능하다
하이버네이트는 SELECT절도 지원한다
FROM 절의 서브쿼리는 아직 안된다
SELECT
case when ,.age <= 10 then '학생요금'
else '일반요금'
end
from Member m
SELECT
case t.name
when '팀A' then '인센티브110%'
else '인센티브105%'
end
from Team t
하나씩 조회해서 null 아니면 반환
SELECT coalesce(m.username, '이름 없는 회원') from Member m
두 값이 같으면 null, 다르면 첫번쨰 값 반환
SELECT NULLIF(m.username, '관리자') from Member m
CONCAT
SUBSTRING
TRIM
LOWER, UPPER
LENGTH
LOCATE
ABS, SQRT, MOD
SIZE, INDEX