공부/JPA

Spring Data JPA - 네이티브 쿼리

데부한 2022. 8. 3. 22:01
반응형

출처 : 인프런 실전! 스프링 데이터 JPA

Spring Data JPA 게시글은 대부분 인프런의 김영한님의 강의인 '실전! 스프링 데이터 JPA' 기반으로 내용을 정리했습니다.

 

네이티브 쿼리

네이티브 쿼리는 JPA에서 제공하는 기능이며 일반적으로 우리가 사용하는 SQL을 사용할 수 있도록 해주는 기능이다. 가급적 네이티브 쿼리는 사용하지 않는 것이 좋다. 정말 최후의 수단으로.. 어쩔 수 없는 상황에만 사용하는 걸 권장한다. 

 

 

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

  • MemberRepository.interface
@Query(value = "select * from member where username = ?", nativeQuery = true)
Member findByNativeQuery(String username);
  • MemberRepositoryTest.class
@Test
public void nativeQuery() {
    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();

    Member result = memberRepository.findByNativeQuery("m1");
    System.out.println("result = " + result);
}
    select
        * 
    from
        member 
    where
        username = ?
result = Member(id=2, username=m1, age=0)

이렇게 사용하면 되는 기능이다. 근데 이 기능은 제약이 되게 많다. 일단 엔티티의 한 컬럼의 데이터만 가져오려면 반환 타입을 변경해야 하는데 반환 타입으로 지정할 수 있는 게 너무 한정적이다. 그리고 뭔가 네이티브 쿼리를 사용해서 데이터를 가져오는 행위 자체가 단순한 조회 목적이라기 보다는 엄청 대용량의 데이터를 join 하거나 할 때 쓰는 등의 제약이 있다.

정리하자면

  • 반환 타입
    • Object[]
    • Tuple
    • DTO(원래는 안됐었지만 최근에 업데이트 됨)
  • 제약
    • Sort 파라미터를 통한 정렬이 정상적으로 동작하지 않을 수 있다.(직접 처리해야 함. 어쩔땐 되고, 어쩔땐 안 된다.)
    • JPQL처럼 애플리케이션 로딩 시점에 문법 확인이 불가능하다.
    • 동적 쿼리가 불가능하다.
  • 네이티브 SQL을 DTO로 조회할 때는 JdbcTemplate이나 myBatis를 사용하는 걸 권장한다.

 

 

Projections 활용

그래도 Projections를 활용하면 더 쉽게 사용할 수 있다. 단 동적 쿼리는 사용하기 쉽지 않다.

  • MemberProjection.interface
public interface MemberProjection {

    Long getId();
    String getUsername();
    String getTeamName();
}
  • MemberRepository.interface
@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);

JPA가 아니라서 count 쿼리를 직접 만들어줘야 한다.

  • MemberRepositoryTest.class
Page<MemberProjection> result = memberRepository
		.findByNativeProjection(PageRequest.of(0, 10));
List<MemberProjection> content = result.getContent();
for (MemberProjection memberProjection : content) {
    System.out.println("memberProjection = " + memberProjection.getUsername());
    System.out.println("memberProjection = " + memberProjection.getTeamName());
}
select
    m.member_id as id,
    m.username,
    t.name as teamName 
from
    Member m 
left join
    team t limit ? offset ?


select
	count(*) 
	from
member
    
    
memberProjection = m1
memberProjection = teamA
memberProjection = m2
memberProjection = teamA

 

반응형