Project/Management

[Management] QueryDSL : QueryReuslt Dto로 반환 & Join Error & Alias orderBy 참조 방법

lakelight 2022. 11. 9. 15:09
728x90
반응형

 

오늘은 QueryDSL 사용 시 Q객체를 사용하지 않고,
결과값을 사용하지 않고 DTO를 이용해서 반환하는 방법을 정리해보겠습니다.

 

1. DTO를 이용해서 반환하는 경우

  • sum()이나 count() 같은 메서드 사용하는 경우
  • join을 이용해 다른 엔티티 정보를 반환하는 경우

 

2. DTO를 반환하는 방법

  • queryFactory.select( Projections.bean ( Entity.class, QEntity.entity.properties ... ) )
    Setter를 이용한 바인딩으로 잘 사용하지는 않는 방법입니다.

 

  • queryFactory.select( Projections.constructor( Entity.class, QEntity.entity.properties ... ) )
    생성자를 이용한 바인딩 방법입니다.

 

  • queryFactory.select( QEntityDto( QEntity.entity.properties ... ) )
    DTO 클래스 내부 생성자 선언할 때 @QueryProjection 어노테이션을 붙여서 사용합니다.

자세한 내용 참고

 

3. 실제 적용한 코드

    public Page<InventoryCheckDto.InventoryCheckDetailResponseDto> readInventoryCheckDetail(InventoryCheckDto.ReadInventoryCheckDetailRequestDto dto, Pageable pageable){
        String[] sortBy = dto.getSortBy();
        Boolean[] sortDesc = dto.getSortDesc();
        List<OrderSpecifier> orderBy = new LinkedList<>();
        readInventoryCheckDetailMultiSortFilter(sortBy, sortDesc, orderBy);

        QueryResults<InventoryCheckDto.InventoryCheckDetailResponseDto> results = queryFactory
                .select(
                        Projections.constructor(InventoryCheckDto.InventoryCheckDetailResponseDto.class,
                        inventoryCheckDetail.item.id.as("itemId"),
                        inventoryCheckDetail.item.name.as("itemName"),
                        inventoryCheckDetail.item.code.as("itemCode"),
                        inventoryCheckDetail.item.standard.as("itemStandard"),
                        inventoryCheckDetail.item.type.as("itemType"),
                        inventoryCheckDetail.item.unit.as("itemUnit"),
                        inventoryCheckDetail.item.version.as("itemVersion"),
                        inventoryCheckDetail.item.storage.id.as("storageId"),
                        inventoryCheckDetail.item.storage.name.as("storageName"),
                        inventoryCheckDetail.item.storageLocation.id.as("storageLocationId"),
                        inventoryCheckDetail.item.storageLocation.area.as("area"),
                        inventoryCheckDetail.currentCount.sum().as("sumCurrentCount"),
                        inventoryCheckDetail.previousCount.sum().as("sumPreviousCount"))
                )
                .from(inventoryCheckDetail)
                .leftJoin(inventoryCheckDetail.item, item)
                .leftJoin(inventoryCheckDetail.item.storage, storage)
                .leftJoin(inventoryCheckDetail.item.storageLocation, storageLocation)
                .where(inventoryCheckDetail.inventoryCheck.id.eq(dto.getInventoryCheckId()))
                .groupBy(item)
                .orderBy(orderBy.stream().toArray(OrderSpecifier[]::new))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetchResults();

        List<InventoryCheckDto.InventoryCheckDetailResponseDto> content = results.getResults();
        Long total = results.getTotal();

        return new PageImpl<>(content, pageable, total);
    }

 

< 코드 작성하면서 알게 된 점 >

- Join 문법 오류

QueryResults<InventoryCheckDto.InventoryCheckDetailResponseDto> results = queryFactory
        .select(
                //생략
        )
        .from(inventoryCheckDetail)
        //**이부분 오류**//
        .leftJoin(storage, inventoryCheckDetail.item.storage)
        //**이부분 오류**//
        .where(inventoryCheckDetail.inventoryCheck.id.eq(dto.getInventoryCheckId()))
        .groupBy(item)
        .orderBy(orderBy.stream().toArray(OrderSpecifier[]::new))
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetchResults();
//Response
{
  "timestamp": "2022-11-09 15:15",
    "path": "/api/inventory/check/detail [POST]",
    "status": "400 BAD_REQUEST",
    "isSuccess": false,
    "message": "inventoryCheckDetail.item.storage is not a root path; nested exception is java.lang.IllegalArgumentException: inventoryCheckDetail.item.storage is not a root path",
}
leftJoin() 이부분 작성 시 첫번째 파라미터에 select(Entity) from(Entity) Entity에 해당하는 값이 먼저 와야합니다. 그래서 바르게 고치면 leftJoin(inventoryCheckDetail.item.storage, storage) 이런식으로 수정해야했습니다.
 

 

- Alias OrderBy에서 참조하는 방법

orderBy.add(new OrderSpecifier(Order.DESC, Expressions.path(Object.class, inventoryCheckDetail.item, "unit")));
기존에는 위와 같은 방법으로 Q 객체를 참조하였습니다. 그런데 sum()을 사용하게 되면서 참조 하는 방법이 다를 것이라고 생각했습니다. 찾아본 결과 다른 포맷으로 참조하는 방법이 있었습니다.

 

orderBy.add(new OrderSpecifier(Order.DESC, Expressions.stringPath("sumCurrentCount")));
그래서 Select 부분에서 지정했던 alias를 통해 참조 할 수 있는 방법이 있었습니다. 그래서 위와 같은 방법을 통해 참조를 진행했습니다.

 

마무리

오랜만에 작성하려고 하니까 바로 생각이 안나서 구글링을 하게 되었습니다.
다음에는 바로 적용하기 위해 포스팅을 하면서 정리를 하였습니다.

그리고 적용하는 과정에서 오류들도 나와서 시간이 지남에 따라
까먹는 부분에 대해서 한번더 정리할 수 있는 시간이었습니다.

포스팅 읽어주셔서 감사합니다.

 

728x90
반응형