멋쟁이v의 개발일지

[Spring] Mybatis 상품관리(3) - 판매자별 상품 목록 / 페이징 본문

0년차/Spring

[Spring] Mybatis 상품관리(3) - 판매자별 상품 목록 / 페이징

멋쟁이v 2023. 8. 5. 17:56
728x90
320x100


상품 목록 조회

판매자별 상품 목록
1️⃣
Controller

➡️ 판매자별 상품 목록으로 주소 요청하는 메서드를 만든다.

  • [코드] GoodsController.java
    @GetMapping("/sellerList")
    public String sellerList(Model model) {
    
        model.addAttribute("title", "판매자별 상품목록 조회");
    
        return "goods/sellerList";
    }

2️⃣
html

➡️ 간단한 화면을 구성한다.

  • [코드] sellerList.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorate="~{layout/default}">
    <head>
        <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}">
    </head>
    
    <body>
    <th:block layout:fragment="customContents">
        <!--/* 검색(searchFragment2 select:상품코드, 상품명, 판매자 아이디, 판매자 이름, 판매자 이메일 */-->
        <!--/* 검색 주소: /goods/goodsList, HTTP method: get */-->
    
        <th:block th:insert="~{fragments/search :: searchFragment2}"></th:block>
    
        <br><br>
        <table>
            <thead>
            <tr>
                <th>판매자아이디</th>
                <th>판매자이름</th>
                <th>판매자이메일</th>
                <th>판매자주소</th>
                <th>상품코드</th>
                <th>상품명</th>
                <th>상품가격</th>
                <th>상품등록일자</th>
            </tr>
            </thead>
            <tbody>
    		     
            </tbody>
        </table>
    </th:block>
    </body>
    </html>

3️⃣
Mapper

➡️ 1) 판매자 1명은 여러개의 상품을 가질 수 있기 때문에 1:N 관계가 된다.

2) Member.java(dto) → private List<Goods> goodsList; 를 추가한다.

3) Mapper.xml → resumltMap에 Member와 Goods를 1:N 관계 맵핑해준다. (collection 사용)

4) 판매자별 상품 목록을 조회하는 쿼리를 만든다.

  • [코드] GoodsMapper.java
    // 판매자별 상품 목록 조회
    public List<Goods> getSellerList();
  • [코드] GoodsMapper.xml
    <resultMap type="Member" id="sellerResultMap">
        <!-- id태그는 조회시 테이블의 PK(기본키) -->
        <!-- result태그는 조회시 테이블의 일반컬럼 혹은 외래키 -->
        <id     column="m_id"       property="memberId"/>
        <result column="m_pw"       property="memberPw"></result>
        <result column="m_name"     property="memberName"></result>
        <result column="m_level"    property="memberLevel"></result>
        <result column="m_email"    property="memberEmail"></result>
        <result column="m_addr"     property="memberAddr"></result>
        <result column="m_reg_date" property="memberRegDate"></result>
    
        <!-- 관계맵핑 1:N collection -->
        <collection property="goodsList" ofType="Goods">
            <id column="g_code"             property="goodsCode"/>
            <result column="g_name"         property="goodsName"/>
            <result column="g_price"        property="goodsPrice"/>
            <result column="g_seller_id"    property="goodsSellerId"/>
            <result column="g_reg_date"     property="goodsRegDate"/>
        </collection>
    </resultMap>
    
    <select id="getSellerList" resultMap="sellerResultMap">
        /* 판매자별 상품 목록 조회 */
        SELECT
            m.m_id,
            m.m_name,
            m.m_email,
            m.m_addr,
            g.g_code,
            g.g_name,
            g.g_price,
            g.g_reg_date
        FROM
            tb_member AS m
            LEFT JOIN
            tb_goods AS g
            on
            m.m_id = g.g_seller_id
        WHERE
            m.m_level = 2;
    </select>

4️⃣
Service

➡️ Mapper에 선언된 메서드를 호출한다. 반환값을 List로 해준다.

  • [코드] GoodsService.java
    /**
     * 판매자별 상품 목록 조회
     * @return
     */
    public List<Goods> getSellerList() {
    
        List<Goods> sellerList = goodsMapper.getSellerList();
        log.info("sellerList : {}", sellerList);
        
        return sellerList;
    }

5️⃣
Controller, html

➡️ 1) 서비스단에 선언한 메서드를 호출하고, 반환값을 model에 담아서 화면에 전달한다.

