본문 바로가기
Spring/SpringBoot

[SpringBoot] QueryDSL Projections로 결과값 핸들링하기

by J4J 2022. 7. 9.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 QueryDSL Projections 사용하는 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

Projections 사용 이유

 

제가 Projections을 사용하는 이유는 Repository 내부에서 데이터를 조회할 때 Entity 외의 값 (ex, DTO)으로 편리하게 리턴 받아 사용할 수 있도록 하기 위해서입니다.

 

일반적으로 Proejctions을 사용하지 않는다면 다음과 같이 select의 return값으로 Tuple을 전달받은 뒤 DTO 등으로 변환하여 리턴하는 방식도 있습니다.

 

package com.spring.querydsl.repository;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.spring.querydsl.entity.QStudent;
import com.spring.querydsl.entity.SomeStudent;

@Repository
public class StudentQueryRepository {
	
	@Autowired
	private JPAQueryFactory jpaQueryFacotry;
	
	public List<SomeStudent> findForSomeStudentList() {
		List<Tuple> tupleList = jpaQueryFacotry.select(QStudent.student.name,
                                                	       QStudent.student.age)
                                             	       .from(QStudent.student)
                                             	       .fetch();
		
		List<SomeStudent> someStudentList = new ArrayList<>();
		for(Tuple tuple : tupleList) {
			SomeStudent someStudent = SomeStudent.builder()
                                             	       	     .name(tuple.get(QStudent.student.name))
                                             	       	     .age(tuple.get(QStudent.student.age))
                                             	       	     .build();
			
			someStudentList.add(someStudent);
		}
		
		return someStudentList;
	}
}

 

 

 

하지만 Tuple을 이용할 경우 위와 같이 Entity 객체에 사용되는 변수값들을 한번 더 이용해서 변경을 해줘야 하는 번거로움들이 있습니다.

 

지금 당장은 그냥 이렇게 작성해도 되지 않나? 라는 생각이 들 수도 있겠지만, 테이블 규모가 커지고 또한 프로젝트의 규모가 커질수록 이런 반복적인 작업들을 해야 하는 것은 개발 및 유지보수를 할 때 불편하며 효율적이지 못한 상황을 만들어 냅니다.

 

 

반응형

 

 

Projections은 위의 상황을 개선하는데 도움을 줍니다.

 

Projections은 아래와 같이 데이터를 select 할때부터 저장되고자 하는 class를 지정하여 필드에 맞는 데이터를 바로 넣어줄 수 있습니다.

 

package com.spring.querydsl.repository;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.spring.querydsl.entity.QStudent;
import com.spring.querydsl.entity.SomeStudent;

@Repository
public class StudentQueryRepository {
	
	@Autowired
	private JPAQueryFactory jpaQueryFacotry;

	public List<SomeStudent> findForSomeStudentList() {
		return jpaQueryFacotry.select(Projections.fields(SomeStudent.class,
                                                                 QStudent.student.name,
                                                                 QStudent.student.age))
                                      .from(QStudent.student)
                                      .fetch();
	}
}

 

 

 

한눈에 봐도 Tuple을 사용하는 것보다 더 효율적이란 것을 느낄 수 있을 것으로 보입니다.

 

 

 

Projections 사용 방법

 

Projections의 사용 방법은 크게 3가지로 구분됩니다.

 

  • Field 조회
  • 생성자 조회
  • Setter 조회

 

 

 

[ 1. Field 조회 ]

 

Field 조회는 제가 위에서 사용한 방법입니다.

 

데이터를 담고 싶은 클래스의 Field 명을 활용하는 겁니다.

 

SomeStudent 클래스의 구조는 다음과 같이 되어 있습니다.

 

package com.spring.querydsl.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SomeStudent {
	private String name;
	private int age;
}

 

 

 

클래스 내부에 변수들은 name, age가 각각 있기에 Projections을 이용하여 데이터를 조회할 때 name, age에 맞는 변수명들을 다음과 같이 각각 넣어주면 데이터가 정상적으로 적재됩니다.

 

package com.spring.querydsl.repository;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.spring.querydsl.entity.QStudent;
import com.spring.querydsl.entity.SomeStudent;

@Repository
public class StudentQueryRepository {
	
	@Autowired
	private JPAQueryFactory jpaQueryFacotry;

	public List<SomeStudent> findForSomeStudentList() {
		return jpaQueryFacotry.select(Projections.fields(SomeStudent.class,
                                                                 QStudent.student.name,
                                                                 QStudent.student.age))
                                      .from(QStudent.student)
                                      .fetch();
	}
}

 

 

728x90

 

 

[ 2. 생성자 조회 ]

 

생성자는 데이터를 담고 싶은 클래스의 생성자를 이용하는 방법입니다.

 

클래스에 존재하는 생성자를 이용하는 방법이기 때문에 다음과 같이 필드명이 동일하지 않아도 정상적으로 사용 가능합니다.

 

package com.spring.querydsl.repository;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.spring.querydsl.entity.QStudent;
import com.spring.querydsl.entity.SomeStudent;

@Repository
public class StudentQueryRepository {
	
	@Autowired
	private JPAQueryFactory jpaQueryFacotry;

	public List<SomeStudent> findForSomeStudentList() {
		return jpaQueryFacotry.select(Projections.constructor(SomeStudent.class,
                                                                      Expressions.asString(QStudent.student.name).as("randomName"), // 필드명 변경하기
                                                                      QStudent.student.age))
                                      .from(QStudent.student)
                                      .fetch();
	}
}

 

 

 

[ 3. Setter 조회 ]

 

Setter도 다른 것들과 마찬가지로 데이터를 담고 싶은 클래스의 Setter를 이용하는 방법입니다.

 

아무래도 Setter를 이용하는 방법이기 때문에 field와 유사하게 이름이 같지 않으면 데이터가 정상적으로 적재되지 않습니다.

 

package com.spring.querydsl.repository;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.spring.querydsl.entity.QStudent;
import com.spring.querydsl.entity.SomeStudent;

@Repository
public class StudentQueryRepository {
	
	@Autowired
	private JPAQueryFactory jpaQueryFacotry;

	public List<SomeStudent> findForSomeStudentList() {
		return jpaQueryFacotry.select(Projections.bean(SomeStudent.class,
                                                               QStudent.student.name,
                                                               QStudent.student.age))
                                      .from(QStudent.student)
                                      .fetch();
	}
}

 

 

 

 

 

 

 

이상으로 QueryDSL Projections 사용하는 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글