jsp

[jsp] 게시판 - 답변형게시판 생성,삭제 / 페이징처리 / 조회수증가

sian han 2022. 5. 13. 00:03

※ Connection pool

커넥션 풀 객체를 생성하고 관리하는 방법에는

커넥션 풀에서 커넥션 객체 생성하고 관리하는 것이 있다.

 

  - 커넥션 풀에 미리 여분의 커넥션을 만들어 놓고, 사용자의 요청이 있으면

    메모리에 이미 만들어져 있는 커넥션을 부여하고, 사용된 커넥션 객체는 다시 커넥션 풀로 회수

  - 반드시 컨테이너에 1개만 만들어지도록 패턴을 만들어야 함

 

 

※트랜잭션

  - 논리적인 작업단위

  - 여러가지 DML 작업들을 (데이터를 입력하는 DML) 하나의 단위로 묶어둔 것

  - 은행에서 계좌 입출금과 같은개념

         ex ) A통장에서 출금한 돈이 B통장에 정확히 입금이 확인되면 그때 거래를 성사시킴

 

  - 해당 트랜잭션 내에 있는 모든 DML이 성공해야 해당 트랜잭션이 성공하는 것이고

    만약 1개의 DML이라도 실패하면 전체가 실패하게 됨

  - 트랜잭션의 시작은 DML이고, 완료하려면 TCL, DCL, DDL이 입력 되면 됨

  - Savepoint를 설정하여, 설정한 savepoint까지 트랜잭션 관리 가능

 

 

 

▶ jsp에서 제공하는 트랜잭션 처리에 대한 메서드 : commit() rollback()

   

  - JDBC API의 Connection 객체는 commit(), rollback() 메서드를 제공

COMMIT [WORK] [TO SAVEPOINT savepoint_name];
ROLLBACK [WORK] [TO SAVEPOINT savepoint_name];

  = > COMMIT, ROLLBACK 모두 TRANSACTION 처리를 위해 존재

 

 

 

jsp는 기본적으로 Autocommit 으로 쿼리문이 Autocommit에 의해 자동으로 수행된다.

하지만 트랜잭션을 처리할 때는 오토커밋에 의해 자동으로 commit을 사용하면 안된다.

jsp 의 오토커밋이 자동으로 작동되지 못하게 하려면 setAutoCommit(false) 로 지정해야한다 ! 

 

 

 


※ 게시판만들기

 

▶ 답변형 게시판 만들기

 

하나의 글에 대한 답변과 또 연관된 다른 답변들을 하나의 그룹으로 묶어주어야 한다.

 

▷ 컬럼

[1] groupNo : 하나의 글에 연관된 모든 글들을 하나의 그룹으로 묶어주는 컬럼

       - ex ) 16번 글에대한 답글인 19,17,18 은 모두 하나의 groupNo 를 갖는다

[2] step : 글이 몇 번째 단계의 답변인지를 나타내는 컬럼

[3] sortNo : 글의 정렬 순서를 지정해주는 컬럼

 

 

SortNo 은 자신이 들어가야 할 위치(SortNo)보다 높은 SortNo 의 글이 이미 존재할 경우,

SortNo 가 높은 글들은 모두 +1을 해주어야 함

Update reboard set SortNo = SortNo + 1 
Where GroupNo = 120 and SortNo > 2

 

 

 

 

 

▶ 페이징처리

<%
int currentPage=1; //현재 페이지
//페이지 링크를 눌렀을때 처리
if(request.getParameter("currentPage")!=null){ 
currentPage= Integer.parseInt(request.getParameter("currentPage"));
}
//int totalRecord=17; //총 레코드(행) 수
int pageSize=5; //한 페이지에 보여줄 레코드(행) 수
int totalPage=(int)Math.ceil((float)totalRecord/pageSize); //총 페이지 수
int blockSize=10; //한 블럭에 보여줄 페이지 수
//[1]시작 [10]끝
int firstPage=currentPage-((currentPage-1)%blockSize); //[1], [11], [21],[31]...
int lastPage = firstPage + (blockSize-1); //[10], [20],[30],[40]...
//페이지당 ArrayList에서의 시작 index => 0, 5, 10, 15, 20…
int curPos = (currentPage-1)*pageSize;
//페이지당 글 리스트 시작 번호
int num = totalRecord-(curPos); %

PagingVO 를 생성해야 함

