74일 차 - 게시판, 페이징, ROWNUM (23.04.13)

yvonne·2023년 4월 13일
0

📂Spring

목록 보기
6/18
post-thumbnail

📝 페이징


📌 1. mysql에서는 rownum으로 처리

  • 🔎 rownum: 조회 후 순번을 매길 때 사용
    sql이 실행된 결과에 넘버링을 해준다

✔ emp 테이블에서 10명만 추출해서 보여주는 쿼리문

select rownum, emp. * from emp where rownum <= 10;
  • 결과

✔ rownum의 오류

select rownum rn, bid, bname btitle from mvc_board where rownum > 10 and rownum <= 20;
  • 결과
  • 오류의 이유
    ROWNUM에 1이 할당되고 SELECT 실행 전 WHERE 조건의 ROWNUM (1) > 10 조건이 성립하지 않기 때문에 무효화
    이러한 이유로 SQL 작성 시 ROWNUM 조건은 반드시 1이 포함되어야 한다.
  • 🔊 쿼리문의 실행 순서
  1. FROM/WHERE절이 먼저 처리된다.
  2. ROWNUM이 할당되고, FROM/WHERE 절에서 전달되는 각각의 출력 로우에 대해 증가(increment)가 된다.
  3. SELECT가 적용된다.
  4. GROUP BY 조건이 적용된다.
  5. HAVING이 적용된다.
  6. ORDER BY 조건이 적용된다.

✔ 🔎 ROWNUM 10번부터 20번까지 추출하려면?

  SELECT * FROM (
     SELECT ROWNUM AS RNUM, A.* FROM (
          select rownum rn, bid, bname btitle from mvc_board
    ) A WHERE ROWNUM <= 20
) WHERE RNUM > 10;
  • 결과



🔎 스프링에서 페이징 적용 (지난 글 Board 게시판에 이어서 코딩)

✔ BoardController.java

package edu.example.ex.controller;

import org.springframework.beans.factory.annotation.Autowired;
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 edu.example.ex.page.Criteria;
import edu.example.ex.page.PageVO;
import edu.example.ex.service.BoardService;
import edu.example.ex.vo.BoardVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그
@Controller
@RequestMapping("/jboard/*")

public class BoardController {
	@Autowired
	private BoardService boardService;

	@GetMapping("/list")
	public String view_list(Model model) {
		log.info("view_list() ..");

		model.addAttribute("boards", boardService.getList());

		return "/board/list";
	}

	@GetMapping("/content_view")
	public String content_view(BoardVO boardVO, Model model) {
		log.info("content_view() ..");
		int bid = boardVO.getBid();
		boardVO = boardService.get(bid);

		model.addAttribute("content_view", boardVO);

		return "/board/content_view";
	}

	@PostMapping("/modify")
	public String modify(BoardVO boardVO, Model model) {
		log.info("modify() ..");
		int rn = boardService.modify(boardVO);
		log.info("modify() .. result number::" + rn);

		return "redirect:list";
	}

	@GetMapping("/delete")
	public String delete(BoardVO boardVO, Model model) {
		log.info("delete() ..");
		int rn = boardService.delete(boardVO);
		return "redirect:list";
	}

	@GetMapping("/write_view")
	public String write_view() {
		return "/board/write_view";
	}

	@PostMapping("/write")
	public String write(BoardVO boardVO) {
		log.info("write() ..");
		int rn = boardService.write(boardVO);
		return "redirect:list";
	}

	@GetMapping("/reply_view")
	public String reply_view(BoardVO boardVO, Model model) {
		log.info("reply_view()..");
		model.addAttribute("reply_view", boardService.get(boardVO.getBid()));
		return "/board/reply_view";
	}

	@GetMapping("/reply")
	public String reply(BoardVO boardVO) {
		log.info("reply() ..");
		boardService.registerReply(boardVO);
		return "redirect:list"; // 로직 수행 후 list로 redirect 함
	}

