QueryDSL을 이용하여 entity 전체를 가져오는 방법 말고, 조회 대상을 지정하여 원하는 값만 조회하는 것을 프로젝션이라고 합니다.
프로젝션 대상이 하나일 경우에는 반환되는 타입이 프로젝션 대상의 타입입니다.
@Test
public void oneProjectionTest() {
QMember m = QMember.member;
query.select(m.name)
.from(m)
.fetch()
.stream()
.forEach(name -> log.info("name is : " + name));
}
위의 결과를 보면 Member 엔티티의 name은 String 타입이므로 프로젝션 대상이 하나일 때, List<String> 타입이 조회 결과로 반환되는 것을 볼 수 있습니다.
만약 두 개 이상일 경우에는 어떤 타입이 반환될까요??
@Test
public void twoProjectionsTest() {
QMember m = QMember.member;
List<Tuple> results = query.select(m.name, m.age)
.where(m.age.goe(30L))
.from(m)
.fetch();
results.stream()
.forEach(tuple -> {
log.info("name is " + tuple.get(0, String.class));
log.info("age is " + tuple.get(m.age));
});
}
프로젝션 대상이 둘 이상이라면 Tuple 타입을 반환합니다. Tuple을 조회할 때는 get() 메서드를 이용하면 됩니다.
사용해보니 get() 으로 조회하는 방법으로 두 가지가 있는 것 같습니다.
첫 번째 방법은 get() 메서드의 첫 번째 파라미터로 프로젝션 대상의 순번, 두 번째 파라미터는 해당 값의 타입을 명시하는 방법입니다.
위의 예제 상에서 첫 번째 로그를 찍는 tuple.get(0, String.class) 코드입니다. select 대상에서 첫 번째 값으로 String 타입인 m.name 으로 지정하였으므로 0번 째 조회 결과를 가져오는 것입니다.
두 번째 방법은 간단하게 조회한 쿼리 타입을 바로 지정하는 것입니다.
로그를 찍는 코드상에서 tuple.get(m.age) 에 해당하는 방법입니다.
저는 두 가지 방법 중에서 두 번째 방법이 더 가독성이 있어 보입니다.
쿼리의 결과를 특정 객체로 받고 싶을 때는 QueryDSL에서 제공하는 Projections 클래스를 이용하면 됩니다.
Projections 클래스를 이용하여 객체를 생성하는 방법 3가지
- 프로퍼티 접근
- 필드 직접 접근
- 생성자 사용
package com.edu.querydsl_training.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {
private String name;
private Long age;
}
예제를 위해 MemberDTO 클래스를 생성하였습니다. 해당 클래스는 단순하게 Member의 이름과 나이를 담는 DTO클래스입니다.
먼저 프로퍼티를 이용한 접근 방법을 살펴보겠습니다.
프로퍼티 접근 방법은 Projections.bean() 메서드를 이용하여 객체를 생성할 수 있고, setter() 메서드를 사용해서 값을 주입시켜줍니다.
@Test
public void simpleProjectionTest() {
QMember m = QMember.member;
query.select(Projections.bean(MemberDTO.class, m.name, m.age.as("age")))
.from(m)
.fetch()
.stream()
.forEach(memberDTO -> {
log.info("member name : " + memberDTO.getName());
log.info("member age : " + memberDTO.getAge());
});
}
필드 직접 접근 방법
Projections.fields() 메서드를 이용하여 값을 주입시킵니다. 필드의 접근 지정자를 private로 지정해도 동작하며, setter() 메서드가 없어도 정상적으로 값이 주입됩니다.
@Test
public void simpleProjectionTest_2() {
QMember m = QMember.member;
query.select(Projections.fields(MemberDTO.class, m.name, m.age))
.from(m)
.fetch()
.stream()
.forEach(memberDTO -> {
log.info("member name : " + memberDTO.getName());
log.info("member age : " + memberDTO.getAge());
});
}
생성자를 이용하는 방법
Projections.constructor() 메서드를 이용하면 됩니다. 해당 메서드는 생성자를 이용하여 객체에 값을 주입하며, 지정한 프로젝션과 생성자의 파라미터 순서가 같아야 합니다.
@Test
public void simpleProjectionTest_3() {
QMember m = QMember.member;
query.select(Projections.constructor(MemberDTO.class, m.name, m.age))
.from(m)
.fetch()
.stream()
.forEach(value -> {
log.info("member name : " + value.getName());
log.info("member age : " + value.getAge());
});
}
'Study > JPA' 카테고리의 다른 글
[ JPA ] Save 후 Id 구하기 (0) | 2024.08.11 |
---|---|
@Embedded, @AttributeOverride (0) | 2022.06.03 |
[JPA] Transactional(readOnley=true) 가 성능 향상이 되는 이유 (0) | 2022.05.31 |
[JPA] @AttributeOverride - 매핑 정보 재정의 (0) | 2022.05.25 |
페이징 QueryDSL (0) | 2022.05.13 |
댓글