
Projections
- 엔티티 대신에 DTO를 편리하게 조회할 때 사용 전체 엔티티가 아니라 만약 회원 이름만 딱 조회
- 메서드 이름은 자유, 반환 타입으로 인지
- 조회할 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회(Projection)
인터페이스 기반 Closed Projections
public interface UsernameOnly {
String getUsername();
}
List<UsernameOnly> findProjectionsByUsername(String username);
인터페이스 기반 Open Proejctions
- 스프링의 SpEL 문법도 지원
- SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산한다! 따라서 JPQL SELECT 절 최적화가 안된다
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
클래스 기반 Projection
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
List<UsernameOnlyDto> findProjectionsByUsername(@Param("username") String username);
List<UsernameOnlyDto> result = memberRepository.findProjectionsByUsername("m1");
for (UsernameOnlyDto usernameOnly : result) {
System.out.println(usernameOnly);
}
동적 Projections
<T> List<T> findProjectionsByUsername(@Param("username") String username, Class<T> type);
List<UsernameOnlyDto> result = memberRepository.findProjectionsByUsername("m1", UsernameOnlyDto.class);
for (UsernameOnlyDto usernameOnly : result) {
System.out.println(usernameOnly);
}
중첩 구조 처리
- 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
- 프로젝션 대상이 ROOT가 아니면
- LEFT OUTER JOIN 처리
- 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산
public interface NestedClosedProjections {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
List<NestedClosedProjections> result = memberRepository.findProjectionsByUsername("m1", NestedClosedProjections.class);
for (NestedClosedProjections nestedClosedProjections : result) {
System.out.println(nestedClosedProjections);
}