프로젝션?
entity전체를 가져오는 것이 아니라 select 대상 지정하여 원하는 값만 조회하는 것
프로젝션 대상이 하나
- 프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있음
- 프로젝션 대상이 둘 이상이면 튜플이나 DTO로 조회
@Test
public void simpleProjection() {
List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
for (String s : result) {
System.out.println("s=" + s);
}
}
튜플 조회
프로젝션 대상이 둘 이상일 때 사용
com.querydsl.core.Tuple
@Test
public void tupleProjection() {
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
for (Tuple t : result) {
String username = t.get(member.username);
System.out.println("username = " + username);
Integer age = t.get(member.age);
System.out.println("age = " + age);
}
}
순수 JPA에서 DTO 조회
MemberDto
@Data
@NoArgsConstructor
public class MemberDto {
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
순수 JPA에서 DTO 조회 코드
순수 JPA에서 DTO를 조회할 때는 new 명령어를 사용해야함
- DTO의 package이름을 다 적어줘야해서 지저분함
- 생성자 방식만 지원함
List<MemberDto> result = em.createQuery(
"select new study.querydsl.dto.MemberDto(m.username, m.age) " +
"from Member m", MemberDto.class)
.getResultList();
Querydsl 빈 생성(Bean population)
결과를 DTO 반환할 때 사용, 다음 3가지 방법 지원
- 프로퍼티 접근
- 필드 직접 접근
- 생성자 사용
프로퍼티 접근
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto dto : result) {
System.out.println("dto = " + dto);
}
}
필드 직접 접근
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto dto : result) {
System.out.println("dto = " + dto);
}
}
생성자 사용
@Test
public void findDtoByConstructor() {
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (MemberDto dto : result) {
System.out.println("dto = " + dto);
}
}
참고) 별칭이 다른 경우 - as 사용
@Data
public class UserDto {
private String name;
private int age;
}
@Test
public void findUserDto() {
List<UserDto> result = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
member.age))
.from(member)
.fetch();
for (UserDto dto : result) {
System.out.println("dto = " + dto);
}
}
서브쿼리에 별칭 적용
@Test
public void findUserDto2() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
.select(Projections.fields(UserDto.class,
member.username.as("name"),
ExpressionUtils.as(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")
)
).from(member)
.fetch();
for (UserDto dto : result) {
System.out.println("dto = " + dto);
}
}
@QueryProjection
MemberDto 생성자에 어노테이션 적용
./gradlew compileQuerydsl
- QMemberDto 생성 확인
@Data
@NoArgsConstructor
public class MemberDto {
private String username;
private int age;
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Test
public void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
for (MemberDto dto : result) {
System.out.println("dto = " + dto);
}
}
- 이 방법은 컴파일러로 타입을 체크할 수 있으므로 가장 안전한 방법이다.
- 다만 DTO에 QueryDSL 어노테이션을 유지해야 하는 점과 DTO까지 Q 파일을 생성해야 하는 단점이 있다.
distinct
참고: distinct는 JPQL의 distinct와 같다.
@Test
public void findDistinct() {
List<String> result = queryFactory
.select(member.username).distinct()
.from(member)
.fetch();
for (String s : result) {
System.out.println("s=" + s);
}
}
'💻dev > 🌱Java+Spring' 카테고리의 다른 글
Querydsl | 수정, 삭제 벌크 연산하기 (0) | 2023.05.16 |
---|---|
Querydsl | 동적 쿼리 - BooleanBuilder, Where 다중 파라미터 (0) | 2023.05.16 |
Querydsl | 쿼리dsl의 기본 문법(설명 추가 예정) (0) | 2023.05.16 |
Spring Data JPA | 네이티브 쿼리 (0) | 2023.05.12 |
Spring Data JPA | Projections(프로젝션) (0) | 2023.05.12 |