Home Jpql
Post
Cancel

Jpql

JPQL(Java Persistence Query Language)

JPA만의 기술을 사용했을 때는 DB의 데이터 조회를 식별자를 통한 조회와 객체 그래프를 통한 탐색만 가능했다.

  • 식별자를 통한 조회: em.find()
  • 객체 그래프 탐색: a.getB().getC()

두 가지 조회 방법으로만 어플리케이션 개발을 한다면 모든 엔티티를 메모리에 올려두고
애플리케이션에서 필터링하는 등의 성능 낭비가 발생하게 된다.

이러한 낭비를 막기 위해 JPA는 데이터베이스에서 상황별 조건에 맞게 필터링하여 필요한 데이터를 가져오는 JPQL을 제공한다.

JPQL은 Java Persistence Query Language의 약자로 JPA에서 SQL을 추상화하여 만든 객체 지향 쿼리 언어이다.

SQL을 추상화하였기에 특정 데이터베이스에 의존적이지 않다.

SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등 SQL과 문법이 유사하다.

SQL은 데이터베이스 테이블을 대상으로 쿼리를 작성하지만 JPQL은 엔티티 객체를 대상으로 쿼리를 작성한다.

select m From Member m where m.name like ‘%hello%' 에서 Member는 테이블 이름이 아니다.

JPQL에서는 테이블이 아닌 @Entity(name = “”)에 지정된 엔티티 이름을 가르키고 있다.
(지정하지 않았다면 default 값 → 클래스 이름)




Query Method

사용하려는 Repository에 JpaRepository만 상속해주면 스프링의 AOP 기능을 사용하여 구현까지 자동으로 해준다.

간단한 문법으로 객체의 CRUD가 가능하다.

기본적으로 find + "객체" + By + "변수" 로 사용하는데 중간에 객체는 생략 가능하다.

1
2
3
public interface CommentRepository extends JpaRepository<Comment, Long> {
    List<Comment> findAllByRegistryId(int idx);
}

Query Method를 통해서 간단하게 필요한 객체를 조회할 수 있지만
조금 더 복잡한 조건을 사용하려면 메소드의 길이가 증가하게 된다는 단점이 있다.

이에 대한 해결로는 JPQL(Java Persistence Query Language)가 있다.



코드로 보는 JPQL 사용 이유

정렬

1
2
3
public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByNameContainsOrderByPriceAsc(String name);
}

findByNameContainsOrderByPriceAsc를 보면 name을 포함하고 가격을 오름차순으로 정렬해서 출력한다.

그런데 찾는 조건이 많아질 수록 이름이 너무 길어진다.

그래서 정렬 부분을 따로 파라미터로 받아 올 수 있다.

List<Product> findByNameContains(String name, Sort sort);


test 코드로 실행을 하면 아래와 같이 사용할 수 있다.