2) 전달받은 데이터를 타임리프 구문으로 화면에 뿌려준다.

3) 타임리프 반복문(each) 의 상태값을 사용하여 테이블 css에 활용한다. ex) th:each=”l, status : ${}” → status.index, status.size

  • [코드] GoodsController.java
    /**
         * 판매자별 상품 목록
         * @param model
         * @return
         */
        @GetMapping("/sellerList")
        public String sellerList(Model model) {
    
            List<Goods> sellerList = goodsService.getSellerList();
    
            model.addAttribute("title", "판매자별 상품목록 조회");
            model.addAttribute("sellerList", sellerList);
    
            return "goods/sellerList";
        }
  • [코드] sellerList.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorate="~{layout/default}">
    <head>
        <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}">
    </head>
    
    <body>
    <th:block layout:fragment="customContents">
        <!--/* 검색(searchFragment2 select:상품코드, 상품명, 판매자 아이디, 판매자 이름, 판매자 이메일 */-->
        <!--/* 검색 주소: /goods/goodsList, HTTP method: get */-->
    
        <th:block th:insert="~{fragments/search :: searchFragment2}"></th:block>
    
        <br><br>
        <table>
            <thead>
            <tr>
                <th>판매자아이디</th>
                <th>판매자이름</th>
                <th>판매자이메일</th>
                <th>판매자주소</th>
                <th>상품코드</th>
                <th>상품명</th>
                <th>상품가격</th>
                <th>상품등록일자</th>
            </tr>
            </thead>
            <tbody>
            <th:block th:unless="${#lists.isEmpty(sellerList)}"
                      th:each="l : ${sellerList}">
                <th:block th:unless="${#lists.isEmpty(l.goodsList)}"
                          th:each="gl, status : ${l.goodsList}">
    
                    <tr>
                        <th:block th:if="${status.index == 0}">
                            <td th:text="${l.memberId}" th:rowspan="${status.size}"></td>
                            <td th:text="${l.memberName}" th:rowspan="${status.size}"></td>
                            <td th:text="${l.memberEmail}" th:rowspan="${status.size}"></td>
                            <td th:text="${l.memberAddr}" th:rowspan="${status.size}"></td>
                        </th:block>
    
                        <td th:text="${gl.goodsCode}"></td>
                        <td th:text="${gl.goodsName}"></td>
                        <td th:text="${gl.goodsPrice}"></td>
                        <td th:text="${gl.goodsRegDate}"></td>
                    </tr>
                </th:block>
    
    
                <th:block th:if="${#lists.isEmpty(l.goodsList)}">
                    <tr>
                        <td th:text="${l.memberId}"></td>
                        <td th:text="${l.memberName}"></td>
                        <td th:text="${l.memberEmail}"></td>
                        <td th:text="${l.memberAddr}"></td>
                        <td colspan="4">등록된 상품이 없습니다.</td>
                    </tr>
                </th:block>
            </th:block>
            <tr th:if="${#lists.isEmpty(sellerList)}">
                <td colspan="8">등록된 상품이 없습니다.</td>
            </tr>
            </tbody>
        </table>
    </th:block>
    </body>
    </html>

페이징

로그인 이력 조회
1️⃣
Controller

➡️ 1) 로그인 이력으로 주소 요청하는 메소드를 생성한다.

2) 요청 값으로 현재 페이지를 받는다. (기본값 1)

  • [코드] MemberController.java
    /**
     * 로그인 이력 조회
     * @param model
     * @return
     */
    @GetMapping("/loginHistory")
    public String loginHistory(Model model,
                               @RequestParam(value = "currentPage", required = false, defaultValue = "1") int currentPage) {
    
        model.addAttribute("title", "로그인 이력 조회");
        model.addAttribute("currentPage", currentPage);
    
        return "login/loginHistory";
    }

2️⃣
html

➡️ 주소 요청 시 간단한 화면을 보여주도록 해놓는다.

  • [코드] loginHistory.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorate="~{layout/default}">
    <head>
        <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}">
    </head>
    
    <body>
    <th:block layout:fragment="customContents">
        <!--/* 검색(searchFragment2 select:상품코드, 상품명, 판매자 아이디, 판매자 이름, 판매자 이메일 */-->
        <!--/* 검색 주소: /goods/goodsList, HTTP method: get */-->
    
        <th:block th:insert="~{fragments/search :: searchFragment2}"></th:block>
    
        <br><br>
        <table>
            <thead>
            <tr>
                <th>회원아이디</th>
                <th>회원이름</th>
                <th>회원이메일</th>
                <th>회원등급</th>
                <th>로그인일자</th>
                <th>로그아웃일자</th>
            </tr>
            </thead>
            <tbody>
    
            </tbody>
            <tfoot>
            
            </tfoot>
        </table>
    </th:block>
    </body>
    </html>

