본문 바로가기
Back-End/Spring

@QueryProjection 으로 Querydsl 작성

by 어렵다어려웡 2021. 5. 11.

개발환경

- Gradle

- Spring Boot

- Postgresql

 

페이징 용도로만 사용하는 것인 줄 알았지만 내 착각이였다.

최근에 면접을 보고 해당 회사에서 Querydsl을 위주로 사용해서 개발하신다는 말을 듣고

짧게나마 사용했던 Querydsl을 공부해보고자 이번 개인 프로젝트 진행하면서 사용을 해봤습니다..

 

@Configuration
public class QuerydslConfiguration {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 구글에 검색해서보면 JPAQueryFactory를 주입해서 사용하신다는 갓졸두님.. 

사실 나는 JPQLQuery로 Querydsl을 작성해봤기때문에 JPAQueryFactory의 존재는 몰랐습니다.

 

무작정 따라해보니 bean 설정이 안되었다는 에러가 계속나서 위 코드처럼 JPAQueryFactory를 설정해서 우선 해결..

 

 

우선 구현은 아래와 같이 해보았다.

@Repository
@Log4j2
public class OrderQueryRepository extends QuerydslRepositorySupport {

    private JPAQueryFactory jpaQueryFactory;

    private QMember member;
    private QOrder order;
    private QDelivery delivery;
    private QOrderItem orderItem;

    public OrderQueryRepository(JPAQueryFactory jpaQueryFactory) {
        super(Order.class);
        this.jpaQueryFactory = jpaQueryFactory;
        this.member = QMember.member;
        this.order = QOrder.order;
        this.delivery = QDelivery.delivery;
        this.orderItem = QOrderItem.orderItem;
    }
    
	// 특정 사용자의 주문 조회
    public List<OrderInfoDto> findOrderByMember(Member findMember) {
        QueryResults<OrderInfoDto> queryResults = jpaQueryFactory
                .select(new QOrderInfoDto(
                        order.orderId,
                        member.name,
                        order.orderStatus.stringValue(),
                        delivery.deliveryStatus.stringValue(),
                        order.orderItemList.size(),
                        order.regDate))
                .from(order)
                .leftJoin(order.orderer, member)
                .leftJoin(order.deliveryInfo, delivery)
                .where(order.orderer.eq(findMember))
                .orderBy(order.orderId.desc())
                .fetchResults();

        List<OrderInfoDto> result = queryResults.getResults();

        return result;
    }
}

 

* OrderInfoDto

@NoArgsConstructor
@Setter
@Getter
public class OrderInfoDto {
    // 주문번호, 주문상태, 주문상품 개수, 배송정보, 주문자의 이름
    private Long orderId;

    private String orderStatus;
    private String deliveryStatus;
    private String ordererName;

    private LocalDateTime regdate;
    private int orderItemCount;

    @QueryProjection
    public OrderInfoDto(Long orderId, String ordererName,
   		String orderStatus, String deliveryStatus, 
    	int orderItemCount, LocalDateTime regdate) {
        
        this.orderId = orderId;
        this.ordererName = ordererName;
        this.orderStatus = orderStatus;
        this.deliveryStatus = deliveryStatus;
        this.orderItemCount = orderItemCount;
        this.regdate = regdate;
    }
}

현재 화면단 설계를 배제한 상태로 진행하다보니 어떤걸 전달할지 고민되서 간단하게 필요할것 같은 정보만 넣었다..

 

QuerydslRepositorySupport 클래스의 경우 페이징처리를  하지 않는다면 굳이 넣지않아도 된다고 들었다.

하지만 곧 할것이니 우선 넣었으며 @RequiredArgsConstructor 로 주입하여 사용해도 되지만 Q도메인을 초기화 시키기위해서 생성자를 직접 사용해보았다..

 

@QueryProjection은 해당 클래스의 생성자에 선언을 해서 사용합니다.

그리고 new 연산자를 통해서 select에 내가 지정한 생성자를 인자를 넣어 만들도록 합니다.

나머지 쿼리를 만들어서 넣으면 끝..

 

order.orderStatus.stringValue() 를 사용한 이유는 없습니다.. 

정확한 코드는 아니긴 하지만 String을 도출하기 위해서 임시로 사용했다는 점뿐입니다.

 

여기서 주의할 점은 @QueryProjection을 사용하기 위해서는 List로 만들려는 OrderInfoDto 클래스에 대한

Q도메인이 있어야 한다는 점이다. 따라서 Querydsl을 빌드한 이후로 만든 DTO를 사용하기 전에는

한번더 build를 통해서 Q도메인을 만들고 사용해야 한다.

 

이후 sql결과  -  

1. JPAQueryFactory의 경우 실행시 count쿼리를 무조건 날려주는 것 같다..

2. cast는 stringValue 탓인지 Postgresql의 방언떄문인지 정확하게는 모른다.

 

Hibernate: 
    select
        count(order0_.order_id) as col_0_0_ 
    from
        orders order0_ 
    left outer join
        member member1_ 
            on order0_.member_id=member1_.id 
    left outer join
        delivery delivery2_ 
            on order0_.delivery_id=delivery2_.delivery_id 
    where
        order0_.member_id=?
Hibernate: 
    select
        order0_.order_id as col_0_0_,
        member1_.name as col_1_0_,
        cast(order0_.order_status as varchar) as col_2_0_,
        cast(delivery2_.delivery_status as varchar) as col_3_0_,
        (select
            count(orderiteml3_.order_id) 
        from
            order_item orderiteml3_ 
        where
            order0_.order_id = orderiteml3_.order_id) as col_4_0_,
        order0_.regdate as col_5_0_ 
    from
        orders order0_ 
    left outer join
        member member1_ 
            on order0_.member_id=member1_.id 
    left outer join
        delivery delivery2_ 
            on order0_.delivery_id=delivery2_.delivery_id 
    where
        order0_.member_id=? 
    order by
        order0_.order_id desc