상품 목록 조회
판매자별 상품 목록
➡️ 판매자별 상품 목록으로 주소 요청하는 메서드를 만든다.
[코드] GoodsController.java
@GetMapping("/sellerList") public String sellerList(Model model) { model.addAttribute("title", "판매자별 상품목록 조회"); 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> </tbody> </table> </th:block> </body> </html>
➡️ 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>
➡️ Mapper에 선언된 메서드를 호출한다. 반환값을 List로 해준다.
[코드] GoodsService.java
/** * 판매자별 상품 목록 조회 * @return */ public List<Goods> getSellerList() { List<Goods> sellerList = goodsMapper.getSellerList(); log.info("sellerList : {}", sellerList); return sellerList; }
➡️ 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) 로그인 이력으로 주소 요청하는 메소드를 생성한다.
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"; }
➡️ 주소 요청 시 간단한 화면을 보여주도록 해놓는다.
[코드] 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>
페이징에 대한 비즈니스 로직을 작성한다.
➡️ 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; }
➡️ 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>
➡️ 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