먼저 사실 Jpa를 사용하면 네이티브 쿼리는 왠만하면 사용하지 않는 게 좋음.
정말 정말 방법이 없을 때 최종적으로 사용하는 게 네이티브 쿼리. (리포지토리가 DB끼리의 표준화가 안될 터이니..)
일단 그래도 정말 가끔 어쩔 수 없어서 짜야 할 수도 있으니, 알 긴 해야할 듯.
방법은 정말 간단함.
그냥 Spring Data Jpa에서
@Query(value = "select * from member where username = ?", nativeQuery = true)
Member findByNativeQuery(String username);
jpql 직접 할 때 처럼 저렇게 @Query하고 쿼리 적는 건 똑같은데, 아마 jpql과 문법이 달라 문자열이지만 오류를 표시해 줌. 그 때 nativeQuery를 true로 해 주면 아, 저거 native 쿼리구나, 하고 오류를 더이상 표시하지 않음.
근데 네이티브 SQL 쿼리 오류는 지원 안해줌.
@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.getUsername());
}
이렇게 실행 해 보면
select
*
from
member
where
username = ?
저 쿼리 그대로 나감.
select * from member where username = 'm1';
?에 인자 바인딩 잘 되서 나갔음.
result = m1
결과도 잘 나옴.
근데 아무래도 좀 제약적인 부분이 있음. 물론 좋은 점도 있음.
좋은 점은
페이징이 됨
반환타입 같은 경우는
오브젝트 배열,
튜플
우리가 만든 Dto
제약은 Sort 파라미터를 통한 정렬이 정상동작하지 않을 수 있음. 그냥 쿼리로 넣으라는 뜻 인듯.
JPQL처럼 애플리케이션 로딩 시점에 문법 검증이 안됨.
동적 쿼리가 불가함.
네이티브 쿼리 쓰고 싶으면 간단한 경우에는 저렇게 쓰고, 보통 jdbc템플릿 쓰는 게 좋나봄.
일단 근데 좋은점이 저 Dto, 저번 시간에 배운 Projection을 통해서 정말 간단하게 할 수 있음.
먼저 프로젝션용? 그러니까 Dto용 인터페이스를 만들거임.
public interface MemberProjection {
Long getId();
String getUsername();
String getTeamName();
}
정말 간단하게.
여기서 이제
@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);
이렇게. 보면 as 해서 가져오는 이름을 별칭으로 프로퍼티에 맞추고 있음. 프로젝션이 클래스라면 생성자의 인자 이름에 맞추면 될 듯.
이건 이렇게 하면 join도 가능함.
그리고 이거 페이징으로 할거라, 보면 count쿼리는 아무래도 네이티브 쿼리이니 직접 짜셈.
nativeQuery true 해주고,
페이지로 받을거고, 프로젝션 객체로, PageRequest를 인자로 받을거임.
@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();
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());
}
}
보면 저렇게 페이지로 받으면서, PageRequest.of(0, 10) 하면서 간단하게 Pageable 객체 만들어주면서 인자로 넣어줬음.
static 클래스.of가 뭔가 정보를 주면서 그걸 객체로 만들어 반환해주는 패턴인 듯.
여튼 잘 나옴.
정적 쿼리는 이렇게 가능함. 좀 그냥 간단하게 하고 싶을 때. 페이징도 되고.
그럼 동적 네이티브 쿼리는?
Jdbc 템플릿 같은 외부 라이브러리 사용하셈.
// 대충 동적 쿼리 완성되었다 치고
String sql = "select m.username as username from member m";
List<MemberDto> result = em.createNativeQuery(sql)
.setFirstResult(0)
.setMaxResults(10)
.unwrap(NativeQuery.class)
.addScalar("username")
.setResultTransformer(Transformers.aliasToBean(MemberDto.class))
.getResultList();
}
왜 우리가 커스텀 인터페이스 만들고, 그거 구현한 스프링데이터JPA이름 + Impl 에다가 자동으로 구현되어지는 메소드 말고 직접 구현한 메소드 집어 넣었었잖아. 그렇게 하면 됨.
'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글
28. Projections (0) | 2023.11.26 |
---|---|
27. Query By Example (0) | 2023.11.26 |
26. 명세 (Specifications) (0) | 2023.11.26 |
25. 새로운 엔티티 구별방법, isNew (0) | 2023.11.26 |
24. 스프링 데이터 JPA 구현체 분석 (0) | 2023.11.26 |