public interface UsernameOnly {
String getUsername();
}
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
List<UsernameOnly> findProjectionsByUsername(@Param("username") String name);
// or
List<UsernameOnlyDto> findProjectionsByUsername(@Param("username") String name);
JpaRepository 클래스에서 Projections를 구현한 인터페이스(클래스) 명으로 반환 타입을 지정해주면 된다.(반환 타입으로 인지), 메서드 이름은 자유롭게 지정해도 된다.
@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 m.username from member m
where m.username=‘m1’;
다음과 같이 스프링의 SpEL 문법도 지원한다.
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
이렇게 SpEL 문법을 사용하면, DB 에서 엔티티 필드를 다 조회해온 다음에 계산한다 따라서 JPQL SELECT 절 최적화가 불가능해진다.
다음과 같이 Generic type을 주면, 동적으로 Projections 데이터 변경이 가능해진다.
<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=?
Team 조회 시 모든 쿼리가 나가기 때문에 Select 절 최적화가 불가능해진다. (사용을 지양하자)