	// http://localhost:8282/list2
	// list2?pageNum=5&amount=10">1</a>
	@GetMapping("/list2")
	public String list2(Criteria cri, Model model) { // command 객체 먼저 실행 -> Criteria.java의 Criteria() 메소드 실행하여 처음에는
													// 1,10을 메모리에 저장했다가 주소창에서 pageNum 설정에 따라 setter함수가 적용되어 페이지 변경
		log.info("list2() ..");
		log.info("list2() Criteria " + cri);
		// 게시판 10개씩 가지고 오는 부분
		model.addAttribute("boards", boardService.getList(cri));

// 페이징을 위한 처리
		int total = boardService.getTotal();
		log.info("total" + total);
		
		model.addAttribute("pageMaker", new PageVO(cri, total));

		return "/board/list2";
	}

}

✔ Criteria.java

package edu.example.ex.page;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ToString
@Getter
@Setter

public class Criteria {

	// 페이징 처리를 위해선 페이지 번호와 한페이지당 몇개의 데이터를 보여줄것인지 결정되어야만 함.
	private int pageNum; // 페이지 번호
	private int amount; // 한페이지당 몇개의 데이터를 보여줄것인가?

	public Criteria() {
		this(1, 10);// 기본값 1페이지 10개로 지정
	}

	public Criteria(int pageNum, int amount) {
		this.pageNum = pageNum;
		this.amount = amount;
	}

}

✔ PageVO.java

package edu.example.ex.page;

import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ToString
@Getter
@Setter
public class PageVO {
	// 페이징 처리 할때 필요한 정보들
	private int startPage;// 화면에 보여지는 페이지 시작번호
	private int endPage;// 화면에 보여지는 끝번호
	private boolean prev, next;// 이전과 다음으로 이동 가능한 링크 표시

	private int total;// 전체 게시판 글 수
	private Criteria cri;

	public PageVO(Criteria cri, int total) { // 파라미터 : 페이지 넘버, 전체 게시글 수
		this.cri = cri;
		this.total = total; // 전체 데이타 수
		// ceil : 올림함수
		// Math.ceil(.95); // 1
		// Math.ceil(4); // 4
		// Math.ceil(7.004); // 8
		// Math.ceil(-0.95); // -0
		// Math.ceil(-4); // -4
		// Math.ceil(-7.004); // -7
		// endPage는 현재의 페이지 번호를 기준으로 계산함.

		// 현재 페이지가 3일 경우 : Math.ceil(3/10) * 10 = 10
		// 현재 페이지가 1일 경우 : Math.ceil(1/10) * 10 = 10
		// 현재 페이지가 10일 경우 : Math.ceil(10/10) * 10 = 10
		// 현재 페이지가 11일 경우 : Math.ceil(11/10) * 10 = 20 1.1을 올림함
		// 현재 페이지가 20일 경우 : Math.ceil(20/10) * 10 = 20
		// 현재 페이지가 21일 경우 : Math.ceil(21/10) * 10 = 30

		this.endPage = (int) (Math.ceil(cri.getPageNum() / 10.0)) * 10;
		this.startPage = this.endPage - 9;

		// Total을 통한 endPage의 재계산
		// 10개씩 보여주는 경우, 전체 데이터 수가 80개라고 가정하면 끝번호는 10이 아닌 8이 됨
		int realEnd = (int) (Math.ceil((total * 1.0) / cri.getAmount()));

		if (realEnd <= this.endPage) {
			this.endPage = realEnd;
		}

		// 시작번호가 1보다 큰경우 존재
		this.prev = this.startPage > 1; // <<
		// realEnd가 끝번호(endPage)보다 큰 경우에만 존재
		this.next = this.endPage < realEnd; // >>

	}

