queryDsl의 반환값으로 프로젝션이 여러개일때는 튜플 또는 DTO를 통해 결과값은 반환 받는다.
queryDsl 공부하며 memberDto클래스를 만들고 Dto로 결과를 반환하는 방법 중 첫번째 방법인 setter를 이용했는데 에러가 발생했다.
package study.querydsl3.dto;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Setter
@ToString
public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Test
void projection_dto_QueryDsl_setter(){
List<MemberDto> result = jpaQueryFactory
.select(Projections.bean(MemberDto.class, member.username,member.age))
.from(member)
.fetch();
for (MemberDto s : result) {
System.out.println("s = " + s);
}
}
에러 내용
com.querydsl.core.types.ExpressionException: class com.querydsl.core.types.QBean cannot access a member of class study.querydsl3.dto.MemberDto with modifiers "protected"
QueryDSL의 Projections.bean() 메서드는 기본 생성자와 프로퍼티의 getter/setter를 필요로 합니다. MemberDto 클래스의 기본 생성자 protected로 지정되어 있기 때문에 해당 메서드가 접근할 수 없는 것입니다.
Projections.constructor() 메서드를 이용해 MemberDto 클래스의 생성자를 호출하고, member.username과 member.age 값을 전달하여 MemberDto 객체를 생성합니다. 이렇게 생성된 DTO 객체들은 결과로 반환되어 result 리스트에 저장됩니다.
List<MemberDto> result = jpaQueryFactory
.select(Projections.bean(MemberDto.class, member.username,member.age))
.from(member)
.fetch();
List<MemberDto> result1 = jpaQueryFactory
.select(Projections.fields(MemberDto.class, member.username,member.age))
.from(member)
.fetch();
List<MemberDto> result2 = jpaQueryFactory
.select(Projections.constructor(MemberDto.class, member.username,member.age))
.from(member)
.fetch();
ExpressionUtils.as(source,alias) : 필드나, 서브 쿼리에 별칭 적용 username.as("memberName") : 필드에 별칭 적용
@Test
void projection_dto_QueryDsl_As(){
QMember qMember = new QMember("q");
List<UserDto> result = jpaQueryFactory
.select(Projections.bean(UserDto.class,
member.username.as("user"),
ExpressionUtils.as(JPAExpressions.select(qMember.age.max()).from(qMember),"age")))
.from(member)
.fetch();
@NoArgsConstructor//(access = AccessLevel.PROTECTED)
@Getter
@Setter
@ToString
public class MemberDto {
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Test
void projection_dto_QueryProjection() {
jpaQueryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
}
이 방법은 컴파일러로 타입을 체크할 수 있으므로 가장 안전한 방법이다. 다만 DTO에 QueryDSL 어노테 이션을 유지해야 하는 점과 DTO까지 Q 파일을 생성해야 하는 단점이 있다.