-
#Spring Data Jpa - 14 ProjectionsSpring Data JPA 2021. 3. 26. 09:50
Projections(쿼리 select 절에 들어갈 데이터 <가져올 필드>)
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
엔티티 대신에 DTO를 편리하게 조회할 때 사용 전체 엔티티가 아니라 만약 회원 이름만 딱 조회하고 싶으면?
Spring Data JPA - Reference Documentation
Example 109. Using @Transactional at query methods @Transactional(readOnly = true) public interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") v
docs.spring.io
public interface UsernameOnly { String getUsername(); }
인터페이스만 만들면 Spring Data JPA가 구현체를 직접 만들어준다. username속성만 가지고오면 되는구나 해서 구현체에 username을 담에서 반환해준다.
조회할 엔티티의 필드를 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 m.username from member m where m.username=‘m1’;
SQL에서도 select절에서 username만 조회(Projection)하는 것을 확인
인터페이스 기반 Closed Projections 프로퍼티 형식(getter)의 인터페이스를 제공하면, 구현체는 스프링 데이터 JPA가 제공
public interface UsernameOnly { String getUsername(); }
인터페이스 기반 Open Proejctions
다음과 같이 스프링의 SpEL 문법도 지원
public interface UsernameOnly { @Value("#{target.username + ' ' + target.age + ' ' + target.team.name}") String getUsername(); }
open projection
- spl을 쓰며 멤버를 다가져온다 거기에서 SpEL을 계산함,둘다가져와서 문자로 더해서 넣어줌 spring SpEL문법
close projection- 정확하게 매칭 select 절이 최적회됨
단! 이렇게 SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산한다! 따라서 JPQL SELECT 절 최적화가 안된다.
클래스 기반 Projection
다음과 같이 인터페이스가 아닌 구체적인 DTO 형식도 가능 생성자의 파라미터 이름으로 매칭
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);
쿼리가 똑같고 어쩔때는 username 어쩔떄는 userid를 가지고오고 싶을때 사용
사용코드
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 엔티티면, JPQL SELECT 절 최적화 가능
· 프로젝션 대상이 ROOT가 아니면
· LEFT OUTER JOIN 처리
· 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산
정리
· 프로젝션 대상이 root 엔티티면 유용하다.
· 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다!
· 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
· 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자
'Spring Data JPA' 카테고리의 다른 글
#Spring Data Jpa - 15 Native Query (0) 2021.03.26 #Spring Data Jpa - 13 스프링 데이터 JPA 구현체 분석 (0) 2021.03.25 #Spring Data Jpa - 12 확장기능 - Web 확장 (0) 2021.03.25 #Spring Data JPA - 11 확장기능 - Auditing (0) 2021.03.25 #Spring Data JPA - 9 쿼리메소드 -JPA Hint & Lock (0) 2021.03.24