전체 조회 : 부서명 like 기능 있음
데이터베이스(SQL&Docker) 활용
작업 순서: model -> dao -> mappers(xml) -> service -> Controller -> views(jsp)

페이징 처리 : 시작 페이지부터 끝 페이지까지 반복하며 각 페이지 번호를 생성
페이지 조회 : 현재 페이지 페이지 0, 크기 3

Dept 클래스 ( schema.sql 참고 )
DeptDao 클래스 findByEnameContaining()
Dept.xml 클래스
DeptService 클래스를 만들고 findByEnameContaining() 함수를 정의한다.
DeptController 클래스를 만들어서 getEmpAll() 함수를 정의하고,
샘플데이터를 뷰로(dept_all.html) 전송

resources 파일 이전 글 참고  [Spring Boot] - [SpringBoot] 데이터베이스(SQL&Docker)이용한 연결 환경 설정

 

[SpringBoot] 데이터베이스(SQL&Docker 이용) 연결 환경 설정

프로젝트 생성 1. 종속성 설정 2. dependencies 빌드 3. 파일 인코딩 변경 : UTF-8 4. resources 폴더 application.properties 설정 5. resources 폴더 실습용 환경 파일 추가 6. Java 리소스 폴더 만들기(jsp용) 7. 환경 설

soaked.tistory.com


 

제목입력부서 클래스 == 부서 테이블 유사

vo(Value Object) : 테이블과 동일하게 만듬
dto(Data Transfer Object) : 테이블 + 가공컬럼(속성)

extends BaseTimeEntity
@Setter
@Getter
@ToString
@NoArgsConstructo
@AllArgsConstructor
더보기
package com.example.mybatisexam.model.vo;

import com.example.mybatisexam.model.common.BaseTimeEntity;
import lombok.*;

/**
 * packageName : com.example.mybatisexam.model.vo
 * fileName : Dept
 * author : L.DH
 * date : 2023-10-12
 * description : 부서 클래스 == 부서 테이블 유사
 * 요약 :
 * TODO : vo(Value Object) : 테이블과 동일하게 만듬
 * TODO : dto(Data Transfer Object) : 테이블 + 가공컬럼(속성)
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * ———————————————————————————————
 * 2023-10-12         L.DH         최초 생성
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Dept extends BaseTimeEntity {
    private Integer dno; // 부서번호
    private String dname; // 부서명
    private String loc;   // 부서위치
}

 


 

Db 접속 함수들이(CRUD) 있는 클래스(mybatis mapper(=dao) 클래스)

interface 함수만 정의

@Mapper
@Param("속성명") 변수명 : sql 의 매개변수로 전달됨
더보기
package com.example.mybatisexam.dao;

import com.example.mybatisexam.model.common.PageReq;
import com.example.mybatisexam.model.vo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * packageName : com.example.mybatisexam.dao
 * fileName : DeptDao
 * author : L.DH
 * date : 2023-10-12
 * description : Db 접속 함수들이(CRUD) 있는 클래스(mybatis mapper(=dao) 클래스)
 * 요약 :
 * TODO : interface 함수만 정의
 * @Mapper : 인터페이스 위에 달고, mybatis에서 사용할 인터페이스라는 것을 알림
 *           서버 시작시 자동으로 객체가 생성됨
 * @Param("속성명") 변수명 : sql 의 매개변수로 전달됨
 *
 * TODO : xml
 * pageReq.size : 1 페이지당 개수
 * pageReq.page : 현재 페이지 번호
 *  (oracle 12 버전 이상만 OFFSET 가능)
 *  OFFSET #{pageReq.page} * #{pageReq.size} ROWS FETCH FIRST #{pageReq.size} ROWS ONLY
 *  예) (1 페이지당 개수)size = 10, 현재 페이지 번호(page) = 2
 *  2 * 10 = 20 (db 건수 20개 건너뛰고)
 *  10       10 (10 개를 화면에 출력하세요.)
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * ———————————————————————————————
 * 2023-10-12         L.DH         최초 생성
 */
@Mapper
public interface DeptDao {
    /** 전체 조회 : 부서명 like 기능 있음 */
    public List<Dept> findByDnameContaining(@Param("dname") String dname,
                                            PageReq pageReq
                                            );
    /** 전체 테이블 개수 세기 함수 */
    long countByDname(String dname);
}

 

Mybatis 작업 폴더 생성
Backend\03_MybatisExam\MybatisExam\src\main\resources\mappers
.xml OFFSET #{pageReq.page} * #{pageReq.size} ROWS FETCH FIRST #{pageReq.size} ROWS ONLY

