반응형
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="study.example.thboard.mapper.BoardMapper">
<!--게시글 목록 조회-->
<select id="selectBoards" parameterType="Criteria" resultType="BoardVo">
/* 게시글 목록 조회 */
<![CDATA[
SELECT T1.no
, T1.boardNo
, T1.title
, T1.content
, T1.writer
, T1.createDate
, T1.updateDate
FROM
(SELECT /*+ INDEX_DESC(BOARD BOARD_PK)*/
rownum AS no
, BOARD_NO AS boardNo
, TITLE AS title
, CONTENT AS content
, WRITER AS writer
, CREATE_DATE AS createDate
, UPDATE_DATE AS updateDate
, USE_YN
FROM BOARD
WHERE 1=1
AND USE_YN = 'Y'
AND rownum <= #{pageNum} * #{amount}
)T1
WHERE no > (#{pageNum} -1) * #{amount}
]]>
</select>
<!--게시글 전체 카운트-->
<select id="selectTotBoardCnt" resultType="int">
SELECT COUNT(*) FROM BOARD
</select>
</mapper>
오라클 페이지네이션 쿼리 핵심
더보기
/* 샘플 SQL */
SELECT T2.rn
, T2.id
, T2.name
, T2.email
, TO_CHAR(T2.create_date, 'YYYY-MM-DD') AS createDate /* 쿼리의 가장 바깥쪽에서 날짜 -> 문자열 변환을 해주는게 성능에 좋다고 한다. */
FROM (
SELECT ROWNUM as rn
, T1.id
, T1.name
, T1.email
, T1.create_date
FROM
( /* 1차 서브쿼리에서 등록일 내림차순으로 정렬 */
* 혹은 ROW_NUMBER() OVER(ORDER BY create_date DESC) AS rn 이런식으로..
SELECT id
, name
, email
, create_date
FROM user_info
ORDER BY create_date DESC) T1
WHERE ROWNUM <= 30) T2 /* 2차 서브쿼리에서 정렬된 데이터를 1부터 30까지만 30건만 필터링(현재 페이지 번호 * 가져올 게시글 목록 수) */
WHERE T2.rn > 20 // 혹은 >= 21 /* 30건 중 21번부터 30번까지 10건만 최종적으로 필터링(현재 페이지 * 페이지 목록 수 중 시작값은 현재 페이지 - 1 * 가져올 게시글 목록 수) */
정렬을 한 데이터를 서브쿼리로 감싸서 ROWNUM으로 마지막 인덱스까지만 1차 필터링(rownum <= 마지막인덱스)
1차 필터링 된 데이터에서 ROWNUM으로 시작 인덱스로 2차 필터링( rownum >= 시작 인덱스)
페이징 처리 정보 출처: https://okky.kr/articles/282926
페이지 정보 객체
package study.example.thboard.vo;
import lombok.Data;
@Data
//페이지 정보
public class Criteria {
private int pageNum; // 페이지 번호(현재 페이지가 몇 페이지인지)
private int amount; // 한 화면에 출력한 페이지 개수
//기본값은 1페이지의 페이지수 10으로 고정
public Criteria() {
this(1,10);
}
public Criteria(int pageNum, int amount) {
this.pageNum = pageNum;
this.amount = amount;
}
}
페이지네이션 계산(화면에서 사용하기 위함)
package study.example.thboard.vo;
import lombok.Data;
@Data
//페이지네이션 처리
public class PageMaker {
private int startPage; //페이징 화면 하단의 시작 번호(5페이지라고 하면 [1][2][3][4][5] 여기서 제일 첫 번 째 시작번호)
private int endPage; //페이징 화면 하단의 끝 번호(5페이지라고 하면 [1][2][3][4][5] 여기서 제일 끝 번호)
private boolean prev, next; // 이전, 다음 존재 여부
private int totalCount; // 전체 게시글 수
private Criteria cri; // 프론트에서 전달하는 pageNum(현재 페이지), amount(출력 페이지) 전달 역할
public PageMaker(int totalCount, Criteria cri) {
this.totalCount = totalCount;
this.cri = cri;
this.endPage = (int)(Math.ceil(cri.getPageNum()/10.0)) * 10;
this.startPage = this.endPage - 9;
int realEnd = (int)(Math.ceil((totalCount * 1.0) / cri.getAmount()));
if(realEnd < this.endPage) {
this.endPage = realEnd;
}
this.prev = this.startPage > 1;
this.next = this.endPage < realEnd;
}
}
mapper(interface)
package study.example.thboard.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import study.example.thboard.vo.BoardVo;
import study.example.thboard.vo.Criteria;
import java.util.List;
@Repository @Mapper
public interface BoardMapper {
/* 게시글 목록 조회 */
List<BoardVo> selectBoards(Criteria cri);
/* 전체 게시글 카운트 */
int selectTotBoardCnt();
}
service
package study.example.thboard.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import study.example.thboard.mapper.BoardMapper;
import study.example.thboard.vo.BoardVo;
import study.example.thboard.vo.Criteria;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class BoardService {
private final BoardMapper boardMapper;
/**
* 게시글 목록 조회
* @param cri
* @throws Exception
* @return
*/
public List<BoardVo> getBoards(Criteria cri) throws Exception{
return boardMapper.selectBoards(cri);
}
/**
* 게시글 전체 카운트
* @return
*/
public int getTotBoardCnt() {
return boardMapper.selectTotBoardCnt();
}
}
controller
package study.example.thboard.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import study.example.thboard.service.BoardService;
import study.example.thboard.service.FileService;
import study.example.thboard.vo.BoardVo;
import study.example.thboard.vo.Criteria;
import study.example.thboard.vo.PageMaker;
import study.example.thboard.vo.FileVo;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
@Controller
@RequiredArgsConstructor
@Slf4j
@RequestMapping(value = "/")
public class MainController {
private final BoardService boardService;
private final FileService fileService;
/**
* 게시글 목록 조회
* @param cri
* @return
*/
@GetMapping(value = "")
public ModelAndView main(@ModelAttribute Criteria cri) {
ModelAndView mv = new ModelAndView("pages/index");
List<BoardVo> boardList = null;
try {
//전체 게시글 카운트
int totBoardCnt = boardService.getTotBoardCnt();
//게시글 목록 조회(페이징)
boardList = boardService.getBoards(cri);
//화면 페이징 처리 객체
PageMaker pageMaker = new PageMaker(totBoardCnt, cri);
mv.addObject("list", boardList);
mv.addObject("pageMaker", pageMaker);
} catch (Exception e) {
e.printStackTrace();
}
return mv;
}
}
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default_layout}">
<script layout:fragment="script" th:inline="javascript" type="text/javascript">
$(document).ready(function () {
//글쓰기
$("#btnSave").on("click", function () {
$("#frm").attr("action", "/regForm").submit();
});
//검색
$("#btnSearch").on("click", function () {
$("#frm").submit();
});
});
//삭제
function del(no) {
let boardNo = $("<input>").attr("type", "hidden").attr("name", "boardNo").val(no);
$("#frm").attr("method", "post")
.attr("action", "del")
.append(boardNo)
.submit();
}
</script>
<!-- Content -->
<div layout:fragment="content">
<form id="frm" th:object="${boardVo}" action="" method="get">
<main>
<div class="container-fluid px-4">
<h1>게시글 관리</h1>
<table class="table table-striped table-sm">
<thead>
<tr>
<th>순번</th>
<th>제목</th>
<th>내용</th>
<th>작성자</th>
<th>등록일</th>
<th>수정일</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:if="${#lists.size(list) >0}" th:each="list : ${list}">
<td th:text="${list.no}"></td>
<td><a th:href="@{/detail(boardNo=${list.boardNo})}" th:text="${list.title}"></a></td>
<td th:text="${list.content}"></td>
<td th:text="${list.writer}"></td>
<td th:text="${list.createDate}"></td>
<td th:text="${list.updateDate}"></td>
<td><a href="#" th:href="'javascript:del('+${list.boardNo}+')'" class="btn btn-danger">삭제</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 게시판 하단 페이지네이션 영역 start -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<!-- prev -->
<li class="page-item" th:if="${pageMaker.prev} == true">
<a class="page-link" th:href="@{/(pageNum=${pageMaker.startPage}-1)}">Prev</a>
</li>
<!-- pageMaker의 startPage부터 endPage까지 루프, a태그의 href에 idx를 링크(get방식으로 pageNum을 붙여서) -->
<li class="page-item" id="paginate_btn" th:each="idx: ${#numbers.sequence(pageMaker.startPage, pageMaker.endPage)}" th:classappend="${pageMaker.cri.pageNum} == ${idx} ? active : null">
<a class="page-link" th:href="@{/(pageNum=${idx})}" th:text="${idx}"></a>
</li>
<!-- next -->
<li class="page-item" th:if="${pageMaker.next} == true and ${pageMaker.endPage > 0}">
<a class="page-link" th:href="@{/(pageNum=${pageMaker.endPage}+1)}">Next</a>
</li>
</ul>
</nav>
<!-- // 게시판 하단의 페이지네이션 영역 end -->
<button type="button" id="btnSave" class="btn btn-info">글쓰기</button>
</main>
</form>
</div>
</html>
코드 참조: 코드로 배우는 스프링 웹 프로젝트(도서)
반응형
'IT > development' 카테고리의 다른 글
[springBoot] spring boot jar파일 배포 삽질(feat. 리눅스) (0) | 2023.05.13 |
---|---|
[springBoot] 세션 인증 로그인/로그아웃 (0) | 2023.05.08 |
[springBoot] 첨부파일 업/다운로드(Oracle) (0) | 2023.05.07 |
[springBoot] UrlResource 사용 시 경로 에러 (0) | 2023.05.07 |
[mybatis] selectkey값 return(Oracle) (0) | 2023.05.06 |