본문 바로가기

내배캠/TIL

JPQL 관련 트러블 슈팅

프로젝트 진행 중, admin 페이지를 개발하며 일/월별 주문 수와 판매 금액을 보여줘야 했는데 여러 값을 계산해서 한 번에 보여주는 동작이므로 쿼리문을 작성해서 진행했다.

 

jpql로 작성했지만 DATE 부분과 FUNCTION(DATE_FORMAT, ......) 부분이 계속 정상적으로 동작하지 않고 에러를 발생시켰다.

 

 

[jpql]

 

원인을 알지 못하고 찾아도 마땅한 해결 방법이 나오지 않아 결국 native query로 바꿨는데 동작은 잘됐다.

아마 DATE, DATE_FORMAT 같은 함수가 MYSQL에 종속적이고 여기서만 사용되는 함수이다 보니 버전이 잘 맞지 않으면서 실행이 안되었던 것 같다. 

 

nativeQuery를 사용한다고 해도 dto를 반환하니 No converter found capable of converting from type 오류가 발생했다. 이는 반환 값의 getter 형 메서드를 갖고 있는 인터페이스를 만들어 dto 대신 인터페이스를 반환해주니 해결되었다. 그리고 컬럼 이름도 alias를 사용해서 dto의 변수 이름과 똑같이 매핑시켜줘야 한다. 자세한 내용은 아래 참고

 

https://docs.spring.io/spring-data/jpa/reference/repositories/projections.html

 

Projections :: Spring Data JPA

Another way of defining projections is by using value type DTOs (Data Transfer Objects) that hold properties for the fields that are supposed to be retrieved. These DTO types can be used in exactly the same way projection interfaces are used, except that n

docs.spring.io

 

 

[native query]

 

이후 QueryDsl을 배우고 최종적으로 QueryDsl을 사용해서 코드를 수정하였다. Dto로 반환하기 위해 생성자를 패키지부터 찾아서 지저분하게 적어도 되지 않고, mysql에 종속적으로 코드를 작성하고, 컴파일 시점에 오류를 발견할 수 없는 위의 코드보다 최적화되어 사용하기에 훨씬 편리하였다.

 

querydsl을 사용하기 위해 interface를 정의 후 구현체 클래스를 만들어 정의해 주었다. 해당 인터페이스는 Repository가 JpaRepository와 함께 상속받도록 했다. (인터페이스는 다중 상속이 가능하다)

 

 

[QueryDsl]

 

 

private final JPAQueryFactory queryFactory;

    public OrderQueryDslRepositoryImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<OrdersCountDto> countOrdersByDate() {
        return queryFactory
                .select(Projections.constructor(OrdersCountDto.class,
                        Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt).as("orderDate"), order.count().as("totalOrderCount")))
                .from(order)
                .groupBy(Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt))
                .fetch();
    }

    @Override
    public List<OrdersCountDto> countOrdersByMonth() {
        return queryFactory
                .select(Projections.constructor(OrdersCountDto.class,
                        Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m')", order.createdAt).as("orderDate"), order.count().as("totalOrderCount")))
                .from(order)
                .groupBy(Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m')", order.createdAt))
                .fetch();
    }

    @Override
    public List<OrdersPriceDto> getOrdersTotalPriceByDateRange(LocalDateTime start, LocalDateTime end) {
        return queryFactory
                .select(Projections.constructor(OrdersPriceDto.class,
                        Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt).as("orderDate"), order.totalPrice.sum().as("totalPrice")))
                .from(order)
                .where(order.createdAt.between(start, end))
                .groupBy(Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt))
                .fetch();
    }

    @Override
    public List<OrdersPriceDto> getOrdersTotalPriceDaily() {
        return queryFactory
                .select(Projections.constructor(OrdersPriceDto.class,
                        Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt).as("orderDate"), order.totalPrice.sum().as("totalPrice")))
                .from(order)
                .groupBy(Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m-%d')", order.createdAt))
                .fetch();
    }

    @Override
    public List<OrdersPriceDto> getOrdersTotalPriceMonthly() {
        return queryFactory
                .select(Projections.constructor(OrdersPriceDto.class,
                        Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m')", order.createdAt).as("orderDate"), order.totalPrice.sum().as("totalPrice")))
                .from(order)
                .groupBy(Expressions.stringTemplate("DATE_FORMAT({0}, '%Y-%m')", order.createdAt))
                .fetch();
    }

 

 

'내배캠 > TIL' 카테고리의 다른 글

[AWS] Identity and Access Management  (0) 2024.09.30
[AWS] AWS 시작하기  (2) 2024.09.30
Docker 설치 방법 및 오류 해결  (1) 2024.09.27
MyBatis 란?  (1) 2024.09.27
세션 (Session)  (0) 2024.09.24