3️⃣
Service

페이징에 대한 비즈니스 로직을 작성한다.

➡️ 1) controller에서 넘겨준 currentPage 값을 매개변수로 받는 메서드를 생성한다.

2) 페이지에서 보여질 행의 갯수를 정한다.

3) 시작될 행의 인덱스 값을 계산한다.

→ (현재페이지 - 1) * 보여질 행의 갯수

4) 마지막 페이지를 계산한다.

→ 전체 행의 갯수 / 보여질 행의 갯수

5) Map에 3, 4 번의 값을 담아서 mapper에 선언한 메서드의 매개변수에 넣는다.

6) controller에 전달하기 위해 map 객체안의 data를 초기화 한다. → .clear();

7) 마지막 페이지와 로그인 이력을 조회한 데이터를 map에 담아서 리턴해준다.

  • [코드] MemberSerivce.java
    /**
     * 로그인 이력 페이징 처리
     * @param currentPage
     * @return
     */
    public Map<String, Object> getLoginHistory(int currentPage) {
    
        // 행을 5개씩 보여주겠다.
        int rowPerPage = 5;
    
        // 시작될 행의 인덱스 계산
        int startIdx = (currentPage - 1) * rowPerPage;
    
        // 마지막 페이지 계산
        double rowsCnt = memberMapper.getLoginCnt();
        int lastPage = (int) Math.ceil(rowsCnt / rowPerPage);
    
    
    		int startPageNum = 1;
        int endPageNum = (lastPage < 10) ? lastPage : 10;
    
        // 동적페이지 구성 (7page 부터)
        if(lastPage > 10 && currentPage > 6) {
            startPageNum = currentPage - 5;
            endPageNum = currentPage + 4;
            if(endPageNum >= lastPage) {
                startPageNum = lastPage - 9;
                endPageNum = lastPage;
            }
        }
    
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("startIdx", startIdx);
        paramMap.put("rowPerPage", rowPerPage);
    		paramMap.put("startPageNum", startPageNum);
        paramMap.put("endPageNum", endPageNum);
    
    		// 화면에 보여질 로그인 이력 데이터
        List<Map<String, Object>> loginHistoryList = memberMapper.getLoginHistoryList(paramMap);
        log.info("로그인 이력 : {}", loginHistoryList);
    
        // controller에 반환될 data
        paramMap.clear(); // map 객체 data 초기화
        paramMap.put("loginHistoryList", loginHistoryList);
        paramMap.put("lastPage", lastPage);
    
        return paramMap;
    }

4️⃣
Mapper

➡️ 1) 로그인 이력 테이블의 전체 행의 갯수를 구하는 쿼리를 작성한다.

2) 로그인 이력을 조회하는 쿼리를 작성한다.

→ order by, limit 사용

order by desc → 맨뒤 에서부터 차례로 정렬

limit 0, 5 → 인덱스 0부터 5개의 데이터만 보여준다.

  • [코드] MemberMapper.java
    // 로그인 이력 테이블의 전체 행의 갯수
    public int getLoginCnt();
    
    // 로그인 이력 조회
    public List<Map<String, Object>> getLoginHistoryList(Map<String, Object> paramMap);
  • [코드] MemberMapper.xml
    <select id="getLoginCnt" resultType="int">
        /* 로그인 이력 테이블의 전체 행의 갯수 */
        SELECT
            COUNT(*)
        FROM
            tb_login;
    </select>
    
    <select id="getLoginHistoryList" parameterType="map" resultType="map">
        /* 로그인 이력 조회 */
        SELECT
            l.login_date    AS loginDate,
            l.logout_date   AS logoutDate,
            m.m_id          AS memberId,
            m.m_name        AS memberName,
            ml.level_name   AS levelName,
            m.m_email       AS memberEmail
        FROM
            tb_login AS l
            INNER JOIN
            tb_member AS m
            ON
            l.login_id = m.m_id
            INNER JOIN
            tb_member_level AS ml
            ON
            m.m_level = ml.level_num
        ORDER BY l.logout_date DESC
        <if test="startIndex != null and startIndex > -1">
            LIMIT #{startIndex}, #{rowPerPage};
        </if>
    </select>

