오봉이와 함께하는 개발 블로그
스프링 데이터 JPA - Projections 본문
728x90
Projections
엔티티 대신에 DTO를 편리하게 조회할 때 사용한다.
public interface UsernameOnly {
String getUsername();
}
- 조회할 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회(Projection)
public interface MemberRepository ... {
List<UsernameOnly> findProjectionsByUsername(String username);
}
메서드 이름은 자유, 반환 타입으로 인지
@Test
public void projections() throws Exception {
//given
Team teamA = new Team("teamA");
em.persist(teamA);
Member m1 = new Member("m1", 0, teamA);
Member m2 = new Member("m2", 0, teamA);
em.persist(m1);
em.persist(m2);
em.flush();
em.clear();
//when
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1");
//then
Assertions.assertThat(result.size()).isEqualTo(1);
}
select
member0_.username as col_0_0_
from
member member0_
where
member0_.username=?
select member0_.username as col_0_0_ from member member0_ where member0_.username='m1';
SQL에서도 select절에서 username
만 조회(Projection
)하는 것을 확인할 수 있다.
인터페이스 기반 Closed Projections
프로퍼티 형식(getter
)의 인터페이스를 제공하면, 구현체는 스프링 데이터 JPA가 제공
public interface UsernameOnly {
String getUsername();
}
인터페이스 기반 Open Proejctions 다음과 같이 스프링의 SpEL 문법도 지원
다음과 같이 스프링의 SpEL 문법도 지원한다.
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
target.username + ' ' + target.age + ' ' + target.team.name
target의 값을 문자로 더해서 반환한다.
단, 이렇게 SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산하기 때문에 JPQL SELECT절 최적화가 안된다.
클래스 기반 Projection
다음과 같이 인터페이스가 아닌 구체적인 DTO 형식도 가능하다.
생성자의 파라미터 이름(String username
)으로 매칭하기 때문에 파라미터 이름이 달라지면 안된다.
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
동적 Projections
다음과 같이 Generic type을 주면, 동적으로 프로젝션 데이터 번경 가능
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
사용 코드
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1", UsernameOnly.class);
중첩 구조 처리
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
select
m.username as col_0_0_,
t.teamid as col_1_0_,
t.teamid as teamid1_2_,
t.name as name2_2_
from
member m
left outer join
team t
on m.teamid=t.teamid
where
m.username=?
주의
- 프로젝션 대상이 root 엔티티면(여기서는 Member), JPQL SELECT 절 최적화 가능
- 프로젝션 대상이 ROOT가 아니면(여기서는 Team)
- LEFT OUTER JOIN 처리
- 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산한다.
정리
- 프로젝션 대상이 root 엔티티면 유용하다.
- 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다!
- 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
- 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자
인프런 김영한 지식공유자님 강의 : 실전! 스프링 데이터 JPA
728x90
'BE > JPA' 카테고리의 다른 글
Querydsl - 설정, 예제 도메인 모델 (0) | 2022.09.15 |
---|---|
스프링 데이터 JPA - 네이티브 쿼리 (0) | 2022.09.14 |
스프링 데이터 JPA - 새로운 엔티티를 구별하는 방법 (0) | 2022.09.14 |
스프링 데이터 JPA - 스프링 데이터 JPA 구현체 분석 (0) | 2022.09.14 |
스프링 데이터 JPA - Web 확장 페이징과 정렬 (0) | 2022.09.14 |
Comments