select 대상이 하나일 때 타입을 명확하게 지정할 수 있음
List<String> result = queryFactory
.select(member.username)
// .select(member.username).distinct() JPQL의 distinct와 동일
.from(member)
.fetch();
select 대상이 여러 개일 때
import com.querydsl.core.Tuple;
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
MemberDto
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
* @Data
Equivalent to @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode.
기본적으로 getter, setter 등을 만들어주지만 따로 생성자를 만들었기 때문에 기본 생성자를 정의하거나 혹은 @NoArgsConstructor을 선언해줘야 한다.
List<MemberDto> result = em.createQuery("select new study.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class)
.getResultList();
DTO의 package 경로까지 모두 명시해야 하며 new 명령어를 사용해 생성자 방식만 지원
import com.querydsl.core.types.Projections;
1) 프로퍼티 접근
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
기본 생성자와 getter, setter가 있어야하며 setter를 통해 값이 주입된다.
2) 필드 직접 접근
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
getter, setter 없이 바로 값이 주입된다.
3) 생성자 사용
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
기본 생성자와 사용자 정의 생성자가 있어야 하며 생성자의 인자 타입이 맞아야 한다. 인자가 맞지 않는 경우 RUNTIME ERROR가 발생한다.
※ 별칭이 다를 때
UserDto
@Data
@NoArgsConstructor
public class UserDto {
private String name; // username(Member) -> name(UserDto)
private int age;
public UserDto(String name, int age) {
this.name = name;
this.age = age;
}
}
예1) 필드 직접 접근 방식
List<UserDto> result = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
member.age))
.from(member)
.fetch();
실행 결과
userDto = UserDto(name=null, age=10)
userDto = UserDto(name=null, age=20)
userDto = UserDto(name=null, age=30)
userDto = UserDto(name=null, age=40)
예2) 서브쿼리
나이는 최대 나이로 통일하여 모든 member의 이름 조회
import com.querydsl.core.types.ExpressionUtils;
QMember memberSub = new QMember("memberSub");
List<UserDto> result2 = queryFactory
.select(Projections.fields(UserDto.class,
// member.username.as("name"), // 두 방법 다 가능
ExpressionUtils.as(member.username, "name"),
ExpressionUtils.as(JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")
))
.from(member)
.fetch();
예3) 생성자 사용
List<UserDto> result = queryFactory
.select(Projections.constructor(UserDto.class,
member.username,
member.age))
.from(member)
.fetch();
MemberDto
import com.querydsl.core.annotations.QueryProjection;
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
@QueryProjection
public MemberDto(String username, int age) {
this.username = username
this.age = age;
}
}
Gradle > Querydsl > other > compileQuerydsl > DTO Qtype 생성 확인
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
생성자를 이용한 방법은 런타임 오류가 발생하는 반면 QueryProjection은 컴파일러로 타입을 체크할 수 있어 보다 안전하고 인텔리제이의 자동완성 기능을 활용할 수 있다.
하지만 DTO도 Qtype을 생성해야 하고 여러 레이어에 거쳐 사용되는 DTO가 queryDSL에 의존성을 갖게 되어 순수한 DTO로써 활용하지 못한다는 단점이 있다. 또한 아키텍쳐적으로 깔끔하지 않다.
[SPRING] Querydsl | 벌크 연산 (0) | 2021.05.17 |
---|---|
[SPRING] Querydsl | 동적쿼리 (0) | 2021.05.17 |
[SPRING] Querydsl | 문법(2) 조인연산 (0) | 2021.04.30 |
[SPRING] Querydsl | 기본 문법(1) (0) | 2021.04.30 |
[SPRING] Querydsl | queryDSL이란 (0) | 2021.04.29 |
댓글 영역