본문 바로가기
공부/Spring

[QueryDSL] 프로젝션(SELECT 대상)에 따라 다른 결과를 가져와 보자

by JERO__ 2022. 8. 26.

1. 프로젝션에 대상 개수에 따른 결과

프로젝션은 SELECT 대상 지정이다.

1-1. 대상이 하나일 때

  • 타입을 명확하게 지정할 수 있다.

1-2. 대상이 둘 이상

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);
}
  • 대상이 둘 이상이면 튜플이나 DTO로 조회하자
  • 사용하려면 레포지토리 안에서만 사용하고 외부에는 DTO로 반환하자

 

2. DTO를 통해 조회해보자

2-1. JPA에서 DTO로 조회해보자(비교)

List<MemberDto> result = em.createQuery(
                    "select new study.querydsl.dto.MemberDto(m.username, m.age) " +
                            "from Member m", MemberDto.class)
            .getResultList();
  • DTO의 package 이름을 다 적어주기 때문이 지저분하다.
  • 생성자 방식만 지원한다.

2-2. Querydsl에서 DTO로 조회해보자

접근 방법에 3가지 방법이 있다.

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용

하나씩 살펴보자!

프로퍼티 접근 (Setter)

List<MemberDto> result = queryFactory
	.select(Projections.bean(MemberDto.class,
		member.username,
		member.age))
	.from(member)
	.fetch();

필드 직접 접근

List<MemberDto> result = queryFactory 
	.select(Projections.fields(MemberDto.class,
		member.username,
		member.age))
	.from(member)
	.fetch();

엔티티 네임과 DTO 네임(별칭)이 다르다면, 별칭으로 변환해주어야 한다.

List<UserDto> fetch = queryFactory
	.select(Projections.fields(UserDto.class,
		member.username.as("name"),
		ExpressionUtils.as(
			JPAExpressions                  // 서브쿼리
				.select(memberSub.age.max())
				.from(memberSub), "age")
		)
	).from(member)
	.fetch();
  • as : 필드에 별칭 적용
  • ExpressionUtils.as(source,alias) : 필드나, 서브 쿼리에 별칭 적용

생성자 사용

List<MemberDto> result = queryFactory
	.select(Projections.constructor(MemberDto.class,
		member.username,
		member.age))
	.from(member)
	.fetch();
}

 

3. @QueryProjection을 통해 DTO를 select대상으로 바로 설정해보자

DTO 생성자에 @QueryProjection 어노테이션을 추가해보자

  • ./gradlew compileQuerydsl
  • QMemberDto가 생성된다
@QueryProjection
public MemberDto(String username, int age) {
	this.username = username;
	this.age = age;
}

select 절에서 해당 DTO를 바로 사용할 수 있다.

List<MemberDto> result = queryFactory
	.select(new QMemberDto(member.username, member.age))
	.from(member)
	.fetch();
  • 컴파일러로 타입을 체크할 수 있으므로 가장 안전한 방법이다.
  • 하지만, DTO에 QueryDSL 어노테이션을 유지해야 하며 DTO까지 Q파일을 생성한다.
    • DTO가 QueryDSL의 의존성을 가진다.
    • 팀 컨밴션으로 정해서 사용하자

 

4. distinct

JPQL의 distinct와 같다.

List<String> result = queryFactory
	.select(member.username).distinct()
	.from(member)
	.fetch();

 

댓글