기록 해두자.
<%@ 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="fn" uri="<http://java.sun.com/jsp/jstl/functions>"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<http://www.w3.org/TR/html4/loose.dtd>">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<%@ include file="/WEB-INF/include/include-header.jspf" %>
</head>
<body>
<table class="board_view">
<colgroup>
<col width="15%"/>
<col width="35%"/>
<col width="15%"/>
<col width="35%"/>
</colgroup>
<caption>게시글 상세</caption>
<tbody>
<tr>
<th scope="row">글 번호</th>
<td>${map.IDX }</td>
<th scope="row">조회수</th>
<td>${map.HIT_CNT }</td>
</tr>
<tr>
<th scope="row">작성자</th>
<td>${map.CREA_ID }</td>
<th scope="row">작성시간</th>
<td>${map.CREA_DTM }</td>
</tr>
<tr>
<th scope="row">제목</th>
<td colspan="3">${map.TITLE }</td>
</tr>
<tr>
<td colspan="4">${map.CONTENTS }</td>
</tr>
<tr>
<th scope="row">첨부파일</th>
<td colspan="3">
<c:choose>
<c:when test="${fn:length(list) >0 }"> <!-- boolean -->
<c:forEach var="row" items="${list}">
<input type="hidden" id="IDX" value="${row.IDX }">
<a href="#this" name="file">${row.ORIGINAL_FILE_NAME }</a>
(${row.FILE_SIZE }kb)
</c:forEach>
</c:when>
<c:otherwise>첨부된 파일이 없습니다.!</c:otherwise>
</c:choose>
</td>
</tr>
</tbody>
</table>
<a href="#this" class="btn" id="list">목록으로</a>
<a href="#this" class="btn" id="update">수정하기</a>
<a href="#this" class="btn" id="delete">삭제하기</a>
<!-- 이전 글 버튼: PREV_IDX 값이 있을 때만 버튼 노출 -->
<c:if test="${prevNextBoard.PREV_IDX != null}">
<a href="#this" class="btn" id="preBtn">이전 페이지(게시글)</a>
</c:if>
<!-- 다음 글 버튼: NEXT_IDX 값이 있을 때만 버튼 노출 -->
<c:if test="${prevNextBoard.NEXT_IDX != null}">
<a href="#this" class="btn" id="nextBtn">다음 페이지(게시글)</a>
</c:if>
<%@ include file="/WEB-INF/include/include-body.jspf" %>
<script type="text/javascript">
$(document).ready(function(){
$("#list").on("click", function(e){ //목록으로 버튼
e.preventDefault();
fn_openBoardList();
});
$("#update").on("click", function(e){ //수정하기 버튼
e.preventDefault();
fn_openBoardUpdate();
});
$("#delete").on("click", function(e) {
e.preventDefault();
fn_openBoardDelete();
})
$("#preBtn").on("click", function(e) {
e.preventDefault();
fn_openBoardPreview();
})
$("#nextBtn").on("click", function(e) {
e.preventDefault();
fn_openBoardNext();
})
$("a[name='file']").on("click", function(e){ //파일이름
e.preventDefault();
fn_downloadFile($(this));
});
});
function fn_openBoardList(){
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/sample/openBoardList.do' />");
comSubmit.submit();
}
function fn_openBoardUpdate(){
var idx = "${map.IDX}";
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/sample/openBoardUpdate.do' />");
comSubmit.addParam("IDX", idx);
comSubmit.submit();
}
function fn_openBoardDelete(){
if (confirm("정말로 삭제하시겠습니까?")) {
var idx = "${map.IDX}";
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/sample/deleteBoard.do' />");
comSubmit.addParam("IDX", idx);
comSubmit.submit();
}
}
function fn_openBoardPreview(){
var idx = "${map.IDX}";
var prev = "${prevNextBoard.PREV_IDX}";
var next = "${prevNextBoard.NEXT_IDX}";
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/sample/openBoardDetail.do' />");
comSubmit.addParam("IDX", prev);
comSubmit.submit();
}
function fn_openBoardNext(){
var idx = "${map.IDX}";
var prev = "${prevNextBoard.PREV_IDX}";
var next = "${prevNextBoard.NEXT_IDX}";
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/sample/openBoardDetail.do' />");
comSubmit.addParam("IDX", next);
comSubmit.submit();
}
function fn_downloadFile(obj)
{
var idx = obj.parent().find("#IDX").val();
var comSubmit = new ComSubmit();
comSubmit.setUrl("<c:url value='/common/downloadFile.do'/>");
comSubmit.addParam("IDX", idx);
comSubmit.submit();
}
</script>
</body>
</html>
<?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="sample">
<select id="selectBoardList" parameterType="hashmap" resultType="hashmap">
<include refid="common.pagingPre"/>
<![CDATA[
SELECT
ROW_NUMBER() OVER (ORDER BY IDX DESC) RNUM,
IDX,
TITLE,
HIT_CNT,
CREA_DTM
FROM
TB_BOARD
WHERE
DEL_GB = 'N'
]]>
<!-- [수정 시작] 검색 조건 동적 쿼리 추가 -->
<if test="searchKeyword != null and searchKeyword != ''">
<choose>
<!-- 콤보박스 선택: 제목 (TITLE) -->
<when test='searchType == "TITLE"'>
AND TITLE LIKE '%' || #{searchKeyword} || '%'
</when>
<!-- 콤보박스 선택: 내용 (CONTENTS) -->
<when test='searchType == "CONTENTS"'>
AND CONTENTS LIKE '%' || #{searchKeyword} || '%'
</when>
<!-- 콤보박스 선택: 작성자 ID (ID) -->
<when test='searchType == "ID"'>
AND CREA_ID LIKE '%' || #{searchKeyword} || '%'
</when>
</choose>
</if>
<!-- [수정 끝] 검색 조건 동적 쿼리 추가 -->
<include refid="common.pagingPost"/>
</select>
<insert id="insertBoard" parameterType="hashmap" useGeneratedKeys="true" keyProperty="IDX">
<selectKey keyProperty="IDX" resultType="string" order="BEFORE">
SELECT SEQ_TB_BOARD_IDX.NEXTVAL FROM DUAL
</selectKey>
<![CDATA[
INSERT INTO TB_BOARD
(
IDX,
TITLE,
CONTENTS,
HIT_CNT,
DEL_GB,
CREA_DTM,
CREA_ID
)
VALUES
(
#{IDX},
#{TITLE,jdbcType=VARCHAR},
#{CONTENTS,jdbcType=VARCHAR},
0,
'N',
SYSDATE,
'Admin'
)
]]>
</insert>
<!-- <update id="updateHitCnt" parameterType="hashmap">
<![CDATA[
UPDATE TB_BOARD
SET
HIT_CNT = NVL(HIT_CNT,0)+1
WHERE
IDX=#{IDX]
]]>
</update> --><!-- HIT_CNT 가 null일경우, 두번째 반환 -->
<update id="updateHitCnt" parameterType="hashmap">
<![CDATA[
UPDATE TB_BOARD
SET HIT_CNT = HIT_CNT + 1
WHERE IDX=#{IDX}
]]>
</update>
<select id="selectBoardDetail" parameterType="hashmap" resultType="hashmap">
<![CDATA[
SELECT
IDX,
HIT_CNT,
CREA_ID,
CREA_DTM,
TITLE,
CONTENTS
FROM
TB_BOARD
WHERE
IDX=#{IDX}
]]>
</select>
<select id="boardUpdate" parameterType="hashmap" resultType="hashmap">
<!-- <![CDATA[
UPDATE TB_BOARD
SET
TITLE = #{TITLE},
CONTENTS = #{CONTENTS}
WHERE
IDX = #{IDX}
]]> -->
<![CDATA[
SELECT
IDX,
HIT_CNT,
CREA_ID,
CREA_DTM,
TITLE,
CONTENTS
FROM
TB_BOARD
WHERE IDX=#{IDX}
]]>
</select>
<update id="updateBoard" parameterType="hashmap">
<![CDATA[
UPDATE TB_BOARD
SET TITLE=#{TITLE},
CONTENTS=#{CONTENTS}
WHERE
IDX=#{IDX}
]]>
</update>
<delete id="deleteBoard" parameterType="hashmap">
<!-- <![CDATA[
DELETE FROM
TB_BOARD
WHERE IDX=#{IDX}
]]> -->
<![CDATA[
UPDATE TB_BOARD
SET DEL_GB = 'Y'
WHERE IDX=#{IDX}
]]>
</delete>
<select id="selectPrevNextBoard" parameterType="hashmap" resultType="hashmap">
<![CDATA[
select
(select MAX(IDX) from TB_BOARD where IDX < #{IDX} and DEL_GB = 'N') AS PREV_IDX,
(select MIN(IDX) from TB_BOARD where IDX > #{IDX} and DEL_GB = 'N') AS NEXT_IDX
from
dual
]]>
</select>
<insert id="insertFile" parameterType="hashmap">
<![CDATA[
INSERT INTO TB_FILE
(
IDX,
BOARD_IDX,
ORIGINAL_FILE_NAME,
STORED_FILE_NAME,
FILE_SIZE,
CREA_ID
)
VALUES
(
SEQ_TB_FILE_IDX.NEXTVAL,
#{BOARD_IDX},
#{ORIGINAL_FILE_NAME},
#{STORED_FILE_NAME},
#{FILE_SIZE},
'Admin'
)
]]>
</insert>
<select id="selectFileList" parameterType="hashmap" resultType="hashmap">
<![CDATA[
SELECT
IDX,
ORIGINAL_FILE_NAME,
ROUND(FILE_SIZE/1024,1) AS FILE_SIZE
FROM
TB_FILE
WHERE
BOARD_IDX=#{IDX}
AND DEL_GB='N'
]]>
</select>
<update id="deleteFileList" parameterType="hashmap">
<![CDATA[
UPDATE TB_FILE SET
DEL_GB='Y'
WHERE
BOARD_IDX=#{IDX}
]]>
</update>
<update id="updateFile" parameterType="hashmap">
<![CDATA[
UPDATE TB_FILE SET
DEL_GB='N'
WHERE
IDX=#{FILE_IDX}
]]>
</update>
<!-- <![CDATA...요건 혹시모를 쿼리문에 <,> 등의 기호가 들어갈때를 대비하여 씀 -->
</mapper>
package first.sample.dao;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Repository;
import first.common.dao.AbstractDAO;
@Repository("sampleDAO") // DAO 컴포넌트 선언
public class SampleDAO extends AbstractDAO
{
@SuppressWarnings("unchecked") // 경고 무시
public Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception
{
// selectPagingList를 사용하여 페이징 처리된 게시물 목록과 페이징 정보를 함께 반환합니다.
// Controller에서 전달받은 검색 조건(searchType, searchKeyword)을 포함한 Map을 그대로 사용합니다.
return (Map<String, Object>) selectPagingList("sample.selectBoardList", map);
}
public void insertBoard(Map<String, Object> map) throws Exception
{
insert("sample.insertBoard", map);
}
public void insertFile(Map<String, Object> map) throws Exception
{
insert("sample.insertFile", map);
}
@SuppressWarnings("unchecked")
public Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception{
// 게시글 상세 정보를 조회합니다.
return (Map<String, Object>) selectOne("sample.selectBoardDetail", map);
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> selectFileList(Map<String, Object> map) throws Exception
{
// 첨부 파일 목록을 조회합니다.
return (List<Map<String, Object>>) selectList("sample.selectFileList", map);
}
@SuppressWarnings("unchecked")
public Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception
{
// 게시글 수정을 위한 상세 정보를 조회합니다.
return (Map<String, Object>) selectOne("sample.boardUpdate", map);
}
public void updateBoard(Map<String, Object> map) throws Exception
{
update("sample.updateBoard", map);
}
public void deleteBoard(Map<String, Object> map) throws Exception
{
delete("sample.deleteBoard", map);
}
public void updateHitCnt(Map<String, Object> map)
{
// 조회수(HIT_CNT)를 증가시킵니다.
update("sample.updateHitCnt", map);
}
public void deleteFileList(Map<String, Object> map)
{
// 파일의 DEL_GB를 'Y'로 업데이트하여 삭제 처리합니다.
update("sample.deleteFileList", map);
}
public void updateFile(Map<String, Object> map)
{
// 첨부 파일을 업데이트합니다.
update("sample.updateFile", map);
}
public Map<String, Object> selectPrevNextBoard(Map<String, Object> map) throws Exception
{
return (Map<String, Object>) selectOne("sample.selectPrevNextBoard", map);
}
}
package first.sample.service;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public interface SampleService {
Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception;
void insertBoard(Map<String, Object> map, HttpServletRequest request) throws Exception;
Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception;
Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception;
void updateBoard(Map<String, Object> map, HttpServletRequest request) throws Exception;
void deleteBoard(Map<String, Object> map) throws Exception;
}
package first.sample.service;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import first.common.util.FileUtils;
import first.sample.dao.SampleDAO;
@Service("sampleService")
public class SampleServiceImpl implements SampleService
{
Logger log = Logger.getLogger(this.getClass());
@Resource(name="fileUtils")
private FileUtils fileUtils;
@Resource(name="sampleDAO")
private SampleDAO sampleDAO;
@Override
public Map<String, Object> selectBoardList(Map<String, Object> map) throws Exception {
return sampleDAO.selectBoardList(map);
// Controller에서 넘어온 검색 파라미터가 포함된 Map을 그대로 DAO로 전달합니다.
}
@Override
public void insertBoard(Map<String, Object> map, HttpServletRequest request) throws Exception
{
sampleDAO.insertBoard(map);
// 파일 정보 파싱 및 DB에 파일 정보 저장
List<Map<String, Object>> list = fileUtils.parseInsertFileInfo(map, request);
for(int i=0,size=list.size();i<size;i++)
{
sampleDAO.insertFile(list.get(i));
}
// 로그 출력 (기존 코드 유지)
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;
Iterator<String> iterator = multipartHttpServletRequest.getFileNames();
MultipartFile multipartFile = null;
while(iterator.hasNext())
{
multipartFile = multipartHttpServletRequest.getFile(iterator.next());
if(multipartFile.isEmpty() == false)
{
log.debug("--------------file start------------");
log.debug("name :" + multipartFile.getName());
log.debug("filename :" + multipartFile.getOriginalFilename());
log.debug("size :" + multipartFile.getSize());
log.debug("--------------file end--------------\\n");
}
}
}
@Override
public Map<String, Object> selectBoardDetail(Map<String, Object> map) throws Exception
{
/*
* 선처리
String type = (String) map.get("type");
if(type==null) {
//현재값
}else if(type!=null && type.equals("prev")) {
//이전글
}else if(type!=null && type.equals("next")) {
//다음글
}
*/
// 1. 조회수 증가
sampleDAO.updateHitCnt(map);
Map<String, Object> resultMap = new HashMap<String, Object>();
// 2. 게시글 상세 내용 조회
Map<String, Object> tempMap = sampleDAO.selectBoardDetail(map);
resultMap.put("map", tempMap);
// 3. 첨부 파일 목록 조회
List<Map<String, Object>> list = sampleDAO.selectFileList(map);
resultMap.put("list", list);
//후처리
Map<String, Object> mapPrevNextBoard = sampleDAO.selectPrevNextBoard(map);
resultMap.put("prevNextBoard", mapPrevNextBoard);
return resultMap;
}
@Override
public Map<String, Object> boardUpdate(Map<String, Object> map) throws Exception
{
Map<String, Object> resultMap = new HashMap<String, Object>();
// 게시글 수정 화면에 필요한 상세 정보 조회
Map<String, Object> tempMap = sampleDAO.boardUpdate(map);
resultMap.put("map", tempMap);
// 첨부 파일 목록 조회
List<Map<String, Object>> list = sampleDAO.selectFileList(map);
resultMap.put("list", list);
return resultMap;
}
@Override
public void updateBoard(Map<String, Object> map, HttpServletRequest request) throws Exception
{
sampleDAO.updateBoard(map); // 기존 글만 수정 (업데이트)
sampleDAO.deleteFileList(map); // 기존 파일 삭제 처리 (DEL_GB = 'Y'로 업데이트)
// 새로운 파일 정보 파싱 및 업데이트 처리
List<Map<String, Object>> list = fileUtils.parseInsertFileInfo(map, request);
Map<String, Object> tempMap = null;
for(int i=0,size=list.size();i<size;i++)
{
tempMap = list.get(i);
if(tempMap.get("IS_NEW").equals("Y"))
{
// 새로 추가된 파일은 INSERT
sampleDAO.insertFile(tempMap);
}else{
// 기존 파일 중 수정되지 않은 파일은 DEL_GB = 'N'으로 업데이트 (활성화)
sampleDAO.updateFile(tempMap);
}
}
}
@Override
public void deleteBoard(Map<String, Object> map) throws Exception {
sampleDAO.deleteBoard(map);
}
}