본문 바로가기
Spring

Spring의 동적 쿼리 : JPQL , QueryDSL

by 우 석 2024. 11. 20.

1. JPQL

JPQL(Java Persistence Query Language)을 사용하면 @Query 주석을 사용하여 Repository에서 직접 쿼리를 정의할 수 있다. 또한 선택적으로 동적 매개변수를 위해 @Param과 결합할 수 있다.

 

예시) '제품' 엔티티가 있고 '이름' 및 '카테고리'를 기준으로 제품을 필터링한다고 가정

@Repositorypublic interface ProductRepository extends JpaRepository<Product, Long> {
    @Query("SELECT p FROM Product p WHERE (:name IS NULL OR p.name = :name) AND (:category IS NULL OR p.category = :category)")
    List<Product> findProducts(@Param("name") String name, @Param("category") String category);
}
// 사용 예시
List<Product> products = productRepository.findProducts("Laptop", "Electronics");

 

장점:
- 간단한 쿼리를 작성하고 이해하기 쉽다.
- 추가 설정이나 종속성이 필요하지 않다.


단점:
- 쿼리 복잡성이 증가함에 따라 관리 및 확장이 어렵다.
- 제한된 유형의 안전성과 재사용성.

- String으로 작성한 코드로, 메서드가 실행되기 전까지 에러를 찾을 수 없다.

 

2. QueryDSL

QueryDSL은 Java에서 유형이 안전한 SQL과 유사한 쿼리를 작성하기 위한 유연한 API를 제공한다.

통계나 서브 쿼리등 복잡한 쿼리를 작성해야 할 때 사용할 수 있다.

// where 조건이 많은 경우에 booleanbuilder를 적용하여 사용
@RequiredArgsConstructor
public class OrderRepositoryCustomImpl implements OrderRepositoryCustom {

  private final JPAQueryFactory jpaQueryFactory;
  QOrderEntity orderEntity = QOrderEntity.orderEntity;
  QOrderDetailEntity orderDetailEntity = QOrderDetailEntity.orderDetailEntity;

  @Override
  public Page<OrderSearchResponseDto> searchOrders(OrderType orderType, UUID menuId, Pageable pageable) {

    BooleanBuilder predicate = new BooleanBuilder();
    predicate.and(orderEntity.deletionStatus.eq(DeletionStatus.ACTIVE));

    if (orderType != null) {
      predicate.and(orderEntity.orderType.eq(orderType));
    }

    if (menuId != null) {
      predicate.and(orderDetailEntity.menu.id.eq(menuId));
    }

    JPAQuery<OrderSearchResponseDto> query = jpaQueryFactory
        .select(new QOrderSearchResponseDto(orderEntity))
        .leftJoin(orderEntity.orderDetailList, orderDetailEntity)
        .where(predicate)
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize());
   ...
   ...
  }
}

 

장점:

- Q객체를 통해 마치 sql을 작성하는 것처럼 쿼리를 작성할 수 있다. 

- 문자열 기반 쿼리 대신 Java 코드를 사용하므로 리팩토링 쉽다.

- 통계나 서브 쿼리등 복잡한 쿼리를 작성해야 할 때 유용하다.

- 동적이고 복잡한 쿼리를 작성할 수 있다.

 

단점: 

-  QueryDSL API에 대한 지식

- : Q 클래스(예: qUser) 생성이 필요하므로 빌드 프로세스가 복잡해질 수 있다.

 

정리

단순 및 정적 쿼리의 경우
@Query가 포함된 JPQL을 선호한다. 간단하여 이해하기 쉽다.

쿼리가 단순하고 변경될 가능성이 없을 때 효과적이다.

@Query("SELECT p FROM Product p WHERE p.name = :name")
List<Product> findByName(@Param("name") String name);

 

동적, 복잡, 유지 관리 가능한 쿼리가 필요한 경우 QueryDSL