[2022.11.10.목] 도서 수정, 삭제, 페이징 처리하기
* 가격 형식, 날짜 형식
StringUtils 클래스 추가 -> numberToText, dateToText 정의
가격, 날짜 부분을 모두 stringUtils를 씌움
package com.sample.util;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringUtils {
private static final DecimalFormat decimalFormat = new DecimalFormat("#,###.##");
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy년 M월 d일");
/**
* Date를 "2022년 11월 10일" 형식의 문자열로 변환해서 반환한다.
* @param date 날짜
* @return 연월일 형식의 문자열, 날짜가 null이면 빈 문자열 반환
*/
public static String dateToText(Date date) {
if(date==null) {
return "";
}
return simpleDateFormat.format(date);
}
/**
* 정수를 3자리마다 ,가 포함된 문자열로 변환해서 반환한다.
* @param number 정수
* @return ,가 포함된 문자열
*/
public static String numberToText(long number) {
return decimalFormat.format(number);
}
/**
* 실수를 소수점 3번째 자리에서 반올림하고, 정수부는 3자리마다 ,가 포함된 문자열로 변환해서 반환한다.
* @param number 실수
* @return ,가 포함된 문자열(소수점 3번째 자리에서 반올림)
*/
public static String numberToText(double number) {
return decimalFormat.format(number);
}
}
- Book의 detail.jsp 의 등록일자를 날짜형식으로 표시
<tr>
<th>등록일자</th>
<td><%=StringUtils.dateToText(book.getCreatedDate()) %></td>
</tr>
- Book 의 list.jsp와 detail.jsp의 가격을 가격 형식, 효과 주기
<tr>
<th>가격</th>
<td><%=StringUtils.numberToText(book.getPrice()) %> 원</td>
<th>할인가격</th>
<td>
<strong class="text-danger"><%=StringUtils.numberToText(book.getDiscountPrice()) %> 원</strong>
</td>
</tr>
* 할인율 계산
Product 객체의 getter, setter 부분에 계산식 추가
- Product
public int getDiscountPrice() {
return (int) (price*(1-discountRate));
}
public int getDiscountPerCent() {
return (int) (discountRate*100);
}
- Product의 detail.jsp의 할인율 표시
<th>할인가격</th>
<td>
<strong class="text-danger"><%=StringUtils.numberToText(product.getDiscountPrice()) %> 원</strong>
<small class="text-danger">(<%=product.getDiscountPerCent() %>%)</small>
</td>
* 판매여부 표시
* Product -> 판매여부가 "y"이면 "판매중", "y"가 아니면 "재고없음" 표시
- Product 의 list.jsp와 detail.jsp의 판매여부를 배지로 표시
<td>
<%
if("y".equals(product.getOnSell())) {
%>
<span class="badge text-bg-primary">판매중</span>
<%
} else {
%>
<span class="badge text-bg-secondary">재고없음</span>
<%
}
%>
</td>
* Book -> 재고가 0보다 많으면 "판매중", 0이면 "재고없음" 표시
- Book
public boolean isOnSell() {
return stock > 0;
}
- Book의 detail.jsp에서 판매 배지 표시
<th>판매여부</th>
<td>
<%
if(book.isOnSell()){
%>
<span class="badge text-bg-primary">판매중</sapn>
<%
} else {
%>
<span class="badge text-bg-secondary">재고없음</span>
<%
}
%>
</td>
* 삭제
0. 재고가 없는 경우에만 삭제 버튼 활성화, 재고 있으면 삭제 버튼 비활성화
1. detail.jsp의 삭제버튼 링크에서 delete.jsp?bookNo=책번호 URL을 서버로 보내서 도서정보 삭제 요청
2. 톰캣이 요청메시지를 분석해서 URL의 쿼리스트링값을 HttpServletRequest에 요청파라미터로 저장
3. 톰캣은 요청파라미터값이 저장된 HttpServletRequest 객체, HttpServletResponse 객체를 delete.jsp의 _jspService(request, response)를 실행할 때 전달
4. delete.jsp에서는 요청파라미터값 조회
- xml에서 delete SQL 추가
<delete id="deleteBookByNo" parameterClass="int">
delete from
sample_books
where
book_no = #value#
</delete>
- DAO 에서 delete 메소드 추가
public void deleteBookByNo(int no) {
SqlMapper.delete("deleteBookByNo", no);
}
- detail.jsp의 <table> 아래 부분에 삭제버튼 추가, 버튼의 링크에서 delete.jsp로 요청
<div>
<a href="delete.jsp?bookNo=<%=book.getNo() %>" class="btn btn-danger <%=book.isOnSell() ? "disabled" : ""%>">삭제</a>
</div>
- delete.jsp
<%@page import="com.sample.dao.BookDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 요청파라미터에서 책번호 조회
int bookNo = Integer.parseInt(request.getParameter("bookNo"));
// BookDao 객체를 생성해서 deleteBookByNo(int bookNo) 메소드를 실행시킨다.
BookDao bookDao = new BookDao();
bookDao.deleteBookByNo(bookNo);
// 재요청할 URL을 응답으로 보낸다.
response.sendRedirect("list.jsp");
%>
* 수정
1) 수정폼
1. detail.jsp의 수정버튼 링크에서 modifyform.jsp?bookNo=책번호 URL을 서버로 보내서 도서 수정폼 화면 요청
2. 톰캣이 요청메시지를 분석해서 URL의 쿼리스트링값을 HttpServletRequest에 요청파라미터로 저장
3. 톰캣은 요청파라미터값이 저장된 HttpServletRequest 객체, HttpServletResponse 객체를 modifyform.jsp의 _jspService(request, response)를 실행할 때 전달
4. modifyform.jsp에서는 요청파라미터값 조회
- detail.jsp의 수정버튼에서 수정폼 화면 요청
<a href="modifyform.jsp?bookNo=<%=book.getNo() %>" class="btn btn-warning">수정</a>
- modifyform.jsp
수정폼에 원래값이 나와있어야 하니까
책정보를 조회해서 그 값을 입력필드 value에 꼭 넣어둬야함
이 modifyform 은 method="post", action="update.jsp"
-> post형식으로 입력폼 전체를 update.jsp로 요청
<%@page import="com.sample.vo.Book"%>
<%@page import="com.sample.dao.BookDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
<title>도서정보 변경</title>
</head>
<body>
<div class="container py-5">
<h1>도서 정보 수정폼</h1>
<%
// 요청파라미터 값을 조회한다.
int bookNo = Integer.parseInt(request.getParameter("bookNo"));
// 책 번호에 해당하는 책 객체를 조회한다.
BookDao bookDao = new BookDao();
Book book = bookDao.getBookByNo(bookNo);
%>
<p>도서 정보를 수정하고, 업데이트 하세요</p>
<form class="bg-light border p-3" method="post" action="update.jsp">
<!--
hidden 필드
- 화면에 표시되지 않는 폼 입력요소
- 화면에 표시할 필요는 없지만 폼 입력값을 서버로 제출할 때 포함되어야 하는 값을 hidden 필드로 정의한다.
-->
<input type="hidden" name="no" value="<%=book.getNo() %>" />
<div class="mb-3">
<label class="form-label">제목</label>
<input type="text" class="form-control" name="title" value="<%=book.getTitle() %>"/>
</div>
<div class="mb-3">
<label class="form-label">저자</label>
<input type="text" class="form-control" name="author" value="<%=book.getAuthor() %>"/>
</div>
<div class="mb-3">
<label class="form-label">출판사</label>
<select class="form-select" name="publisher">
<option value="" selected="selected" disabled="disabled"> 출판사를 선택하세요</option>
<option value="한빛미디어" <%="한빛미디어".equals(book.getPublisher()) ? "selected" : "" %>> 한빛미디어</option>
<option value="제이펍" <%="제이펍".equals(book.getPublisher()) ? "selected" : "" %>> 제이펍</option>
<option value="위키북스" <%="위키북스".equals(book.getPublisher()) ? "selected" : "" %>> 위키북스</option>
<option value="길벗" <%="길벗".equals(book.getPublisher()) ? "selected" : "" %>> 길벗</option>
<option value="에이콘출판사" <%="에이콘출판사".equals(book.getPublisher()) ? "selected" : "" %>> 에이콘출판사</option>
<option value="이지스퍼블리싱" <%="이지스퍼블리싱".equals(book.getPublisher()) ? "selected" : "" %>> 이지스퍼블리싱</option>
<option value="인사이트" <%="인사이트".equals(book.getPublisher()) ? "selected" : "" %>> 인사이트</option>
<option value="생능출판사" <%="생능출판사".equals(book.getPublisher()) ? "selected" : "" %>> 생능출판사</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">가격</label>
<input type="number" class="form-control" name="price" min="0" value="<%=book.getPrice() %>"/>
</div>
<div class="mb-3">
<label class="form-label">할인가격</label>
<input type="number" class="form-control" name="discountPrice" min="0" value="<%=book.getDiscountPrice() %>"/>
</div>
<div class="mb-3">
<label class="form-label">수량</label>
<input type="number" class="form-control" name="stock" value="<%=book.getStock() %>" min="1" step="1" max=100 />
</div>
<div class="text-end">
<a href="detail.jsp?bookNo=<%=book.getNo() %>" class="btn btn-secondary">취소</a>
<button type="submit" class="btn btn-primary">수정</button>
</div>
</form>
</div>
</body>
</html>
- update로 갈거야
- xml 에서 update SQL
<update id="updateBook" parameterClass="com.sample.vo.Book">
update
sample_books
set
book_title = #title#,
book_author = #author#,
book_publisher = #publisher#,
book_price = #price#,
book_discount_price = #discountPrice#,
book_stock = #stock#,
book_updated_date = sysdate
where
book_no = #no#
</update>
- Dao
public void updateBook(Book book) {
SqlMapper.update("updateBook", book);
}
- update.jsp
원본 조회 -> 객체를 생성해서 담지 않고 내가 지금 바꿀것만 바로 변경 ->Dao 에 업데이트쿼리를 여러개 x
<%@page import="com.sample.vo.Book"%>
<%@page import="com.sample.dao.BookDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// modifyform.jsp에서 제출한 폼입력값을 전부 조회한다.
int no = Integer.parseInt(request.getParameter("no"));
String title = request.getParameter("title");
String author = request.getParameter("author");
String publisher = request.getParameter("publisher");
int price = Integer.parseInt(request.getParameter("price"));
int discountPrice = Integer.parseInt(request.getParameter("discountPrice"));
int stock = Integer.parseInt(request.getParameter("stock"));
// 책 번호로 책정보를 조회한다.
BookDao bookDao = new BookDao();
Book book = bookDao.getBookByNo(no);
// 데이터베이스에 조회한 책정보를 수정폼에서 제출한 값으로 변경한다.
book.setTitle(title);
book.setAuthor(author);
book.setPublisher(publisher);
book.setPrice(price);
book.setDiscountPrice(discountPrice);
book.setStock(stock);
// BookDao 객체의 updateBook(Book book) 메소드를 실행해서 변경된 책정보를 반영시킨다.
bookDao.updateBook(book);
// 이 JSP는 추가/변경/삭제 중 하나를 수행하므로 재요청 URL을 응답으로 보낸다.
response.sendRedirect("list.jsp");
%>
* 페이징 처리 (많은 항목을 최신순 10개씩 보이게 하기)
페이징 처리 1) 페이지번호에 맞는 데이터 출력하기
1. 한 화면에 표시할 행의 개수 결정
final int rows = 10;
2. 현재 페이지 번호 조회
int currentPage = Integer.parseInt(request.getParameter("page"));
3. 현재 페이지 번호에 맞는 조회범위 게산 (1~10, 11~20, ...)
int begin = (currentPage - 1)*rows + 1;
int end = currentPage*rows;
4. Map객체를 생성해서 조회범위 저장
Map<String, Object> param = new HashMap();
param.put("begin", begin);
param.put("end", end);
5. 현재 페이지번호에 맞는 책 목록을 조회하기 위해서 BookDao 객체 생성
BookDao bookDao = new BookDao();
6. 현재 페이지번호에 맞는 책 목록 반환
List<Book> bookList = bookDao.getBooks(param);
- xml 에서 페이지 번호에 맞는 책목록 반환하는 SQL
<select id="getBooks" parameterClass="map" resultClass="com.sample.vo.Book">
select
book_no as no,
book_title as title,
book_author as author,
book_publisher as publisher,
book_price as price,
book_discount_price as discountPrice,
book_stock as stock,
book_created_date as createdDate,
book_updated_date as updatedDate
from
(select
row_number() over (order by book_no desc) row_numbers,
book_no, book_title, book_author, book_publisher, book_price, book_discount_price,
book_stock, book_created_date, book_updated_date
from
sample_books)
where
row_numbers between #begin# and #end#
</select>
- DAO 에서 페이지 번호에 맞는 책목록 반환하는 메소드
@SuppressWarnings("unchecked")
public List<Book> getBooks(Map<String, Object> param){
return (List<Book>) SqlMapper.selectList("getBooks", param);
}
- list.jsp 의 윗부분
<h1>도서 리스트</h1>
<%
// 페이징 처리하기
// 1. 한 화면에 표시할 행의 갯수를 결정하기
final int rows = 10;
// 2. 현재 페이지 번호를 조회하기
int currentPage = Integer.parseInt(request.getParameter("page"));
// 3. 현재 페이지 번호에 맞는 조회범위 계산하기
int begin = (currentPage - 1)*rows + 1;
int end = currentPage*rows;
// 4. Map객체를 생성해서 조회범위를 저장한다.
Map<String, Object> param = new HashMap<>();
param.put("begin", begin);
param.put("end", end);
// 현재 페이지번호에 맞는 책 목록을 조회하기 위해서 BoardDao객체를 생성한다.
BookDao bookDao = new BookDao();
// BoardDao객체의 getAllBooks() 메소드를 실행해서 현재 페이지번호에 맞는 책 목록을 반환받는다.
List<Book> books = bookDao.getBooks(param);
%>
페이징 처리 2) 페이지 내비게이션 표시 (이전, 1, 2, 3, 4, 5, 다음)
1. 한 화면에 표시할 페이지번호 개수 결정하기 ( 한 화면에 5페이지씩만 보여줄거야 )
int pages = 5;
2. 전체 데이터행의 개수 조회
int totalRows = bookDao.getTotalRows();
3. 전체 페이지 개수 계산
int totalPages = (int) (Math.ceil( (double) totalRows / rows ))
( 실수로 나눠야 ceil을 할 수 있음 -> 이걸 다시 정수로 변환 -> 총 페이지 수 )
2. 총 페이지 블록의 개수 계산하기 ( 총 6페이진데 5페이지씩만 보여줄거면 -> 2 페이지 블록 필요 )
int totalPageBlocks = (int) Math.ceil( (double) totalPages / pages )
3. 현재 페이지블록 계산하기 ( 1 ~ 5 페이지는 1페이지 블록, 6~ 나머지는 2페이지 블록 )
int currnetPageBlock = (int ) Math.ceil( (double) curremtPage / pages )
4. 현재 페이지블록에 맞는 시작페이지번호와 끝페이지번호를 계산하기
int beginPage = (currentPageBlock - 1) *pages + 1;
int endPage = currentPageBlock*pages;
if (currentPageBlock == totalPageBlocks ) {
endPage = totalPages;
}
5. 페이지 내비게이션 출력
<%
for (int number = 1; number <= totalPages; number) {
%>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<%
}
%>
- xml 에서 전체 행 개수 조회하는 SQL
<select id="getTotalRows" resultClass="int">
select
count(*)
from
sample_books
</select>
- Dao 에서 전체 행 개수 조회하는 메소드
public int getTotalRows() {
return (Integer) SqlMapper.selectOne("getTotalRows");
}
<%
// 페이지 내비게이션 표시
// 1. 한 화면에 표시할 페이지번호 개수 결정
int pages = 5;
// 2. 전체 책 개수 조회
int totalRows = bookDao.getTotalRows();
// 3. 전체 페이지 개수 조회
int totalPages = (int) Math.ceil((double) totalRows/rows);
// 4. 전체 페이지 블록 개수 계산
int totalPageBlocks = (int) Math.ceil((double) totalPages/pages);
// 5. 현재 페이지가 속한 페이지 블록 계산
int currentPageBlock = (int) Math.ceil((double) currentPage/pages);
// 6. 현재 페이지블록의 시작페이지번호와 끝 페이지번호 계산
int beginPage = (currentPageBlock - 1)*pages + 1;
int endPage = currentPageBlock == totalPageBlocks ? totalPages : currentPageBlock*pages;
%>
<div aria-label="navigation">
<ul class="pagination justify-content-center">
<li class="page-item <%=currentPage <= 1 ? "disabled" : "" %>">
<a class="page-link" href="list.jsp?page=<%=currentPage - 1 %>">이전</a>
</li>
<%
for (int number = beginPage; number <= endPage; number++) {
%>
<li class="page-item <%=currentPage == number ? "active" : "" %>">
<a class="page-link" href="list.jsp?page=<%=number %>"><%=number %></a>
</li>
<%
}
%>
<li class="page-item <%=currentPage >= totalPages ? "disabled" : "" %>">
<a class="page-link" href="list.jsp?page=<%=currentPage + 1%>">다음</a></li>
</ul>
</div>