forDevLife
QueryDSL로 한 방 쿼리 작성하기 본문
이번 과제에서 위와 같은 이슈 조회를 위한 한 방 쿼리 작성을 시도했다. 특이사항으로는 label이 여러 개가 붙을 수 있다는 점이다.
아래에서 파란색은 조인된 관계를 의미하며, 빨간색은 DTO에 매핑될 컬럼을 의미한다.
Issue와의 관계를 분석하면 다음과 같다.
- Issue : user = N : 1
- Issue : milestone = N : 1
Issue와 label은 N:M(다대다) 관계이기 때문에 중간에 attached_label 테이블을 통해 관계를 1:N, N:1로 풀었다.
- Issue : attached_label = 1 : N
- attached_label : label = N : 1
한 방 쿼리를 위해서는 N : 1, 1 : N 관계 모두 FetchJoin을 통해 한 번에 가져올 수 있다.
하지만 Issue, label처럼 다대다로 인한 중간 테이블이 있을 경우에는 어떻게 해야할까?
간단하다. Issue : attached_label = 1 : N을 FetchJoin하고, attached_label : label = N : 1을 FetchJoin하면 된다.
이 방식을 통해 아래와 같이 Issue -> AttachedLabel -> Label로 추가 쿼리 없이 탐색이 가능해진다.
앞서 빨간색으로 표시한 정보를 통해 만들고자 하는 DTO는 다음과 같다.
LabelCoverResponse : label의 정보를 가지는 DTO
@ToString
@Getter
public class LabelCoverResponse {
private String labelName;
private String labelColor;
private String textColor;
public LabelCoverResponse(String labelName, String labelColor, String textColor) {
this.labelName = labelName;
this.labelColor = labelColor;
this.textColor = textColor;
}
}
IssueCoverResponse : LabelCoverResponse를 List로 가지고 있는 DTO
@ToString
@Getter
public class IssueCoverResponse {
private List<LabelCoverResponse> labelCoverResponses;
private String title;
private Long issueId;
private String writer;
private String writerImage;
private LocalDateTime modificationTime;
private String milestoneName;
}
방법 1. Issue로 모두 조회 후 DTO로 매핑
다음과 같이 Issue로 필요한 모든 내용을 가져온 후, Application level에서 DTO로 변형하는 방법이 제일 Simple하다.
1) Querydsl을 통해 IssueList를 조회한다.
2) Application level에서 DTO로 변환한다.
하지만, 이 방식에서는 FetchJoin으로 인해 매핑되지 않을 컬럼도 모두 조회가 된다. 실제 나가는 select 절을 보면 상당히 길다.
방법 2 : 처음부터 DTO로 매핑
column에서 DTO에 매핑할 컬럼만 가져올 수 있다. transform & groupBy 조합을 사용하며, 이는 Querydsl에서 제공하는 Result Aggregation(결과 집합) 기능이다. groupBy는 다음 패키지에 존재한다.
import static com.querydsl.core.group.GroupBy.groupBy;
transform을 사용하면 groupBy에 지정된 key를 기준으로 list를 만들 수 있게 된다.
위에서는 issue.id를 기준으로 그룹화하겠다는 의미이다. fetchJoin()이 아님에 유의하자.
위의 결과로 생성된 쿼리의 결과는 다음과 같으며, transform에서 지정한 컬럼만 가져옴을 알 수 있다.
위와 같이 가져온 데이터를 issue_id별로 그루핑하여 DTO에 매핑하게 된다.
같은 issue_id 그룹 내에서는 Label 관련 컬럼(label_name, label_color, label_text_color)만 다름에 유의하자! 따라서 위와 같이 DTO 내의 List로 매핑이 가능해진다.
해당 방법을 사용한 최종 조회 결과는 다음과 같다.
다만, label정보가 없을 경우에는 아래처럼 null로 채워짐을 알 수 있다.
참고
- list().as()를 통해 alias를 지정할 수 있는 것 같는데, 이걸 하면 projection 오류가 발생한다. 따로 지정 안하니 해결되었다.
'JPA' 카테고리의 다른 글
[JPA] @ElementCollection (0) | 2021.11.21 |
---|