1
2
3
4
5
6
7
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ProductRepositoryTest {
    @Test
    void findByNameContains() {
        List<Product> products = productRepository.findByNameContains("네스프레소", Sort.by(Sort.Order.asc("name"), Sort.Order.asc("price")));
}

Sort.by(Sort.Order.asc("name"), Sort.Order.asc("price"))



페이징

1
2
3
4
public interface ProductRepository extends JpaRepository<Product, Long> {
    Page<Product> findAll(Pageable pageable);
}
1
2
3
4
5
6
7
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ProductRepositoryTest {
void findAllPaging() {
        Page<Product> products = productRepository.findAll(PageRequest.of(0, 5));
       
}

PageRequest가 Pageable의 구현체다.
PageRequest.of로 Pageable 객체를 생성해서 넣어준다.

페이징을 할때 sort.by() 까지 통해서 페이징과 정렬을 한꺼번에 할 수 있다.



위 코드들을 보면 불필요하게 메소드 명이 길다.

복잡한 조건을 메소드로 표현하기 어려울 수 있기 때문에 JPQL을 사용하여 직접 쿼리문을 작성해 객체를 조회할 수 있다.

객체를 대상으로 하는 쿼리문이며 SQL을 추상화해서 사용하기 때문에 어떤 DB를 사용하더라도 문제없이 사용할 수 있다.

Query Method는 쿼리문이 길든 짧든 쿼리문이 날라가는데 (날아간다 = 쿼리문 실행) 불필요하게 이중으로 쿼리문을 날리는 경우가 있다.


db 언어로 쓸수는 있지만 db를 변경하게 되면 다시 언어를 바꿔 써야 하므로 jpql 언어로 쓰는 것을 권장한다.
*아래 예시를 보면서 참고한다.



기본 문법

  • JPQL은 엔티티와 속성은 대소문자를 구분한다.
    하지만 SELECT, FROM, WHERE과 같은 JPQL 키워드는 대소문자를 구분하지 않는다.

  • from Member m에서 m과 같은 별칭(alias)은 필수이다.
    하지만 SQL문에서 별칭 지정을 위해 사용하는 as는 생략 가능하다.
    ex) SELECT m FROM Member m , SELECT m.team FROM Member m

  • @Query(" query 문 ") 으로 사용한다.



JPQL 작성하기

@Query

1
2
3
4
5
6
7
8
@Query("SELECT p FROM Product AS p WHERE p.category = ?1")
List<Product> findByCategory(String category);

@Query("SELECT p FROM Product p WHERE p.category LIKE %:category%")
List<Product> findByCategory2(@Param("category") String category);

@Query("SELECT p.name, p.category, p.price FROM Product p WHERE p.category LIKE %:category%")
List<Object[]> findByCategory3(@Param("category") String category);

@Query 어노테이션
JPQL을 직접 작성하여 사용한다.
직접 사용하는 데이터베이스의 SQL문을 작성해도 가능하다.
단, 데이터베이스를 변경하게 되면 SQL문도 바꿔야한다.


@Param 어노테이션
메서드 인자를 쿼리문에 사용하는 방법

  • 메서드 인자의 위치를 쿼리문에 넣어주는 방법
  • @Param 어노테이션을 사용하는 방법

전자의 경우 파라미터의 순서가 바뀌면 오류가 발생할 가능성이 있다.
따라서, @Param 어노테이션을 사용하는 것을 권장한다.


첫번째 파라미터 가져오기

  1. p.category = ?1 → 언어 바뀌면 또 바꿔야한다.
  2. p.category like %:category%
Like 연산자는 특정 패턴을 포함하는 데이터만을 검색하기 위해 사용한다.
’%’는 0개 이상의 문자라는 의미의 와일드카드(wildcard) 문자이다.
와일드카드(wildcard)란 문자열 내에서 임의의 문자나 문자열을 대체하기 위해 사용되는 기호를 의미한다.

카테고리를 검색하는데 카테고리에 포함되어있는지 검색하고 싶다.

:category

  • 커피라는 글자가 무조건 맞아야 한다.
  • 커피믹스나 믹스커피는 값에 포함되지 않는다.

%:category%

  • 앞이나 뒤에 어떤게 오든 커피라는 글자만 있으면 가져오고 싶으면 %를 사용한다.
  • 믹스커피, 커피믹스 둘 다 출력된다.

:category%

  • 앞에는 무조건 커피로 시작하고 뒤에는 상관없다.
  • 믹스커피는 안나오고 커피믹스는 출력되지 않는다.



모델 class에서 변수설정한 4개중에 3개만 select 하는 경우 object 배열로 받아야한다.

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;
}

List<Object[]> findByCategory(@Param("category") String category);

*모델로 받을 수 없다. List<Product> findByCategory(@Param("category") String category);

화면 캡처 2022-10-10 013747



JPQL은 직접 쿼리문을 문자열로 작성하기 때문에 오타로 인한 컴파일 에러를 통해 확인이 불가능하다.



QueryDSL 이란?

Querydsl 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해 주는 프레임워크


@query 사용시 오타가 발생하면 컴파일 에러를 못 잡는 문제가 있는데 queryDSL은 오타 발생 시 컴파일에러를 잡을 수 있고 query문의 문법적 오류를 잡아주는 등의 장점이 있다.

  • @query(seletc ~~ )
    오타가 났을 때 컴파일에러가 발생하지 않는다. (실행할 때 까지 모른다.)
    → QueryDSL을 쓰면 미리 문법적 오류를 발견할 수 있다.



QueryDSL 장점

  • 문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있다.
  • 자동 완성 등 IDE의 도움을 받을 수 있다.
  • 동적인 쿼리 작성이 편리하다.
  • 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있다.


<

QueryDSL 단점

설정하기가 까다롭다.

출처
[JPA] JPQL이란?
Spring Data JPA(쿼리 메소드)
Spring Data JPA - 쿼리 메서드 기능
Spring Data JPA - Reference Documentation
쿼리 메소드
[JPA] JPQL Query 정리
QueryDSL
Querydsl: 소개와 사용법
패턴 매칭
쿼리 메소드, JPQL, Querydsl 요약

This post is licensed under CC BY 4.0 by the author.