Backend/Spring

[Spring] JPA에서 쿼리 사용하는 방법

누구세연 2024. 7. 11. 21:58

JPA에서 당연한 듯 사용하고 있었던 쿼리 메서드를 사용하면서 쿼리를 표현할 수 있는 방법들이 뭐가 있었지?라는 생각으로 오늘은 쿼리를 표현할 수 있는 방법과 각 방법의 장단점에 대해 정리해 보겠습니다.😎

 

Spring Data JPA 쿼리 메서드

Spring Data JPA는 메서드 이름을 기반으로 쿼리를 자동 생성할 수 있는 기능을 제공합니다.

간단한 쿼리에서는 매우 유용합니다.

 

장점

  • 간편하다!
    메서드의 이름만으로 쿼리를 자동 생성할 수 있습니다.
  • 빠른 개발
    간단한 쿼리의 경우 매우 빠르게 개발할 수 있습니다.

단점

  • 복잡한 쿼리 한계
    매우 복잡한 쿼리는 작성하기 어렵습니다.
  • 메서드 이름 길이
    복잡한 쿼리를 작성하려면 메서드 이름이 매우 길어질 수 있습니다.

예시는 아래와 같습니다.

List<Movie> findByTitleContainingIgnoreCaseOrDirectorContainingIgnoreCaseOrActorsContainingIgnoreCase(String title, String director, String actor);

 

 

Native Query

Native Query는 SQL 쿼리를 직접 사용할 수 있습니다.

복잡한 쿼리나 데이터베이스에 종속적인 기능을 사용할 때 유용합니다.

 

장점

  • 강력한 기능
    SQL의 모든 기능을 사용할 수 있습니다.
  • 최적화 가능
    데이터베이스에 최적화된 쿼리를 작성할 수 있습니다.

단점

  • 데이터베이스 종속성
    특정 데이터베이스에 종속될 수 있습니다.
  • 유지보수 어려움
    SQL 쿼리의 변경이 필요할 때 유지보수가 어려울 수 있습니다.
@Query(value = "SELECT * FROM movies m WHERE m.title LIKE %?1% OR m.director LIKE %?1% OR m.actors LIKE %?1%", nativeQuery = true)
List<Movie> searchByTitleOrDirectorOrActors(String searchTerm);

 

 

JPQL(Java Persistence Query Language)

JPQL은 객체지향 쿼리 언어로 엔티티 객체를 대상으로 쿼리를 작성합니다.

SQL과 유사하지만 데이터베이스 데이블이 아닌 엔티티 클래스를 대상으로 합니다.

 

장점

  • 객체 지향적 접근
    객체를 대상으로 쿼리를 작성하여 더 자연스럽고 직관적입니다.
  • 데이터베이스 독립적
    특정 데이터베이스에 종속되지 않고, 다양한 데이터베이스에 작동합니다.

단점

  • 복잡한 쿼리 작성 어려움
    복잡한 쿼리는 작성하기 어렵습니다.
  • 성능 최적화 어려움
    복잡한 쿼리의 경우 성능 최적화가 어려울 수 있습니다.

예시는 아래와 같습니다.

@Query("SELECT m FROM Movie m WHERE m.title LIKE %?1% OR m.director LIKE %?1% OR m.actors LIKE %?1%")
List<Movie> searchByTitleOrDirectorOrActors(String searchTerm);

 

Criteria API

Criteria API는 동적 쿼리를 생성하는데 유용하며 타입 안전성을 제공합니다.

복잡한 쿼리를 프로그래밍적으로 작성할 수 있습니다.

 

장점

  • 동적 쿼리 지원
    런타임에 동적으로 쿼리를 생성할 수 있습니다.
  • 타입 안전성
    컴파일 시점에 오류를 감지할 수 있어 안정성이 높습니다.

단점

  • 코드 복잡성
    코드가 길고 복잡해질 수 잇습니다.
  • 가독성 저하
    쿼리가 길어질수록 가독성이 떨어질 수 있습니다.

예시는 아래와 같습니다.

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Movie> cq = cb.createQuery(Movie.class);
Root<Movie> movie = cq.from(Movie.class);

List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.like(cb.lower(movie.get("title")), "%" + searchTerm.toLowerCase() + "%"));
predicates.add(cb.like(cb.lower(movie.get("director")), "%" + searchTerm.toLowerCase() + "%"));
predicates.add(cb.like(cb.lower(movie.get("actors")), "%" + searchTerm.toLowerCase() + "%"));

cq.where(cb.or(predicates.toArray(new Predicate[0])));
return entityManager.createQuery(cq).getResultList();

 

QueryDSL

QueryDSL은 타입 안전한 쿼리를 생성할 수 있는 또 다른 방법입니다.

Criteria API 보다 더 직관적이고 간단한 문법을 제공합니다.

 

장점

  • 타입 안정성
    컴파일 시점에 오류를 감지할 수 있어 안정성이 높습니다.
  • 직관적 문법
    Criteria API보다 가독성이 좋고 직관적입니다.

단점

  • 추가 설정 필요
    사용하기 위해 추가 설정, 의존성 주입이 필요합니다
  • 학습 곡선
    새로운 DSL을 배우는데 시간이 걸릴 수 있습니다. 

예시는 아래와 같습니다.

QMovie movie = QMovie.movie;
return queryFactory.selectFrom(movie)
        .where(movie.title.containsIgnoreCase(searchTerm)
                .or(movie.director.containsIgnoreCase(searchTerm))
                .or(movie.actors.any().containsIgnoreCase(searchTerm)))
        .fetch();

 

 

 

 

JPA에서 쿼리를 표현하는 방법은 다양하며 각 방법은 고유한 장단점을 가지고 있습니다.

상황에 맞는 적절한 방법을 선택하는 것이 중요합니다.

간단한 쿼리는 Spring Data JPA의 쿼리 메서드를 사용하고 복잡한 쿼리나 동적 쿼리가 필요한 경우 Criteria API나 QueryDSL을 사용하는 것이 좋습니다.

그리고 데이터베이스에 종속적인 최적화가 필요할 때는 Native Query를 사용하면 좋을 것 같습니다! 🙂