JPA는 CRUD 수준에서는 EntityManager.find() 나 Repository.save() 등으로 충분하지만
복잡한 조회나 조건 검색이 필요한 시점부터 다양한 쿼리 전략이 필요해집니다.
1. JPQL (Java Persistence Query Language)
객체를 대상으로 쿼리하는 SQL (가장 단순한 조회 방법)
- SQL과 문법 유사하지만 테이블이 아닌 엔티티를 대상으로 쿼리
- SELECT m FROM Member m WHERE m.age > 18
- 내부적으로는 SQL로 변환되어 실행됨
장점
- SQL 문법에 익숙하다면 금방 적응 가능
- 표준 JPA 기능으로 설정 없이 사용 가능
단점
- 동적 쿼리 작성이 어려움
→ 문자열을 직접 조합해야 함 → 유지보수 어렵고 버그 유발
- 오타나 구조 오류는 런타임에 발견됨
List<Member> result = em.createQuery(
"SELECT m FROM Member m WHERE m.username LIKE '%kim%'",
Member.class
).getResultList();
2. Criteria API (JPA 표준 동적 쿼리 빌더)
자바 코드로 JPQL을 빌드하는 방식
장점
- 동적 쿼리에 적합
- 컴파일 타임에 타입 오류 검출 가능
단점
- 너무 복잡하고 장황함 → 실무에서는 거의 사용하지 않음
- 가독성과 유지보수성 떨어짐
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
Root<Member> m = query.from(Member.class);
query.select(m).where(cb.equal(m.get("username"), "kim"));
List<Member> resultList = em.createQuery(query).getResultList();
3. QueryDSL (실무 추천)
장점
- 코드로 JPQL을 작성 → 문법 오류는 컴파일 시점에 검출
- 동적 쿼리 작성이 쉽고 가독성이 좋음
- IDE 자동완성, 리팩토링 쉬움
- 실무에서 가장 많이 사용되는 JPA 쿼리 도구
단점
- 초기 설정 필요 (Q클래스 생성, Annotation Processor 설정)
- JPA에 대한 이해도 필요
QMember m = QMember.member;
List<Member> result = queryFactory
.selectFrom(m)
.where(m.age.gt(18))
.orderBy(m.name.desc())
.fetch();
4. Native SQL (순수 SQL 사용)
JPA가 지원하지 않는 DB 고유 기능 사용할 때
장점
- 복잡한 SQL, DB 전용 문법 사용 가능 (예: CONNECT BY, WITH RECURSIVE, 힌트 등)
- 성능 튜닝 시 유용
단점
- SQL에 강하게 의존 → DB 이식성 X
- 결과 매핑이 번거로움 (DTO나 엔티티 수동 매핑 필요)
String sql = "SELECT ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> result = em.createNativeQuery(sql, Member.class).getResultList();
5. JDBC 직접 사용 / Spring JdbcTemplate / MyBatis 병행 사용
- 단, JPA의 영속성 컨텍스트 플러시를 수동으로 해줘야 함