본문 바로가기
Spring/Spring Data Jpa

7. Projections

by wch_t 2024. 4. 13.

1. Projections

장점 : jpql select 최적화 + dto 조회

 

그러나 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.

단순한 쿼리일 때만 사용하고, 중첩 구조와 같이 복잡해지면 QueryDSL을 사용하는 것이 좋다.

 

1) 인터페이스 기반

interface의 구현체를 spring data jpa가 제공한다.

public interface UsernameOnly {
    // open projection : entity 전체를 가지고 와서, 원하는 데이터를 넘긴다. (jpql select 최적화 X)
    // close projection : 원하는 데이터 값만 fit 하게 가지고 온다.
    @Value("#{target.username + ' ' + target.age}")
    String getUsername();
}

 

// "By -" 이 꼭 들어가야 한다. → 해당 변수 이름으로 쿼리를 생성한다.
// username으로 엔티티를 찾고, UsernameOnly 컬럼 필드에 맞게 구현체를 생성한다.
List<UsernameOnly> findUsernameOnlyByUsername(@Param("username") String username);

 

 

2) 클래스 기반

생성자의 파라미터 이름으로 매칭이 된다.

public class UsernameOnlyDto {
    String username;

    public UsernameOnlyDto(String username) {
        this.username = username;
    } 

    public String getUsername() {
        return username;
    }
}
List<UsernameOnlyDto> findUsernameOnlyDtoByUsername(@Param("username") String username);

 

 

+. Generic 반환

위의 예시들처럼 타입을 정해서 반환하지 않고, 유연성 있게 Generic으로 각 타입에 맞게 반환을 지원한다.

<T> List<T> findGenericByUsername(@Param("username") String username, Class<T> type);

 

 

 

3) 중첩 구조 처리

중첩 Projection 부터는 원하는 필드만 가지고 오는 것이 아니라,

Left Outer Join으로 Team entity 자체를 select 한다. (jpql select 최적화 X)

public interface NestedClosedProjections {
    String getUsername();
    TeamInfo getTeam();

    // 중첩 구조
    interface TeamInfo {
        String getName();
    }
}

 

 


 

2. Native Query

네이티브 쿼리를 실무에서 작성할 일은 99% 없다.

그나마 사용한다면 위에서 학습한 Projections 개념을 활용하는 것이 유용하다.

→ QueryDSL을 사용하는 것이 좋으며, 네이티브 쿼리를 DTO로 조회할 때 Jdbc Template을 사용하는 방법도 있다.

 

 

Native SQL을 엔티티가 아닌 DTO로 조회할 때 Projection 개념을 활용할 수 있다.

(= Spring Data JPA. Native SQL + 인터페이스 기반 Projection)

 

 

 

[Spring Data JPA. Native SQL]

@Query(value = "select m.member_id as id, m.username, t.name as teamName " +
        "from member m left join team t",
        countQuery = "select count(*) from member",
        nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);

 

 

[Projection 인터페이스]

public interface MemberProjection {
    Long getId();
    String getUsername();
    String getTeamName();
}

 

 

[테스트]

Page<MemberProjection> result = memberRepository.findByNativeProjection(PageRequest.of(0, 10));
List<MemberProjection> content = result.getContent();
for (MemberProjection memberProjection : content) {
    System.out.println("memberProjection.getId() = " + memberProjection.getId());
    System.out.println("memberProjection.getUsername() = " + memberProjection.getUsername());
    System.out.println("memberProjection.getTeamName() = " + memberProjection.getTeamName());
}

'Spring > Spring Data Jpa' 카테고리의 다른 글

6. 스프링 데이터 JPA 분석  (0) 2024.04.12
5. 확장 기능  (0) 2024.04.11
4. 쿼리 메소드 기능  (0) 2024.03.25
3. 공통 인터페이스 기능  (1) 2024.03.24
2. 예제 도메인 모델  (1) 2024.03.23