728x90
PK와 인덱스
- PK를 생성하면 자동으로 인덱스 생성

인덱스를 이용하기 위한 힌트
- 개발자의 의도를 힌트를 이용해서 전달
- 힌트 구문은 잘못되어도 SQL 처리에 지장을 주지 않음
- 여러 종류의 힌트가 존재 FULL/ INDEX_DESC, INDEX_ASC
/*+ index_desc(tbl_board pk_board) */




데이터를 더 추가한 후
full 힌트


index 힌트


즉 데이터가 많아질수록 order by를 이용하기보다는 index를 이용
페이징 처리 → 오라클의 경우 다른 db와 다름, 좀 더 복잡함 → 실행계획에 따라서 어떻게 접근할지 결정됨
※실행계획 : sql을 작성했을 때 데이터베이스에서 어떻게 처리할지 순서가 결정?
rownum과 인덱스
- rownum 가상 테이터 같은 것(실제 데이터가 아님), 출력되면서 붙는 번호
- 테이블에서 최종적으로 나오면서 붙이는 컬럼
- 인덱스를 통해서 나오는지, full 스캔을 통하는지에 따라서 rownum이 다르게 나




index → 가장 높은 번호가 가장 먼저 테이블에서 접근이 됨
페이지를 위해서는 아래의 SQL을 이용해야함
select /*+ index_desc(tbl_board pk_board) */ rownum, bno, title from tbl_board where bno>0 and rownum <= 10;

위는 1페이지에 해당할 것이다. 그렇다면 2페이지는?
select /*+ index_desc(tbl_board pk_board) */ rownum, bno, title from tbl_board where bno>0 and rownum >= 11 and rownum <=20;
를 작성하고 조회를 해보면 아무것도 뜨지 않음

FILTER에서 맞는 데이터가 하나도 없기 때문임

가능


from에 인라인뷰(select 문을 테이블 값을 들고오는 테이블로 사용 ) → 거기에 조건을 넣어줌
select *
from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum >0 and rownum <=20)
where rn >10;
package org.zerock.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Criteria {
private int pageNum; // 페이지
private int amount; // 개수
public Criteria() {
this(1,10); // 초기값
}
public Criteria(int pageNum, int amount) {
super();
this.pageNum = pageNum;
this.amount = amount;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
(...생략...)
<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
<![CDATA[
select *
from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum > 0 and rownum <= (2 * 10))
where rn > (2-1) *10
]]>
</select>
</mapper>
package org.zerock.mapper;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
@Autowired
private BoardMapper boardMapper;
(...생략...)
@Test
public void testPaging() {
Criteria criteria = new Criteria(); // 1 페이지에 10개
List<BoardVO> list = boardMapper.getListWithPaging(criteria);
for(int i = 0; i < list.size(); i++) {
log.info(list);
}
}
}