	// get 방식의 문구를 좀 더 쉽게 보여주기 위해 만든 함수
	public String makeQuery(int page) {
		UriComponents uriComponentsBuilder = UriComponentsBuilder.newInstance().queryParam("pageNum", page) // pageNum =
																											// 3
				.queryParam("amount", cri.getAmount()) // pageNum=3&amount=10
				.build(); // ?pageNum=3&amount=10
		return uriComponentsBuilder.toUriString(); // ?pageNum=3&amount=10 리턴
	}

}

✔ BoardService.java

package edu.example.ex.service;

import java.util.List;

import edu.example.ex.page.Criteria;
import edu.example.ex.vo.BoardVO;

public interface BoardService {
	public List<BoardVO> getList(); // 리스트

	public BoardVO get(int bno); // 글 보기

	int modify(BoardVO board); // 글 수정
	int delete(BoardVO board); // 글 삭제
	int write(BoardVO board); // 글 등록
	void registerReply(BoardVO board); // 댓글 등록
	
	// 페이징 메서드
	int getTotal(); 
	public List<BoardVO> getList(Criteria criteria); // 오버로딩 (함수 이름은 같고 파라미터 개수,타입이 다른 형태)
}

✔ BoardServiceImpl.java

package edu.example.ex.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import edu.example.ex.mapper.BoardMapper;
import edu.example.ex.page.Criteria;
import edu.example.ex.vo.BoardVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor // +@Autowired 하면 생성자 주입 (없으면 필드 주입)

public class BoardServiceImpl implements BoardService {
	@Autowired
	private final BoardMapper mapper;

	@Override
	public List<BoardVO> getList() {
		log.info("getList() ..");
		return mapper.getList();
	}

	@Override
	public BoardVO get(int bid) {
		log.info("get(int bid) ..");
		return mapper.read(bid);
	}

	@Override
	public int modify(BoardVO board) {
		log.info("modify() ..");
		return mapper.update(board);
	}

	@Override
	public int delete(BoardVO board) {
		log.info("delete() ..");
		return mapper.remove(board);
	}

	@Override
	public int write(BoardVO board) {
		log.info("write() ..");
		return mapper.insert(board);
	}

	@Transactional
	@Override
	public void registerReply(BoardVO board) {
		log.info("registerReply() ..");
		mapper.updateShape(board);
		mapper.insertReply(board);

	}

	@Override
	public int getTotal() {
		log.info("getTotal() ..");
		return mapper.getTotalCount();
	}

	@Override
	public List<BoardVO> getList(Criteria criteria) {
		log.info("getList(Criteria criteria) ..");
		return mapper.getListWithPaging(criteria);
	}

}

✔ BoardMapper.java

package edu.example.ex.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import edu.example.ex.page.Criteria;
import edu.example.ex.vo.BoardVO;

@Mapper // mybatis에 사용할 거라는 것을 알려주는 애너테이션
public interface BoardMapper {
	public List<BoardVO> getList();
	public BoardVO read(int bid); // 파라미터는 url로 넘어오는 bid값을 의미
	public int update(BoardVO board); // ServiceImpl.java에서의 update 정의해주기 (글 수정)
	public int remove(BoardVO board);
	public int insert(BoardVO board);
	void updateShape(BoardVO board); // 답글 위치
	void insertReply(BoardVO board); // 답글 등록

	// 페이징
	int getTotalCount();
	public List<BoardVO> getListWithPaging(Criteria cri);
}

✔ BoardMapper.xml

