Java/SPRING

스프링 [Spirng] iBatis - 리스트, 글보기, 댓글, 추천, 조회수, 글삭제 , 댓글삭제 쿼리 및 기능 구현

백엔드 신입사원( soft 5.10 입사) 2022. 1. 4. 18:03
반응형

sql 작성

<sqlMap namespace="board"> board란 이름으로 getboardlist 등 사용하기위해 이름을 걸어주고  
<typeAlias alias="BoardModel" type="net.nice19.smboard.board.model.BoardModel"/> OO.OO.OO를 계속사용하지 않고 BoardModel이란 단어만으로 쉽게 사용하기위해 typeAlias를 잡아주고  
<typeAlias alias="BoardCommentModel" type="net.nice19.smboard.board.model.BoardCommentModel"/>  
  
<!-- public List<BoardModel> getBoardList(Map map) --> 게시판 리스트를뽑는 sql문  
<select id="getBoardList" parameterClass="java.util.HashMap" resultClass="BoardModel">  
select  
b.idx, b.writer, b.subject,  
b.content, b.hitcount, b.recommendcount,  
to_char(b.writedate, 'YYYY-MM-DD HH:MI:SS') writeDate,  
count(c.idx) as "comment",  
b.writerId, b.fileName, b.rnum  
from (select -------  
a.idx, a.writer, a.subject, a.idx로 잡힌  
a.content, a.hitcount, a.recommendcount,  
a.writedate, a.writerId, a.fileName, rownum rnum ------  
from (select --------  
idx, writer, subject,  
content, hitcount, recommendcount, SQL은 가장 안쪽 부터 동작 from으로 찾은내용을 idx는 내림차순으로  
writedate, writerId, fileName 정보들은 a로 대입해주고  
from jmboard --------  
order by idx desc) a  
) b left outer join jmboard_comment c on b.idx = c.linkedarticlenum ------- 외부 조인  
where rnum between #startArticleNum# and #endArticleNum#  
group by  
b.idx, b.writer, b.subject,  
b.content, b.hitcount, b.recommendcount,  
b.writedate, b.rnum, b.writerId, b.fileName  
order by b.idx desc  
</select>  
  
  
<!-- public int getTotalNum() -->  
<select id="getTotalNum" resultClass="int">  
select count(idx) from jmboard  
</select>  
  
<!-- public int getSearchTotalNum(Map map) -->  
<select id="getSearchTotalNum" resultClass="int">  
select count(idx) from jmboard  
where $type$ like '%$keyword$%'  
</select>  
  
<!-- public List<BoardModel> searchArticle(Map map) -->  
<select id="searchArticle" parameterClass="java.util.HashMap" resultClass="BoardModel">  
select  
b.idx, b.writer, b.subject,  
b.content, b.hitcount, b.recommendcount,  
to_char(b.writedate, 'YYYY-MM-DD HH:MI:SS') writeDate,  
count(c.idx) as "comment",  
b.writerId, b.fileName, b.rnum  
from (select  
a.idx, a.writer, a.subject,  
a.content, a.hitcount, a.recommendcount,  
a.writedate, a.writerId, a.fileName, rownum rnum  
from (select  
idx, writer, subject,  
content, hitcount, recommendcount,  
writedate, writerId, fileName  
from jmboard  
where $type$ like '%$keyword$%'  
order by idx desc) a  
) b left outer join jmboard_comment c on b.idx = c.linkedarticlenum  
where rnum between #startArticleNum# and #endArticleNum#  
group by  
b.idx, b.writer, b.subject,  
b.content, b.hitcount, b.recommendcount,  
b.writedate, b.rnum, b.writerId, b.fileName  
order by b.idx desc  
</select>  
  
<!-- public BoardModel getOneArticle(int idx) -->  
<select id="getOneArticle" parameterClass="int" resultClass="BoardModel">  
select idx, writer, subject, content, hitcount, recommendcount, writedate, writerId, fileName  
from jmboard where idx = #idx#  
</select>  
  
<!-- public List<BoardCommentModel> getCommentList(int idx) -->  
<select id="getCommentList" parameterClass="int" resultClass="BoardCommentModel">  
select idx, writer, content, writeDate, linkedArticleNum, writerId  
from jmboard_comment where linkedArticleNum = #idx#  
order by idx desc  
</select>  
  
