티스토리 뷰

Spring Boot

Spring Boot + JPA Paging 처리

lkh's 2018. 12. 27. 01:10

JPA는 쿼리 메소드의 파라미터에 페이징과 정렬을 전달함으로써, 웹 서비스 개발에서 자주 사용되는 페이징 개발에 대한 편의성을 제공하고 있다.

매개변수에 Pageable을 사용함으로써 아래와 같이 Page 또는 List 타입으로 반환하여 사용할 수 있다. 나는 개인적으로 Page로 리턴받아 처리를 했다.

List 컬렉션에도 기본적으로 제공되는 기능도 많지만, Page에는 페이지 처리를 위한 다양한 기능을 제공하고 있다. 아래참고!

실제 페이징 처리 예제를 통해 확인해보자! 예제코드는 Controller → Service → DB → Service → Domain → Controller → View 순서로 동작한다.


Page<Member> findByName (String name, Pageable pageable);

List<Member> findByName (String name, Pageable pageable);


int getNumber();                      //현재 페이지

int getSize();                            //페이지 크기

int getTotalPages();                  //전체 페이지 수

int getNumberOfElements();  //현재 페이지에 나올 데이터 수

long getTotalElements();          //전체 데이터 수

boolean hasPreviousPage();    //이전 페이지 여부

boolean isFirstPage();              //현재 페이지가 첫 페이지 인지 여부

boolean hasNextPage();            //다음 페이지 여부

boolean isLastPage();                //현재 페이지가 마지막 페이지 인지 여부

Pageable nextPageable();          //다음 페이지 객체, 다음 페이지가 없으면 null

Pageable previousPageable();    //다음 페이지 객체, 이전 페이지가 없으면 null

List<T> getContent();                //조회된 데이터

boolean hasContent();              //조회된 데이터 존재 여부

Sort getSort();                            //정렬정보

 

 (1) HomeController.java

           
@Controller
public class HomeController {

    @Autowired
    QnaService qnaService;

    private static final Logger logger = getLogger(HomeController.class);

    @GetMapping(path = {"/", "/{no}"})
    public String home(@PathVariable(required = false) Long no,  Model model, HttpSession httpSession) {
        logger.debug("sesseion id : {} ", httpSession.getId());
        Paging paging = new Paging(no);
        model.addAttribute("questions", qnaService.findByPaging(paging));
        model.addAttribute("paging", qnaService.obtainPaging(paging));
        return "home";
    }
}

 (2) QnaService.java

   
@Service("qnaService")
public class QnaService {
    private static final Logger log = LoggerFactory.getLogger(QnaService.class);

    @Resource(name = "questionRepository")
    private QuestionRepository questionRepository;

    @Resource(name = "answerRepository")
    private AnswerRepository answerRepository;

    @Resource(name = "deleteHistoryService")
    private DeleteHistoryService deleteHistoryService;


    public Page findByPaging(Paging paging) {
        return questionRepository.findByDeleted(false, PageRequest.of(paging.getPageNo() - 1, Paging.COUNT_OF_PAGING_CONTENTS));
    }

    public int obtainCountOfQuestion() {
        return (int)questionRepository.findByDeleted(false).stream().count();
    }

    public Paging obtainPaging(Paging paging) {
        return paging.of(questionRepository.findByDeleted(false).size());
    }
}

 Controller는 가장 먼저 findByPaging() 메소드를 우선적으로 호출한다. 이 메소드는 결과적으로 해당 페이지에 맞는 질문목록들을 모두 반환하는 메소드이다.

 이 메소드에서 주목해야할 부분은 findByDeleted의 PageRequst.of() 이다.

 new PageRequest() 생성자를 통해서도 Pageable 인터페이스를 구현할 수 있지만, 정적 메소드인 of() 메소드를 사용하자!

 PageRequest.of(현재 페이지, 한 페이지에서 보여주는 페이지의 수, 정렬조건) 로 구성되어 있다. 예를들면 PageRequest.of(2, 5)라고 하면 6번 ~ 10번까지의

 질문목록을 반환하는 것이다.


 다음은 obtainPaging() 메소드를 보자! 이전 메소드를 통해 해당 페이지의 글목록들을 가져올 수 있게 되었다. 이제는 게시판 하단에서 페이징의 네비게이터인

 '<< 1 2 3 4 5 >>' 를 제어해보자. 그리고 이 제어는 Paging 도메인 객체에서 담당하고 있다. 물론 서비스에서도 제어가 가능하도록 개발이 가능하지만,

 서비스에서는 DB와 관련된 내용을 담당하는 것이 맞기도 하고.. 하나의 클래스에는 하나의 역할만을 담당하는 단일책임원칙을 준수해야하기 때문에

 도메인 객체를 별도 생성했다.


 (3) Paging.java

  Paging 클래스는 기존 개발과 유사하기 때문에 설명은 생략하겠다. 그리고 아래 코드를 보면 바로 이해가 될 것이다!

   
public class Paging {

    public static final int COUNT_OF_PAGING_CONTENTS = 3;
    public static final int COUNT_OF_PAGING = 3;

    private int pageNo;
    private int prev;
    private int next;
    private List pages;

    public Paging(Long pageNo) {
        this.pageNo = 1;
        if(pageNo != null) {
            this.pageNo = pageNo.intValue();
        }

        this.pages = new ArrayList<>();
        this.prev = 0;
        this.next = 0;
    }

    public Paging of(long countOfContents) {
        int start = obtainStartPage();
        int end = obtainEndPage(countOfContents);
        int total = obtainTotalPage(countOfContents);

        for (int i = start; i <= end; i++) {
            this.pages.add(i);
        }

        if(start > 1) {
            this.prev = start - 1;
        }

        if(end != total) {
            this.next = end + 1;
        }

        return this;
    }

    public int obtainStartPage() {
        return (this.pageNo / (COUNT_OF_PAGING + 1)) * COUNT_OF_PAGING + 1;
    }

    public int obtainEndPage(long countOfContents) {
        int total = obtainTotalPage(countOfContents);
        if(total < obtainStartPage() + COUNT_OF_PAGING - 1) {
            return total;
        }
        return obtainStartPage() + COUNT_OF_PAGING - 1;
    }

    public int obtainTotalPage(long countOfContents) {
        return (int)Math.ceil((double)countOfContents / COUNT_OF_PAGING_CONTENTS);
    }

    public int getPageNo() {
        return pageNo;
    }

    public int getPrev() {
        return prev;
    }

    public int getNext() {
        return next;
    }

    public List getPages() {
        return pages;
    }

    @Override
    public String toString() {
        return "Paging{" +
                "pageNo=" + pageNo +
                ", prev=" + prev +
                ", next=" + next +
                ", pages=" + pages +
                '}';
    }
}


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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 31
글 보관함