안녕하세요. 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();
}
}
[ 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 사용하는 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'Spring > SpringBoot' 카테고리의 다른 글
[SpringBoot] Oracle과 동일한 AES 암호화하기 (0) | 2022.07.30 |
---|---|
[SpringBoot] MySQL과 동일한 AES 암호화하기 (0) | 2022.07.25 |
[SpringBoot] Found shared references to a collection 에러 (0) | 2022.06.13 |
[SpringBoot] JPA에서 QueryDSL 사용하기 (0) | 2022.04.24 |
[SpringBoot] AWS S3에 파일 업로드하기 (1) | 2022.04.23 |
댓글