QueryDSL 사용하기(SpringBoot 3.0) - QueryDSL 사용하기(4)

Tech/Java 2023. 8. 4. 05:34
728x90
728x90

서론

드디어 아래의 쿼리를 QueryDSL로 뽑아내서 View시켜볼 수 있게 되었다.

select C.CHAMPION_ID, C.PRICE, ROUND((C.PRICE-CP.PRICE)/CP.PRICE*100) as PERCENT
  from Champion C, (select CC.CHAMPION_ID, CC.PRICE
					  from ChampionPriceLog CC
					 where (CC.CHAMPION_ID, CC.CREATE_DATE) in(select CCC.CHAMPION_ID, MAX(CCC.CREATE_DATE) AS CREATE_DATE
																 from ChampionPriceLog CCC
																group by CCC.CHAMPION_ID)
					order by CC.CHAMPION_ID) as CP
 where C.CHAMPION_ID = CP.CHAMPION_ID
 order by C.CHAMPION_ID;

 

 

 

 

테스트

포스팅을 하게 만들었던 위의 쿼리를 사용하기 위해 QueryDSL을 사용해보기로 했다.

QChampion C = QChampion.champion;
QChampionPriceLog CPL = QChampionPriceLog.championPriceLog;

우선 필요한 QClass의 인스턴스를 생성한 뒤

 

List<Tuple> tupleList =
        (List<Tuple>) jpaQueryFactory.select(C.name, CPL.price, C.price)
                .from(CPL)
                .join(C)
                .on(CPL.champion_id.eq(C.id))
                .where(
                        Expressions.list(CPL.champion_id, CPL.create_date)
                                .in(
                                        jpaQueryFactory.select(
                                                        CPL.champion_id, CPL.create_date.max().as("create_date")
                                                )
                                                .from(CPL)
                                                .groupBy(CPL.champion_id)
                                )

                )
                .orderBy(CPL.champion_id.asc())
                .fetch();

조인을 실행한 결과를 Tuple형태로 반환시켰다.

Tuple은 QueryDSL에서 제공하는 컨테이너 클래스로, 쿼리 결과의 각 튜플을 나타낸다.

 

이를 원하는 형태로 View시키기 위해 아래와 같은 DTO객체를 사용했고, DTO에 원하는 정보를 tuple에서 뽑아서 List형태로 반환시켜 줄 계획이기 때문에 아래처럼 DTO와 반환 코드를 작성했다.

 

package com.example.lolchampionsinvestment.domain.champion;

import lombok.Data;

@Data
public class ChampionPriceDto {

    private String name;

    private int price;

    private int percent;

    public ChampionPriceDto(String name, int price, int percent) {
        this.name = name;
        this.price = price;
        this.percent = percent;
    }
}
List<ChampionPriceDto> championPriceDtoList = tupleList.stream()
        .map(tuple -> {
            String name = tuple.get(C.name);
            int price = tuple.get(C.price);
            int cplPrice = tuple.get(CPL.price);
            int percent = Math.round((price - cplPrice) * 100 / price);
            return new ChampionPriceDto(name, price, percent);
        })
        .collect(Collectors.toList());

 

 

select C.CHAMPION_ID, C.PRICE, ROUND((C.PRICE-CP.PRICE)/CP.PRICE*100) as PERCENT
  from Champion C, (select CC.CHAMPION_ID, CC.PRICE
					  from ChampionPriceLog CC
					 where (CC.CHAMPION_ID, CC.CREATE_DATE) in(select CCC.CHAMPION_ID, MAX(CCC.CREATE_DATE) AS CREATE_DATE
																 from ChampionPriceLog CCC
																group by CCC.CHAMPION_ID)
					order by CC.CHAMPION_ID) as CP
 where C.CHAMPION_ID = CP.CHAMPION_ID;

위의 쿼리를 QueryDSL로 수행하기 위한 테스트 코드 전체는 아래와 같다.

 

@SpringBootTest
@Transactional
public class ChampionPriceQueryRepositoryTest {

    @PersistenceContext
    EntityManager em;
    JPAQueryFactory jpaQueryFactory;

    @Autowired
    ChamiponPriceQueryRepository championPriceQueryRepository;
    @Autowired
    ChampionDataParsingService championDataParsingService;

    @BeforeEach
    void init() {
        jpaQueryFactory = new JPAQueryFactory(em);
    }

    @DisplayName("챔피언 별 가장 최근 가격을 가져온다.")
    @Test
    void getChampionsLatestPrice(){
        // given
        List<ChampionPriceDto> championPriceDtoList = championPriceQueryRepository.findAllLatestChampions();

        // when // then
        assertThat(championPriceDtoList.size()).isEqualTo(164);

        assertThat(championPriceDtoList)
                .extracting("name", "price", "percent")
                .containsAnyOf(
                        tuple("아트록스", 7000, -36),
                        tuple("아리", 2000, 0),
                        tuple("카이사", 8000, 0)
                );
    }

}

 

테스트를 위해, 아트록스 데이터는 11000원을 입력해두었다.

원하는 값인 현재 금액 : 7000, 금일 등락 퍼센트인 -36%가 잘 반환된 모습이다.

 

 

 

 

적용

테스트코드를 적용시킨 Repository를 만들고

 

아래의 서비스 객체를 만든 뒤

@Service
@RequiredArgsConstructor
@Transactional
public class ChampionService {

    private final ChamiponPriceQueryRepository championPriceQueryRepository;

    public List<List<ChampionPriceDto>> getAllLatestChampions() {
        List<ChampionPriceDto> list = championPriceQueryRepository.findAllLatestChampions();
        int listSize = list.size();

        List<List<ChampionPriceDto>> groupingList = new ArrayList<>();
        for(int i = 0; i < listSize; i+=8) {
            int endIdx = Math.min(i + 8, listSize);
            groupingList.add(list.subList(i, endIdx));
        }

        return groupingList;
    }
}

 

Controller를 통해 View를 시켜 보았다.

@RestController
public class MainViewController {

    @Autowired
    ChampionService championService;

    @GetMapping("/")
    public ModelAndView mainView() {
        ModelAndView mv = new ModelAndView();
        List<List<ChampionPriceDto>> groupingList = championService.getAllLatestChampions();

        mv.addObject("groupingList", groupingList);
        mv.setViewName("main/main.html");
        return mv;
    }

}

 

8개씩 그룹핑 한 이유는, View를 그렇게 하기 위함이었는데

일단 원하는 대로 잘 들어오긴 했고, 모든 데이터가 다 들어오기 때문에, 메인 화면에 보여주는 것이다 보니 등락폭이 큰 녀석들만 추려서 View시키던, 페이징을 시키는 방향으로 보수작업을 할 것 같다.

 

 

 

 

관련 포스팅

QueryDSL이란? - QueryDSL 사용하기(1)

JPQL이란? - QueryDSL 사용하기(2)

QueryDSL JPA Test (Spring Boot 3.0) - QueryDSL 사용하기(3)

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

방명록