public interface UsernameOnly {
// 조회할 엔티티 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회한다.
String getUsername();
}
public interface MemberRepository extends JpaRepository<Member, Long> {
// 메서드 이름은 자유롭게 하면 되고 반환 타입으로 인지한다.
List<UsernameOnly> findProjectionsByUsername(String username);
}
class MemberRepositoryTest {
@Test
public void projections() throws Exception {
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();
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1");
Assertions.assertThat(result.size()).isEqualTo(1);
}
}
select m.username
from member m
where m.username = 'm1';
원하는 필드로 쿼리가 잘 나간다.
public interface UsernameOnly {
// 조회할 엔티티 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회한다.
String getUsername();
}
getter 즉, 프로퍼티 형식의 인터페이스를 제공하면 스프링 데이터 JPA가 구현체를 알아서 제공한다.
select member0_.member_id as member_i1_1_,
member0_.created_by as created_2_1_,
member0_.created_date as created_3_1_,
member0_.last_modified_by as last_mod4_1_,
member0_.last_modified_date as last_mod5_1_,
member0_.age as age6_1_,
member0_.team_id as team_id8_1_,
member0_.username as username7_1_
from member member0_
where member0_.username = 'm1';
SpEL 문법을 지원한다.
DB에서 엔티티 필드를 다 조회한 다음에 계산하기 때문에 select 절 최적화가 안된다.
클래스 기반 Projection
public class UsernameOnlyDto {
private final String username;
// 생성자의 파라미터 이름으로 매칭한다.
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
class MemberRepositoryTest {
@Test
public void projections() throws Exception {
...
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1", UsernameOnly.class);
}
}
select member0_.username as col_0_0_, team1_.team_id as col_1_0_, team1_.team_id as team_id1_2_, team1_.name as name2_2_
from member member0_
left outer join team team1_ on member0_.team_id = team1_.team_id
where member0_.username = 'm1';