spring mybatis 셋팅이 끝났다면 이제 게시판을 만들 차례이다.

공지사항 게시판을 예로 들어 먼저 공지사항 리스트 페이지를 만들어 보자.


가장 먼저 게시판을 위한 controller, dao, mapper, vo를 생성하자.


<프로젝트 패키지 구성>

필자는 위와 같이 패키지와 클래스를 생성하였다.


Controller 소스를 구성하기 전에 기존에 pom.xml에 등록했던 lombok 라이브러리의 기능을 사용하기 위해 lombok.config라는 파일을 생성하자.

경로 : 프로젝트 하위(pom.xml과 동일한 경로)


<lombok.config 소스 코드>

lombok.log.fieldName=logger

이 설정은 @Slf4j 어노테이션 사용 시 static logger를 생성하지 않고 바로 log를 찍을 수 있게 해준다.(아래의 소스들을 보면 확인이 가능하다.)


<게시판 table 생성>

CREATE TABLE `push_notice` (
  `notice_id` int(11) NOT NULL AUTO_INCREMENT,
  `notice_title` varchar(32) NOT NULL,
  `notice_coments` varchar(4000) NOT NULL,
  `mod_date` timestamp NULL DEFAULT NULL,
  `use_yn` varchar(2) NOT NULL DEFAULT 'Y',
  PRIMARY KEY (`notice_id`)
)

필자는 위와같이 테이블을 생성하였다.


1. Controller 단 구성


<NoticeController.java 소스 코드>

package com.spring.test.controller;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.spring.test.dao.NoticeDao;
import com.spring.test.vo.NoticeVo;
import com.spring.test.vo.PagingVo;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class NoticeController {

	@Resource(name = "noticeDao")
	private NoticeDao noticeDao;
	
	@RequestMapping(value = "/noticeList")
	public String noticeList(@ModelAttribute("Notice") NoticeVo notice,
							@RequestParam(defaultValue="1") int curPage,
							Model model
								) {
		logger.info("noticeList page");
		int listCnt = noticeDao.getNoticeListCount();
		PagingVo paging = new PagingVo(listCnt, curPage);
		notice.setStartIndex(paging.getStartIndex());
		notice.setCntPerPage(paging.getPageSize());
			
		List<NoticeVo> noticeList=noticeDao.getNoticeList(notice);
		model.addAttribute("noticeList", noticeList);
		model.addAttribute("listCnt", listCnt);
		model.addAttribute("paging", paging);
		return "notice/noticeList";
	}
	
	@RequestMapping(value = "/noticeRegi")
	public String noticeRegi() {
		logger.info("notice Regi page");
			return "notice/noticeDetail";
	}
	
	@RequestMapping(value = "/noticeDetail/{notice_id}")
	public String noticeEdit(@PathVariable String notice_id, Model model) {
		logger.info("notice detail page [notice_id = {}]",notice_id);
		model.addAttribute("notice",noticeDao.getNoticeOne(notice_id));
		return "notice/noticeDetail";
	}
	
	@ResponseBody
	@RequestMapping(value = "/noticeInsert", method=RequestMethod.POST)
	public int noticeInsert(NoticeVo notice) {
		logger.info("notice insert"); 
		return noticeDao.NoticeInsert(notice);
	}
	
	@ResponseBody
	@RequestMapping(value = "/noticeUpdate", method=RequestMethod.POST)
	public int noticeUpdate(NoticeVo notice) {
		logger.info("notice update {} ", notice.getNotice_id()); 
		return noticeDao.NoticeUpdate(notice);
	}
	
	@ResponseBody
	@RequestMapping(value = "/noticeDelete", method=RequestMethod.POST)
	public int noticeDelete(NoticeVo notice) {
		logger.info("notice delete {} ", notice.getNotice_id()); 
		return noticeDao.NoticeDelete(notice.getNotice_id());
	}	
}

