본문 바로가기
Spring Data JPA

Spring Data JPA - 쿼리 메서드(2)

by 왈레 2023. 4. 13.

5. 반환타입

JPA는 유연환 반환 타입을 지원한다.

 

//컬렉션 -> 데이터가 없으면 빈 컬렉션
List<Member> findByUsername(String name); 

//단건 -> 데이터가 없으면 null
Member findByUsername(String name); 

//단건 -> Optional 처리
Optional<Member> findByUsername(String name);


- Member findByUsername(String name); 에서 반환값이 2개라면 예외발생 -> norrectResultSizeDataAccessException


- JPA Exception은 스프링 Exception으로 변환된다.
JPA를 사용하는 클라이언트 입장에서 동일하게 Exception 처리를 할 수 있다.

- 반환 타입은 굉장히 다양함 (공식문서 참고)

 

6. 페이징과 정렬

페이징과 정렬 파라미터 

- 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 조회해서 hasNext()로 다음 페이지가 있는지 알 수 있다. 

- List (자바컬렉션) : 결과만 반환

 

쿼리가 복잡해지면 Paging 사용 지양

실무에서 중요한 내용인데, 쿼리가 조금 복잡해지면 Paging 사용을 지양해야한다.

그 이유는 total를 구하는 성능이 좋지않아서 그런건데, 특히 join의 경우 join해서 나온 결과값의 Total을 구하기 위해서

total을 구하는 query에 또 join을 쓰기때문이다. 그래서 JPA는 count 쿼리를 분리할 수 있는 기능을 제공한다.

 

sorting 조건이 복잡해지면 JPQL 사용

Sorting도 조건이 복잡해지면 기본으로 제공되는 sort 으로 안되는 경우가 있다. 그럴때는 그냥 JPQL을 사용하면 된다.

 

// JPA (여기엔 없지만 totalPage, 최초 및 마지막 페이지등을 구해야함.)
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.username desc")
            .setParameter("age", age)
            .setFirstResult(offset)
            .setMaxResult(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)
            .setSingleResult();
}

// Spring Data JPA
@Query(value = "select m from Member m left join m.team t", 
       countQuery = "select count(m.username) from Member m") // count query 분리
Page<Member> findByAge(int age, Pageable pageable);


// 테스트 코드
@Test
void pagingTest(){
    //given
    memberRepository.save(new Member("member1", 10));
    memberRepository.save(new Member("member2", 10));

    int age = 10;
    PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));

    //when
    // List<Member>, Slice<Member>로 받아도 된다.
    Page<Member> page = memberRepository.findByAge(age, pageRequest);

    //then
    List<Member> content = page.getContent();
    long totalElements = page.getTotalElements();

    for (Member member : content) {
        System.out.println("member = " + member);
    }
    System.out.println("totalElements = " + totalElements);

    // 참고로 Slice<Member> 슬라이스는 전체갯수, 전체페이지를 가져올 수 없음
    assertThat(content.size()).isEqualTo(3); // 갯수
    assertThat(page.getTotalElements()).isEqualTo(5); // Page는 count 쿼리 포함하니, total 갯수도 알 수 있음
    assertThat(page.getNumber()).isEqualTo(0); // 현재 페이지
    assertThat(page.getTotalPages()).isEqualTo(2); // 총 페이지
    assertThat(page.isFirst()).isTrue();
    assertThat(page.isLast()).isFalse();
    assertThat(page.hasNext()).isTrue();
}

 

 

'Spring Data JPA' 카테고리의 다른 글

Spring Data JPA - 쿼리 메서드(3)  (0) 2023.04.13
Spring Data JPA - 쿼리 메서드(1)  (0) 2023.04.12

댓글