ORM
[QueryDSL] QueryDSL Projection
yyyyMMdd
2024. 2. 1. 17:22
728x90
#. Projection이란?
- Projection은 테이블의 특정 컬럼만 조회할 수 있게 해주는 역할을 합니다.
- DB에서 필요한 데이터만 가져오기에 검색하는 데이터의 양을 줄이고 성능을 최적화하며 네트워크 오버헤드를 최소화하는데 도움이 됩니다.
#. Projection을 사용하는 이유
- 데이터 볼륨 최소화
- 전체 행이나 테이블을 가져오는 것이 아니라 필요한 정보만 검색할 수 있습니다.
- DB와 애플리케이션 간에 전송되는 데이터의 양을 줄이므로 대규모 데이터 세트를 처리할 때 유용합니다. - 성능 향상
- 필요한 열만 선택하면 쿼리의 전반적인 성능과 애플리케이션의 응답성을 향상시킬 수 있습니다.
- 불필요한 데이터 검색으로 인한 트래픽이 증가 및 쿼리 실행 시간이 지연을 방지할 수 있습니다. - 중복 방지
- 현재 쿼리와 관련이 없는 열을 제외하여 결과의 중복을 방지하는 데 도움이 됩니다.
- 이를 통해 애플리케이션의 특정 요구 사항에 맞게 출력을 맞춤화할 수 있습니다.
#. 왜 Projection을 사용해야 할까?
- q객체를 이용해 원하는 컬럼만 가져올 수 있지만 이 경우 리턴을 Tuple객체로 해야 합니다.
- Tuple 객체는 querydsl.core에서 사용하기 때문에 querydsl을 사용하지 않는 곳에서 데이터를 사용하려 하면 제대로 사용하기 어렵고 불편합니다.
- 따라서 projections를 통해 기존 DTO로 리턴 받을 수 있게 할 수 있습니다.
- 위처럼 여러 개의 컬럼을 조회하려 할 때 리턴형을 Tuple로 바꾸라고 오류가 발생합니다.
- 동일한 경우에 Projection을 사용하면 DTO객체 그 자체로 받을 수 있게 됩니다.
1. setter를 이용한 projection
- 위 사진과 같이 Projections.bean을 통해 DTO내 setter에 값을 채워줍니다.
- 장점 : setter를 이용하기에 생성자가 필요하지 않고 순서를 지키지 않아도 됩니다.
- 단점 : 불변성 보장이 어렵고 setter에 들어갈 값이 많아지면 가독성이 떨어집니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
List<MemberEntity> setterResult = jpaQueryFactory.select(Projections.bean(MemberEntity.class,
qMember.name,
qMember.age))
.from(qMember)
.where(qMember.age.eq(34))
.fetch();
return ResponseEntity.ok(setterResult);
}
2. fields를 이용한 projection
- 장점 : 불변성 유지가 가능합니다.
- 단점 : 기본생성자와 getter가 필요하며, 필드 순서를 동일하게 유지해야 합니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
List<MemberEntity> fieldResult = jpaQueryFactory.select(Projections.fields(MemberEntity.class,
qMember.name,
qMember.age))
.from(qMember)
.where(qMember.age.eq(34))
.fetch();
return ResponseEntity.ok(fieldResult);
}
3. constructor를 이용한 projection
- 생성자를 이용한 projection의 경우 select 내에 입력한 값들에 대한 생성자가 있어야 정상적으로 작동합니다.
- 아래 예제의 경우 name과 age를 사용하기 때문에 MemberEntity에 name, age 2개에 대한 생성자가 있다면 정상적으로 처리됩니다.
- 장점 : 불변성을 보장하고 생성자의 인자를 통해 필드를 쉽게 지정할 수 있습니다.
- 단점 : 순서를 정확히 맞춰야 합니다.
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
List<MemberEntity> consturctorResult = jpaQueryFactory.select(Projections.constructor(MemberEntity.class,
qMember.name,
qMember.age))
.from(qMember)
.where(qMember.age.eq(34))
.fetch();
return ResponseEntity.ok(consturctorResult);
}
4. @QueryProjections를 이용한 projection
- 어노테이션을 사용하여 projection 하는 방법입니다.
- 어노테이션을 추가한 생성자는 q파일에 존재해야만 사용이 가능합니다.
- 따라서 어노테이션을 추가했다면 컴파일을 다시 해주시길 바랍니다.
- 장점 : 코드가 간결하고 불변성이 보장되며 순서 또한 상관없고 런타임에서 오류를 잡을 수 있습니다.
- 단점 : 생성자에 어노테이션을 붙이기에 지나친 QueryDSL 의존일 수 있습니다.
// MemeberEntity.java
@QueryProjection
public MemberEntity(String name, int age) {
this.name = name;
this.age = age;
}
// Controller
@GetMapping("proj")
public ResponseEntity<List<MemberEntity> > proj() {
List<MemberEntity> consturctorResult = jpaQueryFactory.select(new QMemberEntity(
qMember.name,qMember.age))
.from(qMember)
.where(qMember.age.eq(34))
.fetch();
return ResponseEntity.ok(consturctorResult);
}
#. 위와 같이 projection을 처리하는 4가지 방법에 대해 알아보았습니다.
#. 어떤 것을 사용해도 큰 문제는 없습니다만 각각의 장단점이 있는 만큼 상황에 따라 선택하여 사용하시길 바랍니다.
===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================
728x90