✔ 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이 포함되어야 한다.
- 🔊 쿼리문의 실행 순서
- FROM/WHERE절이 먼저 처리된다.
- ROWNUM이 할당되고, FROM/WHERE 절에서 전달되는 각각의 출력 로우에 대해 증가(increment)가 된다.
- SELECT가 적용된다.
- GROUP BY 조건이 적용된다.
- HAVING이 적용된다.
- 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;
- 결과
✔ 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>
- 결과
🔎 1) VO 하나에 전부 조인시키기
🔎 2)