2 분 소요

나머지 기능

거의 안쓰는 기능 (알고만 있자.)

가급적 네이티브 쿼리는 사용하지 않는게 좋음. 정말 어쩔 수 없을 때 사용 최근에 나온 궁극의 방법 → 스프링 데이터 Projections 사용

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

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

MemberRepository.java

1
2
@Query(value = "select * from member where username = ?", nativeQuery = true)  
Member findByNativeQuery(String username);

다음과 같이 @Query(value = "select * from member where username = ?", nativeQuery = true)

이렇게 작성하면 실제 쿼리가 나가버린다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test  
public void nativeQueryTest() 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  
    Member result = memberRepository.findByNativeQuery("m1");  
    //then  
    System.out.println("result = " + result);  
  
}

다음과 같이 테스트 했고

진짜 쌩 SQL쿼리가 나간다.

그런데 제약이 너무 많다.

1
2
@Query(value = "select username from member where username = ?", nativeQuery = true)  
Member findByNativeQuery(String username);

이렇게 username으로 바꿔버리면 Member로 리턴하기도 그렇고, 좀 애매하다.

네이티브 SQL을 DTO로 조회할 때는 JdbcTemplete or MyBatis 권장

  • JPQL은 위치 기반 파라미터를 1부터 시작하지만 네이티브 SQL은 0부터 시작
  • 네이티브 SQL을 엔티티가 아닌 DTO로 변환하려면
    • DTO 대신 JPA TUPLE 조회
    • DTO 대신 MAP 조회
    • @SqlResultSetMapping → 복잡
    • Hibernate ResultTransformer를 사용해야함 → 복잡
    • 참고 링크
    • 네이티브 SQL을 DTO로 조회할 때는 JdbcTemplete or MyBatis 권장

Projections 활용

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

MemberProjection 인터페이스 생성

1
2
3
4
5
public interface MemberProjection {  
    Long getId();  
    String getUsername();  
    String getTeamName();  
}
1
2
3
4
5
@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> findByNativeProjections(Pageable pageable);

다음과 같이 리포지토리 단에서 사용할 수 있다. 이번 케이스는 페이징을 하기 위한 메서드이며, 네이티브 쿼리를 사용했다. 또 페이징이기 때문에 countQuery도 작성해 주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test  
public void nativeQueryTest() 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  
    Page<MemberProjection> result = memberRepository.findByNativeProjections(PageRequest.of(0, 10));  
    //then  
    List<MemberProjection> content = result.getContent();  
    for (MemberProjection memberProjection : content) {  
        System.out.println(memberProjection.getUsername());  
        System.out.println(memberProjection.getTeamName());  
    }  
  
}

다음과 같이 페이징과 projection을 이용해서 native쿼리를 사용할 수 도 있다.

다음과 같이 우리가 작성한 쿼리 그대로 나가는 걸 볼 수 있다.

정적 쿼리를 Native쿼리로 사용할 때는 Projections 기능을 활용하는 편이 좋아 보인다. → 하지만 Native쿼리를 안 쓰는 방향, QueryDSL..

태그: ,

카테고리:

업데이트:

댓글남기기