<div class="divPage">
<!-- 페이지 번호 추가 -->
<% if(pageVO.getFirstPage()>1){ %>
<a href="list.jsp?currentPage=<%=pageVO.getFirstPage()-
1%>&category=<%=category%>&keyword=<%=keyword%>">
<img src="../images/first.JPG" border="0">
</a>
<% }//if %>
<%
//[1][2][3][4]...[10]
//[11][12][13][14]...[20]
for(int i=pageVO.getFirstPage(); i<=pageVO.getLastPage();i++){
if(i > pageVO.getTotalPage()) break;
//현재페이지인 경우 링크걸지 말고, 색상 지정
if(i==pageVO.getCurrentPage()){%>
<span style="color:blue;font-weight:bold"><%=i %></span>
<% }else{%>
<a 
href="list.jsp?currentPage=<%=i%>&category=<%=category%>&keyword=<%=keyword%>">
[<%=i %>]
</a>
<% }//if
}//for %>
<%if(pageVO.getLastPage()<pageVO.getTotalPage()){ %>
<a 
href="list.jsp?currentPage=<%=pageVO.getLastPage()+1%>&category=<%=category%>&keyword=<%=keyword%>"
>
<img src="../images/last.JPG" border="0">
</a>
<%}//if %>
<!-- 페이지 번호 끝 -->
</div>

 

 

 

 

 

▶ 삭제하기 - flag 처리

 

1. 답변이 있는 원본글인 경우에는 레코드를 삭제하지 말고 DelFlag = “Y” 로 update 한다

 

    deleteReboard 저장 프로시저 생성

                                   프로시저 : 특정 기능을 수행하지만 값을 반환하지는 않는 서브프로그램

                                                    ex ) pd2테이블에 입력하는 프로시저 pd2_insert

create or replace procedure deleteReboard
(m_no number,
m_step number,
m_groupno number)
is
cnt number;
begin
--원본글인 경우
if m_step = 0 then 
--답변글이 존재하는지 체크
select count(*) into cnt from reboard
where groupNo = m_groupno;
--답변글이 존재하는 경우
if cnt >1 then
update reboard set DelFlag = 'Y'
where no = m_no; 
else --답변글이 없는 경우
delete reboard where no=m_no; 
end if; 
else --답변글 자체인 경우
delete reboard where no=m_no;
end if;
commit;

 

  ▽ deleteReboard 저장 프로시저 사용

exec deleteReboard(5, 1, 5)

 

 

2. list 페이지에서, DelFlag를 조회해 와서 값이 “Y”이 면 제목에 링크 걸지 말고, font color를 회색으로 보여 준다

 

 

 

 

 

▶ 조회수 증가

list 에서 글 제목을 click 하면 바로 detail 로 가는것이아니라

countUpdate 를 거쳐 readcount 컬럼의 값을 1 증가시켜준 후 detail 페이지로 넘어간다

list - countUpdate - detail 

 

 1. 리스트에서 글제목클릭하면 countUpdate 로 이동

 

<a href="countUpdate.jsp?no=<%=vo.getNo()%>">
<%=Utility.cutString(vo.getTitle(), 35) %></a>

 

2. countUpdate 에서 dao 객체 생성하며 메서드 불러옴

    메서드가 1이상의 값을 반환하면 detail 페이지로 이동

<body>
<%	
	//list.jsp에서 제목 클릭하면 get방식으로 이동
	//=> http://localhost:9090/herbmall/reBoard/countUpdate.jsp?no=6
	//1
	String no=request.getParameter("no");
	if(no==null || no.isEmpty()){%>
		<script type="text/javascript">
			alert('잘못된 url입니다.');
			location.href="list.jsp";
		</script>
	
		<%	return;
	}
	
	//2
	ReBoardDAO dao = new ReBoardDAO();
	
	try{
		int cnt=dao.updateCount(Integer.parseInt(no));
		//3
		if(cnt>0){
			response.sendRedirect("detail.jsp?no="+no);
		}else{%>
			<script type="text/javascript">
				alert("조회수 증가 실패!");
				history.back();
			</script>	
	<%	}
	}catch(SQLException e){
		e.printStackTrace();
	}
	
%>
</body>

    - 해당메서드 : readcount+=1