컨트롤러 구성은 위에서 부터 게시판 리스트 페이지, 등록페이지, 수정페이지, 게시판 정보 insert, 게시판 정보 update, 게시판 정보 delete 로 구성하였다.

하나의 메소드에서 여러개의 기능을 하도록 소스를 구성할 수 있으나, 보기 쉽게 할 수 있도록 메소드를 나뉘었다.

또한 noticeList()에서 페이징 처리를 할 수 있도록 소스를 구성하였다.


-> 여기서 @ResponseBody 어노테이션을 사용한 메소드는 view의 ajax로 부터의 요청에 대해 view 즉 jsp의 경로를 돌려주는게 아닌 문자열만 리턴하도록 처리된다.

-> Spring에서 제공하는 @Autowired 어노테이션 대신 java에서 제공하는 @Resource 어노테이션을 사용하여 가용성을 높였다.


2. model 단 구성


<PagingVo.java 소스코드>

package com.spring.test.vo;

import lombok.Data;

@Data
public class PagingVo {
	/** 한 페이지당 게시글 수 **/
    private int pageSize = 10;
    
    /** 한 블럭(range)당 페이지 수 **/
    private int rangeSize = 10;
    
    /** 현재 페이지 **/
    private int curPage = 1;
    
    /** 현재 블럭(range) **/
    private int curRange = 1;
    
    /** 총 게시글 수 **/
    private int listCnt;
    
    /** 총 페이지 수 **/
    private int pageCnt;
    
    /** 총 블럭(range) 수 **/
    private int rangeCnt;
    
    /** 시작 페이지 **/
    private int startPage = 1;
    
    /** 끝 페이지 **/
    private int endPage = 1;
    
    /** 시작 index **/
    private int startIndex = 0;
    
    /** 이전 페이지 **/
    private int prevPage;
    
    /** 다음 페이지 **/
    private int nextPage;
    
    public PagingVo(int listCnt, int curPage){
        
        /**
         * 페이징 처리 순서
         * 1. 총 페이지수
         * 2. 총 블럭(range)수
         * 3. range setting
         */
        
        // 총 게시물 수와 현재 페이지를 Controller로 부터 받아온다.
        /** 현재페이지 **/
        setCurPage(curPage);
        /** 총 게시물 수 **/
        setListCnt(listCnt);
        
        /** 1. 총 페이지 수 **/
        setPageCnt(listCnt);
        /** 2. 총 블럭(range)수 **/
        setRangeCnt(pageCnt);
        /** 3. 블럭(range) setting **/
        rangeSetting(curPage);
        
        /** DB 질의를 위한 startIndex 설정 **/
        setStartIndex(curPage);
    }
 
    public void setPageCnt(int listCnt) {
        this.pageCnt = (int) Math.ceil(listCnt*1.0/pageSize);
    }
    public void setRangeCnt(int pageCnt) {
        this.rangeCnt = (int) Math.ceil(pageCnt*1.0/rangeSize);
    }
    public void rangeSetting(int curPage){
        
        setCurRange(curPage);        
        this.startPage = (curRange - 1) * rangeSize + 1;
        this.endPage = startPage + rangeSize - 1;
        
        if(endPage > pageCnt){
            this.endPage = pageCnt;
        }
        
        this.prevPage = curPage - 1;
        this.nextPage = curPage + 1;
    }
    public void setCurRange(int curPage) {
        this.curRange = (int)((curPage-1)/rangeSize) + 1;
    }
    public void setStartIndex(int curPage) {
        this.startIndex = (curPage-1) * pageSize;
    }
}


<NoticeVo.java 소스코드>

package com.spring.test.vo;

import lombok.Data;

@Data
public class NoticeVo {
	private String notice_id;
	private String notice_title;
	private String notice_coments;
	private String use_yn;
	private String mod_date;
	private int startIndex;
	private int cntPerPage;
}

