Database ~ SQL

[QueryDsl] DTO로 결과 반환하기 (Projections)

yooverd 2025. 4. 3. 20:08
728x90
반응형
SMALL

QueryDsl을 활용하여 데이터를 조회할 때, @Entity로 매핑된 클래스와 다른 형태로 데이터를 조회해야 할 때가 종종 발생한다.

이때 활용하기 좋은 방법 몇가지를 소개하려고 한다.

 

기본  TUPLE  활용

우선 말해두고 싶은 점은 이 방식은 QueryDsl을 사용하는 레이어 외의 다른 영역에서도 사용할 예정이라면 의존성 관리 측면에서 추천하지 않는 방식이다.

Tuple이라는 클래스는 QueryDsl 이 제공하는 클래스이기 때문에 외부에서 사용하는 경우, 의존성에 영향을 미치게 되므로 내부에서 사용하는 경우에 활용하는 것이 좋아 보인다.

List<Tuple> categoryTupleList = jpaQueryFactory
        .select(
                qCategoriesJpo.categoryname,
                qCategoriesJpo.description
        )
        .from(qCategoriesJpo)
        .fetch();


for (Tuple row : categoryTupleList) {
    // tuple 의 get 메서드를 활용하여 컬럼 값 추출
    String categoryname = row.get(qCategoriesJpo.categoryname);
    String description = row.get(qCategoriesJpo.description);

    System.out.println("카테고리 : " + categoryname);
    System.out.println("설명 : " + description);
}

 

 


 

 

아래의 세가지 방법은 QueryDsl에서 제공하는 Projections 클래스를 활용하는 방식이다.

이 방식들을 사용하면 쿼리 결과를 DTO 클래스 등에 바로 입력하여 사용할 수 있다.

 

프로퍼티 접근법

Projections 의 beans 메서드를 활용하는 방법으로, 지정한 클래스의 setter를 통해 컬럼 값을 넣는 방식이다.

기본생성자를 이용하여 클래스를 생성하고 setter를 통해 값을 넣는 것이 특징이다.

setter 를 활용하기 때문에 불변 객체엔 값을 제대로 할당할 수 없다.

List<SampleDto> categories2 = jpaQueryFactory
        .select(Projections.bean(SampleDto.class,
                qCategories.categoryname,
                qCategories.description)
        ).from(qCategories)
        .fetch();

 

 

필드 직접 접근법

Projections 의 fields 메서드를 활용하는 방법으로, 지정한 클래스의 필드에 값을 직접 주입하는 방식이다.

@Autowired 처럼 동작하기 때문에 컬럼명과 필드명을 올바르게 일치시켜야 값이 제대로 주입된다.

마찬가지로 불변 객체엔 값을 제대로 주입할 수 없다.

List<SampleDto> categories3 = jpaQueryFactory
        .select(Projections.fields(SampleDto.class,
                qCategories.categoryname,
                qCategories.description)
        ).from(qCategories)
        .fetch();

 

 

생성자 접근법

Projections 의 consturctor 메서드를 활용하는 방법으로, 지정한 클래스의 적절한 생성자를 찾아 값을 넣는 방식이다.

참고로 constructor 메서드의 내부를 살펴보면 전달받은 파라미터를 각각 클래스타입 배열과 값 리스트로 나누어 활용한다.

파라미터의 순서가 정확하지 않는 경우, 의도와 다르게 값이 들어갈 수도 있고, 오류 발생 시 런타임 오류가 발생한다.

따라서 생성자에 명시한 파라미터의 순서를 정확히 맞춰 값을 넘겨주어야 한다.

List<SampleDto> categories4 = jpaQueryFactory
        .select(Projections.constructor(SampleDto.class, qCategories.categoryname, qCategories.description)
        ).from(qCategories)
        .fetch();

 

@QueryProjection

컴파일 시점에 Q클래스를 만들어 활용하는 방식이다.

이 방식 또한 생성자를 활용하는데, 차이점은 본 클래스의 생성자가 아닌 Q클래스의 생성자를 활용한다는 점이다.

따라서 기존 생성자 방식에서 잡지 못했던 오류를 컴파일 과정에서 발견할 수 있으므로 생성자 접근법과 비교했을 때 더 안정적이라고 볼 수 있다.

그러나 QueyDsl이 제공하는 아노테이션인 만큼 의존성 관리에 주의가 필요하다.

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class SampleDto {

    private Long categoryid;
    private String categoryname;
    private String description;
 
    @QueryProjection
    public SampleDto(String categoryname, String description) {
        this.categoryname = categoryname;
        this.description = description;
    }
}

 

List<SampleDto> categories5 = jpaQueryFactory
        .select(new QSampleDto(qCategories.categoryname, qCategories.description))
        .from(qCategories)
        .fetch();

 

 

참고로 DTO로 조회한 것들은 영속성 관리 대상이 아니다.

 

참고

http://querydsl.com/static/querydsl/4.0.1/reference/ko-KR/html_single/#d0e2002

https://pyoungt.tistory.com/215

https://velog.io/@bagt/QueryDsl-DTO-Projection

https://yeoon.tistory.com/149

 

 

 

728x90
반응형
LIST