subin9804 / starducks

KOSTA 268기 최종 프로젝트 2팀 starducks ERP
5 stars 2 forks source link

페이지네이션 객체를 이용한 구현 및 fragment로 재사용하기 #76

Open subin9804 opened 9 months ago

subin9804 commented 9 months ago

pagination.java

/**
 * 페이지네이션
 * @param <T>
 */
@Getter @Setter @ToString
public class Pagination<T> {

    private int page; // 현재 페이지
    private long total; // 전체 레코드 수
    private int prev; // 전 구간 마지막 페이지
    private int next; // 다음 구간 마지막 페이지
    private int lastPage; // 마지막 페이지
    private int pageCnt; // 구간별 페이지 갯수
    private int pagePerCnt; // 1 페이지당 레코드 수
    private boolean isFirstCnt; // 첫 번째 구간 여부
    private boolean isLastCnt; // 마지막 구간 여부
    private List<Integer> pages; // 구간별 페이지 번호
    private String baseUrl;

    public Pagination(Page<T> data, String url) {
        this(data.getNumber() + 1, data.getTotalElements(), data.getSize(), url);
    }

    public Pagination(int page, long total, String url) {
        this(page, total, 20, url);
    }

    public Pagination(int _page, long _total, int _pagePerCnt, String url) {
        total = _total;
        pagePerCnt = _pagePerCnt;
        page = _page <= 0 ? 1 : page;

        if (total < 1) {
            return;
        }

        baseUrl = url;
        baseUrl = baseUrl == null ? "" : baseUrl;

        this.pageCnt = pageCnt < 1 ? 10 : pageCnt; // 페이지 구간이 0 이하 인 경우는 기본값 10 지정
        this.lastPage = (int) Math.ceil(total / (double) this.pagePerCnt);  // 마지막 페이지

        page = page < 1 ? 1 : page; // 페이지가 0 이하 일 경우 1로 고정
        if (page > this.lastPage) page = this.lastPage; // 페이지가 마지막 페이지보다 크다면 마지막 페이지로 고정

        pages = new ArrayList<>();

        /** 페이지 구간 구하기 S */
        int cnt = (int) Math.ceil(this.page / (double) this.pageCnt) - 1; // 현재 페이지 구간 번호
        int lastCnt = (int) Math.ceil(this.lastPage / (double) this.pageCnt) - 1; // 마지막 페이지 구간 번호

        if (cnt == 0) this.isFirstCnt = true;  // 첫번째 페이지 구간 체크
        if (cnt == lastCnt) this.isLastCnt = true; // 마지막 페이지 구간 체크
        /** 페이지 구간 구하기 E */

        /** 구간별 페이지 번호 S */
        int start = cnt * this.pageCnt + 1;
        for (int i = start; i < start + this.pageCnt; i++) {
            pages.add(i);

            if (i == this.lastPage) { // 마지막 페이지에서 멈춤
                break;
            }
        }
        /** 구간별 페이지 번호 E */

        /** 전 구간 마지막 페이지 S */
        if (!this.isFirstCnt) {
            prev = cnt * this.pageCnt;
        }
        /** 전 구간 마지막 페이지  E */
        /** 다음 구간 시작 페이지 S */
        if (!this.isLastCnt) {
            next = (cnt + 1) * this.pageCnt + 1;
        }
        /** 다음 구간 시작 페이지  E */
    }
}

Repository.java(예시)

public interface RentalBookRepository extends JpaRepository<RentalBook, String>, QuerydslPredicateExecutor<RentalBook> {