<!-- public BoardCommentModel getOneComment(int idx) -->  
<select id="getOneComment" parameterClass="int" resultClass="BoardCommentModel">  
select idx, writer, content, writeDate, linkedArticleNum, writerId  
from jmboard_comment where idx = #idx#  
</select>  
  
<!-- public void writeArticle(WriteArticle writeArticle) -->  
<insert id="writeArticle" parameterClass="BoardModel">  
insert into jmboard(idx, writer, subject, content, hitcount, recommendcount, writeDate, writerId, fileName)  
values(BOARDLISTSEQ.nextVal, #writer#, #subject#, #content#, 0, 0, sysdate, #writerId#, #fileName#)  
</insert>  
  
<!-- public void writeComment(BoardCommentModel boardCommentModel) -->  
<insert id="writeComment" parameterClass="BoardCommentModel">  
insert into jmboard_comment(idx, writer, content, writeDate, linkedArticleNum, writerId)  
values(BOARDCOMMENTSEQ.nextVal, #writer#, #content#, sysdate, #linkedArticleNum#, #writerId#)  
</insert>  
  
<!-- public void updateHitcount(Map map) -->  
<update id="updateHitcount" parameterClass="java.util.HashMap">  
update jmboard  
set hitcount = #hitcount#  
where idx = #idx#  
</update>  
  
<!-- public void updateRecommendcount(Map map) -->  
<update id="updateRecommendcount" parameterClass="java.util.HashMap">  
update jmboard  
set recommendcount = #recommendcount#  
where idx = #idx#  
</update>  
  
<!-- public void deleteComment(int idx) -->  
<delete id="deleteComment" parameterClass="int">  
delete from jmboard_comment  
where idx = #idx#  
</delete>  
  
<!-- public void deleteArticle(int idx) -->  
<delete id="deleteArticle" parameterClass="int">  
delete from jmboard  
where idx = #idx#  
</delete>  
  
<!-- public void modifyArticle(BoardModel boardModel) -->  
<update id="modifyArticle" parameterClass="BoardModel">  
update jmboard  
set subject = #subject#,  
content = #content#,  
fileName = #fileName#  
where idx = #idx#  
</update>  
  
</sqlMap>  

리스트, 글보기, 댓글, 추천, 조회수, 글삭제 , 댓글삭제 정보를 주고받는 SET, GET 메서드

private int idx;  
private String writer;  
private String content;  
private String writeDate;  
private int linkedArticleNum;  
private String writerId;

private int rnum;  
private int idx;  
private String writer;  
private String subject;  
private String content;  
private int hitcount = 0;  
private int recommendcount = 0;  
private int comment = 0;  
private String writeDate;  
private String writerId;  
private String fileName;

SET, GET를 사용하기 위한 Dao.

어디서 사용되는지 확인할수있게 SQL이 작성된 이름과 통일.

HashMap, SqlMapClientTemplate

HashMap은 Map 인터페이스를 구현한 대표적인 Map컬렉션. Map인터페이스를 상속하고 있어 Map의 성질을 가지고 있다

Map은 키와 값으로 구성되며 Entry객체를 저장하는 구조를 가지고 있는 자료 구조.

키와 값은 모두 객체이고 값은 중복 저장될 수 있으나, 키는 중복 저장이 될 수 없다. 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값이 대치된다. HashMap이란 내용은 이름처럼 Hashing을 사용하기에 많은 양의 데이터를 검색하는데 뛰어난 성능이 있다.

valueMap.put(키, 값);

동작하기 위한 컨트롤러 Code.

@Controller  
@RequestMapping("/board") // 컨트롤러 지정 /board요청이 오면  
public class BoardController {  
//DI  
private ApplicationContext context = new ClassPathXmlApplicationContext("/config/applicationContext.xml"); // 객체 선언  
private BoardService boardService = (BoardService) context.getBean("boardService");  
  
  
//User variable  
// article, page variables  
private int currentPage = 1; // 변수선언하고  
private int showArticleLimit = 10; // change value if want to show more articles by one page  
private int showPageLimit = 10; // change value if want to show more page links  
private int startArticleNum = 0;  
private int endArticleNum = 0;  
private int totalNum = 0;  
  
  
// 파일업로드 패턴  
private String uploadPath = "C:\\Users\\User\\eclipse-workspace\\SummerBoard\\src\\main\\webapp\\files\\";  
  
  
@RequestMapping("list.do") // 요청이 오면 get방식의  
public ModelAndView boardList(HttpServletRequest request, HttpServletResponse response) { // 모델엔뷰에 request, response등 타입을 받고  
  
String type = null; // 변수 선언  
String keyword = null;  
  
//set variables from request parameter  
// 페이지가 널이거나 , 페이지가 없거나, 페이지의 값이 0 이라면  
if(request.getParameter("page") == null || request.getParameter("page").trim().isEmpty()  
|| request.getParameter("page").equals("0")) {  
currentPage = 1; // 페이지의 값은 1  
} else { // 그게 아니라면  
currentPage = Integer.parseInt(request.getParameter("page")); // 페이지를 인트값으로 변경후 currentpage에 저장  
}  
  
if(request.getParameter("type") != null) { // 타입이 널이아닐경우  
type = request.getParameter("type").trim(); // type에 저장하고  
}  
  
if(request.getParameter("keyword") != null) { // 키워드가 널이아닐경우  
keyword = request.getParameter("keyword").trim(); // keyword에 저장하고  
}  
  
// expression article variables value  
// 페이지수를 계산하고  
startArticleNum = (currentPage - 1) * showArticleLimit + 1;  
endArticleNum = startArticleNum + showArticleLimit -1;  
  
//get boardList and get page html code  
List<BoardModel> boardList;  
if(type != null && keyword != null) { // type과 keyword가 널이 아닐경우(검색되는경우)  
boardList = boardService.searchArticle(type, keyword, startArticleNum, endArticleNum); // 보드서비스 searcharticle 객체에서 searcharticle를 받아 리스트를 꺼내고  
totalNum = boardService.getSearchTotalNum(type, keyword); // totalnum도 역시 보드서비스 객체에서 값을 꺼낸다  
} else { // 그게 아니라면  
boardList = boardService.getBoardList(startArticleNum, endArticleNum); // 보드서비스에 보스리스트에서 스타트와 앤드넘만꺼내고  
totalNum = boardService.getTotalNum(); // totalnum도 객체를꺼낸다.  
}  
  
StringBuffer pageHtml = getPageHtml(currentPage, totalNum, endArticleNum, showArticleLimit, showPageLimit, type, keyword);  
  
ModelAndView mav = new ModelAndView();  
mav.addObject("boardList", boardList);  
mav.addObject("pageHtml", pageHtml);  
mav.setViewName("/board/list");  
  
return mav;  
}  
  
  
private StringBuffer getPageHtml(int currentPage, int totalNum, int endArticleNum, int showArticleLimit,  
int showPageLimit, String type, String keyword) {  
StringBuffer pageHtml = new StringBuffer();  
int startPage = 0;  
int lastPage = 0;  
  
// expression page variables  
startPage = ((currentPage-1) / showArticleLimit) * showPageLimit +1;  
lastPage = startPage + showPageLimit -1;  
  
if(lastPage > totalNum / showArticleLimit) {  
lastPage = (totalNum / showArticleLimit) +1;  
}  
  
//create page html code  
// if: when no search  
if(type == null && keyword == null) { // 타입과 키워드가 널 아무것도 없을경우  
if(currentPage == 1) { // 페이지가 1일경우  
pageHtml.append("<span>"); // html에 보여질 페이징 소스를 작성하고  
} else {  
pageHtml.append("<span><a href=\"list.do?page="+(currentPage-1)+ "\"><이전></a>&nbsp;&nbsp;");  
}  
  
for(int i = startPage; i <= lastPage; i++) {  
if(i == currentPage) {  
pageHtml.append(".&nbsp;<strong>");  
pageHtml.append("<a href=\"list.do?page="+i+"\" class=\"page\">" +i +"</a>");  
pageHtml.append("&nbsp;</strong>");  
} else {  
pageHtml.append(".&nbsp;<a href=\"list.do?page="+i+"\" class=\"page\">"+i+"</a>&nbsp;");  
}  
}  
if(currentPage == lastPage){  
pageHtml.append(".</span>");  
} else {  
pageHtml.append(".&nbsp;&nbsp;<a href=\"list.do?page=" + (currentPage+1)+ "\"><다음></a></span>");  
}  
  
  
//else:when search  
}else {  
if(currentPage == 1) {  
pageHtml.append("<span>");  
} else {  
pageHtml.append("<span><a href=\"list.do?page=" + (currentPage-1)+"&type="+type+ "&keyword="+keyword+"\"><이전></a>&nbsp;&nbsp;");  
}  
  
for(int i= startPage; i<= lastPage; i++) {  
if(i == currentPage){  
pageHtml.append(".&nbsp;<strong>");  
pageHtml.append("<a href=\"list.do?page=" +i + "&type=" + type + "&keyword=" + keyword + "\" class=\"page\">" + i + "</a>&nbsp;");  
pageHtml.append("&nbsp;</strong>");  
} else {  
pageHtml.append(".&nbsp;<a href=\"list.do?page=" +i + "&type=" + type + "&keyword=" + keyword + "\" class=\"page\">" + i + "</a>&nbsp;");  
}  
}  
if(currentPage == lastPage) {  
pageHtml.append("</span>");  
} else {  
pageHtml.append(".&nbsp;&nbsp;<a href=\"list.do?page="+(currentPage+1)+"&type="+type+"&keyword="+keyword+ "\"><다음></a></span>");  
}  
}  
return pageHtml;  
}  
  
@RequestMapping("/view.do") // 요청이오면  
public ModelAndView boardView(HttpServletRequest request) {  
int idx = Integer.parseInt(request.getParameter("idx"));// 받아오는 idx를 인트타입으로 변환하고  
BoardModel board = boardService.getOneArticle(idx); // get Selected article model 기존조회수를 가져와  
boardService.updateHitcount(board.getHitcount()+1, idx); // update hitcount 조회카운트 조회수를 +1만큼 올린다  
  
List<BoardCommentModel> commentList = boardService.getCommentList(idx); //get comment list  
  
ModelAndView mav = new ModelAndView();  
mav.addObject("board", board);  
mav.addObject("commentList", commentList);  
mav.setViewName("/board/view");  
return mav;  
  
}  
  
@RequestMapping("/write.do") // 게시글 작성 get방식의 요청 write.do가 오면  
public String boardWrite(  
@ModelAttribute("BoardModel") BoardModel boardModel  
) {  
return "/board/write"; // 정보를 가지고 리턴하고  
}  
  
@RequestMapping(value="/write.do", method=RequestMethod.POST) // 게시글 작성 후 write.do 포스트 방식요청이 오면  
public String boardWriteProc(  
@ModelAttribute("BoardModel") BoardModel boardModel, MultipartHttpServletRequest request  
) {  
// get upload file  
MultipartFile file = request.getFile("file"); // 받아오는 파일의 정보를 file이란 이름으로 받고  
String fileName = file.getOriginalFilename(); // file에 저장된 정보중 filename을 (이름을) 받고  
File uploadFile = new File(uploadPath + fileName); // 위에 설정한 파일업로드패턴과 파일이름을 합쳐 업로드파일을 만들고  
  
if(uploadFile.exists()) { // 만들어둔 uploadfile이름이 같은것이 있을경우  
fileName = new Date().getTime() + fileName; // 파일이름에 시간을 합쳐서  
uploadFile = new File(uploadPath + fileName); // 새로 파일업로드에 업로드패턴과 파일이름을 합쳐서 만든다  
}  
  
// save upload file to uploadpath  
try {  
file.transferTo(uploadFile); // transferto메소드로 업로드 패턴위치에 업로드파일로 업로드된 파일을 저장한다.  
} catch(Exception e) {  
  
}  
boardModel.setFileName(fileName); // 자바빈에 파일이름을 저장.  
  
//new line code change to <br> tag  
String content = boardModel.getContent().replaceAll("\r\n", "<br>");  
// 만일 \r\n요청이 오면 <br>로 변환하고 content에 담고  
boardModel.setContent(content); // 자바빈에 저장한다.  
  
boardService.writeArticle(boardModel); // board.xml에서 처리할수있게 자바빈저장내용을 boardservice에 담는다  
return "redirect:list.do"; // 그리고 리턴  
}  
  
@RequestMapping("/commentWrite.do") //댓글 get방식 요청이 오면  
public ModelAndView commentWriteProc(  
@ModelAttribute("CommentModel") BoardCommentModel commentModel  
) {  
// new line code change to <br> tag  
String content = commentModel.getContent().replaceAll("\r\n", "<br>"); // \r\n요청이오면 <br>로 변환  
commentModel.setContent(content); // 변환된 정보를 commentmodel에 저장  
  
boardService.writeComment(commentModel); // 자바빈에 저장된 정보를 boardservice.writeComment로 넘긴다.  
ModelAndView mav = new ModelAndView();  
mav.addObject("idx", commentModel.getLinkedArticleNum()); // idx란 이름으로 linkew정보를 저장하고  
mav.setViewName("redirect:view.do"); // 경로로  
return mav; //리턴한다.  
}  
  
@RequestMapping("/modify.do") // 게시글 수정 get방식 요청이 오면  
public ModelAndView boardModeify(HttpServletRequest request, HttpSession session) {  
String userId = (String) session.getAttribute("userId"); // 세션에 저장된 아이디를 받아서  
int idx = Integer.parseInt(request.getParameter("idx")); // 번호를 받아서  
  
BoardModel board = boardService.getOneArticle(idx); // 서버단에 저장된 idx를 board에 받아서  
// <br> tag change to new line code  
String content = board.getContent().replaceAll("<br>", "\r\n"); // br 에서 \r\n변환한후  
board.setContent(content); // 변환내용을 저장하고  
  
ModelAndView mav = new ModelAndView(); // 사용하기위해 객체생성한뒤  
  
if(!userId.equals(board.getWriterId())) { // 서버에저장된 아이디와 세션아이디가 다르다면  
mav.addObject("errCode", "1"); // 오류 코드 1번을  
mav.addObject("idx", idx); // idx란 이름으로 idx값을 가지고  
mav.setViewName("redirect:view.do"); // 란 경로로  
} else { // 만일 세션과 서버에 저장된 아이디가 같다면  
mav.addObject("board", board); // board이름으로 board값을 가지고  
mav.setViewName("/board/modify"); // 란 경로로  
}  
return mav; // 리턴  
}  
  
@RequestMapping(value = "/modify.do", method=RequestMethod.POST) //게시글 수정 post 방식 요청 오면  
public ModelAndView boardModifyProc(  
@ModelAttribute("BoardModel") BoardModel boardModel, MultipartHttpServletRequest request  
){  
String orgFileName = request.getParameter("orgFile"); // 저장된 파일불러오고  
MultipartFile newFile = request.getFile("newFile"); // 저장할 파일불러와  
String newFileName = newFile.getOriginalFilename(); // 저장할 파일 이름에는 String 타입으로 잡고  
  
boardModel.setFileName(orgFileName); // 옛 파일이름을 담아고  
  
// if : when want to change upload file  
if(newFile != null && !newFileName.equals("")) { //새로운 파일이 널이아니거나 ""과 같지 않다면(새로운 파일을 업로드하는데)  
if(orgFileName != null || !orgFileName.equals("")) { // 예전파일이 널이아니거나 혹은 ""과 같지 않다면 (예전파일이 존재한다면)  
//remove uploaded file 예전파일을 지운다  
File removeFile = new File(uploadPath + orgFileName);  
removeFile.delete();  
}  
// create new upload file 새로운 파일 업로드  
File newUploadFile = new File(uploadPath + newFileName);  
try {  
newFile.transferTo(newUploadFile); // 저장한다  
} catch(Exception e) {  
e.printStackTrace();  
}  
boardModel.setFileName(newFileName); // 새로운 파일도 db에 저장  
}  
  
//new line code change to <br> tag  
String content = boardModel.getContent().replaceAll("\r\n", "<br>"); // 변환하고  
boardModel.setContent(content); // 저장  
  
boardService.modifyArticle(boardModel); // 내용을 sql에 넣고  
  
ModelAndView mav = new ModelAndView();  
mav.addObject("idx", boardModel.getIdx()); // 내용을 가지고  
mav.setViewName("redirect:/board/view.do"); // 경로로  
return mav; //리턴  
}  
  
  
@RequestMapping("/delete.do") //게시글 글삭제 요청이 오면  
public ModelAndView boardDelete(HttpServletRequest request, HttpSession session) {  
String userId = (String) session.getAttribute("userId"); // 세션 id를 받아  
int idx = Integer.parseInt(request.getParameter("idx")); // idx번호를 받아  
  
BoardModel board = boardService.getOneArticle(idx); // 서버에 idx 받은것을 담고  
ModelAndView mav = new ModelAndView();  
  
if(!userId.equals(board.getWriterId())) { // 아이디가 같지 않다면  
mav.addObject("errCode", "1"); // 에러코드 1을  
mav.addObject("idx", idx); // 번호를 담아  
mav.setViewName("redirect:view.do"); //리다이렉트  
} else { // 아이디가 같으면 ( check comments 댓글 확인 )  
List<BoardCommentModel> commentList = boardService.getCommentList(idx); // 조회된 글번호를 받아  
if(commentList.size() > 0) { // 글번호가 0보다 크다면 댓글이 있다면  
mav.addObject("errCode", "2"); // 에러코드 2를 출력  
mav.addObject("idx", idx); // 번호를 담아  
mav.setViewName("redirect:view.do"); // 리다이렉트  
} else { // 그것도 아니라면 (완벽하게 처리될경우)  
if(board.getFileName() != null) { // if: when the article has upload file - remove that  
//파일이 널이 아니면 ( 파일이 있다면 )  
File removeFile = new File(uploadPath + board.getFileName()); // 그 파일 경로 + 이름을 받아  
removeFile.delete(); // 삭제  
}  
boardService.deleteArticle(idx); // 글도 삭제  
mav.setViewName("redirect:list.do"); // 리다이렉트  
}  
}  
return mav; // 리턴한다.  
}  
  
@RequestMapping("/commentDelete.do") // 댓글 삭제요청이오면  
public ModelAndView commendDelete(HttpServletRequest request, HttpSession session){ // modelandview객체에 reqiest session담아  
int idx = Integer.parseInt(request.getParameter("idx"));// 번호를 받아오고  
int linkedArticleNum = Integer.parseInt(request.getParameter("linkedArticleNum")); // 받아오는 값을 인트타입으로 변경  
  
String userId = (String) session.getAttribute("userId");// 세션아이디를 받고  
BoardCommentModel comment = boardService.getOneComment(idx); // 서버에 담고  
  
ModelAndView mav = new ModelAndView(); // Modelandview 객체를 만들고  
  
if(!userId.equals(comment.getWriterId())){ // id와 댓글 id가 틀린지 확인하고  
mav.addObject("errCode", "1"); // 틀리면 오류송출  
} else {  
boardService.deleteComment(idx); // 같다면 댓글번호를받아 삭제하고  
}  
  
mav.addObject("idx", linkedArticleNum); // move back to the article 정보를 담고  
mav.setViewName("redirect:view.do"); // 가지고 리다이렉트 시킨다  
  
return mav; // 리턴  
}  
  
@RequestMapping("/recommend.do") // 요청이 오면  
public ModelAndView updateRecommendcount(HttpServletRequest request, HttpSession session){ // 세션과 request를 받아  
int idx = Integer.parseInt(request.getParameter("idx")); // 인트타입으로 idx를 받아오고  
String userId = (String) session.getAttribute("userId");// 세션아이디를 가져오고  
BoardModel board = boardService.getOneArticle(idx); //서버에 담고  
  
ModelAndView mav = new ModelAndView();  
  
if(userId.equals(board.getWriterId())){// 만일 본인 게시글에 본인아이디라면  
mav.addObject("errCode", "1"); // 에러 출력  
} else {// 그게아니라면  
boardService.updateRecommendCount(board.getRecommendcount()+1, idx);// 기존추천수에 +1된 카운트 시키고  
}  
  
mav.addObject("idx", idx); // 정보를 담아  
mav.setViewName("redirect:/board/view.do"); // 리다이렉트 시킨다  
  
return mav; // 리턴한다  
}  
  
  
  
  
}

addObject() 메서드는 Model의 addAttribute와 비슷한 기능이나

Model의 기능과는 다르게 Object를 key, value로 넘겨준다.

addObject("변수명 이름", "값");

setViewName("페이지명 경로");
반응형