Jpql
Jpql
JPQL (Java Persistence Query Language)
JPQL이 필요한 이유
JPA만 사용할 경우 데이터 조회 방법은 기본적으로 아래 두 가지뿐이다.
- 식별자를 통한 조회
em.find(Member.class, id)
- 객체 그래프 탐색
member.getTeam().getName()
이 방식만으로 애플리케이션을 개발하면 문제가 생긴다.
모든 데이터를 메모리에 올려둔 뒤 application 단에서 조건을 걸어 필터링해야 한다
→ 필요 없는 데이터까지 전부 조회하게 되어 성능 낭비가 발생한다.
이러한 문제를 해결하기 위해
JPA는 DB에서 조건에 맞는 데이터만 조회할 수 있도록 JPQL(Java Persistence Query Language) 을 제공한다.
JPQL이란?
- JPQL은 JPA에서 제공하는 객체 지향 쿼리 언어
- SQL을 추상화하여 만들었기 때문에 DB에 의존적이지 않다.
- 문법은 SQL과 매우 유사하다.
SQL vs JPQL 차이
| 구분 | SQL | JPQL |
|---|---|---|
| 대상 | 테이블 | 엔티티 |
| 조회 기준 | 컬럼 | 필드 |
| 의존성 | DB 의존 | DB 독립 |
JPQL 예시
1
select m from Member m where m.name like '%hello%'
Member는 테이블 이름이 아니다@Entity(name = "")에 지정된 엔티티 이름- 지정하지 않았다면 클래스 이름이 기본값
Query Method
Spring Data JPA에서는 JpaRepository만 상속하면 메서드 이름만으로 쿼리를 자동 생성해준다.
1
2
3
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findAllByRegistryId(Long idx);
}
장점
- 구현 코드가 필요 없다
- 간단한 조건 조회에 매우 편리하다
단점
- 조건이 복잡해질수록 메서드 이름이 지나치게 길어진다
- 가독성이 급격히 떨어진다
Query Method의 한계
정렬 예시
1
2
3
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContainsOrderByPriceAsc(String name);
}
이 메서드는 다음 의미를 가진다.
- name을 포함하고
- price 기준 오름차순 정렬
하지만 조건이 늘어나면 메서드 이름이 지나치게 길어지고 읽기 어려워진다.
그래서 정렬을 파라미터로 분리할 수 있다.
1
List<Product> findByNameContains(String name, Sort sort);
테스트 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@DataJpaTest
class ProductRepositoryTest {
@Test
void findByNameContains() {
List<Product> products =
productRepository.findByNameContains(
"네스프레소",
Sort.by(
Sort.Order.asc("name"),
Sort.Order.asc("price")
)
);
}
}
페이징 예시
1
2
3
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<Product> findAll(Pageable pageable);
}
1
2
3
4
5
6
7
8
9
@DataJpaTest
class ProductRepositoryTest {
@Test
void findAllPaging() {
Page<Product> products =
productRepository.findAll(PageRequest.of(0, 5));
}
}
PageRequest는Pageable의 구현체- 페이징과 정렬을 함께 사용할 수도 있다
문제
- 메서드 이름이 여전히 길다
- 복잡한 조건은 표현하기 어렵다
- 가독성이 좋지 않다
→ 이럴 때 JPQL을 사용한다.
JPQL 기본 문법
- 엔티티 이름과 필드는 대소문자를 구분
- SELECT, FROM, WHERE 같은 키워드는 대소문자 구분 없음
- 별칭(alias)은 필수
as키워드는 생략 가능
1
SELECT m FROM Member m
JPQL은 @Query 어노테이션을 통해 사용한다.
JPQL 작성하기 (@Query)
1
2
@Query("SELECT p FROM Product p WHERE p.category = ?1")
List<Product> findByCategory(String category);
1
2
@Query("SELECT p FROM Product p WHERE p.category LIKE %:category%")
List<Product> findByCategory2(@Param("category") String category);
1
2
@Query("SELECT p.name, p.category, p.price FROM Product p WHERE p.category LIKE %:category%")
List<Object[]> findByCategory3(@Param("category") String category);
파라미터 전달 방식
1. 위치 기반 파라미터
?1,?2- 순서가 바뀌면 오류 발생 가능
2. 이름 기반 파라미터 (권장)
:category@Param과 함께 사용
LIKE
| 표현 | 의미 |
|---|---|
:category | 정확히 일치 |
%:category% | 앞뒤 모두 포함 |
:category% | 해당 값으로 시작 |
ex) "연어"
%연어%→ 새우연어, 연어초밥연어%→ 연어초밥만연어→ 정확히 연어만
일부 컬럼만 조회할 경우
1
2
3
4
5
6
7
8
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String category;
private int price;
}
3개 컬럼만 조회하면 엔티티로 받을 수 없고 Object 배열로 받아야 한다.
1
List<Object[]> findByCategory(@Param("category") String category);
❌ List<Product> 사용 불가
JPQL의 단점
- 쿼리를 문자열로 작성
- 오타가 있어도 컴파일 시점에 알 수 없다
- 실행 시점에서야 오류 확인 가능
QueryDSL이란?
문자열이 아닌 코드로 쿼리를 작성하는 프레임워크
JPQL의 단점을 보완한다.
QueryDSL 장점
- 컴파일 시점에 문법 오류 확인 가능
- IDE 자동 완성 지원
- 동적 쿼리 작성이 쉬움
- 조건 재사용이 가능
QueryDSL 단점
- 초기 설정이 까다롭다
REFERENCE
Spring Data JPA - Reference Documentation
[JPA] JPQL Query 정리
쿼리 메소드, JPQL, Querydsl 요약
This post is licensed under CC BY 4.0 by the author.