📘 목차
- JPQL 등장 배경과 한계
- 기본 문법
- 반환 타입 및 결과
- 파라미터 바인딩
- Embedded Type
- 프로젝션 (Projection)
- 페이징 (Paging)
- JOIN
- CASE 식
- 함수
1. JPQL 등장 배경과 한계
객체 중심 설계와 JPA 의 한계
- JPA는 객체(Entity) 중심의 데이터베이스 접근을 지향한다.
- 단순 조회는 em.find() 또는 객체 간 참조 탐색으로 가능하다.
- 모든 데이터를 객체로 불러올 수는 없음
- 특정 조건 기반의 복잡한 검색은 불가능
→ 조건 기반 조회가 필요한 상황에서는 SQL 같은 쿼리가 필요해짐
JPQL의 등장
- JPQL(Java Persistence Query Language)은 객체(Entity)를 대상으로 SQL과 유사한 문법으로 작성된 객체지향 쿼리 언어이다.
- 특징
- Entity와 필드를 대상으로 질의 (테이블/컬럼 X)
- SQL 추상화를 통해 다양한 DB에서 동일하게 사용 가능
- 영속성 컨텍스트를 활용한 1차 캐시, 지연 로딩 가능
- 타입 안정성 보장 (컴파일 시점 오류 확인)
String jpql = "SELECT p FROM Product p WHERE p.price > :minPrice";
List<Product> products = em.createQuery(jpql, Product.class)
.setParameter("minPrice", 1000)
.getResultList();
JPQL의 한계와 동적 쿼리
- JPQL은 문자열로 작성된 정적 쿼리 → 조건이 달라질 경우 문자열 직접 조합 필요
String jpql = "SELECT p FROM Product p WHERE 1=1";
if (name != null) jpql += " AND p.name = :name";
if (price != null) jpql += " AND p.price = :price";
* 문제점
- 가독성 저하
- 유지보수 어려움
- 문법 오류 컴파일 시 검출 불가
- 조건이 많아질수록 복잡성 증가
→ 따라서 JPQL만으로 복잡한 동적 쿼리를 처리하는 것은 한계
JPA에서 지원하는 쿼리 방식
- JPA는 JPQL 외에도 다양한 쿼리 방법을 제공하며, 각각의 목적과 사용처가 다르다.
방식 | 설명 | 특징 |
JPQL | Entity 기반의 정적 쿼리 | SQL 유사 문법, 객체 지향 |
QueryDSL | Java 코드 기반 쿼리 빌더 | 동적 쿼리에 최적, 가독성 좋고 유지보수 쉬움 |
JPA Criteria API | JPQL을 코드로 생성 | 타입 안정성은 높지만 가독성 낮고 복잡 |
Native SQL | SQL 직접 사용 | DB 종속적, 복잡 쿼리/튜닝에 유리 |
* 실무에서는 상황에 맞게 병행 사용
- 단순 정적 쿼리 → JPQL
- 복잡한 조건 쿼리 → QueryDSL
- 성능 최적화 또는 특수 쿼리 → Native SQL
2. 기본 문법
- Entity와 필드를 대상으로 쿼리 작성
- SQL과 유사하지만 테이블이 아닌 객체(Entity) 단위로 질의
SELECT e FROM Employee e WHERE e.salary > 5000
- SELECT 절: 반환할 엔티티 또는 필드 지정
- FROM 절: 조회 대상 엔티티 지정 (테이블 X)
- WHERE, GROUP BY, HAVING, ORDER BY 등 SQL 문법과 유사하게 사용
3. 반환 타입 및 결과
- TypedQuery<T>: 명확한 반환 타입 지정
- Query: 반환 타입 미지정
반환 대상 | 반환 타입 |
Entity | Entity 객체 (영속 상태) |
필드 1개 | 단일 값 (ex. String, Integer) |
필드 여러 개 | Object[] 또는 DTO |
// Entity 반환
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
// 단일 필드
TypedQuery<String> query = em.createQuery("SELECT m.name FROM Member m", String.class);
// 복수 필드
List<Object[]> result = em.createQuery("SELECT m.name, m.age FROM Member m").getResultList();
4. 파라미터 바인딩
- 이름 기준 바인딩 권장 (:paramName)
/* 이름 기준 바인딩 */
String jpql = "SELECT m FROM Member m WHERE m.name = :name AND m.age >= :age";
TypedQuery<Member> query = em.createQuery(jpql, Member.class);
query.setParameter("name", "홍길동");
query.setParameter("age", 20);
List<Member> result = query.getResultList();
/* 위치 기준 바인딩 */
String jpql = "SELECT m FROM Member m WHERE m.name = ?1 AND m.age >= ?2";
TypedQuery<Member> query = em.createQuery(jpql, Member.class);
query.setParameter(1, "홍길동");
query.setParameter(2, 20);
List<Member> result = query.getResultList();
5. Embedded Type
- @Embeddable로 선언된 값 타입도 JPQL에서 사용 가능
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
}
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@Embedded
private Address address;
}
SELECT m FROM Member m WHERE m.address.city = 'Seoul'
6. 프로젝션 (Projection)
- 전체 엔티티, 단일 필드, 여러 필드를 선택할 수 있음
- DTO 프로젝션은 new 키워드로 가능
// Entity 전체
SELECT m FROM Member m
// 단일 필드
SELECT m.name FROM Member m
// DTO 생성
SELECT new com.example.dto.MemberDto(m.name, m.age) FROM Member m
* DTO 프로젝션은 new 패키지.클래스명(필드...) 형식 필수
7. 페이징 (Paging)
- setFirstResult(), setMaxResults() 사용
- JPQL 자체에는 LIMIT 문 없음 (JPA API에서 처리)
em.createQuery("SELECT m FROM Member m ORDER BY m.name", Member.class)
.setFirstResult(0) // 조회 시작 위치 (offset)
.setMaxResults(10) // 조회할 데이터 수 (limit)
.getResultList();
8. JOIN
- 엔티티 간 연관관계를 바탕으로 조인 수행
SELECT m FROM Member m JOIN m.team t WHERE t.name = 'TeamA'
JOIN 종류
JOIN 종류 | 키워드 | 설명 |
내부 조인 | JOIN 또는 INNER JOIN | 연관된 엔티티만 조회 (기본) |
외부 조인 | LEFT [OUTER] JOIN | 연관된 엔티티가 없어도 조회 |
외부 조인 | RIGHT [OUTER] JOIN | 거의 사용되지 않음 |
조인 조건 사용 | JOIN ... ON | 조인 조건을 명시적으로 지정 (JPA 2.1+) |
페치 조인 | JOIN FETCH | 연관 엔티티를 즉시 로딩 (N+1 문제 해결용) |
9. CASE 식
- 조건 분기에 따라 결과를 다르게 반환
SELECT
CASE
WHEN m.age >= 60 THEN '시니어'
WHEN m.age >= 30 THEN '중년'
ELSE '청년'
END
FROM Member m
10. 함수
- JPQL 표준 함수 및 DB 함수 (dialect 의존) 사용 가능
함수 | 설명 |
CONCAT(a, b) | 문자열 연결 |
SUBSTRING(str, start, len) | 문자열 자르기 |
LOCATE(str, substr) | 위치 반환 |
SIZE(c) | 컬렉션 크기 |
UPPER(str) / LOWER(str) | 대소문자 변환 |
LENGTH(str) | 문자열 길이 |
SELECT CONCAT(m.name, '님') FROM Member m
'Spring > 강의' 카테고리의 다른 글
[📘 심화 Spring] 3-4. N+1 문제 (0) | 2025.06.09 |
---|---|
[📘 심화 Spring] 3-2. API 예외 처리 (0) | 2025.06.09 |
[📘 심화 Spring] 3-1. Spring Bean 생명주기 (0) | 2025.06.09 |
[📘 심화 Spring] 2-2. JPA 고급 매핑 (0) | 2025.06.09 |
[📘 심화 Spring] 2-1. JPA 연관관계 매핑 (1) | 2025.06.09 |