이번시간에는 서브쿼리와 프로젝션에 대해서 알아보자.
서브쿼리는 우리가 sql에서 말하는 서브쿼리와 동일하다.
일단 서브쿼리의 예제를 한번 살펴보자.

QItem item = QItem.item;
QItem itemSub = QItem.item;
return from(item)
  .where(item.price.eq(
    JPAExpressions.select(itemSub.price.max()).from(itemSub)
  )).fetchOne();

그냥 딱 봐도 일반 sql와 문법이 비슷하기에 설명은 하지 않아도 될 듯 싶은데.. item에서 가장 비싼 상품을 찾아서 반환하는 그런 query이다. 아주 간단하기 때문에 눈으로 흘겨봐도 알 듯싶다. 물론 복잡한거는 나도 모른다.
우리는 예젠에는 JPASubQuery 클래스를 생성해서 사용했다. 하지만 버전이 올라가면서 JPAExpressions 클래스를 사용하면 된다. 모든 함수가 static 함수이다.

이번에는 프로젝션을 알아보자. select 절에 조회대상을 지정하는 것을 프로젝션이라 한다.
예를 들어 보면 쿼리에서 select id, name … 이렇게 조회 대상을 지정해주는 것이다.

QItem item = QItem.item;
List<Tuple> fetch = from(item).select(item.name, item.price).fetch();
fetch.stream().forEach(i -> {
  System.out.println(i.get(item.name));
  System.out.println(i.get(item.price));
});

select를 이용해서 조회 대상을 지정해 줄 수 있다. 하지만 이 방법은 Tuple로 반환한다. Tuple로 반환하기 때문에 다시 Item에 넣어줘야 한다. 그래서 이방법은 조금 불편하다.

QItem item = QItem.item;
ConstructorExpression<ItemDto.NamePrice> constructor = Projections.constructor(ItemDto.NamePrice.class, item.name, item.price);
return from(item).select(constructor).fetch();

아까보다 조금 편한 방식이긴 하지만 Dto를 만들어줘야 하는 단점(?) 글쎄 단점이라기 보다 클래스를 한개더 만들어야 한다는거? 단점은 아니다. dto를 만들어 주면 오히려 더 원하는 값이 어떤 어떤게 들어갔는지 더 조회하지 않아도 코드만 봐서 알 수 있어서 오히려 더 좋은거 같다.

public class ItemDto {

  @Data
  @AllArgsConstructor
  public static class NamePrice{
    private String name;
    private int price;
  }
}

우리의 lombok이 있으니 훨씬 간편해 졌다.

서브쿼리는 from에 할 수 없는건가? 대충 찾다가 못찾아서 일단 패스. 차근차근 알아 봐야겠다.
다음엔 마지막으로 조인을 좀더 심도있게 알아보고 서브쿼리도 좀더 심도 있게 알아 봐야겠다.
오늘은 이만!