romworld

Spring 14 - 페이징 처리, 검색 기능, 계층형 쿼리, LOOP을 이용한 UPDATE 쿼리문 본문

Spring

Spring 14 - 페이징 처리, 검색 기능, 계층형 쿼리, LOOP을 이용한 UPDATE 쿼리문

inderrom 2023. 2. 1. 16:36

 

1. 페이징 처리를 구현해보자
2. 검색을 할 수 있다
3. 계층형 쿼리 실행

 

페이징 처리를 위한

<AticlePage.java> 파일을 만든다.

 

package kr.or.ddit.util;

import java.util.List;

import kr.or.ddit.vo.LprodVO;

//페이징 관련 정보 + 게시글 데이터
public class ArticlePage<T> {   
   // 전체글 수
   private int total;
   // 현재 페이지 번호
   private int currentPage;
   // 전체 페이지 수
   private int totalPages;
   // 시작 페이지 번호
   private int startPage;
   // 종료 페이지 번호
   private int endPage;
   // 블록 크기
   private int blockSize;
   // 글 데이터
   private List<T> content;
   
   // 생성자(Constructor) : 페이징 정보를 생성 및 초기화
   // size : 한 화면에 보여질 행의 수
   public ArticlePage(int total, int currentPage, int size
		   , List<T> content) {
      this.total = total;
      this.currentPage = currentPage;
      this.content = content;
      
      // 만약 글 수가 0일 때
      if(total==0) {
         totalPages = 0;
         startPage = 0;
         endPage = 0;
      }else {// 전체 글 수가 1 이상일 때
    	  // 1) 전체 페이지 구하기 시작 -----
         // 전체 페이지 수 = 전체글 수 / 한 화면에 보여질 목록의 행 수
         totalPages = total/size;
         
         // 전체글 수 % 한 화면에 보여질 목록의 행 수
         //   => 0이 아니면(나머지가 있다면)? 페이지를 1증가
         if(total%size>0) {
            totalPages++;
         }
         //1) 전체 페이지 구하기 끝 -----
         
         //2) 시작 페이지 구하기 시작 -----
         // 시작페이지 = 현재페이지/ 페이지크기*페이지크기 + 1
         // 1set : [1][2][3][4][5]다음
         // 2set : 이전[6][7][8][9][10]다음
         // 3set : 이전[11][12]
         startPage = currentPage / 5 * 5 + 1;
         
         // 현재페이지 % 페이지크기 => 0일 때 보정
         if(currentPage % 5 ==0) {
        	 // 페이지크기를 빼줌
        	 startPage -= 5;
         }
         //2) 시작 페이지 구하기 끝 -----
         
         //3) 종료 페이지 구하기 시작 -----
         // 종료페이지번호 = 시작페이지번호 + (페이지크기 -1)
         endPage = startPage + (5 - 1);
         
         // 종료페이지번호 > 전체페이지수보다 클 때 보정
         if(endPage > totalPages) {
        	 endPage = totalPages;
         }
         //3) 종료 페이지 구하기 끝 -----
      }
   }//end ArticlePage 생성자

	public int getTotal() {
		return total;
	}
	
	public void setTotal(int total) {
		this.total = total;
	}
	
	public int getCurrentPage() {
		return currentPage;
	}
	
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}
	
	public int getTotalPages() {
		return totalPages;
	}
	
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	
	public int getStartPage() {
		return startPage;
	}
	
	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}
	
	public int getEndPage() {
		return endPage;
	}
	
	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}
	
	public int getBlockSize() {
		return blockSize;
	}
	
	public void setBlockSize(int blockSize) {
		this.blockSize = blockSize;
	}
	
	public List<T> getContent() {
		return content;
	}
	
	public void setContent(List<T> content) {
		this.content = content;
	}
	
	// 데이터가 없으면 전체 글의 수는 0
	public boolean hasNoArticles() {
		return total == 0;
	}
   
	// 데이터가 있으면
	public boolean hasArticles() {
		return total > 0;
	}
}

 

 

<LprodController.java>

  • list 메서드를 페이징 처리하기위해 수정해주자.
  • 우선 전체 행의 수 구하기
  • 한 페이지에 보여질 행의 수 구하기
