[JPA] Projections

컴공생의 코딩 일기·2023년 2월 4일
0

JPA

목록 보기
14/14
post-thumbnail

Projections

  • 엔티티 대신에 DTO를 편리하게 조회할 때 사용
  • 전체 엔티티가 아니라 엔티티의 회원 이름만 조회하고 싶을 때 사용하면 좋음

Projections 사용 방법

public interface UsernameOnly {
 String getUsername();
}
  • 인터페이스 기반 방식 : 조회할 엔티티의 필드를 getter 혁식으로 지정하면 해당 필드만 선택해서 조회
public class UsernameOnlyDto {
 private final String username;
 
 public UsernameOnlyDto(String username) {
 this.username = username;
 }
 
 public String getUsername() {
 return username;
 }
}
  • 클래스 기반 방식 : 클래스 기반으로 구체적인 DTO 형식도 가능
    • 생성자 파라미터 이름으로 매칭
List<UsernameOnly> findProjectionsByUsername(@Param("username") String name);
// or
List<UsernameOnlyDto> findProjectionsByUsername(@Param("username") String name);

JpaRepository 클래스에서 Projections를 구현한 인터페이스(클래스) 명으로 반환 타입을 지정해주면 된다.(반환 타입으로 인지), 메서드 이름은 자유롭게 지정해도 된다.

test 코드 구현

@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’;

인터페이스 기반 Open Projections

다음과 같이 스프링의 SpEL 문법도 지원한다.

public interface UsernameOnly {
 @Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
 String getUsername();
}

이렇게 SpEL 문법을 사용하면, DB 에서 엔티티 필드를 다 조회해온 다음에 계산한다 따라서 JPQL SELECT 절 최적화가 불가능해진다.

동적 Projections

다음과 같이 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();
 }
}

JPQL 쿼리

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 절 최적화가 불가능해진다. (사용을 지양하자)

주의

  • 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
  • 프로젝션 대상이 ROOT가 아니면
    • LEFT OUTER JOIN 처리
    • 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산
    • JOIN 쿼리를 사용하면 Projections 사용을 지양하자
profile
더 좋은 개발자가 되기위한 과정

0개의 댓글