JPA는 복잡한 검색 조건을 사용해서 엔티티 객체를 조회할 수 있는 다양한 쿼리 기술을 지원한다.
SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.SQL이 데이터베이스 테이블을 대상으로 하는 데이터 중심의 쿼리라면 JPQL은 엔티티 객체를 대상으로 하는 객체지향 쿼리이다.
JPQL을 사용하면 JPA는 이 JPQL을 분석한 다음 적절한 SQL을 만들어 데이터베이스를 조회한다.
그리고 조회한 결과로 엔티티 객체를 생성해서 반환한다.
JPQL: Java Persistence Query LanguageCriteria: Criteria Query (JPQL을 편하게 작성하도록 도와주는 API, 빌더클래스 모음네이티브 SQL: Native SQL (JPA에서 JPQL 대신 직접 SQL을 사용할 수 있다.)QueryDSL: Criteria 쿼리처럼 JPQL을 편하게 작성하도록 도와주는 빌더 클래스 모음, 비표준 오픈소스 프레임워크다.JDBC직접 사용 : MyBatis 같은 SQL 매퍼 프레임워크 (필요하면 JDBC를 직접 사용할 수 있다.)
가장 중요한건 JPQL이다. Criteria나 QueryDSL은 JPQL을 편하게 작성하도록 도와주는 빌더 클래스일 뿐이다. 따라서 JPQL을 이해해야 나머지도 이해할 수 있다.
// JPQL 사용
String jpql = "select m from Member as m where m.username = 'kim'";
List<Memver> resultList = em.createQuery(jpql, Member.class).getResultList();
Criteria는 JPQL을 생성하는 빌더 클래스다.
Criteria의 장점은 문자가 아닌 query.select(m).where(...)처럼 프로그래밍 코드로 JPQL을 작성할 수 있다는 점이다.
Criteria 쿼리를 사용하게되면 문자기반 쿼리의 단점을 보완할 수 있다.
예를 들면 JPQL에서 select m from Membbbbeer m처럼 오타가 있다고 가정하면, 컴파일은 성공하고 애플리케이션을 서버에 배포할 수 있다. 문제는 해당 쿼리가 실행되는 런타임 시점에 오류가 발생한다는 점이다.
Criteria의 장점
- 컴파일 시점에 오류를 발견할 수 있다.
- IDE를 사용하면 코드 자동완성을 지원한다.
- 동적 쿼리를 작성하기 편하다.
(JPA는 2.0부터 Criteria를 지원한다.)
// Criteria 사용 준비
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.createQeury(cq).getResultList();
아쉬운 점은 m.get("username")에서 필드 명을 문자로 작성했다. 메타 모델을 사용하면 온전히 코드만 사용해서 쿼리를 작성할 수 있다.
// 메타 모델 사용 전 -> 사용 후
m.get("username") -> m.get(Member_.username)
결론
Criteria가 가진 장점이 많지만 모든 장점을 상쇄할 정도로 복잡하고 장황하다. 따라서 사용하기 불편한 건 물론이고 Criteria로 작성한 코드도 한눈에 들어오지 않는다는 단점이 있다.
QueryDSL도 Criteria처럼 JPQL빌더 역할을 한다.
QueryDSL의 장점은 코드 기반이면서 단순하고 사용하기 쉽다. 그리고 작성한 코드도 JPQL과 비슷해서 한눈에 들어온다. QueryDSL과 Criteria를 비교하면 Criteria는 너무 복잡하다.
// 준비
JPAQuery query = new JPAQuery(em);
QMember member = QMember.member;
// 쿼리, 결과조회
List<Member> members =
query.from(member)
.where(member.username.eq("kim"))
.list(member);
QueryDSL도 어노테이션 프로세서를 사용해서 쿼리 전용 클래스를 만들어야 한다. QMember는 Member 엔티티 클래스를 기반으로 생성한 QueryDSL 쿼리 전용 클래스다.
JPA는 SQL을 직접 사용할 수 있는 기능을 지원 = 네이티브 SQL
String sql =
"SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList =
em.createVativeQuery(sql, Member.class).getResultList();