Querydsl 수업을 듣고 정리한 내용입니다.
✏️ 프로젝션이란?
select
대상을 지정하는 것이다.
@Test
public void simpleProjection() {
List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
for (String s : result) {
System.out.println("s = " + s);
}
}
실행 결과
프로젝션 대상이 둘 이상일 때 사용한다.
com.querydsl.core.Tuple
@Test
public void tupleProjection() {
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
for (Tuple tuple : result) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
System.out.println("username = " + username);
System.out.println("age = " + age);
}
}
Tuple
은 리포지토리 계층 안에서 쓰는 정도는 괜찮지만, 그 밖의 계층에서는 DTO로 변환하는 것이 좋다.
실행 결과
MemberDto
package study.querydsl.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
순수 JPA에서 DTO 조회 코드
@Test
public void findDtoByJPQL() {
List<MemberDto> result = em.createQuery("select new study.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class)
.getResultList();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
new
명령어를 사용해야 한다.실행 결과
결과를 DTO 반환할 때 사용하며, 3가지 방법을 지원한다.
- 프로퍼티 접근
- 필드 직접 접근
- 생성자 사용
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
Projections.bean(주입대상클래스, 프로퍼티1, 프로퍼티2)
방식으로 값을 주입한다.
실행 결과
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
Getter
, Setter
가 없어도 필드에 바로 값을 주입한다.Projections.fields(주입대상클래스, 필드1, 필드2)
실행 결과
✔️ 별칭이 다를 때
UserDto
package study.querydsl.dto;
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
public UserDto() {
}
public UserDto(String name, int age) {
this.name = name;
this.age = age;
}
}
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
ExpressionUtils.as(JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
ExpressionUtils.as(source, alias)
: 필드나, 서브 쿼리에 별칭을 적용한다.username.as("memberName")
: 필드에 별칭 적용한다.
실행 결과
@Test
public void findDtoByConstructor() {
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
실행 결과
✔️ 생성자 + @QueryProjection
package study.querydsl.dto;
import com.querydsl.core.annotations.QueryProjection;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class MemberDto {
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@QueryProjection
을 붙여준다.@QueryProject
추가후, compileQuerydsl 클릭
QMemberDto
생성되었다.
✔️ @QueryProjection 활용
@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);
}
}
QueryDSL
어노테이션을 유지해야 하고 DTO까지 Q파일을 생성해야 하는 단점이 있다.
실행 결과
✔️ distinct 사용
List<String> result = queryFactory
.select(member.username).distinct()
.from(member)
.fetch();
distinct
와 같다.