230208 Spring 페이징, MySQL limit함수
페이징
페이징은 get방식으로 처리한다.
이동할 때 페이지 번호를 가지고 다닌다.
페이징 처리하는 로직을 클래스로 분류한다
->Criteria클래스, PageVO클래스
sql에 변경을 주는 클래스, sql로 화면을 그리는 클래스. (반드시는 아님)
MYSQL
오라클은 limit함수가 없어서 인라인뷰로 해줘야 한다.
MySQL에서 페이징은 limit함수를 이용한다!
limit(데이터시작, 데이터개수)
select * from table
order by num desc
limit 0,10;
1~10번 데이터가 조회된다.
select * from table
order by num desc
limit 10,20;
11번~30번 데이터가 조회된다.
select *
from trip order by tno desc limit 0,10; -- 1번에서 10개의 데이터
select * from trip order by tno desc limit 10,10; -- 11번에서 10개
select * from trip order by tno desc limit 10,50; -- 11번에서 50개
Critera클래스
-데이터시작과 데이터개수를 뽑아서 sql로 넘겨준다.
pageNum//페이지번호
count //몇개의 데이터를 보여줄건가.
기본생성자로 pageNum=1, count=10을 넣고
매개변수를 받는 생성자에서 사용자가 원하는 걸 넣어준다.
계산하는 메서드를 만들어서 sql문에 전달하게 해준다.
public int getPageStart() {
return (pageNum - 1) * count;
}
처럼.
pageNum은 클릭하는 페이지, count는 값이 변할 수 있으니 그대로.
1페이지를 누르면 (1-1)*10=0, 카운트는 10.
그래서 limit에서 0,10이 되니 1~10번 글을 뽑고,
가령 3페이지를 누르면 (3-1)*10=20, 카운트는 10.
->limit 20,10이니 21번~31번 글을 뽑는다.
이 함수를 sql문으로.
select * from table order by num desc limit #{pageStart}, #{count}
Criteria 클래스 생성
-sql문에 페이지번호, 데이터개수 전달해줄 클래스
기본생성자를 만들 때 초기값을 지정해주려면 롬복을 사용할 수 없다.
getter setter toString은 롬복 @Data로.
getPageStart를 getter로 메서드를 만들어서 sql문에서 pageStart를 쓸 수 있다.
Criteria.java
package com.cordhistroy.myweb.util;
import lombok.Data;
//sql문에 페이지번호, 데이터개수 전달해줄 클래스
@Data
public class Criteria {
private int page; //페이지번호
private int amount; //데이터개수
public Criteria() {
this.page=1;
this.amount=10;
}
public Criteria (int page, int amount) {
this.page=page;
this.amount=amount;
}
//limit함수의 페이지시작 부분에 들어갈 getter
public int getPageStart() {
return (page-1)*amount;
}
}
Controller
spring은 vo를 여러개 매개변수로 받을 수 있다.
//목록
@RequestMapping("/notice_list")
public String notice_list(Criteria cri, Model model) {
//데이터
ArrayList<TripVO> list = tripService.getList(cri);
//페이지네이션
int total=tripService.getTotal();
PageVO pageVO=new PageVO(cri, total);
System.out.println(pageVO.toString());
//값 넘기기
model.addAttribute("list",list);
model.addAttribute(pageVO);
return "trip/notice_list";
}
mapper.xml
<select id="getList" resultType="TripVO" >
select * from trip
order by tno desc limit #{pageStart}, #{amount}
</select>
getPageStart로 지정된 pageStart부터 amount만큼의 컬럼을 화면에 그린다.
가령 전체에서 9페이지고 10개씩 뿌려진다면,
(9-1)*10인 80이 pageStart가 된다.
limit 80,10이므로 9페이지에선 81~90번 글이 나오는 것.
1페이지면 1번~10번글이 나온다.
PageVO 클래스
-페이징 계산 처리 클래스
- total : 게시판 글 전체 개수
- endPage : 게시판 화면에 보여질 마지막 페이지 번호
-11~20페이지 클릭시 20 - startPage : 게시판 화면에 보여질 첫번째 페이지 번호
-endPage가 20일때 11 - realEnd : 게시판의 실제 마지막 페이지 번호
-총 게시물이 50개 일 때 5, 51개라면 6 - prev : 이전 페이지 버튼 활성화 여부
-startPage가 1이면 비활성화, 1이 아니면 활성화. - next : 다음 페이지 버튼 활성화 여부
-realEnd가 endPage보다 크면 활성화.
이 클래스는 외우지 말고 이해할 것.
pageVO는 생성되는 순간 값을 받아야 한다. 생성자 안에서 처리할 것.
+)Math.ceil-올림
이전버튼과 다음버튼은, 1~10/ 11~20/ 21~30을 왔다갔다하는 버튼임
PageVO
package com.cordhistroy.myweb.util;
import lombok.Data;
//화면에 그려지는 페이지네이션의 값을 계산하는 클래스
@Data
public class PageVO {
private int end; //페이지네이션 끝번호
private int start; //페이지네이션 시작번호
private boolean next; //다음버튼 활성화여부
private boolean prev; //이전버튼 활성화여부
private int realEnd; //페이지네이션 실제끝번호
private int page; //사용자가 조회하는 페이지번호
private int amount; //화면의 1페이지에 나타나는 데이터개수
private int total; //전체게시글 수
private Criteria cri; //페이지기준클래스
private int pageCnt=5; //페이지네이션 개수
//생성자 - pageVO가 만들어질 때 Criteria와 total을 반드시 받아야 한다.
public PageVO(Criteria cri, int total) {
//계산에 필요한 값(페이지번호, 데이터개수, 전체게시글수, cri)을 초기화
this.page=cri.getPage();
this.amount=cri.getAmount();
this.total = total;
this.cri=cri;
//1. 끝페이지
//page가 1~10 ->끝페이지 10
//page가 11~20 ->끝페이지 20
//(int)Math.ceil(페이지번호/10.0)*페이지네이션 수
this.end=(int)Math.ceil(this.page/(double)pageCnt)*pageCnt;
//2. 시작페이지번호 계산
//end - 페이지네이션 수 + 1
this.start=this.end-pageCnt+1;
//3. 실제끝번호 계산
//데이터가 60개라고 가정할 때, end = 6
//데이터가 112개라고 가정할 때, 11번페이지 조회시 end=12
//데이터가 356개라고 가정할 때, 32번페이지 조회시 end=36
//(int)Math.ceil(전체게시글 수 / 데이터개수)
this.realEnd=(int)Math.ceil(total/(double)this.amount);
//4. 마지막 페이지번호의 재계산이 필요.
//데이터가 112개라고 가정할 때, 5번페이지 조회시 end=10, realEnd=12 --->10
//데이터가 112개라고 가정할 때, 11번페이지 조회시, end=20, realEnd=12 --->12
//end>realEnd면 realEnd가 마지막페이지여야 한다.
//끝번호>실제끝번호 이면 실제끝번호를 따라감
this.end = this.end>this.realEnd?this.realEnd:this.end;
//5. 이전버튼
//start는 1, 11, 21, 31 ...로 증가되는데 1보다 크면 true
this.prev=this.start>1;
//6.다음버튼
//조건- realEnd가 end보다 크면 true
this.next=this.realEnd>this.end;
}
}
-getter, setter는 @Data로.
-페이지네이션:한번에 보여줄 페이지 수
Controller에서 전체게시글 수 조회가 필요.
int total=tripService.getTotal();
PageVO pageVO=new PageVO(cri, total);
public int getTotal();//전체게시글수 조회
mapper.xml
<select id="getTotal" resultType="int">
select count(*) as total from trip
</select>
list.jsp에서
<c:forEach var="colList" items="${list}" varStatus="num">
<tr>
<td>${pageVO.total-((pageVO.page-1)*pageVO.amount)-num.count+1}</td>
<td class="tit_notice"><a href="notice_view?tno=${colList.tno}">${colList.title}</a></td>
<td>${colList.hit}</td>
<td><fmt:formatDate value="${colList.regdate}" pattern="yyyy-MM-dd" /></td>
</tr>
</c:forEach>
로, 글의 번호를 total의 끝에서부터 첫번째까지 준다.
limit이 사용되었으니 page와 amount에 따라 지정한 만큼 반복문이 돌아간다.
페이징처리
<div class="pagination">
<!-- 5.맨처음으로 -->
<a href="notice_list?page=1&amount=${pageVO.amount}" class="firstpage pbtn"><img src="${pageContext.request.contextPath}/resources/img/btn_firstpage.png" alt="첫 페이지로 이동"></a>
<!-- 3.이전페이지네이션 -->
<c:if test="${pageVO.prev }">
<a href="notice_list?page=${pageVO.start-1}&amount=${pageVO.amount}" class="prevpage pbtn"><img src="${pageContext.request.contextPath}/resources/img/btn_prevpage.png" alt="이전 페이지로 이동"></a>
</c:if>
<!-- 1.페이지네이션 -->
<c:forEach var="num" begin="${pageVO.start}" end="${pageVO.end}">
<a href="notice_list?page=${num}&amount=${pageVO.amount}"><span class="pagenum ${pageVO.page==num?'currentpage':''}">${num}</span></a>
</c:forEach>
<!-- 2.다음페이지네이션 -->
<c:if test="${pageVO.next }">
<a href="notice_list?page=${pageVO.end+1}&amount=${pageVO.amount}" class="nextpage pbtn"><img src="${pageContext.request.contextPath}/resources/img/btn_nextpage.png" alt="다음 페이지로 이동"></a>
</c:if>
<!-- 4.맨마지막으로 -->
<a href="notice_list?page=${pageVO.realEnd}&amount=${pageVO.amount}" class="lastpage pbtn"><img src="${pageContext.request.contextPath}/resources/img/btn_lastpage.png" alt="마지막 페이지로 이동"></a>
</div>
클래스 currentpage는 조회하는 페이지와 반복문의 num이 같을 때만 주면 됨.
->삼항연상자로 부여
이전버튼과 다음버튼 이동은 PageVO.prev와 next의 조건에 맞춰 활성화되거나 비활성화된다. <c:if>문을 통해.
->페이지네이션의 첫번째인 start는 1,11,21,31...로 증가된다. 1일 때는 앞으로 이동하기가 없다.
->페이지의 끝인 realEnd가 페이지네이션의 마지막인 end보다 작으면 끝부분임. 다음으로 이동하기가 없다.
버튼 a링크에서, amount는 pageVO.amount로 준다. 고정값10으로 주는 게 아님.