->lombok의 @Data 어노테이션을 사용하여 setter, getter 생성을 생략할 수 있다.


<NoticeMapper.java 소스코드>

package com.spring.test.dao;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.spring.test.vo.NoticeVo;

@Repository(value = "noticeMapper")
public interface NoticeMapper {
	public int noticeListCount();
	public List<NoticeVo> noticeList(NoticeVo notice);
	public NoticeVo noticeOne(String notice_id);
	public int noticeInsert(NoticeVo notice);
	public int noticeUpdate(NoticeVo notice);
	public int noticeDelete(String notice_id);
}


<NoticeDao.java 소스코드>

package com.spring.test.dao;

import java.util.List;

import javax.annotation.Resource;
import org.springframework.stereotype.Service;

import com.spring.test.vo.NoticeVo;

@Service(value = "noticeDao")
public class NoticeDao{

	@Resource(name = "noticeMapper")
	private NoticeMapper noticeMapper;
	
	public int getNoticeListCount() {
		return noticeMapper.noticeListCount();
	}
	
	public List<NoticeVo> getNoticeList(NoticeVo notice){
		return noticeMapper.noticeList(notice);
	}
	
	public NoticeVo getNoticeOne(String notice_id) {
		return noticeMapper.noticeOne(notice_id);
	}
	
	public int NoticeInsert(NoticeVo notice) {
		return noticeMapper.noticeInsert(notice);
	}
	
	public int NoticeUpdate(NoticeVo notice) {
		return noticeMapper.noticeUpdate(notice);
	}
	
	public int NoticeDelete(String notice_id) {
		return noticeMapper.noticeDelete(notice_id);
	}
}

-> NoticeDao.java에서 현재 소스에는 없지만 추후 트랜잭션을 처리하기에 용이할 것 같다.


<noticeMapper.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">
 <mapper namespace="com.spring.test.dao.NoticeMapper">
 	<resultMap id="NoticeVo" type="com.spring.test.vo.NoticeVo">
		<result property="notice_id" column="notice_id"/>
		<result property="notice_title" column="notice_title"/>
		<result property="notice_coments" column="notice_coments"/>
		<result property="mod_date" column="mod_date"/>
		<result property="use_yn" column="use_yn"/>
	</resultMap>
	
	<select id="noticeListCount" resultType="Integer">
		select count(*)
		from push_notice;
	</select>
	
	<select id="noticeList" resultMap = "NoticeVo">
		select	notice_id,
				notice_title,
				notice_coments,
				use_yn,
				DATE_FORMAT(mod_date, '%Y-%m-%d') mod_date
		from push_notice
		order by notice_id
		limit #{startIndex}, #{cntPerPage}
		
	</select>
	<select id="noticeOne" parameterType="String" resultMap = "NoticeVo">
		select	notice_id,
				notice_title,
				notice_coments
		from push_notice
		where notice_id = #{notice_id}
	</select>
	<insert id="noticeInsert" parameterType="com.spring.test.vo.NoticeVo">
		insert into push_notice
		(
			notice_title,
			notice_coments,
			mod_date
		)
		values
		(
			#{notice_title},
			#{notice_coments},
			NOW()
		)
	</insert>
	<update id="noticeUpdate" parameterType="com.spring.test.vo.NoticeVo">
		update push_notice set
			notice_title = #{notice_title},
			notice_coments = #{notice_coments},
			mod_date = NOW()
		where notice_id = #{notice_id}
	</update>
	<delete id="noticeDelete" parameterType="String">
		delete from push_notice
		where notice_id = #{notice_id}
	</delete>
 </mapper>

비즈니스 로직을 다양한 방법으로 구성할 수 있으나 필자는 위와 같이 구성하는 것이 추후 유지보수 하거나 트랜잭션을 구성하는데 용이하다고 판단되었다.


3. View 단 구성

View단은 noticeList.jsp와 noticeDetail.jsp로 구성하였으며, jquery는 3.3.1버전을 사용하였다.