public int updateCount(int no) throws SQLException {
		Connection con=null;
		PreparedStatement ps=null;

		try {
			con=pool.getConnection();

			String sql="update reBoard set readcount=readcount+1"
		               + " where no=?";
			ps=con.prepareStatement(sql);
			ps.setInt(1, no);

			int cnt=ps.executeUpdate();
			System.out.println("조회수 증가 결과 cnt="+cnt+", 매개변수 no="+no);

			return cnt;
		}finally {
			pool.dbClose(ps, con);
		}
	}

 

 

 

 

 

 

 

 

 

 

 

 

 

++ 22.06.07 추가

 

reboard.xml

  => new.gif 를 위한 작업

	<select id="selectAll" parameterType="searchVo" resultType="reBoardVo">
		select *
		from
		(
		    select rownum as RNUM, A.*
		    from
		    (
		        select B.*,(sysdate-regdate)*24 as dataTerm
		        from reBoard B
		        <include refid="searchWhere"></include>
		        order by groupNo desc, sortNo
		    )A
		)
		where RNUM>#{firstRecordIndex} 
		<![CDATA[
		and RNUM<=#{firstRecordIndex} + #{recordCountPerPage} ]]>
	</select>

이때 생기는 dataTerm 컬럼을 vo 에 넣어줘야 정상작동 됨 !  

 

 

 

 

 

Spring boot 답변형 게시판 list

 

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE HTML>
<html lang="ko">
<head>
<title>자료실 글 목록 - 허브몰</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="<c:url value='/css/mainstyle.css'/>" />
<link rel="stylesheet" type="text/css" href="<c:url value='/css/clear.css'/>" />
<link rel="stylesheet" type="text/css" href="<c:url value='/css/formLayout.css'/>" />
<link rel="stylesheet" type="text/css" href="<c:url value='/css/mystyle.css'/>" />

<script type="text/javascript" src="<c:url value='/js/jquery-3.6.0.min.js'/>"></script>
<script type="text/javascript">	
	$(function(){
		$('.divList table.box2 tbody tr').hover(function(){
			$(this).css('background','lightblue');
		}, function(){
			$(this).css('background','');
		});
	});
	
	//페이지 번호 클릭시 실행할 함수
	function pageProc(curPage){
		$('input[name=currentPage]').val(curPage);
		$('form[name=frmPage]').submit();
	}
</script>
<style type="text/css">
	body{
		padding:5px;
		margin:5px;
	 }	
</style>	
</head>	
<body>
<h2>자료실</h2>
<c:if test="${!empty param.searchKeyword }">
	<p>검색어 : ${param.searchKeyword}, ${pagingInfo.totalRecord} 건 검색되었습니다.</p>
</c:if>
<!-- 페이징 처리를 위한 form -->
<form action="<c:url value='/reBoard/list.do'/>" 
	method="post" name="frmPage">
	<input type="text" name="searchKeyword" value="${param.searchKeyword }">
	<input type="text" name="searchCondition" 
		value="${param.searchCondition }">
	<input type="text" name="currentPage" >	
</form>

<div class="divList">
<table class="box2"
	 	summary="자료실에 관한 표로써, 번호, 제목, 작성자, 작성일, 조회수에 대한 정보를 제공합니다.">
	<caption>자료실</caption>
	<colgroup>
		<col style="width:10%;" />
		<col style="width:50%;" />
		<col style="width:15%;" />
		<col style="width:15%;" />
		<col style="width:10%;" />		
	</colgroup>
	<thead>
	  <tr>
	    <th scope="col">번호</th>
	    <th scope="col">제목</th>
	    <th scope="col">작성자</th>
	    <th scope="col">작성일</th>
	    <th scope="col">조회수</th>
	  </tr>
	</thead> 
	<tbody>  
	<c:if test="${empty list }">
		<tr>
			<td colspan="5" class="align_center">해당 글이 존재하지 않습니다.</td>
		</tr>
	</c:if>
	<c:if test="${!empty list }">		
		<!--게시판 내용 반복문 시작  -->
		<c:forEach var="vo" items="${list }">
	    	<tr  style="text-align:center">
				<td>${vo.no}</td>
				<td style="text-align:left">
				<c:if test="${vo.delFlag=='Y'}">
                	<span style="color:gray">삭제된 글입니다.</span>
                </c:if>
               
                <c:if test="${vo.delFlag!='Y'}">
					<!-- 답변글인 경우 단계별로 화살표 이미지 보여주기 -->
					<c:if test="${vo.step>0}">
	                     <c:forEach var="i" begin="1" end="${vo.step}">
	                        &nbsp;
	                     </c:forEach>
	                     <img src='<c:url value="/images/re.gif"/>' 
	                     	alt="화살표 이미지">
	               	</c:if>
					
					<!-- 파일이 첨부된 경우 file 이미지 보여주기 -->
					<c:if test="${!empty vo.fileName }">
	                     <img src='<c:url value="/images/file.gif"/>'>
	                </c:if>
	                
					<a href
					="<c:url value='/reBoard/countUpdate.do?no=${vo.no}'/>">
						<c:if test="${fn: length(vo.title)>30}">
			                  ${fn:substring(vo.title,0,30) }...            
			            </c:if>
			            <c:if test="${fn: length(vo.title)<=30}">
			                  ${vo.title }            
			            </c:if>
					</a>
					
					<!-- 24시간 이내의 글인 경우 new 이미지 보여주기 -->
					<c:if test="${vo.dateTerm<24 }">
	                	<img src="<c:url value='/images/new.gif'/>" 
	                		alt="new이미지"/>
	                </c:if> 
	            </c:if>      
				</td>					
				<td>${vo.name}</td>
				<td>
					<fmt:formatDate value="${vo.regdate}" pattern="yyyy-MM-dd"/>
				</td>
				<td>${vo.readcount}</td>		
			</tr>			
		</c:forEach>	 
		<!--반복처리 끝  -->
	 </c:if>
	 </tbody>
</table>	   
</div>
<div class="divPage">
	<!-- 이전블럭으로 이동 -->
	<c:if test="${pagingInfo.firstPage>1 }">	
		<%-- <a href
='<c:url value="/reBoard/list.do?currentPage=${pagingInfo.firstPage-1}&searchCondition=${param.searchCondition}&searchKeyword=${param.searchKeyword}"/>'> --%>
		<a href="#" onclick="pageProc(${pagingInfo.firstPage-1})">
			<img src='<c:url value="/images/first.JPG"/>'>
		</a>	
	</c:if>
	<!-- 페이지 번호 추가 -->						
	<!-- [1][2][3][4][5][6][7][8][9][10] -->
	<c:forEach var="i" begin="${pagingInfo.firstPage }" 
		end="${pagingInfo.lastPage }">		
		<c:if test="${i==pagingInfo.currentPage }">
			<span style="color: blue;font-weight: bold;font-size: 1em">
				${i }</span>
		</c:if>
		<c:if test="${i!=pagingInfo.currentPage }">			
				<%-- <a href
='<c:url value="/reBoard/list.do?currentPage=${i}&searchCondition=${param.searchCondition}&searchKeyword=${param.searchKeyword}"/>'> --%>
			<a href="#" onclick="pageProc(${i})">	
				[${i}]
			</a>
		</c:if>		
	</c:forEach>
	<!--  페이지 번호 끝 -->
	
	<!-- 다음 블럭으로 이동 -->
	<c:if test="${pagingInfo.lastPage < pagingInfo.totalPage }">	
		<%-- <a href
='<c:url value="/reBoard/list.do?currentPage=${pagingInfo.lastPage+1}&searchCondition=${param.searchCondition}&searchKeyword=${param.searchKeyword}"/>'> --%>
		<a href="#" onclick="pageProc(${pagingInfo.lastPage+1})">
			<img src='<c:url value="/images/last.JPG"/>'>
		</a>	
	</c:if>
</div>
<div class="divSearch">
   	<form name="frmSearch" method="post" 
   		action='<c:url value="/reBoard/list.do"/>'>
        <select name="searchCondition">
            <option value="title" 
            	<c:if test="${param.searchCondition=='title' }">
            		selected="selected"
            	</c:if>
            >제목</option>
            <option value="content" 
            	<c:if test="${param.searchCondition=='content' }">
            		selected="selected"
            	</c:if>
            >내용</option>
            <option value="name" 
            	<c:if test="${param.searchCondition=='name' }">
            		selected="selected"
            	</c:if>
            >작성자</option>
        </select>   
        <input type="text" name="searchKeyword" title="검색어 입력"
        	value="${param.searchKeyword}">   
		<input type="submit" value="검색">
    </form>
</div>

<div class="divBtn">
    <a href='<c:url value="/reBoard/write.do"/>' >글쓰기</a>
</div>

</body>
</html>