<?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="edu.example.ex.mapper.BoardMapper">
	<select id="getList" resultType="edu.example.ex.vo.BoardVO"> <!-- id는 함수이름 resultType은 BoardVO -->
	     <![CDATA[
    select * from mvc_board order by bGroup desc, bStep asc
 ]]>
	</select>

	<select id="read" resultType="edu.example.ex.vo.BoardVO"> 
	     <![CDATA[
	 select * from mvc_board where bid = #{bid}   
 ]]> <!-- content_view 부분 / #{bid}는 파라미터를 넘기는 과정 -->
	</select>

	<update id="update"> 
	     <![CDATA[
	update mvc_board set bname=#{bname}, btitle=#{btitle}, bcontent=#{bcontent} 
           where bid =#{bid}
 ]]> <!-- modify 부분 / bname=#{getBname()}과 같은 의미 -->
	</update>

	<delete id="remove"> 
	     <![CDATA[
	delete from mvc_board where bid = #{bid}
 ]]>
	</delete>

	<insert id="insert">
	 	     <![CDATA[
   insert into mvc_board (bid, bname, btitle, bcontent, bhit, bgroup, bstep, bindent) 
   values (mvc_board_seq.nextval, #{bname}, #{btitle}, #{bcontent}, 0, mvc_board_seq.currval, 0, 0) 

 ]]>
	</insert>

	<!--댓글을 하나씩 미뤄서 정렬을 해줘야 하기 때문에 세로 정렬을 bstep에 +1로 한칸씩 미뤄내줘야한다. -->
	<update id="updateShape">
    <![CDATA[
        update mvc_board set bstep = bstep + 1 where bgroup =#{bgroup} and bstep > #{bstep}
    ]]>
	</update>

	<insert id="insertReply">
 <![CDATA[
    insert into mvc_board (bid, bname, btitle, bcontent, bgroup, bstep, bindent) 
    values (mvc_board_seq.nextval, #{bname}, #{btitle},#{bcontent}, 
              #{bgroup}, #{bstep}+1, #{bindent}+1)
 ]]>
	</insert>

	<select id="getTotalCount" resultType="int">
    <![CDATA[
        select count(*) from mvc_board
    ]]>
	</select>

	<select id="getListWithPaging" resultType="edu.example.ex.vo.BoardVO">
    <![CDATA[
      SELECT * FROM (
            SELECT ROWNUM AS RNUM, A.* FROM (
                     SELECT
                           *
                       FROM
                           mvc_board 
                       order by bGroup desc, bStep asc   
           ) A WHERE ROWNUM <= #{pageNum} * #{amount}
       ) WHERE RNUM > (#{pageNum}-1) * #{amount}
    ]]>
	</select>

</mapper>

✔ empList2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<table width="800" cellpadding="0" cellspacing="0" border="1">
		<tr>
			<th>empno</th>
			<th>ename</th>
			<th>job</th>
			<th>mgr</th>
			<th>hiredate</th>
			<th>sal</th>
			<th>comm</th>
			<th>deptno</th>
		</tr>
		<c:forEach var="emp" items="${emps}">
			<tr>
				<td><a
					href="${pageContext.request.contextPath}/emp/contentView?empno=${emp.empno}">${emp.empno}</td>
				</a>
				<td>${emp.ename}</td>
				<td>${emp.job}</td>
				<td>${emp.mgr}</td>
				<td>${emp.hiredate}</td>
				<td>${emp.sal}</td>
				<td>${emp.comm}</td>
				<td>${emp.deptno}</td>
			</tr>
		</c:forEach>

	</table>
	    <c:if test="${pageMaker.prev}">
         <a href="empList2${pageMaker.makeQuery(pageMaker.startPage - 1) }"> « </a>
      </c:if>

      <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage }" var="idx">
         <c:out value="${pageMaker.cri.pageNum == idx?'':''}" />
         <a href="empList2${pageMaker.makeQuery(idx)}">${idx}</a>
      </c:forEach>
     
      <c:if test="${pageMaker.next && pageMaker.endPage > 0}">
         <a href="empList2${pageMaker.makeQuery(pageMaker.endPage +1) }"> » </a>
      </c:if> <br>
</body>
</html>
  • 결과




📌 2. mybatis 1:n일대다 처리 (조인처리 방법)

🔎 1) VO 하나에 전부 조인시키기

🔎 2)

📌 3. restful 처리

profile
개발 연습장

0개의 댓글