취미로 음악을 하는 개발자

[Spring Framework] 쿼리 메소드 (3) 본문

공대인/Spring[Boot]

[Spring Framework] 쿼리 메소드 (3)

영월특별시 2019. 10. 6. 17:08
728x90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package org.zerock.persistence;
 
import java.util.Collection;
import java.util.List;
 
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import org.zerock.domain.Board;
 
public interface BoardRepository extends CrudRepository<Board, Long>{
    public List<Board> findBoardByTitle(String title);
    public Collection<Board> findByWriter(String writer);
    
    // 작성자에 대한 Like % 키워드 %
    public Collection<Board> findByWriterContaining(String writer);
    // Or 조건의 처리
    public Collection<Board> findByTitleContainingOrContentContaining(
            String title, String content);
    // Title Like % ? % and Bno > ?
    public Collection<Board> findByTitleContainingAndBnoGreaterThan(
            String keyword, Long num);
    // Bno > ? Order by bno Desc
    public Collection<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno);
    
    // bno > ? Order By bno Desc limit ?, ?
    public List<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno, Pageable paging);
    //public List<Board> findByBnoGreaterThan(Long bno, Pageable paging);
    public Page<Board> findByBnoGreaterThan(Long bno, Pageable paging);
}
cs



쿼리 메소드들에는 모든 쿼리 메소드의 마지막 파라미터로 페이지 처리를 할 수 있는 Pageable 인터페이스와 정렬을 처리하는 Sort 인터페이스를 사용할 수 있다.


Pageable 인터페이스는 말 그대로 페이징 처리에 필요한 정보를 제공한다. 보통 가장 많이 사용하는 것은 org.springframework.data.domain.Pageable 인터페이스를 구현한 클래스 중에 PageRequest 클래스를 이용하는 것이다.


예를 들어, findByBnoGreaterThanOrderByBnoDesc()에 Pageable을 적용하면 아래와 같다.


public List<Board> findByBnoGreaterThanOrderByBnoDesc(Long bno, Pageable paging);


코드는 동일하지만, 파라미터에 Pageable이 적용되어 있고, 리턴 타입으로 Collection<> 대신 List<>를 적용한 것이 달라졌습니다. Pageable 인터페이스가 적용되는 경우, 리턴 타입은 org.springframework.data.domian.Slice(Page) 타입, List 타입을 이용해야 한다.


1
2
3
4
5
6
    @Test
    public void testBnoOrderByPaging() {
        Pageable paging = PageRequest.of(010);
        Collection<Board> results = repo.findByBnoGreaterThanOrderByBnoDesc(0L, paging);
        results.forEach(board -> System.out.println(board));
    }
cs


Pageable 인터페이스에는 여러 메소드가 존재하기 때문에 이를 구현하는 대신에 PageRequest 클래스를 이용한 것이 편리하다.

참고로 위의 코드에서는 스프링 부트 2.0부터 달라진 코드를 사용하고 있다. (원래는 Pageable paging = new PageRequest(...))

[3번 줄] 첫 번째 페이지(인덱스)가 0부터 시작해서 10개의 데이터를 가져오는 것이다.



실행되는 SQL을 보면 MySQL이라서 자동으로 limit가 적용되는 것을 볼 수 있다. 정렬 처리에는 Pageable 인터페이스와 같이 Sort 클래스를 이용한다. Sort는 쿼리 메소드에서 OrderBy로 처리해도 되지만, Sort를 이용하면 원하는 방향으로 파라미터로 결정할 수 있다.


* 스프링 부트 2.0 기준 PageRequest 생성자

 생성자

설명 

 PageRequest.of(int page, int size) 

 페이지 번호(0부터 시작함), 페이지당 데이터의 수 

 PageRequest(int page, int size, Sort.Direction direction,

 String.. props)

 페이지 번호, 페이지당 데이터의 수, 정렬 방향, 속성들 

 PageRequest(int page, int size, Sort sort) 

 페이지 번호, 페이지당 데이터의 수, 정렬 방향 


PageRequest의 생성자 중에서 Sort 인터페이스 타입을 파라미터로 전달하면 페이징 처리와 정렬에 대한 처리를 동시에 지정할 수 있다.


public List<Board> findByBnoGreaterThan(Long bno, Pageable paging);


쿼리 메소드에서 OrderBy 없이 Pageable만으로 파라미터로 처리하고 있다. 이것은 PageRequest()를 이용해서 다음과 같이 처리할 수 있다.


1
2
3
4
5
    @Test
    public void testBnoPagingSort() {
        Pageable paging = PageRequest.of(010, Sort.Direction.ASC, "bno");
        Collection<Board> results = repo.findByBnoGreaterThan(0L, paging);
        results.forEach(board -> System.out.println(board));
cs


[3번 줄] bno의 속성 값을 순서대로 정렬




Page<T> 타입


Spring Data JPA에서 결과 데이터가 여러 개인 경우 List<T> 타입을 이용하기도 하지만, Page<T> 타입을 이용하면 Spring MVC와 연동할 때 상당한 편리함을 제공한다. 방금 전 예제의 리턴 타입을 Page<T>로 작성해본다.


    //public List<Board> findByBnoGreaterThan(Long bno, Pageable paging);
    public Page<Board> findByBnoGreaterThan(Long bno, Pageable paging);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @Test
    public void testBnoPagingSort() {
//        Pageable paging = PageRequest.of(0, 10, Sort.Direction.ASC, "bno");
//        Collection<Board> results = repo.findByBnoGreaterThan(0L, paging);
//        results.forEach(board -> System.out.println(board));
        
        Pageable paging = PageRequest.of(010, Sort.Direction.ASC, "bno");
        Page<Board> result = repo.findByBnoGreaterThan(0L, paging);
        
        System.out.println("PAGE SIZE: " + result.getSize());
        System.out.println("TOTAL PAGES: " + result.getTotalPages());
        System.out.println("TOTAL COUNT: " + result.getTotalElements());
        System.out.println("NEXT: " + result.nextPageable());
        
        List<Board> list = result.getContent();
        list.forEach(board -> System.out.println(board));
    }
cs




출력문을 자세히 보면 실제로 SQL문이 두 번 실행된다는 것을 볼 수 있다.

처음은 데이터를 추출하기 위해서 실행되지만, 두 번째는 데이터의 개수를 파악하기 위해서 'select count(...)'가 실행되는 것을 볼 수 있다.


리턴 타입을 Page<T>로 하게 되면 웹 페이징에서 필요한 데이터를 한 번에 처리할 수 있기 때문에 데이터를 위한 SQL과 개수를 파악하기 위한 SQL을 매번 작성하는 불편함이 없어진다.



Comments