설명하기 앞서, QueryDSL을 사용하기 위해서는 JPAQueryFactory를 Bean 등록하고 의존성 주입받아야 한다.
Bean 등록
@Configuration
public class QueryDSLConfig {
@Bean
JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}
이번 포스팅에서 사용할 Repository
이번 포스팅에서 설명하기 위해 사용할 리포지토리는 다음과 같다.
아래에서 작성한 querydsl 코드는 이 repository 안에서 작성된 코드라고 생각하면 된다.
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final JPAQueryFactory queryFactory;
...
}
이 리포지토리는 QueryDSL을 사용하기 위한 사용자 정의 리포지토리로 만든 것이다.
이에 대한 내용은 아래 포스팅을 참고하면 알 수 있다.
QueryDSL Repository 생성하기 (사용자 정의 리포지토리)
Q 클래스 사용
QueryDSL을 적용하고 나면 생성되는 Q 클래스를 사용하는 방법은 총 2가지가 있다.
- new 키워드로 직접 생성해 별칭을 직접 지정하는 방법
- static import 하여 사용하는 방법.
// 1번에 해당하는 방법
QMember qMember = new QMember("m"); // m 이라는 별칭을 직접 지정하였음.
QMember qMember = QMember.member; // 기본 인스턴스 사용 (생성되어있는 Q클래스에 적용된 별칭사용)
// 2번에 해당하는 방법.
import static study.querydsl.entity.QMember.member;
public List<Member> findAllMember() {
return queryFactory
.selectFrom(member)
.fetch();
}
보틍은 2번 static import 하여 사용하는 방법을 많이 선호한다고 한다.
필자도 2번 방법이 코드가 깔끔해지고 추가적인 코드를 작성하지 않아도 되서 이 방법으로만 사용하려한다.
프로젝션
프로젝션이란, select 절에 지정한 대상을 말한다.
// 프로젝션 대상이 하나
List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
// 프로젝션 대상이 둘 이상
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
프로젝션 대상이 하나라면 타입을 명확하게 지정할 수 있지만 (위 예제에서는 username의 반환 타입이 String으로 제네릭을 String 반환 타입으로 지정),
프로젝션 대상이 둘 이상이라면 Tuple 타입이나, DTO 클래스를 반환할 수 있다.
QueryDSL에서 DTO 반환 방법
QueryDSL에서 DTO를 반환할 때 사용하는 방법은 여러가지 방법이 있지만
이 중에서 생성자를 사용하여 반환하는 방법과, @QueryProjection 어노테이션을 사용하는 방법에 대해 알아보고자 한다.
설명하기 앞서 설명에서 사용할 MemberDto는 다음과 같다.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
생성자를 사용하여 반환하는 방법
QueryDSL이 지원하는 Projections 클래스의 constructor 메서드를 사용하여 DTO를 반환할 수 있다.
이 방법을 사용하기 위해서는 MemberDto의 생성자에 정의된 타입이 맞아야 하고, 순서도 맞아야 한다.
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
}
@QueryProjection 어노테이션을 사용하는 방법
이 방법은 DTO의 생성자에 @QueryProjection 어노테이션을 붙여 DTO도 Q 클래스를 생성하여 생성된 QDTO 클래스를 사용하는 방법이다.
사용방법은 다음과 같다.
DTO의 생성자 위에 @QueryProjection 어노테이션을 추가해주고, gradle -> Tasks -> other -> compileJava를 실행해주자.
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
그렇다면 아래 처럼 QMemberDTO가 생성되는것을 볼 수 있다.
이제 QMemberDto를 new 키워드로 다음과 같이 반환하면 된다.
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch()
이 방법은 위 생성자를 사용해 반환하는 방법과 동일하게
@QueryProjection 어노테이션이 붙은 MemberDTO의 생성자의 파라미터 수와 타입을 일치시켜야 한다.
생성된 QMemberDto 코드를 보면 @QueryProjection 어노테이션이 붙은 생성자의 파라미터(username, age)의 타입과 이름이 그대로 들어가 있기 때문이다.
이 방법은 추가로 파라미터를 넣는다던가 오타를 내면, 컴파일러로 타입을 체크할 수 있기 때문에 안전하다는 장점이 있지만,
DTO가 순수하지 못하고 QueryDSL에 의존한다는 단점이 있다.
더 알아보기
'◼ Spring' 카테고리의 다른 글
QueryDSL 동적 쿼리를 작성하는 방법에 대해 알아보자. (0) | 2023.07.11 |
---|---|
QueryDSL 기본 문법 총정리 (0) | 2023.07.10 |
QueryDSL Repository 생성하기 (사용자 정의 리포지토리) (0) | 2023.07.10 |
[Spring] @ModelAttribute와 @RequestBody의 차이점에 대해 쉽게 이해해보자. (API 동작 방식) (0) | 2023.07.05 |
[QueryDSL] 스프링부트 3.x 버전에서 QueryDSL 설정하기 (0) | 2023.06.27 |