<noticeList.jsp 소스코드>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="stylesheet" type="text/css" href="/test/resources/css/noticeList.css"> <script type="text/javascript" src="/test/resources/js/jquery-3.3.1.min.js"></script> <script> $(document).ready(function(){ $("#notice_regi").on("click",function(){ location.href="/test/noticeRegi" }); }); function fn_paging(curPage){ location.href="/test/noticeList?curPage="+curPage; } function notice_push(notice_id){ alert(notice_id); } </script> </head> <body> <div class="title">공지사항</div> <div class="contents"> <input type="button" id="notice_regi" value="등록"> <div class="divTable greenTable"> <div class="divTableHeading"> <div class="divTableRow"> <div class="divTableHead"><input type="checkbox"></div> <div class="divTableHead">NO</div> <div class="divTableHead">제목</div> <div class="divTableHead">등록일자</div> <div class="divTableHead"></div> </div> </div> <c:forEach var="v" items="${noticeList}" varStatus="status"> <div class="divTableBody"> <div class="divTableRow"> <div class="divTableCell"><input type="checkbox"></div> <div class="divTableCell">${status.index+1+(paging.curPage-1)*10}</div> <div class="divTableCell"> <a href="/test/noticeDetail/${v.notice_id}">${v.notice_title}</a> </div> <div class="divTableCell">${v.mod_date}</div> <div class="divTableCell"><input type="button" onclick="notice_push(${v.notice_id})" value="전송"></div> </div> </div> </c:forEach> </div> <div class="greenTable outerTableFooter"> <div class="tableFootStyle"> <div class="links"> <a href="#" onClick="fn_paging(1)">[처음]</a> <c:if test="${paging.curPage ne 1}"> <a href="#" onClick="fn_paging(${paging.prevPage })">[이전]</a> </c:if> <c:forEach var="pageNum" begin="${paging.startPage }" end="${paging.endPage }"> <c:choose> <c:when test="${pageNum eq paging.curPage}"> <span style="font-weight: bold;"> <a href="#" onClick="fn_paging(${pageNum })" style="font-weight: bold; color:red;"> ${pageNum } </a> </span> </c:when> <c:otherwise> <a href="#" onClick="fn_paging(${pageNum })">${pageNum }</a> </c:otherwise> </c:choose> </c:forEach> <c:if test="${paging.curPage ne paging.pageCnt && paging.pageCnt > 0}"> <a href="#" onClick="fn_paging(${paging.nextPage })">[다음]</a> </c:if> <c:if test="${paging.curRange ne paging.rangeCnt && paging.rangeCnt > 0}"> <a href="#" onClick="fn_paging(${paging.pageCnt })">[끝]</a> </c:if> </div> </div> </div> <div> 총 게시글 수 : ${paging.listCnt } / 총 페이지 수 : ${paging.pageCnt } / 현재 페이지 : ${paging.curPage } / 현재 블럭 : ${paging.curRange } / 총 블럭 수 : ${paging.rangeCnt } </div> </div> </body> </html>

