[Spring Data JPA] 나머지 기능들

kiteB·2021년 11월 1일
0

Spring Data JPA

목록 보기
8/8
post-thumbnail

SpecificationsQuery By Example은 실무에서 거의 사용하지 않고 Querydsl로 대체되기 때문에 생략!

[ Projections ]

엔티티 대신 DTO를 편리하게 조회할 때 사용한다.

전체 엔티티 대신 회원 이름만 조회하고 싶은 경우를 가정해보자!

1. 회원 이름만 조회하고 싶은 경우

1) 인터페이스 기반 Closed Projections

public interface UsernameOnly {
    String getUsername();
}
  • 조회할 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회한다. (Projection)
public interface MemberRepositry ... {
    List<UsernameOnly> findProjectionsByUsername(@Param("username") String username);
}
  • 메서드 이름은 자유, 반환 타입으로 인지한다.

테스트 실행 결과

  • username만 조회된 것을 확인할 수 있다!

2) 인터페이스 기반 Open Projections

public interface UsernameOnly {
    @Value("#{target.username + ' ' + target.age}")
    String getUsername();
}
  • 스프링의 SpEL 문법도 제공한다.
  • 단! 이렇게 SpEL 문법을 사용하면 DB에서 엔티티 필드를 다 조회해온 다음에 계산한다
    JPQL SELECT 절 최적화가 안된다!

테스트 실행 결과


3) 클래스 기반 Projection

public class UsernameOnlyDto {

    private final String username;

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

    public String getUsername() {
        return username;
    }
}
  • 인터페이스가 아닌 구체적인 DTO 형식도 가능하다.
  • 생성자의 파라미터 이름으로 매칭한다.

실행 결과


4) 동적 Projections

<T> List<T> findProjectionsByUsername(@Param("username") String username, Class<T> type);
  • Generic type을 주면 동적으로 프로젝션 데이터를 변경할 수 있다.

2. 중첩 구조 처리

public interface NestedClosedProjection {

    String getUsername();
    TeamInfo getTeam();

    interface TeamInfo {
        String getName();
    }
}

실행 결과

  • memberusername만, team은 모두 조회되었다.
    → 첫 번째는 최적화가 되지만, 두 번째부터는 최적화가 안된다😣

🚫 프로젝션 사용 시 주의할 점

  • 프로젝션 대상이 root 엔티티면, JPQL SELECT절 최적화가 가능하다!
  • 프로젝션 대상이 root가 아니면,
    • LEFT OUTER JOIN 처리
    • 모든 필드를 SELECT해서 엔티티로 조회한 뒤에 계산한다.

📌 정리!

  • 프로젝션 대상이 root인 경우 projection이 유용하지만!
    root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다.
    → 즉, 실무의 복잡한 쿼리를 해결하기에는 한계가 있다!
  • 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자!

[ 네이티브 쿼리 ]

가급적 네이티브 쿼리를 사용하지 않는 것이 좋다.
최신 버전에서 스프링 데이터 Projections가 지원되므로 이 기능을 사용하자!

1. 스프링 데이터 JPA 기반 네이티브 쿼리

  • 페이징 지원
  • 반환 타입
    • Object[]
    • Tuple
    • DTO (스프링 데이터 인터페이스 Projections 지원)
  • 제약
    • Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있다. (믿지 말고 직접 처리)
    • JPQL처럼 어플리케이션 로딩 시점에 문법을 확인할 수 없다!
    • 동적 쿼리 불가

2. JPA 네이티브 SQL 지원

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query(value = "select * from member where username = ?", nativeQuery = true)
    Member findByNativeQuery(String username);
}
  • 네이티브 SQL은 위치 기반 파라미터를 0부터 시작한다. (JPQL은 위치 기반 파라미터를 1부터 시작)
  • 네이티브 SQL을 엔티티가 아닌 DTO로 변환하려면
    • DTO 대신 JPA TUPLE 조회
    • DTO 대신 MAP 조회
    • @SqlResultSetMapping → 복잡
    • Hibernate ResultTransformer를 사용해야 한다. → 복잡
    • 네이티브 SQL을 DTO로 조회할 때는 JdbcTemplate or myBatis 권장

3. Projections 활용

예) 스프링 데이터 JPA 네이티브 쿼리 + 인터페이스 기반 Projections 활용

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

동적 네이티브 쿼리

  • 하이버네이트를 직접 활용
  • 스프링 JdbcTemplate, myBatis, jooq같은 외부 라이브러리 사용
profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글