// 요청URI : /lprod/list?currentPage=3
	//1)요청파라미터 : currentPage=2
	//2)요청파라미터 : 없을 때 ---> 이 두 가지 처리를 다 해줘야 함
	// required : 필수있지 아닌지
	// defalutValue : 없을 때 기본 값 -> 1일경우 1페이지로 간주함
	// currentPage=2 파라미터의 타입은 String , 하지만 int 타입의 매개변수로 자동 형변환 가능 (스프링에서 지원함)
	@RequestMapping(value="/list",method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav,
			@RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage) {
		// 검색 조건
		Map<String, String> map = new HashMap<String, String>();
		
		//1) 전체 행의 수 구하기(total)
		int total = this.lprodService.getTotal();
		//2) 한 페이지에 보여질 행의 수(size)
		int size = 10;
		
		//map{"size":"10","currentPage":"1"}
		map.put("size", size+"");
		map.put("currentPage", currentPage+"");
		log.info("map :" + map);
		
		List<LprodVO> lprodVOList = this.lprodService.list(map);
		
		// 페이징 처리 전.. data
		//mav.addObject("data", lprodVOList);
		// 페이징 처리 후 
		mav.addObject("data"
			, new ArticlePage<LprodVO>(total, currentPage, size, lprodVOList));
		// jsp 경로
		// tiles-config.xml의
		// */*은 lprod/list와 {1}/{2}.jsp에 의해 lprod/list.jsp가 forwarding됨
		mav.setViewName("lprod/list");
		
		return mav;
	}

 

 

<lprod_SQL.xml>

  • 전체행의 수 구하는 쿼리
	<!-- 전체 행의 수 (total) -->
		<select id="getTotal" resultType="int">
			SELECT COUNT(*) FROM LPROD
		</select>

 

 

<lprodDao.java>

	// 전체 행의 수 (total)
	// <select id="getTotal" resultType="int">
	public int getTotal() {
		return this.sqlSessionTemplate.selectOne("lprod.getTotal");
	}

 

<lprodService.java>

	// 전체 행의 수 (total)
	public int getTotal();

<lprodServiceImpl.java>

// 전체 행의 수 (total)
	@Override
	public int getTotal() {
		return this.lprodDao.getTotal();
	}

 

 

리스트 매개변수에 Map 추가 dao,service,serviceImpl 전부 고쳐주자
그리고 list 쿼리문도 변경

 

<lprodDao.java>

public List<LprodVO> list(Map<String, String> map){
		return this.sqlSessionTemplate.selectList("lprod.list",map);
	}

<lprodService.java>

	// 메소드 시그니처
	public List<LprodVO> list(Map<String, String> map);

<lprodServiceImple.java>

@Override
	public List<LprodVO> list(Map<String, String> map){
		return this.lprodDao.list(map);
	}

 

우선 LPROD 테이블에 실습 데이터를 많이 집어넣어보자

 

<SQL Developer>

/* pl/sql
package
user function 사용자 정의함수
stored procedure 저장 프로시저
trigger
anonymous block 익명의 블록
*/
/
DECLARE
    MAX_ID NUMBER;
BEGIN
    --가장 마지막 번호 + 1
    SELECT NVL(MAX(LPROD_ID),0)+1 INTO  MAX_ID
    FROM   LPROD;
    
    FOR I IN 701..900 LOOP
        INSERT INTO LPROD(LPROD_ID, LPROD_GU, LPROD_NM)
        VALUES(I,
              'P' || TRIM(TO_CHAR(I,'000')),
              '새롬이'||I);
    END LOOP;
    COMMIT;
END;
/
--32를 P032로..
SELECT 'P' || TRIM(TO_CHAR(701,'000')) FROM DUAL;
--123을 P123으로..
SELECT 'P' || TRIM(TO_CHAR(900,'000')) FROM DUAL;

/* 서브쿼리
SELECT: SCALAR
FROM : INLINE VIEW
WHERE : NESTED
*/
/
WITH U AS(
    SELECT ROW_NUMBER() OVER (ORDER BY T.LPROD_ID DESC) RNUM
        ,T.*
    FROM
    (
        SELECT LPROD_ID, LPROD_GU, LPROD_NM
        FROM LPROD
    ) T
)
SELECT U.*
FROM U
WHERE U.RNUM BETWEEN 51 AND 60;
/

실행시켜보자

 

<여기서 만약  LPROD_NM의 내용을 반복문으로 수정하고 싶다면!>

DECLARE
BEGIN
    FOR I IN 701..900 LOOP
        update LPROD 
        set LPROD_NM = '새롬이'||(select substr(LPROD_NM,4) from lprod where LPROD_ID=I)
        where LPROD_ID=I;
    END LOOP;
END;

 

<lprod_SQL.xml>

  • list 쿼리문 수정
