ㅁ 게시글이 몇개일지 모르기 때문에 웬만해선 앞으론 다 페이징 처리를 한다.
- MyBatis는 페이징 처리를 위한 객체(RowBounds)를 제공해서 쿼리문이 간결해진다.
ㅁ main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>1. insert연습 (공지사항 등록)</h3>
<a href="${ contextPath }/regist.no">공지사항 등록페이지로 이동</a>
<h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
<form action="${ contextPath }/detail.no">
검색할 글번호 : <input type="text" name="noticeNo">
<button type="submit">검색</button>
</form>
<h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
<a href="${ contextPath }/list.no">공지사항 전체 목록 페이지로 이동</a>
<h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>
<a href="${ contextPath }/list.no?page=1">공지사항 목록 페이지(페이징처리)로 이동</a>
<h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
<h6>6. 동적쿼리 연습 (회원 검색)</h6>
</body>
</html>
- 페이징 처리를 적용한 여러행 조회 요청을 위해 새로운 서블릿을 만들어도 되지만 이전걸 재사용한다.
아까는 요청시 전달값이 없었지만 이번에는 요청시 전달값을 전달한다.
- 넘어가는 데이터가 없으면 전체 목록페이지가 보이게 하고, 넘어가는 데이터가 있으면 페이징 처리를 한다.
ㅁ com.br.mybatis.notice.controller의 NoticeListController를 수정한다.
package com.br.mybatis.notice.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;
/**
* Servlet implementation class NoticeListController
*/
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeListController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(request.getParameter("page") == null) {
// 페이징 처리 X
List<NoticeDto> list = new NoticeServiceImpl().selectList();
request.setAttribute("list", list);
request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);
}else {
// 페이징 처리 O
int currentPage = Integer.parseInt(request.getParameter("page"));
int listCount = new NoticeServiceImpl().selectListCount();
PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ㅁ com.br.mybatis.notice.service의 NoticeServiceImpl에 selectListCount 메소드를 만든다.
package com.br.mybatis.notice.service;
import static com.br.mybatis.common.template.Template.getSqlSession;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeServiceImpl implements NoticeService {
private NoticeDao noticeDao = new NoticeDao();
@Override
public int insertNotice(NoticeDto n) {
SqlSession sqlSession = /*Template.*/getSqlSession();
int result = noticeDao.insertNotice(sqlSession, n);
if(result > 0) {
sqlSession.commit();
}
sqlSession.close();
return result;
}
@Override
public NoticeDto selectNotice(int noticeNo) {
SqlSession sqlSession = getSqlSession();
NoticeDto n = noticeDao.selectNotice(sqlSession, noticeNo);
sqlSession.close();
return n;
}
@Override
public List<NoticeDto> selectList() {
SqlSession sqlSession = getSqlSession();
List<NoticeDto> list = noticeDao.selectList(sqlSession);
sqlSession.close();
return list;
}
@Override
public int selectListCount() {
SqlSession sqlSession = getSqlSession();
int listCount = noticeDao.selectListCount(sqlSession);
sqlSession.close();
return listCount;
}
@Override
public List<NoticeDto> selectList(PageInfoDto pi) {
return null;
}
}
- 게시글이 총 몇개인지는 db로부터 조회 한다.
ㅁ com.br.mybatis.notice.dao의 NoticeDao에 selectListCount 메소드를 만든다.
package com.br.mybatis.notice.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeDao {
public int insertNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.insert("noticeMapper.insertNotice", n);
}
public int updateNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.update("noticeMapper.updateNotice", n);
}
public int deleteNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
}
public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
}
public List<NoticeDto> selectList(SqlSession sqlSession) {
return sqlSession.selectList("noticeMapper.selectList");
}
public int selectListCount(SqlSession sqlSession) {
return sqlSession.selectOne("noticeMapper.selectListCount");
}
}
- 게시글이 총 몇개인지는 db로부터 조회 한다.
ㅁ 쿼리
<select id="selectListCount" resultType="_int">
select
count(*)
from notice
where status = 'Y'
</select>
- resultType으로 selectOne() 메소드의 결과가 반환된다. 그 결과를 dao에서 또 service로 return하고 있다.
ㅁ com.br.mybatis.common.dto 패키지에 PageInfoDto 클래스를 처음에 만들었었다.
package com.br.mybatis.common.dto;
public class PageInfoDto {
private int listCount;
private int currentPage;
private int pageLimit;
private int boardLimit;
private int maxPage;
private int startPage;
private int endPage;
public PageInfoDto() {
super();
}
public PageInfoDto(int listCount, int currentPage, int pageLimit, int boardLimit, int maxPage, int startPage,
int endPage) {
super();
this.listCount = listCount;
this.currentPage = currentPage;
this.pageLimit = pageLimit;
this.boardLimit = boardLimit;
this.maxPage = maxPage;
this.startPage = startPage;
this.endPage = endPage;
}
public int getListCount() {
return listCount;
}
public void setListCount(int listCount) {
this.listCount = listCount;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageLimit() {
return pageLimit;
}
public void setPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
}
public int getBoardLimit() {
return boardLimit;
}
public void setBoardLimit(int boardLimit) {
this.boardLimit = boardLimit;
}
public int getMaxPage() {
return maxPage;
}
public void setMaxPage(int maxPage) {
this.maxPage = maxPage;
}
public int getStartPage() {
return startPage;
}
public void setStartPage(int startPage) {
this.startPage = startPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
@Override
public String toString() {
return "PageInfoDto [listCount=" + listCount + ", currentPage=" + currentPage + ", pageLimit=" + pageLimit
+ ", boardLimit=" + boardLimit + ", maxPage=" + maxPage + ", startPage=" + startPage + ", endPage="
+ endPage + "]";
}
}
ㅁ com.br.mybatis.common.utils 패키지에 PagingUtil 클래스를 처음에 만들었었다.
package com.br.mybatis.common.utils;
import com.br.mybatis.common.dto.PageInfoDto;
public class PagingUtil {
// listCount, currentPage, pageLimit, boardLimit을 전달받아
// maxPage, startPage, endPage를 구해서
// PageInfoDto에 담아서 반환한다.
public static PageInfoDto getPageInfoDto(int listCount, int currentPage, int pageLimit, int boardLimit) {
int maxPage = (int)Math.ceil((double)listCount/boardLimit); // 마지막 페이지 수(총 페이지 수)
int startPage = (currentPage - 1) / pageLimit * pageLimit + 1;
int endPage = startPage + pageLimit - 1;
if(endPage > maxPage) {
endPage = maxPage;
}
return new PageInfoDto(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);
}
}
- PagingUtil 클래스의 getPageInfoDto 메소드를 호출하면서
listCount, currentPage, pageLimit, boardLimit을 전달하면
maxPage, startPage, endPage를 구하고 PageInfoDto 객체에 담으면서 생성해서 리턴한다.
ㅁ 컨트롤러로 돌아온다.
package com.br.mybatis.notice.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;
/**
* Servlet implementation class NoticeListController
*/
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeListController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(request.getParameter("page") == null) {
// 페이징 처리 X
List<NoticeDto> list = new NoticeServiceImpl().selectList();
request.setAttribute("list", list);
request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);
}else {
// 페이징 처리 O
int currentPage = Integer.parseInt(request.getParameter("page"));
int listCount = new NoticeServiceImpl().selectListCount();
PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 아직 컨트롤러 클래스의 완성 형태는 아니다.
- PaginUtil 클래스의 getPageInfoDto 메소드 호출시 listCount, currentPage, pageLimit, boardLimit을 넘기면
PageInfoDto 객체가 돌아온다.
- 페이징 바 제작을 위한 PageInfoDto 객체도 얻었으니 사용자에게 보여줄 일부 게시글 목록만을 조회할 서비스 메소드를 호출한다.
ㅁ NoticeServiceImpl 클래스에 selectList 메소드 생성
package com.br.mybatis.notice.service;
import static com.br.mybatis.common.template.Template.getSqlSession;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeServiceImpl implements NoticeService {
private NoticeDao noticeDao = new NoticeDao();
@Override
public int insertNotice(NoticeDto n) {
SqlSession sqlSession = /*Template.*/getSqlSession();
int result = noticeDao.insertNotice(sqlSession, n);
if(result > 0) {
sqlSession.commit();
}
sqlSession.close();
return result;
}
@Override
public NoticeDto selectNotice(int noticeNo) {
SqlSession sqlSession = getSqlSession();
NoticeDto n = noticeDao.selectNotice(sqlSession, noticeNo);
sqlSession.close();
return n;
}
@Override
public List<NoticeDto> selectList() {
SqlSession sqlSession = getSqlSession();
List<NoticeDto> list = noticeDao.selectList(sqlSession);
sqlSession.close();
return list;
}
@Override
public int selectListCount() {
SqlSession sqlSession = getSqlSession();
int listCount = noticeDao.selectListCount(sqlSession);
sqlSession.close();
return listCount;
}
@Override
public List<NoticeDto> selectList(PageInfoDto pi) {
SqlSession sqlSession = getSqlSession();
List<NoticeDto> list = noticeDao.selectList(sqlSession, pi);
sqlSession.close();
return list;
}
}
ㅁ NoticeDao 클래스에 selectList 메소드 생성
package com.br.mybatis.notice.dao;
import java.util.List;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeDao {
public int insertNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.insert("noticeMapper.insertNotice", n);
}
public int updateNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.update("noticeMapper.updateNotice", n);
}
public int deleteNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
}
public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
}
public List<NoticeDto> selectList(SqlSession sqlSession) {
return sqlSession.selectList("noticeMapper.selectList");
}
public int selectListCount(SqlSession sqlSession) {
return sqlSession.selectOne("noticeMapper.selectListCount");
}
public List<NoticeDto> selectList(SqlSession sqlSession, PageInfoDto pi) {
// return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.
int limit = pi.getBoardLimit();
int offset = (pi.getCurrentPage() - 1) * limit;
RowBounds rowBounds = new RowBounds(offset, limit);
return sqlSession.selectList("noticeMapper.selectList", null , rowBounds);
// 완성형태의 쿼리문이라 무언가 전달할 것이 없다.
// 완성시킬 값이 있다면 2번째 자리에 쓰면 된다.
}
}
- 전체 데이터 조회가 아닌, 사용자가 요청한 페이지에 필요한 일부 데이터만 조회한다.
return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.
- 기존에 만들어놓은 selectList 쿼리를 또 쓴다.
마이바티스를 사용하면 인라인뷰로 쿼리를 복잡하게 작성할 필요가 없다.
전체 게시글 중 일부 게시글을 조회하는 쿼리문이 매우 간결하다.
- RowBounds 객체를 제시해서 매개변수 3개짜리 메소드를 사용하면 된다.
※ RowBounds
- 마이바티스에서 페이징 처리를 위해 제공하는 객체
- 전체 게시글을 조회하는 쿼리문을 사용해서 우선은 전체 조회를 해오고,
그 중에 몇 개의 게시글을 건너뛰고 몇 개의 게시글만 반환하게끔 설정할 수 있다.
RowBounds rowBounds = new RowBounds(offset, limit);
- RowBounds 객체를 생성할 때 2개의 매개변수를 제시해야 한다.
- offset : 건너뛸 갯수
- limit : 조회할 갯수 (건너 뛰고 나서 몇개를 조회할 건지)
offset limit
currentPage : 1 1번게시글~5번게시글 0 5
currentPage : 2 6번게시글~10번게시글 5 5
currentPage : 3 11번게시글~15번게시글 10 5
- DB에서 쿼리문으로 전체 게시글을 조회했다.
- 1번 페이지라면 0개의 게시글을 건너뛰고 5개의 게시글을 조회해야 한다.
- 2번 페이지라면 5개의 게시글을 건너뛰고 6번째 데이터부터 5개의 게시글을 조회해야 한다.
- 몇번 페이지냐에 따라서 몇개의 게시글을 건너뛸지는 직접 결정해야 한다.
- 전체 게시글을 조회하는 sql문을 실행할 때 RowBounds 객체를 넘기면 몇개의 게시글을 건너 뛰고 몇개의 게시글을 조회할지 세팅된다.
ㅁ 다시 dao
- selectList 메소드는 3종류가 존재한다.
package com.br.mybatis.notice.dao;
import java.util.List;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeDao {
public int insertNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.insert("noticeMapper.insertNotice", n);
}
public int updateNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.update("noticeMapper.updateNotice", n);
}
public int deleteNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
}
public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
}
public List<NoticeDto> selectList(SqlSession sqlSession) {
return sqlSession.selectList("noticeMapper.selectList");
}
public int selectListCount(SqlSession sqlSession) {
return sqlSession.selectOne("noticeMapper.selectListCount");
}
public List<NoticeDto> selectList(SqlSession sqlSession, PageInfoDto pi) {
// return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.
int limit = pi.getBoardLimit();
int offset = (pi.getCurrentPage() - 1) * limit;
RowBounds rowBounds = new RowBounds(offset, limit);
return sqlSession.selectList("noticeMapper.selectList", null , rowBounds);
}
}
- 완성형태의 쿼리문이라 무언가 전달할 것이 없으면 null을 쓴다.
완성시킬 값이 있다면 2번째 자리에 쓰면 된다.
ㅁ 컨트롤러로 돌아와서 완성한다
package com.br.mybatis.notice.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.common.utils.PagingUtil;
import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;
/**
* Servlet implementation class NoticeListController
*/
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeListController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if(request.getParameter("page") == null) {
// 페이징 처리 X
List<NoticeDto> list = new NoticeServiceImpl().selectList();
request.setAttribute("list", list);
request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);
}else {
// 페이징 처리 O
int currentPage = Integer.parseInt(request.getParameter("page"));
int listCount = new NoticeServiceImpl().selectListCount();
PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);
request.setAttribute("pi", pi);
request.setAttribute("list", list);
request.getRequestDispatcher("/WEB-INF/views/notice/noticePagingList.jsp").forward(request, response);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ㅁ WEB-INF/views/notice/noticePagingList.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>공지사항 목록 (페이징 o)</h3>
<c:choose>
<c:when test="${ empty list }">
존재하는 공지사항이 없습니다
</c:when>
<c:otherwise>
<table border="1">
<thead>
<tr>
<th>글번호</th>
<th>글제목</th>
<th>작성자</th>
<th>작성일</th>
</tr>
</thead>
<tbody>
<c:forEach var="n" items="${ list }">
<tr>
<td>${ n.noticeNo }</td>
<td>${ n.noticeTitle }</td>
<td>${ n.noticeWriter }</td>
<td>${ n.registDate }</td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- 페이징 바 제작 -->
<div>
<c:if test="${ pi.currentPage ne 1 }">
<a href="${ contextPath }/list.no?page=${ pi.currentPage - 1 }">이전</a>
</c:if>
<c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }">
<a href="${ contextPath }/list.no?page=${ p }">${ p }</a>
</c:forEach>
<c:if test="${ pi.currentPage ne pi.maxPage }">
<a href="${ contextPath }/list.no?page=${ pi.currentPage + 1 }">다음</a>
</c:if>
</div>
</c:otherwise>
</c:choose>
</body>
</html>