일치 → BoardMapper 수정
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
(...생략...)
<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
<![CDATA[
select *
from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum > 0 and rownum <= (#{pageNum} * #{amount}))
where rn > (#{pageNum}-1) * #{amount}
]]>
</select>
</mapper>
변경 후 다시 테스트


화면 페이징 처리
package org.zerock.service;
import java.util.List;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
public interface BoardService { // 고객과 의사소통하는 용어로 사용하기 때문에 변수명을 주의
// public void register(BoardVO boardVO);
public Long register(BoardVO boardVO);
public BoardVO get(Long bno);
public int modify(BoardVO boardVO);
public int remove(Long bno);
public List<BoardVO> getList();
public List<BoardVO> getList(Criteria criteria); // 오버로딩
}
package org.zerock.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.mapper.BoardMapper;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j;
@Service
@Log4j
@RequiredArgsConstructor
@ToString
public class BoardSeriveImpl implements BoardService{ // 인터페이스를 사용 : 실제 객체가 무엇인지 몰라도 타입만 보고 코딩할 수 있게 하기 위해서
private final BoardMapper boardMapper; // @RequiredArgsConstructor + final => 자동 주입
// @Override
// public void register(BoardVO boardVO) {
// boardMapper.insert(boardVO);
// }
@Override
public Long register(BoardVO boardVO) {
boardMapper.insertSelectKey(boardVO);
return boardVO.getBno();
}
@Override
public BoardVO get(Long bno) {
return boardMapper.read(bno);
}
@Override
public int modify(BoardVO boardVO) {
return boardMapper.update(boardVO);
}
@Override
public int remove(Long bno) {
return boardMapper.delete(bno);
}
@Override
public List<BoardVO> getList() {
return boardMapper.getList();
}
@Override
public List<BoardVO> getList(Criteria criteria) {
return boardMapper.getListWithPaging(criteria);
}
}
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {
private final BoardService boardService;
// @GetMapping("/list")
// public void list(Model model) { // Model 전달 당시에는 없는데 화면에 추가적인 데이터가 필요한 경우 사용
// log.info("BoardController list()................");
// model.addAttribute("list", boardService.getList());
// }
@GetMapping("/list")
public void list(Model model, Criteria criteria) {
log.info("BoardController list()................");
log.info("criteria : " + criteria);
model.addAttribute("list", boardService.getList(criteria));
}
@GetMapping("/register")
public void registerGet() {
log.info("BoardController regsterGet()............");
}
@PostMapping("/register")
public String register(BoardVO boardVO, RedirectAttributes redirectAttributes) {
log.info("BoardController register()....................");
boardService.register(boardVO);
log.info("register : " + boardVO);
redirectAttributes.addFlashAttribute("result", boardVO.getBno()); // addFlashAttribute 잠깐 결과를 보내는 방식
// ※ redirectAttributes.addAttribute("result : " + boardVO.getBno()) // 브라우저의 링크와 같이 연결, list?bno= 이런 형식으로 할 때 쓰임
return "redirect:/board/list";
}
@PostMapping("/modify")
public String modify(BoardVO boardVO, RedirectAttributes redirectAttributes) {
log.info("BoardController modify()......................");
int count = boardService.modify(boardVO);
if(count == 1) {
redirectAttributes.addFlashAttribute("result", "success");
}
return "redirect:/board/list";
}
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, RedirectAttributes redirectAttributes) {
log.info("BoardController remove()......................");
int count = boardService.remove(bno);
if(count == 1) {
redirectAttributes.addFlashAttribute("result", "success");
}
return "redirect:/board/list";
}
@GetMapping({"/get", "/modify"})
public void get(@RequestParam("bno") Long bno,Model model) {
model.addAttribute("boardVO", boardService.get(bno));
}
}

Mapper에서 작성자, 작성일, 수정일을 들고오지 않음
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
(...생략...)
<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
<![CDATA[
select *
from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title, writer, regdate,updateDate from tbl_board where bno>0 and rownum > 0 and rownum <= (#{pageNum} * #{amount}))
where rn > (#{pageNum}-1) * #{amount}
]]>
</select>
</mapper>

JSP 처리
- BoardController/BoardService/BoardServiceImpl 처리
- list.jsp 처리
페이지 처리에 필요한 정보
- 현재 페이지 번호(page) → Criteria
- 이전과 다음으로 이동 가능한 링크의 표시 여부(prev, next)
- 화면에서 보여지는 페이지의 시작 번호와 끝 번호(startPage, endPage)
startPage = endPage - 9
endPage = (int)(Math.ceil(page/10.0))*10
total을 통한 endPage의 재계산
realEnd = (int)(Math.ceil((total * 1.0) / amount))
if(realEnd < endPage){
endPage = realEnd
}
※끝 페이지에서 계산하는 것이 더 편함
package org.zerock.domain;
import lombok.Getter;
import lombok.ToString;
@ToString
@Getter
public class PageDTO {
private int startPage, endPage;
private boolean prev, next;
private int total;
private Criteria criteria;
public PageDTO(Criteria criteria, int total) {
this.criteria = criteria;
this.total = total;
// 계산
this.endPage = (int)(Math.ceil(criteria.getPageNum()/10.0)) * 10;
this.startPage = endPage - 9;
this.prev = this.startPage > 1;
int realEnd = (int)(Math.ceil((total * 1.0) / criteria.getAmount()));
this.endPage = (realEnd < endPage) ? realEnd : endPage;
this.next = this.endPage < realEnd;
}
}
package org.zerock.mapper;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.domain.PageDTO;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
@Autowired
private BoardMapper boardMapper;
(...생략...)
@Test
public void testPageDTO() {
Criteria criteria = new Criteria();
PageDTO pageDTO = new PageDTO(criteria, 1376395);
log.info(pageDTO);
}
}

화면에 전달(아직까지 total은 들고오지 않음)
package org.zerock.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.domain.PageDTO;
import org.zerock.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {
private final BoardService boardService;
// @GetMapping("/list")
// public void list(Model model) { // Model 전달 당시에는 없는데 화면에 추가적인 데이터가 필요한 경우 사용
// log.info("BoardController list()................");
// model.addAttribute("list", boardService.getList());
// }
@GetMapping("/list")
public void list(Model model, Criteria criteria) {
log.info("BoardController list()................");
log.info("criteria : " + criteria);
model.addAttribute("list", boardService.getList(criteria));
model.addAttribute("page", new PageDTO(criteria, 123));
}
(...생략...)
}
list.jsp에 model 출력
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@include file="../include/header.jsp" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Tables</h1>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
DataTables Advanced Tables
<button id="regBtn" type="button" class="btn btn-xs pull-right">Register New Board</button>
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<table width="100%" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>#번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>수정일</th>
</tr>
</thead>
<tbody>
<c:forEach items="${list}" var="boardVO">
<tr class="odd gradeX">
<td><c:out value="${boardVO.bno}" /></td>
<td><a href="/board/get?bno=<c:out value='${boardVO.bno}' />"><c:out value="${boardVO.title}" /></a></td>
<td><c:out value="${boardVO.writer}" /></td>
<td><fmt:formatDate value="${boardVO.regdate }" pattern="yyyy-MM-dd"/> </td>
<td><fmt:formatDate value="${boardVO.updateDate }" pattern="yyyy-MM-dd" /></td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- /.table-responsive -->
<h3>${page}</h3>
(..생략...)


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@include file="../include/header.jsp" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Tables</h1>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
DataTables Advanced Tables
<button id="regBtn" type="button" class="btn btn-xs pull-right">Register New Board</button>
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<table width="100%" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>#번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>수정일</th>
</tr>
</thead>
<tbody>
<c:forEach items="${list}" var="boardVO">
<tr class="odd gradeX">
<td><c:out value="${boardVO.bno}" /></td>
<td><a href="/board/get?bno=<c:out value='${boardVO.bno}' />"><c:out value="${boardVO.title}" /></a></td>
<td><c:out value="${boardVO.writer}" /></td>
<td><fmt:formatDate value="${boardVO.regdate }" pattern="yyyy-MM-dd"/> </td>
<td><fmt:formatDate value="${boardVO.updateDate }" pattern="yyyy-MM-dd" /></td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- /.table-responsive -->
<h3>${page}</h3>
<div class="pull-right">
<ul>
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<li>${num}</li>
</c:forEach>
</ul>
</div>
(..생략...)

페이지 번호 링크 부트스트랩을 이용 list.jsp
(...생략...)
<h3>${page}</h3>
<div class="pull-right">
<ul class="pagination">
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<li class="page-item"><a class="page-link" href="#">${num}</a></li>
</c:forEach>
</ul>
</div>
(...생략...)


prev, next 버튼
(...생략...)
<h4>${page}</h4>
<div class="pull-right">
<ul class="pagination">
<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<li class="page-item"><a class="page-link" href="#">${num}</a></li>
</c:forEach>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</div>
(...생략...)

Previous, Next list.jsp
(...생략...)
<h4>${page}</h4>
<div class="pull-right">
<ul class="pagination">
<c:if test="${page.prev}">
<li class="page-item"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
</c:if>
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<li class="page-item"><a class="page-link" href="#">${num}</a></li>
</c:forEach>
<c:if test="${page.next}">
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</c:if>
</ul>
</div>
(...생략...)


다음 아래 사진과 같은 작업

(...생략...)
<h4>${page}</h4>
<div class="pull-right">
<ul class="pagination">
<c:if test="${page.prev}">
<li class="page-item"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
</c:if>
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<li class="page-item ${page.criteria.pageNum == num? 'active' :''}"><a class="page-link" href="#">${num}</a></li>
</c:forEach>
<c:if test="${page.next}">
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</c:if>
</ul>
</div>
(...생략...)


728x90
'organize > 스프링' 카테고리의 다른 글
스프링 웹 프로젝트 15 (0) | 2025.01.14 |
---|---|
스프링 웹 프로젝트 14 (0) | 2025.01.13 |
스프링 웹 프로젝트 12 (0) | 2025.01.11 |
스프링 웹 프로젝트 11 (0) | 2025.01.09 |
스프링 웹 프로젝트 10 (0) | 2025.01.08 |