pageReq.size : 1 페이지당 개수 pageReq.page : 현재 페이지 번호 (oracle 12 버전 이상만 OFFSET 가능)
OFFSET #{pageReq.page}#{pageReq.size} ROWS FETCH FIRST #{pageReq.size} ROWS ONLY 예)
(1 페이지당 개수)size = 10, 현재 페이지 번호(page) = 2
2 * 10 = 20 (db 건수 20개 건너뛰고) 10 10 (10 개를 화면에 출력)
더보기
<?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="com.example.mybatisexam.dao.DeptDao">

    <select id="findByDnameContaining" parameterType="PageReq" resultType="Dept">
        SELECT DNO
             , DNAME
             , LOC
             , INSERT_TIME
             , UPDATE_TIME
        FROM TB_DEPT
        WHERE DNAME LIKE '%' || #{dname} || '%'
        OFFSET #{pageReq.page} * #{pageReq.size} ROWS FETCH FIRST #{pageReq.size} ROWS ONLY
    </select>

    <select id="countByDname" parameterType="String" resultType="long">
        SELECT COUNT(dno)
        FROM TB_DEPT
        WHERE DNAME LIKE '%' || #{dname} || '%'
    </select>
</mapper>

 

 


 

부서 서비스 클래스(업무 로직)

페이징 처리 로직

@Service
@Autowired
더보기
package com.example.mybatisexam.service.exam01;

import com.example.mybatisexam.dao.DeptDao;
import com.example.mybatisexam.model.common.PageReq;
import com.example.mybatisexam.model.common.PageRes;
import com.example.mybatisexam.model.vo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * packageName : com.example.mybatisexam.service.exam01
 * fileName : DeptService
 * author : L.DH
 * date : 2023-10-12
 * description : 전체 조회 : like 기능
 * 요약 : 부서 서비스 클래스(업무 로직)
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * ———————————————————————————————
 * 2023-10-12         L.DH         최초 생성
 */
@Service
public class DeptService {
    @Autowired
    DeptDao deptDao;    // db crud 함수들이 있는 클래스

    /** dname like 함수 */
    public PageRes<Dept> findByDnameContaining(String dname,
                                               PageReq pageReq) {
//        TODO : 전체 조회 (like 됨)
        List<Dept> list = deptDao.findByDnameContaining(dname, pageReq);

//        TODO : 페이징 처리 로직
//         1) 총 테이블 개수 :
        long totalCount = deptDao.countByDname(dname);
//        TODO : 생성자 페이지 결과 객체(PageRes)
        PageRes pageRes = new PageRes(
                list,               // 검색 결과(부서) 배열
                pageReq.getPage(),  // 현재 페이지 번호
                totalCount,         // 총 테이블 건수
                pageReq.getSize()   // 1 페이지당 개수
        );

        return pageRes;
    }
}

 


 

부서 컨트롤러 : jsp 연동 : @Controller

서비스 객체 가져오기
전체 조회 : dname like 기능
페이징 요청 객체에 정보 저장
전체 조회 함수 호출
jsp 정보전달(부서배열, 페이징정보)

※ 로그 출력 테스트 해보기

@Slf4j
@Controller
@RequestMapping
@Autowired
@RequestParam - url?변수=값&변수2=값2(쿼리스트링 방식)
@GetMapping
더보기
package com.example.mybatisexam.controller.exam01;

import com.example.mybatisexam.model.common.PageReq;
import com.example.mybatisexam.model.common.PageRes;
import com.example.mybatisexam.model.vo.Dept;
import com.example.mybatisexam.service.exam01.DeptService;
import lombok.extern.slf4j.Slf4j;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * packageName : com.example.mybatisexam.controller.exam01
 * fileName : DeptController
 * author : L.DH
 * date : 2023-10-12
 * description : 전체 조회 : like 기능
 * 요약 : 부서 컨트롤러 : jsp 연동 : @Controller
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * ———————————————————————————————
 * 2023-10-12         L.DH         최초 생성
 */
@Slf4j
@Controller
@RequestMapping("/exam01")
public class DeptController {

    @Autowired
    DeptService deptService; // 서비스 객체 가져오기