<select id="list" parameterType="hashMap" resultType="lprodVO">
		WITH U AS(
		    SELECT ROW_NUMBER() OVER (ORDER BY T.LPROD_ID DESC) RNUM
		        ,T.*
		    FROM
		    (
		        SELECT LPROD_ID, LPROD_GU, LPROD_NM
		        FROM LPROD
		    ) T
		)
		SELECT U.*
		FROM U
		WHERE U.RNUM BETWEEN (#{currentPage}*#{size})-(#{size}-1) 
				AND (#{currentPage}*#{size})
	</select>

 

<LprodVO.java>

  • private int rnum; 추가
  • getter/setter , toString 도 다시 정의한다.
	package kr.or.ddit.vo;
	
	import java.util.Arrays;
	import java.util.List;
	
	import org.springframework.web.multipart.MultipartFile;
	
	public class LprodVO {
		private int rnum;
		private int lprodId;
		private String lprodGu;
		private String lprodNm;
		private MultipartFile[] uploadFile;
		// 상품분류 : 첨부파일 = 1: N
		private List<AttachVO> attachVOList;
		
		public LprodVO() {
		}
	
		
		public LprodVO(int lprodId) {
			this.lprodId = lprodId;
		}
	
	
		public int getLprodId() {
			return lprodId;
		}
	
		public void setLprodId(int lprodId) {
			this.lprodId = lprodId;
		}
	
		public String getLprodGu() {
			return lprodGu;
		}
	
		public void setLprodGu(String lprodGu) {
			this.lprodGu = lprodGu;
		}
	
		public String getLprodNm() {
			return lprodNm;
		}
	
		public void setLprodNm(String lprodNm) {
			this.lprodNm = lprodNm;
		}
		
		
		public MultipartFile[] getUploadFile() {
			return uploadFile;
		}
	
	
		public void setUploadFile(MultipartFile[] uploadFile) {
			this.uploadFile = uploadFile;
		}
	
	
		public List<AttachVO> getAttachVOList() {
			return attachVOList;
		}
	
	
		public void setAttachVOList(List<AttachVO> attachVOList) {
			this.attachVOList = attachVOList;
		}
		
		
	
		public int getRnum() {
			return rnum;
		}
	
	
		public void setRnum(int rnum) {
			this.rnum = rnum;
		}
	
	
		@Override
		public String toString() {
			return "LprodVO [rnum=" + rnum + ", lprodId=" + lprodId + ", lprodGu=" + lprodGu + ", lprodNm=" + lprodNm
					+ ", uploadFile=" + Arrays.toString(uploadFile) + ", attachVOList=" + attachVOList + "]";
		}
	}

 

<list.jsp>

  • data를 고쳐주자
  • <c:forEach var="lprodVO" items="${data.content}" varStatus="stat">

<!-- 
1) 페이징 이전
List<LprodVO> lprodVOList
mav.addObject("data", lprodVOList);

2) 페이징 이후
mav.addObject("data", new ArticlePage<LprodVO>(total, currentPage, size, lprodVOList));

ArticlePage에서 List<LprodVO> lprodVOList 꺼내야한다. 
즉 private List<T> content; 를 꺼내오자
data는 ArticlePage이고
data.content는 ArticlePage의 content멤버변수

viv : 포이치 비브

stat.count : 1부터 시작
stat.index : 0부터 시작
-->
<c:forEach var="lprodVO" items="${data.content}" varStatus="stat">
<tr
<c:if test="${stat.count%2!=0}">class="odd"</c:if> 
<c:if test="${stat.count%2==0}">class="even"</c:if>
>
    <td class="sorting_1">${lprodVO.rnum}</td>
    <td>${lprodVO.lprodId}</td>
    <td>${lprodVO.lprodGu}</td>
    <td><a href="/lprod/detail?lprodGu=${lprodVO.lprodGu}">${lprodVO.lprodNm}</a></td>
</tr>
</c:forEach>

 

우선 재기동하고 실행시켜 보자

  • localhost/lprod/list?currentPage=7 
  • 숫자를 바꿔서 넣어보면 페이징 처리가 된 것을 볼 수 있다

 

페이지 이동하기

<list.jsp>

  • 현재페이지를 active 시킨다.
  • Previous, Next 없을 때 비활성화하기 (disabled)

<div class="row">
<div class="col-sm-12 col-md-5">
    <div class="dataTables_info" id="dataTable_info" role="status"
        aria-live="polite">Showing 1 to 10 of 57 entries</div>
</div>
<!-- EL태그 정리
    == : eq(equal)
    != : ne(not equal)
    < : lt(less than)
    > : gt(greater than)
    <= : le(less equal)
    >= : ge(greater equl) 

 -->
