728x90
SpringMyweb(3)
페이징 처리
1. service, mapper 영역에 getList 함수를 선언
2. 등록번호 역순으로 데이터를 조회해서 가지고 나옴
3. model에 담아서 (여기서 모델에 담아줘야 함)
4. 화면에서는 반복문으로 처리
페이지 검색처리
1. 화면에서는 page, amount, searchType, searchName을 넘김
2. criteria에서 검색값을 받음 = getList로 cri 넘김
3. sql문을 바꿈 (동적쿼리)
4. total sql도 바꿈 (동적쿼리) = getTotal로 cri 넘겨서 total값 받아옴
5. 페이지 a태그 클릭시 searchType, searchName을 쿼리스트링으로 넘김
6. 검색 키워드 유지
페이지네이션
1. (페이지네이션 클릭은) 반드시 GET 방식으로만 처리
2. 이동할 때 페이지 번호를 가지고 다니기
3. 페이징 처리하는 로직을 클래스로 분류
▶ Criteria 클래스: SQL로 값을 받아서 던짐
▶ PageVO 클래스: 화면에 그리는 용
NoticeController
//데이터
ArrayList<TripVO> list = tripService.getList(cri);
//페이지네이션
int total = tripService.getTotal();
PageVO pageVO = new PageVO(cri, total);
System.out.println(pageVO.toString()); //확인
Critetia
//limit 함수의 페이지 시작 부분에 들어갈 pageStart getter ▶ TripMapper.xml에서 사용
public int getPageStart() {
return (page - 1) * amount;
}
- 페이징 처리에 필요한 시작 위치 계산
- amount: 한 페이지에 표시되는 게시물의 수, 고정값 X
TripMapper.xml
<select id="getList" resultType="TripVO">
<!-- select tno, title, hit, regdate from trip order by tno desc -->
<!-- 동적쿼리구문의 test에는 vo의 getter, map의 key값이 들어갑니다. -->
select * from trip
order by tno desc
limit #{pageStart}, #{amount}
<!-- limit 시작위치, 가져올 행의 수 -->
</select>
- limit 10, 20이면 11번째부터 20개이므로 11-30번 데이터가 조회됨
TripMapper.xml
<select id="getTotal" resultType="int">
select count(*) as total from trip
</select>
- PageVO에 넘겨줄 total: 데이터베이스에 저장된 총 게시물의 수 받아오기 (검색시에는 검색되는 모든 게시물의 수)
PageVO
package com.coding404.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가 만들어질 때 cri, total을 받는다.
public PageVO(Criteria cri, int total) {
//계산에 필요한 값(페이지번호, 데이터개수, 전체게시글수, cri)을 초기화
this.page = cri.getPage(); //Criteria에 있음
this.amount = cri.getAmount(); //Criteria에 있음
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(전체게시글수 / 데이터개수), 정수/정수는 정수라 올림이 잘 안일어나므로 하나는 double로 캐스팅
this.realEnd = (int)Math.ceil(this.total / (double)this.amount);
//4. 마지막 페이지번호를 다시 계산 ▶ 걍 작은거 따라감
//데이터가 112개라고 가정할 때, 5번 페이지 조회시 end=10, realEnd=12
//데이터가 112개라고 가정할 때, 11번페이지 조회시, end=20, realEnd=12
//끝번호 > 실제끝번호 라면 실제끝번호를 따라감
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 = realEnd > this.end;
}
}
- PageVO가 만들어질 때 cri, total 값을 받아서 필요한 값을 계산
- 생성됨과 동시에 처리가 되어있어야 하므로 생성자 안에서 계산
- pageCnt는 페이지네이션 수 (화면에 몇개 보여줄 것인가)
note_list.jsp
<div class="pagination">
<!-- 5. 맨 처음으로 -->
<a href="notice_list?page=1&amount=${pageVO.amount }&searchType=${pageVO.cri.searchType}&searchName=${pageVO.cri.searchName}" 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}&searchType=${pageVO.cri.searchType}&searchName=${pageVO.cri.searchName}" 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}&searchType=${pageVO.cri.searchType}&searchName=${pageVO.cri.searchName}"><span class="pagenum ${pageVO.page == num ? 'currentpage' : '' }">${num }</span></a> <!-- currentpage 있으면 백그라운드 색상 가짐 -->
</c:forEach>
<!-- 2. 다음 페이지네이션 -->
<c:if test="${pageVO.next}">
<a href="notice_list?page=${pageVO.end+1 }&amount=${pageVO.amount }&searchType=${pageVO.cri.searchType}&searchName=${pageVO.cri.searchName}" 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 }&searchType=${pageVO.cri.searchType}&searchName=${pageVO.cri.searchName}" class="lastpage pbtn"><img src="${pageContext.request.contextPath }/resources/img/btn_lastpage.png" alt="마지막 페이지로 이동"></a>
</div>
- 현재
- 다음 - 다음 페이지가 존재 한다면, 다음 페이지 구할 때 페이지의 끝번호 + 1, 예시로 현재 1-5까지 보여지면 6-10까지로 바뀜, 현재 페이지는 6
- 이전 - 이전 페이지가 존재 한다면, 이전 페이지 구할 때 페이지의 첫번호 - 1, 예시로 현재 6-10까지 보여지면 1-5까지로 바뀜, 현재 페이지는 5
- 맨 처음, 맨 마지막 - page=1 혹은 realEnd값으로 주고, amount는 한 페이지에 나타나는 데이터의 개수
- currentPage: pageVO의 page와 현재 보는 num이 같을 때 currentPage 활성화
검색 기능
1. 검색폼, 페이징폼에서 pageNum, amount, searchType, searchName을 각각 전송 (hidden 이용)
2. Criteria에 search키워드 search타입 변수 추가
3. 페이징 쿼리를 동적쿼리로 변경, 전체게시글 쿼리를 동적쿼리로 변경
4. 화면에 나타나는 에러 처리
검색버튼 클릭시 pageNum은 1로 처리 (2페이지에서 검색하면 2페이지로 이동)
select 박스에 검색 조건이 남게, input 박스에 검색 단어가 남게 처리
동적쿼리
- 마이바티스의 태그로써 조건문을 써서 쿼리의 실행을 제어
- jstl 구문과 사용방법이 유사
- 마이바티스의 test="" 구문 안에 작성되는 값은 VO의 getter나, map의 key값이 쓰임
- 대표적 태그: if, choose(when, otherwise), foreach, include 등
- n개씩 보여줄 양(amount) 처리: script에서 amount.value 처리하고 submit으로 보냄
- select 박스에 검색 조건이 남게
- input 박스에 검색 단어가 남게 처리
- 검색버튼 클릭시 pageNum은 1로 처리
<select id="getTotal" resultType="int">
select count(*) as total from trip
<if test="searchType == 'title' ">where title like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'content' ">where content like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'writer' ">where writer like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'titcont' ">where title like concat('%', #{searchName}, '%')
or content like concat('%', #{searchName}, '%')</if>
<if test="searchType == null or searchType == '' ">where 1=1 </if> <!-- 동적 쿼리에서 어떤 상황에서도 에러 안나게 하려고 1=1로 true 만들어줌 -->
</select>
- PageVO에 넘겨줄 total - 데이터베이스에 저장된 총 게시물의 수 받아오기 (검색시에는 검색되는 모든 게시물의 수)
<select id="getList" resultType="TripVO">
<!-- 동적쿼리구문의 test에는 vo의 getter, map의 key값이 들어갑니다. -->
select * from trip
<!-- where 컬럼 like 값 -->
<!-- if 구문: 보통 이거 씀-->
<if test="searchType == 'title' ">where title like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'content' ">where content like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'writer' ">where writer like concat('%', #{searchName}, '%') </if>
<if test="searchType == 'titcont' ">where title like concat('%', #{searchName}, '%')
or content like concat('%', #{searchName}, '%')</if>
<if test="searchType == null or searchType == '' ">where 1=1 </if> <!-- 동적 쿼리에서 어떤 상황에서도 에러 안나게 하려고 1=1로 true 만들어줌 -->
<!-- choose 구문 -->
<!--
<choose>
<when test="searchType == 'title'">where title like concat('%', #{searchName}, '%')</when>
<when test="searchType == 'content'">where content like concat('%', #{searchName}, '%')</when>
<when test="searchType == 'writer'">where writer like concat('%', #{searchName}, '%')</when>
<when test="searchType == 'titcont'">where title like concat('%', #{searchName}, '%')
or content like concat('%', #{searchName}, '%')</when>
<otherwise>where 1=1</otherwise>
</choose>
-->
order by tno desc
limit #{pageStart}, #{amount}
<!-- limit 시작위치, 가져올 행의 수 -->
</select>
- TripVO의 결과로 받아와서 forEach로 화면에 뿌림
- 검색시에도 유지되게 처리
정리
- model.attribute를 사용하여 list, pageVO로 받아와서 화면에서 동적으로 표현
오늘 하루
더보기
기억에 남는 부분
-
-
어려운 부분
-
-
문제 해결 부분
-
-
728x90
'Server > Spring' 카테고리의 다른 글
[인프런] 스프링 핵심원리 - 이론 및 실습 요구사항 & 1. 회원 도메인 (0) | 2023.11.11 |
---|---|
[Spring] DAO, VO, DTO, Entity (0) | 2023.06.20 |
[Spring] SpringMyweb 실습(2) - 게시판 구현 (0) | 2023.02.08 |
[Spring] tiles, Lombok, SpringMyweb 실습(1) - 생성 및 기본 설정 (0) | 2023.02.07 |
[Spring] MyBatis, Mapper, TestMapper 실습 (0) | 2023.02.06 |