    /** 전체 조회 : dname like 기능(+) */
//  todo: @RequestParam - url?변수=값&변수2=값2(쿼리스트링 방식)
    @GetMapping("/dept")
    public String getDeptAll(
            @RequestParam(defaultValue = "") String dname
            , @RequestParam(defaultValue = "0") int page
            , @RequestParam(defaultValue = "3") int size
            , Model model
    ){
//      todo: 페이징 요청 객체에 정보 저장
//        page : 현재페이지 번호, size : 1 페이지당 개수
        PageReq pageReq = new PageReq(page, size);

//      todo: 전체 조회 함수 호출
        PageRes<Dept> pageRes
                = deptService.findByDnameContaining(dname, pageReq);
//      todo: jsp 정보전달(부서배열, 페이징정보)
        model.addAttribute("dept", pageRes.getContent());             // 부서배열
        model.addAttribute("currentPage", pageRes.getNumber());       // 현재 페이지 번호
        model.addAttribute("totalItems", pageRes.getTotalElements()); // 전체 테이블 건수
        model.addAttribute("totalPages", pageRes.getTotalPages());    // 전체 페이지 개수
        model.addAttribute("startPage", pageRes.getStartPage());      // 현재 시작 페이지 번호
        model.addAttribute("endPage", pageRes.getEndPage());          // 현재 끝 페이지 번호

        log.debug(model.toString()); // 로그 출력

        return "exam01/dept/dept_all.jsp";
    }
}

 


 

dept_all.jsp
form 안에 input 태그의 value 값들이 -> springboot 함수로 전달됨

startPage : 1부터 시작
currentPage : 0부터 시작
더보기
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%-- header --%>
<jsp:include page="../../common/header.jsp"/>

<div class="container">
    <%-- TODO: 검색어 시작 --%>
    <%--  form 안에 input 태그의 value 값들이 -> springboot 함수로 전달됨--%>
    <form class="row g-3 justify-content-center" action="/exam01/dept">
        <div class="col-auto">
            <%--        TODO: 부서명 라벨--%>
            <label for="dname" class="visually-hidden">Dname</label>
            <%--        TODO: 검색창--%>
            <input type="text" class="form-control" id="dname" placeholder="dname" name="dname">
            <%--        TODO: hidden(숨김) -> 백엔드로 날라감 page = 0, size = 3--%>
            <input type="hidden" class="form-control" id="page" name="page" value="0">
            <input type="hidden" class="form-control" id="size" name="size" value="3">
        </div>
        <div class="col-auto">
            <button type="submit" class="btn btn-primary mb-3">Search</button>
        </div>
    </form>
    <%-- TODO: 검색어 끝 --%>

    <%-- TODO : 테이블 반복문 시작 --%>
    <table class="table">
        <thead class="thead-dark">
        <tr>
            <th scope="col">ID</th>
            <th scope="col">Name</th>
            <th scope="col">위치</th>
            <th scope="col">등록일자</th>
            <th scope="col">수정일자</th>
        </tr>
        </thead>
        <tbody>
        <c:forEach var="data" items="${dept}">
            <tr>
                <td>${data.dno}</td>
                <td>${data.dname}</td>
                <td>${data.loc}</td>
                <td>${data.insertTime}</td>
                <td>${data.updateTime}</td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
    <%-- TODO : 테이블 반복문 끝 --%>

    <%-- TODO : 페이지 번호 시작 --%>
    <div class="d-flex justify-content-center">
        <ul class="pagination">
            <%-- TODO : 첫 페이지 번호 --%>
            <%-- TODO : startPage : 1부터 시작 --%>
            <%-- TODO : currentPage : 0부터 시작 --%>
            <li class="page-item ${(startPage==1)? 'disabled': ''}">
                <a class="page-link" href="/exam01/dept?page=${startPage-2}&size=${3}">Previous</a>
            </li>
            <%-- TODO : 실제 페이지 번호들(반복) --%>
            <%--  사용법 : <c:forEach var="data" begin="시작값" end="끝값">반복문</c:forEach>--%>
            <c:forEach var="data" begin="${startPage}" end="${endPage}">
                <li class="page-item ${(currentPage+1==data)? 'active': ''}">
                    <a class="page-link" href="/exam01/dept?page=${data-1}&size=${3}">
                            ${data}
                    </a>
                </li>
            </c:forEach>
            <%-- TODO : 끝 페이지 번호--%>
            <li class="page-item ${(endPage==totalPages)? 'disabled': ''}">
                <a class="page-link" href="/exam01/dept?page=${endPage}&size=${3}">Next</a>
            </li>
        </ul>
        <%-- TODO : 페이지 번호 끝 --%>
    </div>

    <script>
        let obj = "${dept}";
        let obj2 = "${currentPage}";
        let obj3 = "${totalItems}";
        let obj4 = "${totalPages}";
        let obj5 = "${startPage}";
        let obj6 = "${endPage}";

        console.log("obj", obj);
        console.log("obj2", obj2);
        console.log("obj3", obj3);
        console.log("obj4", obj4);
        console.log("obj5", obj5);
        console.log("obj6", obj6);
    </script>

    <%-- footer --%>
    <jsp:include page="../../common/footer.jsp"/>

</div>
</body>
</html>