<div class="col-sm-12 col-md-7">
    <div class="dataTables_paginate paging_simple_numbers"
        id="dataTable_paginate">
        <ul class="pagination">
            <li class="paginate_button page-item previous
                <c:if test='${data.startPage <6}' >disabled</c:if>
            " id="dataTable_previous">
                <a href="/lprod/list?currentPage=${data.startPage-5}"
                aria-controls="dataTable" data-dt-idx="0" tabindex="0"
                class="page-link">Previous</a></li>
            <c:forEach var="pNo" begin="${data.startPage}" end="${data.endPage}">
            <li class="paginate_button page-item
                    <c:if test='${param.currentPage==pNo}'>active</c:if>
                     ">
            <a href="/lprod/list?currentPage=${pNo}"
                aria-controls="dataTable" data-dt-idx="1" tabindex="0"
                class="page-link">${pNo}</a></li>
            </c:forEach>
            <li class="paginate_button page-item next
                <c:if test='${data.endPage == data.totalPages}'>disabled</c:if>
            " id="dataTable_next">
            <a href="/lprod/list?currentPage=${data.startPage+5}" aria-controls="dataTable" data-dt-idx="7" tabindex="0"
                class="page-link">Next</a></li>
        </ul>
    </div>
</div>
</div>


BOOK도 페이징 처리 해보자!

<SQL Developer>에서 데이터를 추가해준다.

DECLARE
    MAX_ID NUMBER;
BEGIN
    SELECT NVL(MAX(BOOK_ID),0)+1 INTO MAX_ID
    FROM BOOK;
    
    FOR I IN 50..250 LOOP
        INSERT INTO BOOK(BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE, CONTENT)
        VALUES(I,'제목'||I,'소설',12000,SYSDATE,'내용'||I);
    END LOOP;
    COMMIT;
END;

 

 

 

<bookController.java>

  • LPROD와 마찬가지로 list메서드 수정 ( getTotal())
  • public int getTotal(String keyword)
	// 1)요청URI : /list
	//		요청파라미터 : {}
	// 2)요청URI : /list?keyword=새롬이
	//		요청파라미터 : {"keyword" :"새롬이"
	// 요청 파라미터 : {}
	// 방식 : get
	// 스프링에서 요청파라미터를 매개변수로 받을 수 있음
	//required=false 파라미터가 넘어오는 게 없을 수도 있다. 
	@RequestMapping(value="/list",method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav,
			@RequestParam(value="keyword", required=false) String keyword,
			@RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage) {
		
		// 검색 조건
		Map<String, String> map = new HashMap<String, String>();
		
		//1) 전체 행의 수 구하기 (total)
		int total = this.bookService.getTotal(keyword);
		//2) 한 페이지에 보여질 행의 수 (size)
		int size = 10;
		
		map.put("size", size+"");
		map.put("currentPage",currentPage+"");
		map.put("keyword", keyword);
		
		List<BookVO> bookVOList = this.bookService.list(map);
		
		log.info("bookVOList : " + bookVOList);
		//1) 페이징 전
		// 데이터
		//mav.addObject("data", bookVOList);
		//2)페이징 후
		mav.addObject("data", new ArticlePage<BookVO>(total, currentPage, size, bookVOList));
		
		// jsp(뷰) : book폴더에 있는 list.jsp를 forwarding(jsp를 해석, 컴파일하여 html로 리턴)
		mav.setViewName("book/list");
		
		return mav;
	}

 

dao, service 똑같이 map 파라미터를 바꿔주고 getTotal()메서드를 추가해준다.

 

<book_SQL.xml>

1. 전체 행의 수를 구하는 쿼리문

	<!-- 전체 행의 수 (total) -->
		<select id="getTotal" resultType="int">
			SELECT COUNT(*) FROM BOOK
		</select>

2. 검색기능은 중복으로 사용하기 때문에 따로 빼준다.

  	  <sql id="where">
  	  	<!-- 검색 -->
	  	<if test="keyword!=null and keyword!=''">
  			AND(
  				TITLE LIKE '%' || #{keyword} || '%'
  		  OR CATEGORY LIKE '%' || #{keyword} || '%'
  		  OR CONTENT  LIKE '%' || #{keyword} || '%'
  			)
  		</if>
  	  </sql>