5️⃣
Controller, html

➡️ 1) 서비스단에서 선언한 메서드를 호출한다.

2) map으로 반환되어서 .get 메서드를 사용해서 data를 꺼내온다.

3) model에 담아서 화면에 전달한다.

4) footer 부분에 페이징하는 화면을 보여준다.

5) 반복문을 돌릴때 numbers 유틸리티를 사용한다.

→ .sequence(1, 5) : 1부터 5까지 1씩 증가

sequence(1, 5, 2) → 1부터 5까지 2씩 증가

  • [코드] MemberController.java
    /**
     * 로그인 이력 조회
     * @param model
     * @return
     */
    @GetMapping("/loginHistory")
    public String loginHistory(Model model,
                               @RequestParam(value = "currentPage", required = false, defaultValue = "1") int currentPage) {
    
        Map<String,Object> resultMap = memberService.getLoginHistory(currentPage);
        List<Map<String, Object>> loginHistoryList = (List<Map<String, Object>>) resultMap.get("loginHistoryList");
        int lastPage = (int) resultMap.get("lastPage");
        int startPageNum = (int) resultMap.get("startPageNum");
        int endPageNum = (int) resultMap.get("endPageNum");
    
        model.addAttribute("title", "로그인 이력 조회");
        model.addAttribute("currentPage", currentPage);
        model.addAttribute("loginHistoryList", loginHistoryList);
        model.addAttribute("lastPage", lastPage);
        model.addAttribute("startPageNum", startPageNum);
        model.addAttribute("endPageNum", endPageNum);
    
        return "login/loginHistory";
    }
  • [코드] loginHistory.html
    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorate="~{layout/default}">
    <head>
        <link rel="stylesheet" type="text/css" th:href="@{/css/table.css}">
    </head>
    
    <body>
    <th:block layout:fragment="customContents">
    
        <br><br>
        <table>
            <thead>
            <tr>
                <th>회원아이디</th>
                <th>회원이름</th>
                <th>회원이메일</th>
                <th>회원등급</th>
                <th>로그인일자</th>
                <th>로그아웃일자</th>
            </tr>
            </thead>
            <tbody>
            <th:block th:unless="${#lists.isEmpty(loginHistoryList)}"
                      th:each="l : ${loginHistoryList}">
                <tr>
                    <td th:text="${l.memberId}"></td>
                    <td th:text="${l.memberName}"></td>
                    <td th:text="${l.memberEmail}"></td>
                    <td th:text="${l.levelName}"></td>
                    <td th:text="${l.loginDate}"></td>
                    <td th:text="${l.logoutDate}"></td>
                </tr>
            </th:block>
            <tr th:if="${#lists.isEmpty(loginHistoryList)}">
                <td colspan="6">회원 로그인 이력을 조회할 수 없습니다.</td>
            </tr>
            </tbody>
            <tfoot>
            <tr>
                <td colspan="6">
                    <a th:href="@{/member/loginHistory}">[맨처음으로]</a>
                    <a th:if="${currentPage > 1}"
                       th:href="@{/member/loginHistory(currentPage=${currentPage-1})}">[이전]</a>
                    <label th:unless="${currentPage > 1}">[이전]</label>
    
                    <th:block th:each="num : ${#numbers.sequence(startPageNum, endPageNum)}">
                        <a th:if="${currentPage != num}"
                           th:href="@{/member/loginHistory(currentPage=${num})}"
                           th:text="|[${#numbers.formatInteger(num,2)}]|"></a>
                        <label th:if="${currentPage == num}"
                               th:text="|[${#numbers.formatInteger(num,2)}]|"></label>
                    </th:block>
    
                    <a th:if="${currentPage < lastPage}"
                       th:href="@{/member/loginHistory(currentPage=${currentPage+1})}">[다음]</a>
                    <label th:unless="${currentPage < lastPage}">[다음]</label>
                    <a th:href="@{/member/loginHistory(currentPage=${lastPage})}">[맨마지막으로]</a>
                </td>
            </tr>
            </tfoot>
        </table>
    </th:block>
    </body>
    </html>


tag : #Spring #Mybatis #Controller #Service #Mapper # SQL #each #sequence #페이징 #limit


Uploaded by N2T

728x90
320x100
Comments