    default Page<RentalBook> getBooks(BookSearch bookSearch) {
        /** 페이징 처리 S */
        int page = bookSearch.getPage();
        page = page < 1 ? 1 : page;
        int limit = bookSearch.getLimit();
        limit = limit < 1 ? 20 : limit;

        Pageable pageable = PageRequest.of(page - 1, limit);
        /** 페이징 처리 E */

        /** 검색 조건 처리 S */
        BooleanBuilder builder = new BooleanBuilder();
        QRentalBook rentalBook = QRentalBook.rentalBook;
        String bookId = bookSearch.getBookId();
        String sopt = bookSearch.getSopt();
        String skey = bookSearch.getSkey();
        String[] status = bookSearch.getStatus();
        String[] rentalType = bookSearch.getRentalType();

        if (bookId != null && !bookId.isBlank()) { // 도서ID
            builder.and(rentalBook.bookId.contains(bookId));
        }

        if (sopt != null && !sopt.isBlank() && skey != null && !skey.isBlank()) {
            BooleanBuilder orBuilder = new BooleanBuilder();
            if (sopt.equals("bookNm")) { // 책이름
                orBuilder.or(rentalBook.bookNm.contains(skey));
            } else if (sopt.equals("author")) { // 저자
                orBuilder.or(rentalBook.author.contains(skey));
            } else if (sopt.equals("publisher")) { // 출판사
                orBuilder.or(rentalBook.publisher.contains(skey));
            } else {
                orBuilder.andAnyOf(rentalBook.bookNm.contains(skey),
                                rentalBook.author.contains(skey),
                                rentalBook.publisher.contains(skey));
            }

            builder.and(orBuilder);
        }

        if (status != null && status.length > 0) { // 대여 상태 조회
            List<RentalStatus> statuses = Arrays.stream(status).map(RentalStatus::valueOf).toList();
            builder.and(rentalBook.status.in(statuses));
        }

        if (rentalType != null && rentalType.length > 0) { // 도서 종류
            List<RentalType> rentalTypes = Arrays.stream(rentalType).map(RentalType::valueOf).toList();
            builder.and(rentalBook.rentalType.in(rentalTypes));
        }
        /** 검색 조건 처리 E */

        Page<RentalBook> data = findAll(builder, pageable);
        return data;
    }
}

ListService.java

@Service
@RequiredArgsConstructor
public class BookListService {

    private final EntityManager em;

    private final RentalBookRepository repository;

    private Page<RentalBook> data;

    public BookListService gets(BookSearch bookSearch) {

        data = repository.getBooks(bookSearch);

        return this;
    }

    public List<RentalBook> toList() {
        List<RentalBook> books = data.getContent();
        books.stream().forEach(em::detach); // 영속성 분리

        return books;
    }

    public Page<RentalBook> getPage() {
        return data;
    }
}

Controller.java(예시)

    @GetMapping
    public String index(BookSearch bookSearch, Model model, HttpServletRequest request) {

        List<RentalBook> books = listService.gets(bookSearch).toList();
        Page<RentalBook> page = listService.getPage();

        String url = request.getContextPath() + "/admin/book";
        Pagination<RentalBook> pagination = new Pagination<>(page, url);
        model.addAttribute("bookSearch", bookSearch);
        model.addAttribute("pagination", pagination);
        model.addAttribute("books", books);
        return "admin/book/index";
    }

pagination.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="pagination" th:if="${pagination != null}" th:object="${pagination}">
    <ul class='pagination'>
        <th:block th:if="*{page > pageCnt}">
            <li class='page first'><a th:href="@{{url}(url=*{baseUrl}, page=1)}">처음</a></li>
            <li class='page prev'><a th:href="@{{url}(url=*{baseUrl}, page=*{prev})}">이전</a></li>
        </th:block>
        <th:block th:each="p : *{pages}">
            <li class='page' th:if="${p == pagination.page}" th:classappend="on">
                <a th:href="@{{url}(url=*{baseUrl}, page=${p})}" th:text="${p}"></a>

            </li>
        </th:block>
        <li  th:if="*{next  > 0}" class='page next'><a th:href="@{{url}(url=*{baseUrl}, page=*{next})}">다음</a></li>
        <li th:if="*{page < lastPage}" class='page last'><a th:href="@{{url}(url=*{baseUrl}, page=*{lastPage})}">마지막</a></li>
    </ul>
</th:block>
</html>