3. list 와 getTotal 쿼리문에 include 한다.

  • <include refid="where"></include>
  • 이때 WHERE 1 =1 를 꼭 써주도록 한다.
  	<select id="list" parameterType="hashMap" resultType="bookVO">
  		WITH T AS(
    		SELECT ROW_NUMBER() OVER(ORDER BY BOOK_ID DESC) RNUM
   		 , BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE, CONTENT
    		FROM   BOOK
    		WHERE 1 = 1
  			<include refid="where"></include>
		)
		SELECT T.RNUM, T.BOOK_ID, T.TITLE, T.CATEGORY, T.PRICE
		     , T.INSERT_DATE, T.CONTENT 
		FROM   T
		WHERE T.RNUM BETWEEN (#{currentPage}*#{size})-(#{size}-1) 
				AND (#{currentPage}*#{size})
  	</select>
    
<!-- 전체 행의 수 (total) -->
		<select id="getTotal" parameterType="String" resultType="int">
			SELECT COUNT(*) FROM BOOK
			WHERE 1 = 1
			<include refid="where"></include>
		</select>

 

<list.jsp>

  • <c:forEach var="bookVO" items="${data.content}" varStatus="stat">  data.content를 해주어야만 리스트가 나온다.
  • 테이블 안에 tfoot 태그 작성
  • 꼭 tr , td안에 작성해준다.
  • keyword 검색시 페이징도 같이 처리해준다.
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery.3.6.0.js"></script>
<title>도서관리시스템</title>
</head>
<body>
	<!-- 검색 시작  -->
	<p>
		<!-- 
		action 속성 및 그 값이 생략 시 , 현재 URI(/list)를 재요청.
		method는 GET(form 태그의 기본 HTTP 메소드는 GET임)
		 -->
<!-- 	<form action="/list" method="get"> -->
		<form>
			<input type="text" name="keyword" value="${param.keyword}"
				placeholder="검색어를 입력해주세요." />
			<!--  /list?keyword=새롬이 -->
			<input type="submit" value="검색" />
		</form>
	</p>
	<!--  검색 끝 -->
	<p>
	<table border="1">
		<thead>
			<tr>
				<th>번호</th>
				<th>제목</th>
				<th>카테고리</th>
				<th>가격</th>
			</tr>
		</thead>
		<!-- 
			forEach태그? 배열(String[][],int[][],Collection(List,Set)또는
				Map(HashTable, HashMap, SortedMap)에 저장되어 있는 값들을
				순차적으로 처리할 때 사용함. 자바의 for, do-while을 대신하여 사용
			
			data : mav.addObject("data", bookVOList)	
			data : List<BookVO>
			var : 변수(variable)
			items : 아이템(배열, Collection, Map)
			varStatus : 루프 정보르 담은 객체
				- index : 루프(반복) 실행 시 현재 인덱스(0부터 시작)
				- count : 실행 회수(1부터 시작, 보통 행번호를 출력)
		 -->
		<tbody>
			<c:forEach var="bookVO" items="${data.content}" varStatus="stat">
			<tr>
				<td>${bookVO.rnum}</td>
				<td><a href="/detail?bookId=${bookVO.bookId}">${bookVO.title}</a></td>
				<td>${bookVO.category}</td>
				<td><fmt:formatNumber type="number" maxFractionDigits="3"
					value="${bookVO.price}" /></td>
			</tr>
			</c:forEach>
		</tbody>
		<tfoot>
			<tr>
				<td colspan="4">
				<!-- 페이지 넘기기 -->
					<ul class="pagination">
						<li class="paginate_button page-item previous
							<c:if test='${data.startPage <6}' >disabled</c:if>
						" id="dataTable_previous">
							<a href="/list?currentPage=${data.startPage-5}"
							aria-controls="dataTable" data-dt-idx="0" tabindex="0"
							class="page-link">Previous</a></li>
						<c:forEach var="pNo" begin="${data.startPage}" end="${data.endPage}">
						<li class="paginate_button page-item
								<c:if test='${param.currentPage==pNo}'>active</c:if>
								 ">
						<a href="/list?currentPage=${pNo}"
							aria-controls="dataTable" data-dt-idx="1" tabindex="0"
							class="page-link">${pNo}</a></li>
						</c:forEach>
						<li class="paginate_button page-item next
							<c:if test='${data.endPage == data.totalPages}'>disabled</c:if>
						" id="dataTable_next">
						<a href="/list?currentPage=${data.startPage+5}" aria-controls="dataTable" data-dt-idx="7" tabindex="0"
							class="page-link">Next</a></li>
					</ul>
				<!-- 페이지 넘기기 끝 -->
				
				</td>
			
			</tr>
		</tfoot>
	</table>
	</p>
	
	<p>
		<a href="/create">책 생성</a>
	</p>
</body>
</html>

 


LPROD 에도 검색 기능을 만들자!

Book에서 했던 방식과 똑같이 keyword를 사용한다.
dao,service에서는 getTotal(String keyword) 파라미터에 keyword를 넣어준다.

 

<LprodController.java>

// 요청URI : /lprod/list?currentPage=3
	//1)요청파라미터 : currentPage=2
	//2)요청파라미터 : 없을 때 ---> 이 두 가지 처리를 다 해줘야 함
	// required : 필수있지 아닌지
	// defalutValue : 없을 때 기본 값 -> 1일경우 1페이지로 간주함
	// currentPage=2 파라미터의 타입은 String , 하지만 int 타입의 매개변수로 자동 형변환 가능 (스프링에서 지원함)
	@RequestMapping(value="/list",method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav,
			 @RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage,
			 @RequestParam(value="size", required=false, defaultValue="10") int size,
			 @RequestParam(value="keyword", required=false) String keyword
			 ) {
		//검색 조건
		Map<String,String> map = new HashMap<String, String>();
				
		//1) 전체 행의 수 구하기(total)
		int total = this.lprodService.getTotal(keyword);
		//3) 한 페이지에 보여질 행의 수(size)
//		int size = 10;
		
		//map{"size":"10","currentPage":"1","keyword":""}
		map.put("size", size+"");
		map.put("currentPage", currentPage+"");
		map.put("keyword", keyword);
		log.info("map : " + map);
		
		List<LprodVO> lprodVOList = this.lprodService.list(map);
		
		//페이징 처리 전.. data
		//mav.addObject("data", lprodVOList);
		//페이징 처리 후..
		mav.addObject("data"
			, new ArticlePage<LprodVO>(total, currentPage, size, lprodVOList));
		//jsp경로
		//tiles-config.xml의 
		// */*은 lprod/list와 {1}/{2}.jsp에 의해 lprod/list.jsp가 forwarding됨
		mav.setViewName("lprod/list");
		
		return mav;
	}

 

<lprod_SQL.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">
  <!-- namespace : xml 파일이 여러개일 수 있으므로
  		이를 구별하기 위한 식별 용도로 사용 -->
  <mapper namespace="lprod">
  
	<!-- LPROD : ATTACH : 1 : N -->
	<resultMap type="lprodVO" id="lprodMap">
		<result property="lprodGu" column="LPROD_GU"/>
		<result property="lprodId" column="LPROD_ID" />
		<result property="lprodNm" column="LPROD_NM" />
		<collection property="attachVOList" resultMap="attachMap"></collection>
	</resultMap>
	
	<resultMap type="attachVO" id="attachMap">
		<result property="etpId" column="ETP_ID"/>
		<result property="seq" column="SEQ"/>
		<result property="filename" column="FILENAME"/>
		<result property="filesize" column="FILESIZE"/>
		<result property="thumbnail" column="THUMBNAIL"/>
		<result property="regdate" column="REGDATE"/>
	</resultMap>
	
	<sql id="where">
  	  	<!-- 검색 -->
	  	<if test="keyword!=null and keyword!=''">
  			AND(
  		  	 LPROD_GU LIKE '%' || #{keyword} || '%'
  		  OR LPROD_NM  LIKE '%' || #{keyword} || '%'
  			)
  		</if>
  	  </sql>
	                
	                                                                                                                                                                                                                                                              
	<!-- //map{"size":"10","currentPage":"1"} -->
	<select id="list" parameterType="hashMap" resultType="lprodVO">
		WITH U AS(
		    SELECT ROW_NUMBER() OVER (ORDER BY T.LPROD_ID DESC) RNUM
		        ,T.*
		    FROM
		    (
		        SELECT LPROD_ID, LPROD_GU, LPROD_NM
		        FROM LPROD
		        WHERE 1 = 1
  			<include refid="where"></include>
		    ) T
		)
		SELECT U.*
		FROM U
		WHERE U.RNUM BETWEEN (#{currentPage}*#{size})-(#{size}-1) 
				AND (#{currentPage}*#{size})
	</select>
	
	<!-- 상품분류 자동생성 -->
	<select id="getLprodId" resultType="int">
		SELECT NVL(MAX(TO_NUMBER(LPROD_ID)),0)+1 FROM LPROD
	</select>
	
	<!-- 상품분류코드 자동생성 -->
	<select id="getLprodGu" resultType="String">
		SELECT SUBSTR(MAX(LPROD_GU),1,1) || (SUBSTR(MAX(LPROD_GU),2) + 1)
		FROM LPROD
	</select>
	
	<!-- 상품분류 정보를 입력 -->
	<insert id="createPost" parameterType="lprodVO">
		<selectKey order="BEFORE" keyProperty="lprodId" resultType="int">
			SELECT NVL(MAX(LPROD_ID),0)+1 FROM LPROD
		</selectKey>
		INSERT INTO LPROD(LPROD_ID, LPROD_GU, LPROD_NM)
		VALUES(#{lprodId},(SELECT SUBSTR(MAX(LPROD_GU),1,1) || (SUBSTR(MAX(LPROD_GU),2) + 1)
		FROM LPROD),#{lprodNm})
	</insert>
	
	<!-- 상품분류 상세보기 -->
	<select id="detail" parameterType="lprodVO" resultMap="lprodMap">
		SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
        	B.SEQ, B.FILENAME, B.FILESIZE, B.THUMBNAIL, B.REGDATE, B.ETP_ID
		FROM LPROD A LEFT JOIN ATTACH B ON(A.LPROD_GU = B.ETP_ID)
		WHERE A.LPROD_GU = #{lprodGu}
	</select>
	
	<!-- 상품분류 정보변경 -->
	<update id="updatePost" parameterType="lprodVO">
		UPDATE LPROD
		SET    LPROD_NM = #{lprodNm} 
		WHERE LPROD_GU =  #{lprodGu}
	</update>
	
	<!-- 상품분류 정보 삭제 -->
	<delete id="deletePost" parameterType="lprodVO">
		DELETE FROM LPROD
		WHERE LPROD_GU = #{lprodGu}
	</delete>
	
	<!-- 첨부파일 등록  -->
	<insert id="uploadFormAction" parameterType="attachVO">
		<selectKey resultType="int" order="BEFORE" keyProperty="seq" >
			SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
		</selectKey>
		INSERT INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE)
		VALUES(
		    #{seq},#{filename},#{filesize},#{thumbnail},SYSDATE
		)
	</insert>
	
	<!-- 다중 insert 시 update 태그를 사용
		파라미터 : List<AttachVO attachVOList
		insert, update, delete의 경우 resultType은 생략
		separator=" " : 공백으로 구분
		-->
		<update id="createPostAttach" parameterType="java.util.List">
		<!-- 
		order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
		 -->
			<selectKey resultType="int" order="BEFORE" keyProperty="seq">
				SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
			</selectKey>
			<!-- index : 반복 시 index값. 0부터 시작. -->
		<foreach collection="list" item="attachVO"
				open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
				index="idx" >
				INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID) 
				VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},#{attachVO.thumbnail},sysdate,
					#{attachVO.etpId})
			</foreach>
		</update>
		
		<!-- 전체 행의 수 (total) -->
		<select id="getTotal" resultType="int">
			SELECT COUNT(*) FROM LPROD
			WHERE 1 = 1
			<include refid="where"></include>
		</select>
  </mapper>

 

<list.jsp>

option value 고정
검색어 고정
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!-- Page Heading -->
<h1 class="h3 mb-2 text-gray-800">Tables</h1>
<p class="mb-4">
	DataTables is a third party plugin that is used to generate the demo
	table below. For more information about DataTables, please visit the <a
		target="_blank" href="https://datatables.net">official DataTables
		documentation</a>.
</p>

<!-- DataTales Example -->
<div class="card shadow mb-4">
	<div class="card-header py-3">
		<h6 class="m-0 font-weight-bold text-primary">DataTables Example</h6>
	</div>
	<div class="card-body">
		<div class="table-responsive">
			<div id="dataTable_wrapper" class="dataTables_wrapper dt-bootstrap4">
					<div class="row" style="width:100%;">
				<!--  action="/lprod/list" method="get" 생략가능 
				요청URI : /lprod/list?keyword=새롬이&currentPage=2&size=10
				-->
					<form name="frm" id="frm" action="/lprod/list" method="get"
					 style="width:100%;">
					<div class="col-sm-12 col-md-6" style="float:left;">
						<div class="dataTables_length" id="dataTable_length">
							<label>Show <select name="size"
								aria-controls="dataTable"
								class="custom-select custom-select-sm form-control form-control-sm">
								<option <c:if test="${param.size == 10}">selected</c:if>value="10" >10</option>
									<option value="25" <c:if test="${param.size == 25}">selected</c:if>>25</option>
									<option value="50" <c:if test="${param.size == 50}">selected</c:if>>50</option>
									<option value="100" <c:if test="${param.size == 100}">selected</c:if>>100</option></select> entries
							</label>
						</div>
					</div>
					<div class="col-sm-12 col-md-6" style="float:right;">
						<div id="dataTable_filter" class="dataTables_filter">
							<label>Search:<input type="search"
								name="keyword" 
								value="${param.keyword}"
								class="form-control form-control-sm" 
								placeholder="검색어를 입력해주세요"
								aria-controls="dataTable" /></label>
							<button type="submit" class="btn btn-primary">검색</button>
						</div>
					</div>
					</form>
				</div>
				<div class="row">
					<div class="col-sm-12">
						<table class="table table-bordered dataTable" id="dataTable"
							width="100%" cellspacing="0" role="grid"
							aria-describedby="dataTable_info" style="width: 100%;">
							<thead>
								<tr role="row">
									<th class="sorting sorting_asc" tabindex="0"
										aria-controls="dataTable" rowspan="1" colspan="1"
										aria-sort="ascending"
										aria-label="순번: activate to sort column descending"
										style="width: 64.75px;">순번</th>
									<th class="sorting" tabindex="0" aria-controls="dataTable"
										rowspan="1" colspan="1"
										aria-label="상품분류 아이디: activate to sort column ascending"
										style="width: 80.6094px;">상품분류 아이디</th>
									<th class="sorting" tabindex="0" aria-controls="dataTable"
										rowspan="1" colspan="1"
										aria-label="상품분류 구분: activate to sort column ascending"
										style="width: 55.7969px;">상품분류 구분</th>
									<th class="sorting" tabindex="0" aria-controls="dataTable"
										rowspan="1" colspan="1"
										aria-label="상품분류 명: activate to sort column ascending"
										style="width: 30.1719px;">상품분류 명</th>
								</tr>
							</thead>
							<tbody>
							<!-- 
								1) 페이징 이전
								List<LprodVO> lprodVOList
								mav.addObject("data", lprodVOList);
								
								2) 페이징 이후
								mav.addObject("data", new ArticlePage<LprodVO>(total, currentPage, size, lprodVOList));
								
								ArticlePage에서 List<LprodVO> lprodVOList 꺼내야한다. 
								즉 private List<T> content; 를 꺼내오자
								data는 ArticlePage이고
								data.content는 ArticlePage의 content멤버변수
								
								viv : 포이치 비브
								
								stat.count : 1부터 시작
								stat.index : 0부터 시작
							 -->
							 <c:forEach var="lprodVO" items="${data.content}" varStatus="stat">
								<tr
								<c:if test="${stat.count%2!=0}">class="odd"</c:if> 
								<c:if test="${stat.count%2==0}">class="even"</c:if>
								>
									<td class="sorting_1">${lprodVO.rnum}</td>
									<td>${lprodVO.lprodId}</td>
									<td>${lprodVO.lprodGu}</td>
									<td><a href="/lprod/detail?lprodGu=${lprodVO.lprodGu}">${lprodVO.lprodNm}</a></td>
								</tr>
							</c:forEach>
							</tbody>
						</table>
					</div>
				</div>
				<div class="row">
					<div class="col-sm-12 col-md-5">
						<div class="dataTables_info" id="dataTable_info" role="status"
							aria-live="polite">Showing 1 to 10 of 57 entries</div>
					</div>
					<!-- EL태그 정리
						== : eq(equal)
						!= : ne(not equal)
						< : lt(less than)
						> : gt(greater than)
						<= : le(less equal)
						>= : ge(greater equl) 
					
					 -->
					<div class="col-sm-12 col-md-7">
						<div class="dataTables_paginate paging_simple_numbers"
							id="dataTable_paginate">
							<ul class="pagination">
								<li class="paginate_button page-item previous
									<c:if test='${data.startPage <6}' >disabled</c:if>
								" id="dataTable_previous">
								<!-- &keyword=${param.keyword} -->
									<a href="/lprod/list?currentPage=${data.startPage-5}&keyword=${param.keyword}"
									aria-controls="dataTable" data-dt-idx="0" tabindex="0"
									class="page-link">Previous</a></li>
								<c:forEach var="pNo" begin="${data.startPage}" end="${data.endPage}">
								<li class="paginate_button page-item
										<c:if test='${param.currentPage==pNo}'>active</c:if>
										 ">
								<a href="/lprod/list?currentPage=${pNo}&keyword=${param.keyword}"
									aria-controls="dataTable" data-dt-idx="1" tabindex="0"
									class="page-link">${pNo}</a></li>
								</c:forEach>
								<li class="paginate_button page-item next
									<c:if test='${data.endPage == data.totalPages}'>disabled</c:if>
								" id="dataTable_next">
								<a href="/lprod/list?currentPage=${data.startPage+5}&keyword=${param.keyword}" aria-controls="dataTable" data-dt-idx="7" tabindex="0"
									class="page-link">Next</a></li>
							</ul>
						</div>
					</div>
				</div>
				<div class="row">
				<!-- 요청URI : /lprod/create
					방식 : GET
				 -->
					<a href="/lprod/create" class="btn btn-primary btn-icon-split">
	                    <span class="icon text-white-50">
	                        <i class="fas fa-flag"></i>
	                    </span>
	                    <span class="text">상품분류 추가</span>
	                </a>
                </div>
			</div>
		</div>
	</div>
</div>

 

Comments