[Spring Data JPA] 쿼리 메소드 기능2
목차
- 파라미터 바인딩
- 반환 타입
- 페이징과 정렬
파라미터 바인딩
- 위치 기반
- 이름 기반
select m from Member m where m.username = ?0 // 위치 기반
select m from Member m where m.username = :name // 이름 기반
파라미터 바인딩
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = :name")
Member findMembers(@Param("name") String username);
}
[참고]
코드 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 사용!(위치 기반은 순서가 바뀔수도 있다..)
컬렉션 파라미터 바인딩
Collection
타입으로 in절 지원
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
반환 타입
스프링 데이터 JPA는 유연한 반환 타입 지원
List<Member> findByUsername(String name); // 컬렉션
Member findByUsername(String name); // 단건
Optional<Member> findByUsername(String name); // 단건 Optional
[참고] 스프링 데이터 JPA 공식 문서
조회 결과가 많거나 없으면?
- 컬렉션
- 결과 없음 : 빈 컬렉션 반환
- 단건 조회
- 결과 없음 :
null
반환 - 결과가 2건 이상 :
javax.persistence.NonUniqueResultException
예외 발생
- 결과 없음 :
[참고]
단건으로 지정한 메서드를 호출하면 스프링 데이터 JPA는 내부에서 JPQL의Query.getSingleResult()
메서드를 호출한다. 이 메서드를 호출했을 때 조회 결과가 없으면javax.persistence.NoResultException
예외가 발생하는데 개발자 입장에서는 다루기가 불편하기 때문에, 스프링 데이터 JPA는 단건 조회할 때 이 예외가 발생하면 예외를 무시하고null
을 반환해준다.
페이징과 정렬
- 검색 조건 : 나이가 10살
- 정렬 조건 : 이름으로 내림차순
- 페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 3건
순수 JPA
MemberJpaRepository
public List<Member> findByPage(int age, int offset, int limit) {
return em.createQuery(
"select m from Member m where m.age = :age order by m.usrname desc")
.setParameter("age", age)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public long totalCount(int age) {
return em.createQuery(
"select count(m) from Member m where m.age = :age", Long.class)
.setParameter("age", age)
.getSingleResult();
}
- 테스트 코드에서 age = 10, offset = 0, limit = 3을 넘겨준다.
스프링 데이터 JPA
페이징과 정렬 파라미터
org.springframework.data.domain.Sort
: 정렬 기능org.springframework.data.domain.Pageable
: 페이징 기능 (내부에Sort
포함)
특별한 반환 타입
org.springframework.data.domain.Page
: 추가 count 쿼리 결과를 포함하는 페이징org.springframework.data.domain.Slice
: 추가 count 쿼리 없이 다음 페이지만 확인 가능- 내부적으로 limit + 1 조회
List
(자바 컬렉션) : 추가 count 쿼리 없이 결과만 반환
페이징과 정렬 사용 예
Page<Member> findByUsername(String name, Pageable pageable); // count 쿼리 사용
Slice<Member> findByUsername(String name, Pageable pageable); // count 쿼리 사용 X
List<Member> findByUsername(String name, Pageable pageable); // count 쿼리 사용 X
List<Member> findByUsername(String name, Sort sort);
Page 사용 예제 정의 코드
public interface MemberRepository extends JpaRepository<Member, Long> {
Page<Member> findByAge(int age, Pageable pageable);
}
Page 사용 예제 실행 코드
@Test
void page() {
// given
memberRepository.save(new Member("member1", 10));
memberRepository.save(new Member("member2", 10));
memberRepository.save(new Member("member3", 10));
memberRepository.save(new Member("member4", 10));
memberRepository.save(new Member("member5", 10));
//when
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Page<Member> page = memberRepository.findByAge(10, pageRequest);
//then
List<Member> content = page.getContent(); //조회된 데이터
assertThat(content.size()).isEqualTo(3); //조회된 데이터 수
assertThat(page.getTotalElements()).isEqualTo(5); //전체 데이터 수
assertThat(page.getNumber()).isEqualTo(0); //페이지 번호
assertThat(page.getTotalPages()).isEqualTo(2); //전체 페이지 번호
assertThat(page.isFirst()).isTrue(); //첫번째 항목인가?
assertThat(page.hasNext()).isTrue(); //다음 페이지가 있는가?
}
- 두 번째 파라미터로 받은
Pageable
은 인터페이스다. 따라서, 실제 사용할 때는 해당 인터페이스를 구현한org.springframework.data.domain.PageRequest
객체를 사용한다. PageRequest
생성자의 첫 번째 파라미터에는 현재 페이지를, 두 번째 파라미터에는 조회할 데이터 수를 입력한다. 여기에 추가로 정렬 정보도 파라미터로 사용할 수 있다.
[주의] : Page는 1부터가 아닌 0부터 시작이다.
Slice는 count 쿼리가 나가지 않는다.(스크롤을 끝까지 내리면 더보기가 있는 것과 같다.) 더 보기와 같은 기능 때문에 limit에 1을 더해서 값이 다음에 더 있는지 체크한다.
count 쿼리 분리
@Query(value = "select m from Member m",
countQuery = "select count(m.username) from Member m")
Page<Member> findMemberAllCountBy(Pageable pageable);
페이지를 유지하면서 엔티티를 DTO로 변환하기
Page<Member> page = memberRepository.findByAge(10, pageRequest);
Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
실습
- Page
- Slice(count X) 추가로 limit + 1을 조회한다. 그래서 다음 페이지 여부 확인(Ex. 모바일 리스트)
- List(count X)
- 카운트 쿼리 분리(복잡한 sql에서 사용, 데이터는 left join, 카운트는 left join 안해도 된다.)
- 실무에서 매우 중요!!
<출처 : 인프런 - 실전! 스프링 데이터 JPA(김영한)>
댓글남기기