궁극의 방법 QueryProjection 그것을 알아보자!
MemberDto에다가 바로 QueryProjection이라고 적어주자,
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
그리고 gradle 파일에가서 compileQuerydsl을 눌러주면 dto도 Q파일로 생성이 된다.
Test에서는 아래와 같이 해준다.
@Test
public void findDtoByQueryProjection() {
queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
}
expression이 딱딱 맞기 때문에 컴파일 시점에 타입이 안맞으면 오류를 내준다.
public QMemberDto(com.querydsl.core.types.Expression<String> username, com.querydsl.core.types.Expression<Integer> age) {
super(MemberDto.class, new Class<?>[]{String.class, int.class}, username, age);
}
돌려보면 결과가 잘 나온다.
@Test
public void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
/* select
member1.username,
member1.age
from
Member member1 */ select
member0_.username as col_0_0_,
member0_.age as col_1_0_
from
member member0_
memberDto = MemberDto(username=member1, age=10)
memberDto = MemberDto(username=member2, age=20)
memberDto = MemberDto(username=member3, age=30)
memberDto = MemberDto(username=member4, age=40)
constructor는 오류를 냈을 때 실행하는 순간이 되서야 오류를 찾을수가 있다. 런타임 오류다.
@Test
public void findDtoByConstructor() {
// member의 username과 age랑 타입을 맞춰야 한다.
List<UserDto> result = queryFactory
.select(Projections.constructor(UserDto.class,
member.username,
member.age,
member.id))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (UserDto UserDto : result) {
System.out.println("UserDto = " + UserDto);
}
}
똑같은 방식을 프로젝션으로 하면 컴파일오류가 바로 보인다.
@Test
public void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age, member.id))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
왜냐 Querydsl이 만들어 놓은 생성자에는 type으로 두가지만 받아들이게 되어서 그렇다.
그래서 다른 인자가 추가로 들어오거나 하면 에러가 나게 된다.
굉장히 잘 설계가 되어있다.(Command + p를 눌려서 이름도 확인해주자.) 매우 편리하다.
public QMemberDto(com.querydsl.core.types.Expression<String> username, com.querydsl.core.types.Expression<Integer> age) {
super(MemberDto.class, new Class<?>[]{String.class, int.class}, username, age);
}
하지만 단점이 Q파일을 생성해야 하고 즉 MemberDto에 @QueryProjection을 넣어줘야 하고
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
@Test
public void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
그리고 MemberDto에서 @QueryProjection을 넣는 순간 memberdto에서 querydsl에 대한 의존성을 가지게 된다.
querydsl 라이브러리를 뺀다고 하면 아래 코드들이 다 영향을 받을 것이다.
// MemberDto
package study.querydsl.dto;
import com.querydsl.core.annotations.QueryProjection;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Dto는 보통 리포지토리에서 Dto를 조회한 다음에 서비스에서도 쓰고 컨트롤러에서도 쓰고 api를 바로 반환하기도 한다.
여러 Layer에 걸쳐서 돌아다닌다. 그렇게 흘러가는 Dto안에 @QueryProjection이 들어가 있는 것이다.
(순수한 Dto가 아닌 Querydsl에 의존적이다.)
프로젝션 결과반환을 전체적으로 정리를 해보자.
- 결과가 하나인 거는 타입으로 바로 지정하면 된다.
- 결과가 둘 이상일때는 튜플을 써야했고
- 그리고 tuple은 가급적 리포지토리 계층안에서만 쓰고
- DTO 조회할때는 순수 JPA에서는 new operation을 써야한다.
- Querydsl은 세가지 방법을 지원한다.
- setter - 프로퍼티 접근방법
- 필드에 직접 접근
- 생성자 constructor 사용하는 방법
그리고 생성자를 사용하는 방식에서는 QueryProjection 방식까지 지원해주고
Dto도 Q파일로 생성을 해서
query를 날릴때 아래와 같이 활용을 할 수가 있다.
대신 DTO가 Querydsl annotation에 의존하게 된다.
마지막으로 참고로 distinct도 알아두자.
<출처 김영한: 실전! Querydsl >
https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard
실전! Querydsl - 인프런 | 강의
Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...
www.inflearn.com
'Spring > QueryDSL' 카테고리의 다른 글
동적 쿼리 - Where 다중 파라미터 사용 (0) | 2022.04.21 |
---|---|
동적 쿼리 - BooleanBuilder (0) | 2022.04.21 |
프로젝션 결과 반환 - DTO 조회 (0) | 2022.04.21 |
프로젝션과 결과 반환 - 기본 (0) | 2022.04.20 |
Case 문 & 상수, 문자 더하기 (0) | 2022.04.20 |