동적 쿼리

Specification

Criteria API

QueryDSL

QBE vs Specification vs @Query vs QueryDSL 비교

동적 쿼리

Specification

Specification은 복잡한 쿼리나 런타임에 쿼리 조건이 결정되는 동적 쿼리를 JPA의 Criteria API를 활용하여 생성할 수 있도록 도와주는 인터페이스임

특징

동적 쿼리 생성

타입 세이프

추상 메서드

주어진 root(기본 엔티티 타입)와 CriteriaQuery에 대해 Predicate 형식으로 엔티티 쿼리의 WHERE 절을 만드는 메서드임

@Nullable
Predicate toPredicate(Root<T> root, @Nullable CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);

예시

엔티티 정의

@Entity
public class User {
  @Id
  @GeneratedValue
  private Long id;
  private String firstName;
  private String lastName;
  private int age;

  // Getters and Setters
}

Specification 정의 및 사용

동적 쿼리 조건 정의

hasLastName 메서드는 주어진 string 값을 가진 엔티티가 있는지 확인하는 동적 쿼리를 반환하고

hasAgeGreaterThan 메서드는 주어진 int 값보다 큰 값을 가진 엔티티가 있는지 필터링하는 동적 쿼리를 반환함

public class UserSpecification {

  public static Specification<User> hasLastName(String lastName) {
    return (root, query, builder) ->
            builder.equal(root.get("lastName"), lastName);
  }

  public static Specification<User> hasAgeGreaterThan(int age) {
    return (root, query, builder) ->
            builder.greaterThan(root.get("age"), age);
  }
} 

리포지토리 인터페이스 정의

JpaSpecificationExecutor 확장 필요

public interface SpecUserRepository extends JpaRepository<SpecUser, Long>, JpaSpecificationExecutor<SpecUser> {
}

서비스 객체의 비즈니스 로직에서 정의한 Specifiaction 사용

public List<SpecUser> findUsers(String lastName, int age) {
  Specification<SpecUser> spec = Specification
          .where(SpecUserSpecification.hasLastName(lastName))
          .and(SpecUserSpecification.hasAgeGreaterThan(age));

  return specUserRepository.findAll(spec);
}

Criteria API

QueryDSL

QBE vs Specification vs @Query vs QueryDSL 비교

QBE, Specification, QueryDSL: 동적 쿼리 생성

@Query: 정적 쿼리

동적 쿼리는 애플리케이션 실행 시점에 조건이나 파라미터에 따라 쿼리의 구조가 동적으로 생성되는 쿼리를 말함

미리 정의된 쿼리가 아니라 사용자 입력, 비즈니스 로직, 애플리케이션 상태 등과 같은 요소에 따라 쿼리의 구조와 내용을 동적으로 변경할 수 있음

일반 쿼리와의 비교

특징 동적 쿼리 일반 쿼리
구조 및 쿼리 생성 시점 실행 시점에 따라 동적으로 변경 및 생성 고정된 쿼리 구조, 컴파일 시점에 생성
유연성 조건에 따라 여러 쿼리를 조합 조건 변경 불가
용도 조건이 동적으로 변경되거나 다중 조건 조합이 필요할 때 단순한 조건 처리

1. QBE

엔티티 인스턴스를 기반으로 동적 쿼리를 생성하는 방식

장점

단점

2. Specification

JPA Criteria API를 사용하여 동적으로 쿼리를 생성하는 방식

장점

단점

3. @Query

JPA 리포지토리 메서드에 직접 JPQL이나 네이티브 SQL을 정의하는 방식

쿼리의 구조가 고정되어 있으며 컴파일 시점에 결정됨

장점

단점

4. QueryDSL

타입 안전한 쿼리를 작성할 수 있는 DSL(Domain-Specific Language)로 자바 코드로 SQL과 비슷한 구문을 사용해서 동적 쿼리를 작성할 수 있음

장점

단점

선택 가이드

단순한 CRUD: @Query 또는 스프링 데이터 JPA의 기본 메서드(findById, findAll 등)

복잡한 검색 조건 또는 동적 쿼리 및 타입 안전성: QueryDSL

성능 최적화: @Query, QueryDSL

재사용 가능한 동적 쿼리가 필요한 경우: Specification