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();