<noticeDetail.jsp 소스코드>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="stylesheet" type="text/css" href="/test/resources/css/noticeDetail.css"> <script type="text/javascript" src="/test/resources/js/jquery-3.3.1.min.js"></script> <script> $(document).ready(function(){ //공지사항 신규 등록 $("#notice_regist").on("click",function(){ var formData = $("#notice_form").serialize(); $.ajax({ type : "post", url : "/test/noticeInsert", data : formData, success : function(data){ if(data==1) alert("등록 완료"); else alert('등록 실패'); }, error : function(error){ alert("등록 실패"); console.log("notice insert fail : "+error); } }); }); //공지사항 수정 $("#notice_edit").on("click",function(){ var formData = $("#notice_form").serialize(); $.ajax({ type : "post", url : "/test/noticeUpdate", data : formData, success : function(data){ if(data==1) alert("수정 완료"); else alert('수정 실패'); }, error : function(error){ alert("수정 실패"); console.log("notice update fail : "+error); } }); }); //공지사항 삭제 $("#notice_delete").on("click",function(){ var formData = $("#notice_form").serialize(); alert(formData); $.ajax({ type : "post", url : "/test/noticeDelete", data : formData, success : function(data){ if(data==1){ alert("삭제 완료"); location.href="/push/noticeList"; }else alert('삭제 실패'); }, error : function(error){ alert("삭제 실패"); console.log("notice delete fail : "+error); } }); }); $("#notice_backPage").on("click",function(){ location.href="/test/noticeList"; }); }) </script> </head> <body> <div class="big_title">공지사항 수정/삭제/추가</div> <div class="big_contents"> <form id="notice_form"> <input type="hidden" id="notice_id" name="notice_id" value="${notice.notice_id }"> <div class="title"> <div> 제목 </div> <div> <input type="text" id="notice_title" name="notice_title" value="${notice.notice_title}"> </div> </div> <div class="contents"> <div> 내용 </div> <div> <textarea id="notice_coments" name="notice_coments">${notice.notice_coments}</textarea> </div> </div> </form> <div class="footer"> <c:if test="${null eq notice }"> <input type="button" id="notice_regist" value="등록"> </c:if> <c:if test="${null ne notice }"> <input type="button" id="notice_edit" value="수정"> </c:if> <input type="button" id="notice_backPage" value="뒤로"> <input type="button" id="notice_delete" value="삭제"> </div> </div> </body> </html>

noticeDetail.jsp에서 ajax를 사용하여 등록, 수정, 삭제 기능을 처리하였다.

-> ajax에서 serialize()메소드를 통해 POST로 넘길 form data들을 객체 변수로 생성하여 controller에서 NoticeVo 변수로 받을 수 있다.


<noticeList.css 소스코드>

.title{
	position : relative;
	text-align : center;
	left : 50%;
	width : 200px;
	margin-left : -100px;
}
.contents{
	position : relative;
	top : 30px;
	left : 50%;
	width : 1000px;
	margin-left : -500px;
}		
div.greenTable {
  font-family: Georgia, serif;
  border: 0px solid #949494;
  background-color: #FFFFFF;
  width: 100%;
  border-collapse: collapse;
}
.divTable.greenTable .divTableCell, .divTable.greenTable .divTableHead {
  border: 1px solid #000000;
  padding: 3px 2px;
}
.divTable.greenTable .divTableBody .divTableCell {
  font-size: 13px;
}
.divTable.greenTable .divTableHeading .divTableHead {
  font-size: 15px;
  font-weight: bold;
  color: #000000;
  text-align: center;
}
.greenTable .tableFootStyle {
  font-size: 13px;
  font-weight: bold;
}
.greenTable .tableFootStyle {
  font-size: 13px;
}
.greenTable .tableFootStyle .links {
	 text-align: right;
}
.greenTable .tableFootStyle .links a{
  display: inline-block;
  color: #101010;
  padding: 2px 8px;
  border-radius: 5px;
}
.greenTable.outerTableFooter {
  border-top: none;
}
.greenTable.outerTableFooter .tableFootStyle {
  padding: 3px 5px; 
}
.divTableRow>div:first-child{                                        
	width : 5px;
	text-align : center;
}
.divTableRow>div:nth-child(2){
	width : 10px;
	text-align : center;
}
.divTableRow>div:nth-child(3){
	width : 700px;
}
.divTableRow>div:nth-child(4){
	width : 100px;
	text-align : center;
}
.divTableRow>div:nth-child(5){
	width : 10px;
	text-align : center;
}


/* DivTable.com */
.divTable{ display: table; }
.divTableRow { display: table-row; }
.divTableHeading { display: table-header-group;}
.divTableCell, .divTableHead { display: table-cell;}
.divTableHeading { display: table-header-group;}
.divTableFoot { display: table-footer-group;}
.divTableBody { display: table-row-group;}

.divTableBody a,.tableFootStyle a{
	text-decoration:none;
	color : black;
}


<noticeDetail.css 소스코드>

body{
	margin : 0px;
	padding : 0px;
}
.big_title{
	position : relative;
	text-align : center;
	left : 50%;
	width : 200px;
	margin-left : -100px;
}
.big_contents{
	position : relative;
	top : 30px;
	left : 50%;
	width : 800px;
	margin-left : -400px;
}
.title{
	position : absolute;
	border-top : 1px solid black;
	width : 100%;
}
.contents{
	position : absolute;
	top : 30px;
	width : 100%;
	border-top : 1px solid black;
	border-bottom : 1px solid black;
}
.footer{
	position : absolute;
	width : 140px;
	top : 560px;
	left: 50%;
	margin-left : -70px;
}
.title>div, .contents>div{
	display : inline-block;
	padding : 3px 0 3px 0;
	width : 100%;
}
.title>div:first-child, .contents>div:first-child{
	width : 100px;
	text-align : center;
}
.title>div:nth-child(2), .contents>div:nth-child(2){
	width : 600px;
}
.title input[type=text] {
	width : 602px;
}
.contents textarea {
	width : 600px;
	height : 500px;
}
.contents>div:first-child{
	position : relative;
	bottom : 250px;
}

페이징 처리 로직은 https://gangnam-americano.tistory.com/18 을 참고하였다.(참고 사이트에서는 일부 소스가 누락되어있음)


4. 모두 적용이 되었다면 tomcat에 올려 실행시켜 보자.(tomcat 7을 사용하였다.)


<공지사항 리스트 페이지>




<공지사항 상세 페이지>





<공지사항 등록 페이지>




'spring' 카테고리의 다른 글

spring quartz 스케쥴링 java config  (3) 2019.02.15
spring 스케쥴링 설정  (0) 2019.02.12
view에서 특정 함수 반복 실행 방지  (0) 2019.02.12
spring mybatis 셋팅  (0) 2019.02.11

개발 환경 : STS(3.9.5), spring(5.1.2), mybatis, openjdk1.8, mariaDB 등


sts에서 spring legacy project중에 spring mvc project를 생성한다.


pom.xml 사용 라이브러리 : 

- jstl : jsp에서 jstl 태그를 사용하기 위함

- mariadb-java-clien : mariaDB와 connnection을 맺기 위함

- mybatis-spring  : Spring에서 연동을 지원하는 mybatis

- mybatis              

- spring-jdbc : Spring에서 지원하는 JDBC

- commons-dbcp : 커넥션풀을 담당하는 Apache Commons DBCP

- commons-lang3 : 문자열 라이브러리를 apache commons lang으로 통일하기 위함

- log4jdbc-remix : mybatis 로그를 출력하기 위함

- lombok : @data, @slf4 어노테이션 등을 사용하기 위함

- springloaded : java 소스 수정 시 tomcat을 자동으로 reload해주기 위함(tomcat restart를 안해도 된다)


<pom.xml 소스>

		<dependency>
		    <groupId>jstl</groupId>
		    <artifactId>jstl</artifactId>
		    <version>1.2</version>
		</dependency>
		<dependency>
		    <groupId>org.mariadb.jdbc</groupId>
		    <artifactId>mariadb-java-client</artifactId>
		    <version>1.1.7</version>
		</dependency>
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis-spring</artifactId>
		    <version>1.3.2</version>
		</dependency>
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.4.6</version>
		</dependency>
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>5.1.4.RELEASE</version>
		</dependency>
		<dependency>
		    <groupId>commons-dbcp</groupId>
		    <artifactId>commons-dbcp</artifactId>
		    <version>1.4</version>
		</dependency><dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-lang3</artifactId>
		    <version>3.0</version>
		</dependency>
		<dependency>
		    <groupId>org.lazyluke</groupId>
		    <artifactId>log4jdbc-remix</artifactId>
		    <version>0.2.7</version>
		</dependency>
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <version>1.18.4</version>
		    <scope>provided</scope>
		</dependency>
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>springloaded</artifactId>
		    <version>1.2.8.RELEASE</version>
		</dependency>


위와 같이 셋팅 한 후에 mybatis 설정을 위해 config파일들을 추가해야 한다.

추가 경로(이클립스 경로) : src/main/webapp/WEB-INF/spring/

추가 경로 하위에 mybatis-config-base.xml와 mybatis-context.xml을 추가한다.


<mybatis-context.xml 소스 코드>

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"> <bean id="jdbcProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:META-INF/jdbc.properties" /> </bean> <bean id="dataSourceSpied" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource"> <constructor-arg ref="dataSourceSpied" /> <property name="logFormatter"> <bean class="net.sf.log4jdbc.tools.Log4JdbcCustomFormatter"> <property name="loggingType" value="MULTI_LINE" /> <property name="sqlPrefix" value="SQL : "/> </bean> </property> </bean> <bean id="sqlSessionFactory" class="com.spring.test.util.RefreshableSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.spring.test"/> <property name="configLocation" value="WEB-INF/spring/mybatis-config-base.xml" /> <property value="classpath:com/spring/**/dao/mapper/*Mapper.xml" name="mapperLocations" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.spring.test" /> <property name="annotationClass" value="org.springframework.stereotype.Repository"/> </bean> </beans>

기본적인 mybatis셋팅에서 dataSource를 셋팅하는 부분이 살짝 바뀌었다. 이는 mybatis 로그를 출력하기 위한 셋팅이다.

또한 sqlSessionFactory 부분에서 RefreshableSqlSessionFactoryBean.java를 참조하여 셋팅하였는데 mapper 수정 시 tomcat restart를 하지 않고 바로 적용되도록 하기 위함이다.


<RefreshableSqlSessionFactoryBean.java 소스코드>

package com.spring.test.util; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.DisposableBean; import org.springframework.core.io.Resource; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean { private static final Log log = LogFactory.getLog(RefreshableSqlSessionFactoryBean.class); private SqlSessionFactory proxy; private int interval = 500; private Timer timer; private TimerTask task; private Resource[] mapperLocations; /** * 파일 감시 쓰레드가 실행중인지 여부. */ private boolean running = false; private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public void setMapperLocations(Resource[] mapperLocations) { super.setMapperLocations(mapperLocations); this.mapperLocations = mapperLocations; } public void setInterval(int interval) { this.interval = interval; } /** * @throws Exception */ public void refresh() throws Exception { if (log.isInfoEnabled()) { log.info("refreshing sqlMapClient."); } w.lock(); try { super.afterPropertiesSet(); } finally { w.unlock(); } } /** * 싱글톤 멤버로 SqlMapClient 원본 대신 프록시로 설정하도록 오버라이드. */ public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); setRefreshable(); } private void setRefreshable() { proxy = (SqlSessionFactory) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSessionFactory.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // log.debug("method.getName() : " + method.getName()); return method.invoke(getParentObject(), args); } }); task = new TimerTask() { private Map<Resource, Long> map = new HashMap<Resource, Long>(); public void run() { if (isModified()) { try { refresh(); } catch (Exception e) { log.error("caught exception", e); } } } private boolean isModified() { boolean retVal = false; if (mapperLocations != null) { for (int i = 0; i < mapperLocations.length; i++) { Resource mappingLocation = mapperLocations[i]; retVal |= findModifiedResource(mappingLocation); } } return retVal; } private boolean findModifiedResource(Resource resource) { boolean retVal = false; List<String> modifiedResources = new ArrayList<String>(); try { long modified = resource.lastModified(); if (map.containsKey(resource)) { long lastModified = ((Long) map.get(resource)) .longValue(); if (lastModified != modified) { map.put(resource, new Long(modified)); modifiedResources.add(resource.getDescription()); retVal = true; } } else { map.put(resource, new Long(modified)); } } catch (IOException e) { log.error("caught exception", e); } if (retVal) { if (log.isInfoEnabled()) { log.info("modified files : " + modifiedResources); } } return retVal; } }; timer = new Timer(true); resetInterval(); } private Object getParentObject() throws Exception { r.lock(); try { return super.getObject(); } finally { r.unlock(); } } public SqlSessionFactory getObject() { return this.proxy; } public Class<? extends SqlSessionFactory> getObjectType() { return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class); } public boolean isSingleton() { return true; } public void setCheckInterval(int ms) { interval = ms; if (timer != null) { resetInterval(); } } private void resetInterval() { if (running) { timer.cancel(); running = false; } if (interval > 0) { timer.schedule(task, 0, interval); running = true; } } public void destroy() throws Exception { timer.cancel(); } }


<mybatis-config-base.xml 소스코드>

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="false"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25000"/> </settings> <typeHandlers> <!-- java.sql.Timestamp 를 java.util.Date 형으로 반환 --> <typeHandler javaType="java.sql.Timestamp" handler="org.apache.ibatis.type.DateTypeHandler"/> <typeHandler javaType="java.sql.Time" handler="org.apache.ibatis.type.DateTypeHandler"/> <typeHandler javaType="java.sql.Date" handler="org.apache.ibatis.type.DateTypeHandler"/> </typeHandlers> </configuration>

위의 소스는 mybatis 설정 정보 파일로 필요한 옵션을 추가해서 사용하면 되겠다.(mybaits.org 또는 구글링)에서 참조


위의 파일을 추가한 후 설정을 읽어오기 위해 root-context.xml파일을 수정한다.

<root-context.xml 소스 코드>

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Root Context: defines shared resources visible to all other web components --> <import resource="mybatis-context.xml" /> </beans>


root-context.xml의 소스를 수정한 후 mybatis-context.xml에서 설정한 dataSource 정보를 읽어오기 위해 jdbc.properties 파일을 추가한다.

추가 경로 : src/main/resource/META-INF/

<jdbc.properties 소스 코드>

# Maria DB jdbc.driverClassName=org.mariadb.jdbc.Driver jdbc.url=jdbc:mariadb://localhost:3306/javaboja?useUnicode=true&characterEncoding=utf-8&interactiveClient=true&autoReconnect=true&autoReconnectForPools=true&zeroDateTimeBehavior=convertToNull jdbc.username = javaboja jdbc.password = javaboja

마지막으로 mybatis 쿼리 로그를 출력하기 위해 log4j.xml을 수정하자.

<log4j.xml 소스 코드>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.spring.test">
		<level value="info" />
	</logger>
	 <!-- Query Loggers -->
    <logger name="jdbc.sqlonly" additivity="false">
        <level value="INFO" />
        <appender-ref ref="console-infolog" />
    </logger>
    <logger name="jdbc.resultsettable" additivity="false">
        <level value="INFO" />
        <appender-ref ref="console" />
    </logger>
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

위 소스 코드에서 각 패키지를 입력하는 부분은 자기 자신의 프로젝트 패키지 명에 맞게 수정해줘야 한다.

여기까지 진행했다면 셋팅은 끝났다. 이제 게시판을 만들어 보자.

'spring' 카테고리의 다른 글

spring quartz 스케쥴링 java config  (3) 2019.02.15
spring 스케쥴링 설정  (0) 2019.02.12
view에서 특정 함수 반복 실행 방지  (0) 2019.02.12
spring mybatis 게시판  (1) 2019.02.12

+ Recent posts