SampleController.java 일부
@GetMapping("/product/{cat}/{pid}")
public String[] getPath(@PathVariable("cat") String cat, @PathVariable("pid") Integer pid){
return new String[] {"category:"+cat, "productid:" + pid};
}
http://localhost:8080/sample/product/bags/1234 실행화면
Ticket.java
package net.developia.spring04.domain;
import lombok.Data;
@Data
public class Ticket {
private int tno;
private String owner;
private String grade;
}
SampleController.java 일부
@PostMapping("/ticket")
public Ticket convert(@RequestBody Ticket ticket) {
log.info("convert............ticket" + ticket);
return ticket;
}
SampleControllerTests.java
package net.developia.spring04;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.aspectj.lang.annotation.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.google.gson.Gson;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import net.developia.spring04.domain.Ticket;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml", "file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@Log4j
public class SampleControllerTests {
@Setter(onMethod_ = { @Autowired })
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before(value = "")
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void testConvert() throws Exception {
Ticket ticket = new Ticket();
ticket.setTno(123);
ticket.setOwner("Admin");
ticket.setGrade("AAA");
String jsonStr = new Gson().toJson(ticket);
log.info(jsonStr);
mockMvc.perform(post("/sample/ticket")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonStr)).andExpect(status().is(200));
}
}
ERD Cloud
https://www.erdcloud.com/
ace.sql
create table tbl_reply (
rno number(10, 0),
bno number(10, 0) not null,
reply varchar2(1000) not null,
replyer varchar2(50) not null,
replyDate date default sysdate,
updateDate date default sysdate
);
create sequence seq_reply;
alter table tbl_reply add constraint pk_reply primary key (rno);
alter table tbl_reply add constraint fk_reply_board
foreign key (bno) references tbl_board (bno);
ReplyVO.java
package net.developia.spring04.domain;
import java.util.Date;
import lombok.Data;
@Data
public class ReplyVO {
private Long rno;
private Long bno;
private String reply;
private String replyer;
private Date replyDate;
private Date updatedate;
}
ReplyMapper.java
package net.developia.spring04.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import net.developia.spring04.domain.Criteria;
import net.developia.spring04.domain.ReplyVO;
public interface ReplyMapper {
public int insert(ReplyVO vo);
public ReplyVO read(Long bno);
public int delete(Long rno);
public int update(ReplyVO reply);
public List<ReplyVO> getListWithPaging(@Param("cri") Criteria cri, @Param("bno") Long bno);
}
ReplyMapper.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="org.zerock.mapper.ReplyMapper">
<insert id="insert">
insert into tbl_reply (rno, bno, reply, replyer)
values (seq_reply.nextval, #{bno}, #{reply}, #{replyer})
</insert>
<select id="read" resultType="org.zerock.domain.ReplyVO">
select * from tbl_reply where rno = #{rno}
</select>
<delete id="delete">
delete from tbl_reply where rno = #{rno}
</delete>
<update id="update">
update tbl_reply set reply = #{reply}, replyer = #{replyer}, updatedate = sysdate where rno = #{rno}
</update>
<select id="getListWithPaging" resultType="org.zerock.domain.ReplyVO">
select rno, bno, reply, replyer, replyDate, updatedate
from tbl_reply
where bno = #{bno}
order by rno asc
</select>
</mapper>
ReplyMapperTests.java 일부
package net.developia.spring04.mapper;
import java.util.stream.IntStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import net.developia.spring04.domain.ReplyVO;
@RunWith(SpringRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
//Java Config
//@ContextConfiguration(classes = { org.zerock.config.RootConfig.class })
@Log4j
public class ReplyMapperTests {
private Long[] bnoArr = {458862L, 458860L, 458858L, 458857L, 458856L};
@Setter(onMethod_ = @Autowired)
private ReplyMapper mapper;
@Test
public void testCreate() {
IntStream.rangeClosed(1, 10).forEach(i -> {
ReplyVO vo = new ReplyVO();
// 게시물의 번호
vo.setBno(bnoArr[i % 5]);
vo.setReply("댓글 테스트" + i);
vo.setReplyer("replyer" + i);
mapper.insert(vo);
});
}
}
ReplyMapperTests.java
@Test
public void testMapper() {
log.info(mapper);
}
@Test
public void testRead() {
Long targetRno = 5L;
ReplyVO vo = mapper.read(targetRno);
log.info(vo);
}
@Test
public void testDelete() {
Long targetRno = 1L;
mapper.delete(targetRno);
}
@Test
public void testUpdate() {
Long targetRno = 10L;
ReplyVO vo = mapper.read(targetRno);
vo.setReply("Update Reply ");
int count = mapper.update(vo);
log.info("UPDATE COUNT : " + count);
}
ReplyMapperTests.java 일부
@Test
public void testList() {
Criteria cri = new Criteria();
List<ReplyVO> replies = mapper.getListWithPaging(cri, bnoArr[0]);
replies.forEach(reply -> log.info(reply));
}
ReplyService.java
package net.developia.spring04.service;
import java.util.List;
import net.developia.spring04.domain.Criteria;
import net.developia.spring04.domain.ReplyVO;
public interface ReplyService {
public int register(ReplyVO vo);
public ReplyVO get(Long rno);
public int modify(ReplyVO vo);
public int remove(Long rno);
public List<ReplyVO> getList(Criteria cri, Long bno);
}
ReplyServiceImpl.java
package net.developia.spring04.service;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import net.developia.spring04.domain.Criteria;
import net.developia.spring04.domain.ReplyVO;
import net.developia.spring04.mapper.ReplyMapper;
@Service
@Log4j
public class ReplyServiceImpl implements ReplyService{
@Setter(onMethod_ = @Autowired)
private ReplyMapper mapper;
@Override
public int register(ReplyVO vo) {
log.info("register........." + vo);
return mapper.insert(vo);
}
@Override
public ReplyVO get(Long rno) {
log.info("get............" + rno);
return mapper.read(rno);
}
@Override
public int modify(ReplyVO vo) {
log.info("modify............" + vo);
return mapper.update(vo);
}
@Override
public int remove(Long rno) {
log.info("remove.............." + rno);
return mapper.delete(rno);
}
@Override
public List<ReplyVO> getList(Criteria cri, Long bno) {
log.info("get Reply List of a Board " + bno);
return mapper.getListWithPaging(cri, bno);
// return List<ReplyVO>
}
}
ReplyController.java
package net.developia.spring04.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import net.developia.spring04.domain.Criteria;
import net.developia.spring04.domain.ReplyVO;
import net.developia.spring04.service.ReplyService;
@RequestMapping("/replies/")
@RestController
@Log4j
public class ReplyController {
@Setter(onMethod_ = @Autowired)
private ReplyService service;
@PostMapping(value = "/new", consumes = "application/json", produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> create(@RequestBody ReplyVO vo) {
log.info("ReplyVO : " + vo);
int insertCount = service.register(vo);
log.info("Reply INSERT COUNT : " + insertCount);
return insertCount == 1 ? new ResponseEntity<>("success", HttpStatus.OK) : new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
@GetMapping(value = "/pages/{bno}/{page}", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<List<ReplyVO>> getList(@PathVariable("page") int page, @PathVariable("bno") Long bno) {
log.info("getList.............");
Criteria cri = new Criteria(page, 10);
log.info(cri);
return new ResponseEntity<>(service.getList(cri, bno), HttpStatus.OK);
}
@GetMapping(value = "/{rno}", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<ReplyVO> get(@PathVariable("rno") Long rno) {
log.info("get : " + rno);
return new ResponseEntity<>(service.get(rno), HttpStatus.OK);
}
@DeleteMapping(value = "/{rno}", produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> remove(@PathVariable("rno") Long rno) {
log.info("remove: " + rno);
return service.remove(rno) == 1
? new ResponseEntity<>("success", HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
@RequestMapping(method = {RequestMethod.PUT, RequestMethod.PATCH},
value = "/{rno}",
consumes = "application/json",
produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> modify(@RequestBody ReplyVO vo, @PathVariable("rno") Long rno){
vo.setRno(rno);
log.info("rno: " + rno);
log.info("modify: " + vo);
return service.modify(vo) == 1
? new ResponseEntity<>("success", HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
reply.js
console.log("Reply Module..........................");
var replyService = (function(){
function add(reply, callback, error){
console.log("add reply................");
$.ajax({
type: 'post',
url: '/replies/new',
data: JSON.stringify(reply),
contentType: "application/json; charset=utf-8",
success: function(result, status, xhr){
if(callback){
callback(result);
}
},
error: function(xhr, status, er) {
if(error){
error(er);
}
}
})
};
function getList(param, callback, error){
var bno = param.bno;
var page = param.page || 1;
$.getJSON("/replies/pages/" + bno + "/" + page + ".json",
function(data){
if (callback) {
callback(data);
}
}).fail(function(xhr, status, err){
if (error) {
error();
}
});
};
function remove(rno, callback, error) {
$.ajax({
type: 'delete',
url: '/replies/' + rno,
success: function(deleteResult, status, xhr){
if(callback){
callback(deleteResult);
}
},
error: function(xhr, status, er){
if(error){
error(er);
}
}
})
};
function update(reply, callback, error){
console.log("RNO : " + reply.rno);
$.ajax({
type: 'put',
url: '/replies/' + reply.rno,
data: JSON.stringify(reply),
contentType: "application/json; charset=utf-8",
success: function(result, status, xhr){
if(callback){
callback(result);
}
},
error: function(xhr, status, er){
if(error){
error(er);
}
}
});
};
function get(rno, callback, error){
$.get("/replies/" + rno + ".json", function(result){
if(callback){
callback(result);
}
}).fail(function(xhr, status, err){
if(error){
error();
}
})
};
function displayTime(timeValue){
var today = new Date();
var gap = today.getTime() - timeValue;
var dateObj = new Date(timeValue);
var str = "";
if( gap < (1000 * 60 * 60 *24 )) {
var hh = dateObj.getHours();
var mi = dateObj.getMinutes();
var ss = dateObj.getSeconds();
return [ (hh > 9 ? '' : '0') + hh, ':', (mi > 9 ? '' : '0' ) + mi, ':', (ss>9 ? '': '0' ) + ss].join('');
} else {
var yy = dateObj.getFullYear();
var mm = dateObj.getMonth() + 1; // getMonth() is zero-based
var dd = dateObj.getDate();
return [ yy, '/', (mm > 9 ? '' : '0') + mm, '/', (dd > 9 ? '' : '0') + dd ].join('');
}
};
return {
add: add,
getList: getList,
remove: remove,
update: update,
get: get,
displayTime: displayTime
};
})();
get.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ include file="../includes/header.jsp" %>
<!-- Modal 추가할 차례 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">REPLY MODAL</h4>
</div>
<div class="modal-body">
<div class="form-grouop">
<label>Reply</label>
<input class="form-control" name="reply" value="New Reply!!">
</div>
<div class="form-group">
<label>Replyer</label>
<input class="form-control" name="replyer" value="replyer">
</div>
<div class="form-group">
<label>Reply Date</label>
<input class="form-control" name="replyDate" value="">
</div>
</div>
<div class="modal-footer">
<button id="modalModBtn" type="button" class="btn btn-warning">Modify</button>
<button id="modalRemoveBtn" type="button" class="btn btn-danger">Remove</button>
<button id="modalRegisterBtn" type="button" class="btn btn-primary">Register</button>
<button id="modalCloseBtn" type="button" class="btn btn-default">Close</button>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="/resources/js/reply.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var bnoValue = '<c:out value="${board.bno}"/>';
var replyUL = $(".chat");
showList(1);
function showList(page){
replyService.getList({bno:bnoValue, page: page || 1}, function(list){
var str = "";
if( list == null || list.length == 0){
replyUL.html("");
return;
}
for (var i = 0, len = list.length || 0; i < len; i++){
str += "<li class='list clearfix' data-rno='" + list[i].rno + "'>";
str += "<div><div class='header'><strong class='primary-font'>" + list[i].replyer + "</strong>";
/* str += "<small class='pull-right text-muted'>" + list[i].replyDate + "</small></div>"; */
str += "<small class='pull-right text-muted'>" + replyService.displayTime(list[i].replyDate) + "</small></div>";
str += "<p>" + list[i].reply + "</p></div></li>";
}
replyUL.html(str);
})
}
var modal = $('.modal');
var modalInputReply = modal.find("input[name='reply']");
var modalInputReplyer = modal.find("input[name='replyer']");
var modalInputReplyDate = modal.find("input[name='replyDate']");
var modalModBtn = $('#modalModBtn');
var modalRemoveBtn = $('#modalRemoveBtn');
var modalRegisterBtn = $('#modalRegisterBtn');
$("#addReplyBtn").on('click', function(e){
console.log('addReplyBtn');
modal.find("input").val("");
modalInputReplyDate.closest('div').hide();
modal.find("button[id != 'modalCloseBtn']").hide();
modalRegisterBtn.show();
$(".modal").modal("show");
})
///
modelRegisterBtn.on("click", function(e){
// p423 시작할 차
})
var operForm = $("#operForm");
$("button[data-oper='modify']").on("click", function(e){
operForm.attr("action", "/board/modify").submit();
});
$("button[data-oper='list']").on("click", function(e){
operForm.find("#bno").remove();
operForm.attr("action", "/board/list");
operForm.submit();
})
});
</script>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Board Register</h1>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">Board Read Page</div>
<div class="panel-body">
<div class="form-group">
<label>Bno</label> <input class="form-control" name='bno' value='<c:out value="${board.bno}"/>' readonly="readonly">
</div>
<div class="form-group">
<label>Title</label> <input class="form-control" name='title' value='<c:out value="${board.title}"/>' readonly="readonly">
</div>
<div class="form-group">
<label>Text area</label> <textarea class="form-control" rows="3" name='content' readonly="readonly"><c:out value="${board.content}"/></textarea>
</div>
<div class="form-group">
<label>Writer</label> <input class="form-control" name='writer' value='<c:out value="${board.writer}"/>' readonly="readonly">
</div>
<button data-oper='modify' class="btn btn-default">Modify</button>
<button data-oper='list' class="btn btn-info">List</button>
<form id='operForm' action="/board/modify" method="get">
<input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno}"/>'>
<input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum}"/>'>
<input type='hidden' name='amount' value='<c:out value="${cri.amount}"/>'>
<input type='hidden' name='keyword' value='<c:out value="${cri.keyword}"/>'>
<input type='hidden' name='type' value='<c:out value="${cri.type}"/>'>
</form>
</div>
</div>
</div>
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-comments fa-fw"></i> Reply
<button id="addReplyBtn" class="btn btn-primary btn-xs pull-right">New Reply</button>
</div>
<div class="panel-body">
<ul class="chat">
<li class="left clearfix" data-rno="12">
<div>
<div class="header">
<strong class="primary-font">user00</strong>
<small class="pull-right text-muted">2018-01-01 13:13</small>
</div>
<p>Good job!</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<%@ include file="../includes/footer.jsp" %>