프로젝션과 결과 반환
프로젝션
select 할 대상을 지정하는 것
프로젝션 대상이 하나일 때
class Projection {
void example() {
List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
}
}
대상이 하나면 타입을 명확하게 지정할 수 있다.
프로젝션 대상이 둘일 때
Tuple
class Projection {
void example() {
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);
}
}
}
튜플
결과가 여러 개일 때 담을 수 있도록 만든 Querydsl 객체
리포지토리 계층에서만 사용하고 다른 레이어가 종속되지 않게 한다.
대상이 둘 이상이면 튜플이나 DTO로 조회해야 한다.
순수 JPA에서 DTO 조회
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
엔티티로 가져오면 필요하지 않은 데이터도 다 가져와야 하므로 필요한 데이터만 가져올 수 있게 DTO를 만든다.
Querydsl이 일단 MemberDto를 만든 다음 작업하기 때문에 MemberDto에 디폴트 생성자가 꼭 필요하다.
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Test
void findDtoByJPQL() {
List<MemberDto> result = em.createQuery(
"select new study.querydsl.dto.MemberDto(m.username, m.age) " +
"from Member m", MemberDto.class)
.getResultList();
}
}
select에 DTO 타입을 new 명령어로 명시해준다.
select m from Member m
이라고 하면 Member 엔티티를 조회하기 때문에 타입이 맞지 않는다.
DTO의 패키지 일므을 다 적어야 해서 지저분하다.
생성자 방식만 지원한다.
Querydsl 빈 생성
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Test
void findDtoBySetter() {
List<MemberDto> result = queryFactory
// bean이 setter로 주입해준다.
.select(Projections.bean(
MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
}
}
별칭이 다를 때
@Data
public class UserDto {
private String name;
private int age;
}
username인데 DTO에는 name으로 되어있어 일치하지 않을 경우 사용한다.
ExpressionUtils.as(source, alias)
필드나 서브 쿼리에 별칭을 적용한다.
username.as("memberName")
필드에 별칭을 적용한다.
@QueryProjection
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
생성자에 @QueryProjection을 붙인 뒤 compileQuerydsl 한다.
QMemberDto 클래스가 생성된다.
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@Test
void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
// DTO 클래스를 new로 바로 넣으면 된다.
// 생성자로 만들기 때문에 타입과 파라미터를 다 맞춰줘서 안정적으로 만들 수 있다.
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
}
}
컴파일러로 타입을 체크할 수 있어 안전하다.
생성자를 통한 빈 생성 방식은 필드를 다르게 넣어도 컴파일 시점에 잡을 수 없다.
DTO에 Querydsl 애너테이션을 유지하고 Q 파일까지 만들어야 하는 단점이 있다.
Querydsl에 의존적인 설계가 된다.
Last updated
Was this helpful?