- Transaction
만약 DB의 데이터를 수정하는 도중에 예외가 발생한다면 이전 상태로 롤백하기 위해 사용되는 것이 트랜잭션이다. 트랜잭션은 더 이상 쪼갤 수 없는 최소 작업 단위를 의미한다. 그래서 트랜잭션은 commit으로 성공 하거나 rollback으로 실패 이후 취소되어야 한다.
- Spring에서 제공하는 Transaction을 이용해 코딩을 해보겠다.
- 트랜잭션을 위해서 라이브러리를 추가하겠다.
- setup 프로젝트에도 동일하게 적용시켜준다.
- /ex04/pom.xml
.....
<!-- 트랜잭션 처리를 위한 라이브러리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
.....
- /ex04/src/main/webapp/WEB-INF/spring/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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<property name="url" value="jdbc:log4jdbc:mysql://127.0.0.1:3306/boarddb"></property>
<property name="username" value="board"></property>
<property name="password" value="pass"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <<<---
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/mapper/**/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" <<<---
destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
</bean>
<context:component-scan base-package="com.example.mapper"></context:component-scan>
<context:component-scan base-package="com.example.service"></context:component-scan>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven />
</beans>
- /ex04/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
.....
<context:component-scan base-package="com.example.controller" />
<context:component-scan base-package="com.example.service"/>
</beans:beans>
- /ex04/src/main/java/com/example/domain/BoardVO.java
package com.example.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
public class BoardVO {
private int bno;
private String title;
private String content;
private String writer;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
private Date regdate;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
private Date updatedate;
private int viewcount;
.....
- /ex04/src/main/resources/mapper/BoardMapper.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.example.mapper.BoardMapper">
<!-- list 출력 -->
<select id="list" resultType="com.example.domain.BoardVO">
select * from tbl_board
order by bno desc
limit 0,5
</select>
<!-- 글 읽어오기 read -->
<select id="read" resultType="com.example.domain.BoardVO">
select * from tbl_board
where bno=#{bno}
</select>
<!-- 조회수 update -->
<update id="updateview">
update tbl_board set viewcount=viewcount+1
where bno=#{bno}
</update>
</mapper>
- mapper interface를 만들겠다.
- /ex04/src/main/java/com/example/mapper/BoardMapper.java
package com.example.mapper;
import java.util.List;
import com.example.domain.BoardVO;
public interface BoardMapper {
public List<BoardVO> list();
public BoardVO read(int bno);
public void updateview(int bno);
}
- 트랜잭션 작업을 위해서 인터페이스를 가지고 구현체를 만들겠다.
- /ex04/src/main/java/com/example/mapper/BoardMapperImpl.java
package com.example.mapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.domain.BoardVO;
// 구현체임을 나타낸다.
@Repository
public class BoardMapperImpl implements BoardMapper {
// db 실행을 위한 세션 연결
@Autowired
SqlSession session;
// namespace 지정
String namespace = "com.example.mapper.BoardMapper";
@Override
public List<BoardVO> list() {
return session.selectList(namespace + ".list"); // .xml 의 id와 동일하게
}
@Override
public BoardVO read(int bno) {
return session.selectOne(namespace + ".read", bno); // .xml 의 id와 동일하게
}
@Override
public void updateview(int bno) {
session.update(namespace + ".updateview", bno); // .xml 의 id와 동일하게
}
}
- 테스트 해보겠다.
- /ex04/src/test/java/com/example/controller/MysqlTest.java
package com.example.controller;
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.SpringJUnit4ClassRunner;
import com.example.mapper.BoardMapper;
import com.example.mapper.MysqlMapper;
@RunWith(SpringJUnit4ClassRunner.class) // 먼저 SpringJUnit4ClassRunner.class
// import한다.
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class MysqlTest {
@Autowired
BoardMapper bmapper;
// list get test
@Test
public void list(){
bmapper.list();
}
}
- 이제 트랜잭션을 위한 service interface를 만들어 보겠다.
- /ex04/src/main/java/com/example/service/BoardService.java
package com.example.service;
import com.example.domain.BoardVO;
public interface BoardService {
public BoardVO read(int bno);
}
- 구현체를 만들겠다.
- /ex04/src/main/java/com/example/service/BoardServiceImpl.java
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.domain.BoardVO;
import com.example.mapper.BoardMapper;
// 서비스 어노테이션
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
BoardMapper bmapper;
@Override
public BoardVO read(int bno) {
bmapper.updateview(bno);
return bmapper.read(bno);
}
}
- 테스트 해보겠다.
- /ex04/src/test/java/com/example/controller/MysqlTest.java
package com.example.controller;
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.SpringJUnit4ClassRunner;
import com.example.domain.BoardVO;
import com.example.domain.Criteria;
import com.example.mapper.BoardMapper;
import com.example.mapper.MysqlMapper;
import com.example.service.BoardService;
@RunWith(SpringJUnit4ClassRunner.class) // 먼저 SpringJUnit4ClassRunner.class
// import한다.
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class MysqlTest {
@Autowired
BoardMapper bmapper;
@Autowired
BoardService service;
// relation list get test
@Test
public void list(){
bmapper.list();
}
// tuple read test
// @Test
// public void read(){
// bmapper.read(259);
// }
// update test
@Test
public void read(){
service.read(262);
}
}
- 데이터 무결성을 위해서 트랜잭션 어노테이션을 추가해주겠다.
- /ex04/src/main/java/com/example/service/BoardServiceImpl.java
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.domain.BoardVO;
import com.example.mapper.BoardMapper;
// 서비스 어노테이션
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
BoardMapper bmapper;
// 트랜잭션 어노테이션 둘중의 하나라도 오류가 나면 이전 상태로 롤백한다.
@Transactional
@Override
public BoardVO read(int bno) {
bmapper.updateview(bno);
return bmapper.read(bno);
}
}
- 이제 컨트롤러를 만들겠다.
- /ex04/src/main/java/com/example/controller/BoardController.java
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.domain.BoardVO;
import com.example.mapper.BoardMapper;
import com.example.service.BoardService;
@Controller
//@RequestMapping("/board")
public class BoardController {
// @Autowired 해당 변수 및 메서드에 스프링이 관리하는 Bean을 자동으로 매핑
@Autowired
BoardMapper bmapper;
@Autowired
BoardService bservice;
// 2. 데이터 페이지에 출력
@RequestMapping("/list")
public String getList(){
return "list";
}
// 1. data get
@RequestMapping("/list.json")
@ResponseBody // 데이터 출력을 위한 어노테이션
public List<BoardVO> list(){
return bmapper.list();
}
}
- 이제 리스트 출력 페이지를 만들겠다.
- 리스트의 row 클릭시 특정 데이터를 불러오는 read 페이지를 만들겠다.
- /ex04/src/main/webapp/WEB-INF/views/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="/resources/home.css" />
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
</head>
<body>
<h1>[게시판 목록]</h1>
<table id="tbl"></table>
<script id="temp" type="text/x-handlebars-template">
<tr class="title">
<td width="50">No.</td>
<td width="250">제목</td>
<td width="100">작성자</td>
<td width="200">작성일</td>
<td width="200">수정일</td>
<td width="50">조회수</td>
</tr>
{{#each .}}
<tr class="row" onClick="location.href='/read?bno={{bno}}'">
<td>{{bno}}</td>
<td>{{title}}</td>
<td>{{writer}}</td>
<td>{{regdate}}</td>
<td>{{updatedate}}</td>
<td>{{viewcount}}</td>
</tr>
{{/each}}
</script>
</body>
<script>
getList();
function getList(){
$.ajax({
type:'get',
url:'/list.json',
dataType:'json',
success:function(data){
var temp = Handlebars.compile($('#temp').html());
$('#tbl').html(temp(data));
}
})
}
</script>
</html>
- /ex04/src/main/java/com/example/controller/BoardController.java
.....
// 3. 특정 row의 데이터 불러오기
@RequestMapping("/read")
public String getRead(int bno, Model model){
model.addAttribute("vo", bservice.read(bno));
return "read";
}
.....
- /ex04/src/main/webapp/WEB-INF/views/read.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시글 정보</title>
<style>
h1 {
text-align: center;
}
h4:nth-child(1) {
text-align: left;
}
h4:nth-child(2) {
text-align: right;
}
</style>
</head>
<body>
<h1>[게시글 정보]</h1>
<div>
<h1>${vo.title }</h1>
<h4>
<f:formatDate value="${vo.regdate}" pattern="yyyy-MM-dd HH:mm:ss" />
</h4>
<h4>조회수 : ${vo.viewcount }</h4>
<hr />
<p>${vo.content }</p>
<a href="/list">목록으로</a>
</div>
</body>
</html>
- 이제 댓글 갯수를 출력해보도록 하겠다.
- go to mysql
# 테이블에 컬럼 삽입
alter table tbl_board add replycount int default 0;
# 댓글 테이블 데이터 업데이터
update tbl_board set replycount=(select count(*) from tbl_reply where tbl_reply.bno = tbl_board.bno)
where bno>0;
# 확인
select * from tbl_board order by bno desc;
- /ex04/src/main/java/com/example/domain/BoardVO.java
.....
private int replycount;
.....
- /ex04/src/main/webapp/WEB-INF/views/list.jsp
.....
<script id="temp" type="text/x-handlebars-template">
<tr class="title">
<td width="50">No.</td>
<td width="250">제목</td>
<td width="100">작성자</td>
<td width="200">작성일</td>
<td width="200">수정일</td>
<td width="50">조회수</td>
<td width="50">댓글수</td> <<<---
</tr>
{{#each .}}
<tr class="row" onClick="location.href='/read?bno={{bno}}'">
<td>{{bno}}</td>
<td>{{title}}</td>
<td>{{writer}}</td>
<td>{{regdate}}</td>
<td>{{updatedate}}</td>
<td>{{viewcount}}</td>
<td>{{replycount}}</td> <<<---
</tr>
{{/each}}
</script>
.....
- 이제 댓글 입력 댓글 삭제 작업을 해보도록 하겠다.
- /ex04/src/main/resources/mapper/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="com.example.mapper.ReplyMapper">
<select id="list" resultType="com.example.domain.ReplyVO">
select * from tbl_reply
where bno=#{bno}
order by rno desc
limit 0,5
</select>
</mapper>
- /ex04/src/main/java/com/example/domain/ReplyVO.java
package com.example.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
public class ReplyVO {
private int rno;
private int bno;
private String reply;
private String replyer;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
private Date replydate;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Seoul")
private Date updatedate;
.... getter, setter, tostring
- 인터페이스를 만들겠다.
- /ex04/src/main/java/com/example/mapper/ReplyMapper.java
package com.example.mapper;
import java.util.List;
import com.example.domain.ReplyVO;
public interface ReplyMapper {
public List<ReplyVO> list(int bno);
}
- /ex04/src/main/java/com/example/mapper/ReplyMapperImpl.java
package com.example.mapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.domain.ReplyVO;
@Repository
public class ReplyMapperImpl implements ReplyMapper {
@Autowired
SqlSession session;
String namespace="com.example.mapper.ReplyMapper";
@Override
public List<ReplyVO> list(int bno) {
return session.selectList(namespace+".list",bno);
}
}
- 이제 컨트롤러를 만들겠다.
- /ex04/src/main/java/com/example/controller/ReplyController.java
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.domain.ReplyVO;
import com.example.mapper.ReplyMapper;
@RestController
@RequestMapping("/reply")
public class ReplyController {
@Autowired
ReplyMapper mapper;
// @Autowired
@RequestMapping("/list.json")
public List<ReplyVO> list(int bno) {
return mapper.list(bno);
}
}
- 이제 리드 페이지에 출력하도록 하겠다.
- /ex04/src/main/webapp/WEB-INF/views/read.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
<title>게시글 정보</title>
<style>
h1 {
text-align: center;
}
h4:nth-child(1) {
text-align: left;
}
h4:nth-child(2) {
text-align: right;
}
.box {
text-align: left;
margin-top: 10px;
padding: 10px;
border-bottom: 1px dotted gray;
}
.replyer {
font-weight: bold;
color: #396dc0;
margin-bottom: 5px;
}
.replydate {
text-align: right;
}
.replyBox {
padding: 10px;
width: 850px;
margin: 0px auto;
border: 1px dotted gray;
}
</style>
</head>
<body>
<!-- 게시글 란 -->
<h1>[게시글 정보]</h1>
<div>
<h1>${vo.title }</h1>
<h4>
<f:formatDate value="${vo.regdate}" pattern="yyyy-MM-dd HH:mm:ss" />
</h4>
<h4>조회수 : ${vo.viewcount }</h4>
<hr />
<p>${vo.content }</p>
<a href="/list">목록으로</a>
</div>
<hr />
<!-- 댓글 입력란 -->
<div>
<h3>댓글</h3>
<h4>댓글수:${vo.replycount}</h4>
<input type="text" id="txtReply" size=80 />
<button id="btnInsert">등록</button>
</div>
<hr />
<!-- 댓글 출력란 -->
<div id="reply"></div>
<script id="temp" type="text/x-handlebars-template">
{{#each .}}
<div class="box">
<div class="replyer">
<span>[{{rno}}]</span>
<span>{{replyer}}</span>
</div>
<div class="reply">
<span>{{reply}}</span>
</div>
<div class="replydate">
<span>{{replydate}}</span>
<a href="#">삭제</a>
</div>
</div>
{{/each}}
</script>
</body>
<script>
var bno = "${vo.bno}"
getList();
// 댓글 가지고오기
function getList() {
$.ajax({
type : 'get',
url : '/reply/list.json',
dataType : 'json',
data : {
"bno" : bno
},
success : function(data) {
var temp = Handlebars.compile($('#temp').html());
$("#reply").html(temp(data));
}
})
}
</script>
</html>
- 댓글 입력 삭제할 수 있도록 하겠다.
- /ex04/src/main/resources/mapper/ReplyMapper.xml
....
<!-- 댓글 입력 -->
<insert id="insert">
insert into tbl_reply(bno,reply,replyer)
values(#{bno},#{reply},'user03')
</insert>
<!-- 댓글 삭제 -->
<delete id="delete">
delete from tbl_reply
where rno=#{rno}
</delete>
</mapper>
- /ex04/src/main/java/com/example/mapper/ReplyMapper.java
.....
public void insert(ReplyVO vo);
public void delete(int rno);
}
- /ex04/src/main/java/com/example/mapper/ReplyMapperImpl.java
.....
@Override
public void insert(ReplyVO vo) {
session.insert(namespace+".insert",vo);
}
@Override
public void delete(int rno) {
session.delete(namespace+".delete",rno);
}
}
- 이제 컨트롤러
- /ex04/src/main/java/com/example/controller/ReplyController.java
.....
// bno, reply 가지고감
@RequestMapping("/insert")
public void insert(ReplyVO vo) {
mapper.insert(vo);
}
// rno 가지고감
@RequestMapping("/delete")
public void delete(int rno) {
mapper.delete(rno);
}
}
1. insert
- /ex04/src/main/webapp/WEB-INF/views/read.jsp
.....
// 등록 버튼을 눌렀을때
$('#btnInsert').on('click',function(){
var reply = $("#txtReply").val();
$.ajax({
type:'get',
url:'/reply/insert',
data:{"bno":bno, "reply":reply},
success:function(){
alert("등록되었습니다.");
// 댓글란 초기화
$("#txtReply").val("");
getList();
}
})
})
</script>
</html>
2. delete
- /ex04/src/main/webapp/WEB-INF/views/read.jsp
// 삭제 버튼을 눌렀을때
$('#reply').on('click','.box a',function(e){
e.preventDefault();
var rno = $(this).attr("href");
$.ajax({
type:'get',
url:'/reply/delete',
data:{"rno":rno},
success:function(){
alert("삭제되었습니다.");
getList();
}
})
})
</script>
</html>
- 트랜잭션 연습을 다시 한번 해보겠다.
- 프로젝트 ex05를 setup 프로젝트를 복사해서 생성한다.
- ex04에서 BoardVO, ReplyVO를 가지고 온다.
- /ex05/src/main/resources/mapper/Board.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.example.mapper.BoardMapper">
<select id="list" resultType="com.example.domain.BoardVO">
select * from tbl_board
order by bno desc
limit 0,5
</select>
</mapper>
- 인터페이스 생성한다.
- /ex05/src/main/java/com/example/mapper/BoardDAO.java
package com.example.mapper;
import java.util.List;
import com.example.domain.BoardVO;
// 인터페이스는 메서드의 틀만 잡아주는 역할을 한다.
public interface BoardDAO {
public List<BoardVO> list();
}
- 틀(인터페이스)을 이용해 클래스를 만들어주도록 하겠다.
- /ex05/src/main/java/com/example/mapper/BoardDAOImpl.java
package com.example.mapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.domain.BoardVO;
@Repository
// add unimplements method
public class BoardDAOImpl implements BoardDAO {
// 세션을 이용해서 SQL문을 실행한다.
@Autowired
SqlSession session;
String namespace="com.example.mapper.BoardMapper";
@Override
public List<BoardVO> list() {
return session.selectList(namespace+".list"); // namespace위치에 id가 list인
}
}
- 테스트 해보겠다.
- /ex05/src/test/java/com/example/controller/MysqlTest.java
package com.example.controller;
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.SpringJUnit4ClassRunner;
import com.example.mapper.BoardDAO;
import com.example.mapper.MysqlMapper;
@RunWith(SpringJUnit4ClassRunner.class) // 먼저 SpringJUnit4ClassRunner.class
// import한다.
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class MysqlTest {
// 자동으로 연결
@Autowired
private BoardDAO dao;
// 연결해서 getTime을 테스트
@Test
public void getList() {
dao.list();
}
}
- 특정 bno를 read 할 수 있도록 하겠다.
- /ex05/src/main/resources/mapper/Board.xml
.....
<!-- 특정 데이터 READ -->
<select id="read" resultType="com.example.domain.BoardVO">
select * from tbl_board
where bno=#{bno}
</select>
</mapper>
-/ex05/src/main/java/com/example/mapper/BoardDAO.java
....
public BoardVO read(int bno);
}
- /ex05/src/main/java/com/example/mapper/BoardDAOImpl.java
.....
// add unimplements method(read)
@Override
public BoardVO read(int bno) {
return session.selectOne(namespace+".read",bno);
}
}
- /ex05/src/test/java/com/example/controller/MysqlTest.java
.....
// 연결해서 read을 테스트
@Test
public void read() {
dao.read(262);
}
}
- 이제 컨트롤러에서 실행해보도록 하겠다.
- Board 컨트롤러를 만들겠다.
- /ex05/src/main/java/com/example/controller/BoardController.java
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.domain.BoardVO;
import com.example.mapper.BoardDAO;
@Controller
public class BoardController {
@Autowired
BoardDAO dao;
@ResponseBody
@RequestMapping("/list.json")
public List<BoardVO> list(){
return dao.list();
}
}
- 이제 페이지에 출력해보도록 하겠다.
- /ex05/src/main/java/com/example/controller/BoardController.java
.....
// 2. 리스트 페이지 만들기
@RequestMapping("/list")
public String listPage(){
return "list";
}
.....
- 페이지를 만든다.
- /ex05/src/main/webapp/WEB-INF/views/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
</head>
<body>
<h1>[게시판목록]</h1>
<table id="tbl" border=1></table>
<script id="temp" type="text/x-handlebars-template">
{{#each .}}
<tr onClick="location.href='read?bno={{bno}}'">
<td width=100>{{bno}}</td>
<td width=300>{{title}}</td>
<td width=100>{{writer}}</td>
<td width=100>{{viewcount}}</td>
<td width=100>{{replycount}}</td>
</tr>
{{/each}}
</script>
</body>
<script>
getList();
function getList(){
$.ajax({
type: "get",
url: "/list.json",
dataType: "json",
success: function(data){
var temp=Handlebars.compile($("#temp").html());
$("#tbl").html(temp(data));
}
});
}
</script>
</html>
- 이제 특정 bno를 읽어 페이지에 보여줄 수 있도록 하겠다.
- /ex05/src/main/java/com/example/controller/BoardController.java
.....
// 3. 리드 페이지 주소 지정
@RequestMapping("/read")
public String readPage(int bno, Model model) {
model.addAttribute("vo", dao.read(bno));
return "read";
}
.....
- 리드 페이지를 만들겠다.
- /ex05/src/main/webapp/WEB-INF/views/read.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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">
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
<title>게시글 정보</title>
</head>
<body>
<h1>[게시글 정보]</h1>
<div>
<h3>${vo.title }</h3>
<h4>조회수 : ${vo.viewcount }</h4>
<h4>댓글수 : ${vo.replycount }</h4>
<hr />
<p>${vo.content }</p>
<a href="/list">목록으로 이동</a>
</div>
</body>
</html>
- 이제 조회수 증가가 되도록 하겠다.
- /ex05/src/main/resources/mapper/Board.xml
.....
<!-- 조회수가 증가하게 하는 update -->
<update id="updateview">
update tbl_board set viewcount=viewcount+1
where bno=#{bno}
</update>
</mapper>
- DAO에 update 틀을 만들겠다.
-/ex05/src/main/java/com/example/mapper/BoardDAO.java
....
public void updateview(int bno);
}
- 메서드를 오버라이딩 하겠다.
- /ex05/src/main/java/com/example/mapper/BoardDAOImpl.java
.....
// add unimplements method(viewcount update)
@Override
public void updateview(int bno) {
session.update(namespace+".updateview",bno);
}
}
- 서비스 생성
- /ex05/src/main/java/com/example/service/BoardService.java
package com.example.service;
import com.example.domain.BoardVO;
public interface BoardService {
public BoardVO read(int bno);
}
- 인터페이스 재정의 임플리먼트
-/ex05/src/main/java/com/example/service/BoardServiceImpl.java
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.domain.BoardVO;
import com.example.mapper.BoardDAO;
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
BoardDAO dao;
@Transactional // 트랜잭션 설정
@Override
public BoardVO read(int bno) {
dao.updateview(bno);
return dao.read(bno);
}
}
- 이제 컨트롤러에서 서비스의 메서드를 실행할 수 있도록 하겠다.
- /ex05/src/main/java/com/example/controller/BoardController.java
.....
@Controller
public class BoardController {
@Autowired
BoardDAO dao;
@Autowired
BoardService service;
// 3. 리드 페이지 주소 지정
@RequestMapping("/read")
public String readPage(int bno, Model model) {
model.addAttribute("vo", service.read(bno));
return "read";
}
.....
- 댓글 목록 출력 댓글 입력 댓글 삭제를 마찬가지로 해보도록 하겠다.
/ex05/src/main/resources/mapper/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="com.example.mapper.ReplyMapper">
<!-- 댓글 목록 가지고 오기 -->
<select id="list" resultType="com.example.domain.ReplyVO">
select * from tbl_reply
where bno=#{bno}
order by rno desc
limit 0,5
</select>
<!-- 댓글 입력 insert -->
<insert id="insert">
insert into tbl_reply(bno,reply,replyer)
values(#{bno},#{reply},'user02')
</insert>
<!-- 댓글 삭제 delete -->
<delete id="delete">
delete from tbl_reply
where rno=#{rno}
</delete>
v
</mapper>
- 인터페이스 생성
/ex05/src/main/java/com/example/mapper/ReplyDAO.java
package com.example.mapper;
import java.util.List;
import com.example.domain.ReplyVO;
public interface ReplyDAO {
public List<ReplyVO> list(int bno);
public void insert(ReplyVO vo);
public void delete(int rno);
}
- 인터페이스 오버리이딩 임플리먼트 클래스 생성
/ex05/src/main/java/com/example/mapper/ReplyDAOimpl.java
package com.example.mapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.domain.ReplyVO;
@Repository
public class ReplyDAOimpl implements ReplyDAO {
@Autowired
SqlSession session;
BoardDAO dao;
String namespace="com.example.mapper.ReplyMapper";
@Override
public List<ReplyVO> list(int bno) {
return session.selectList(namespace+".list",bno);
}
@Override
public void insert(ReplyVO vo) {
session.insert(namespace+".insert",vo);
}
@Override
public void delete(int rno) {
session.delete(namespace+".delete",rno);
}
}
- 컨트롤러 생성
/ex05/src/main/java/com/example/controller/ReplyController.java
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.domain.ReplyVO;
import com.example.mapper.ReplyDAO;
@RestController
@RequestMapping("/reply")
public class ReplyController {
@Autowired
ReplyDAO dao;
// 3. 댓글 delete
@RequestMapping(value="/delete", method=RequestMethod.POST)
public void delete(int rno){
dao.delete(rno);
}
// 2. 댓글 insert
@RequestMapping(value="/insert", method=RequestMethod.POST)
public void insert(ReplyVO vo){
dao.insert(vo);
}
// 1. json list get
@RequestMapping("/list.json")
public List<ReplyVO> list(int bno){
return dao.list(bno);
}
}
- 이제 read 페이지에 댓글을 출력해주도록 하겠다.
/ex05/src/main/webapp/WEB-INF/views/read.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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">
<link rel="stylesheet" href="/resources/home.css" />
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
<style>
h1 {
text-align: center;
}
h4:nth-child(1) {
text-align: left;
}
h4:nth-child(2) {
text-align: right;
}
.box {
text-align: left;
margin-top: 10px;
padding: 10px;
border-bottom: 1px dotted gray;
}
.replyer {
font-weight: bold;
color: #396dc0;
margin-bottom: 5px;
}
.replydate {
text-align: right;
}
.replyBox {
padding: 10px;
width: 850px;
margin: 0px auto;
border: 1px dotted gray;
}
</style>
<title>게시글 정보</title>
</head>
<body>
<h1>[게시글 정보]</h1>
<!-- 글 정보 -->
<div>
<h3>${vo.title }</h3>
<h4>조회수 : ${vo.viewcount }</h4>
<hr />
<p>${vo.content }</p>
<a href="/list">목록으로 이동</a>
</div>
<hr />
<!-- 댓글 입력란 -->
<div>
<h3>댓글</h3>
<h4>댓글수:${vo.replycount}</h4>
<input type="text" id="txtReply" size=80 />
<button id="btnInsert">등록</button>
</div>
<hr />
<!-- 댓글 출력란 -->
<div id="reply"></div>
<script id="temp" type="text/x-handlebars-template">
{{#each .}}
<div class="box">
<div class="replyer">
<span>[{{rno}}]</span>
<span>{{replyer}}</span>
</div>
<div class="reply">
<span>{{reply}}</span>
</div>
<div class="replydate">
<span>{{replydate}}</span>
<a href="{{rno}}">삭제</a>
</div>
</div>
{{/each}}
</script>
</body>
<script>
var bno = "${vo.bno}"
getList();
// 댓글 가지고오기
function getList() {
$.ajax({
type : 'get',
url : '/reply/list.json',
dataType : 'json',
data : {
"bno" : bno
},
success : function(data) {
var temp = Handlebars.compile($('#temp').html());
$("#reply").html(temp(data));
}
})
}
// 등록 버튼을 눌렀을때
$('#btnInsert').on('click', function() {
var reply = $("#txtReply").val();
$.ajax({
type : 'post',
url : '/reply/insert',
data : {
"bno" : bno,
"reply" : reply
},
success : function() {
alert("등록되었습니다.");
// 댓글란 초기화
$("#txtReply").val("");
getList();
}
})
})
// 삭제 버튼을 눌렀을때
$('#reply').on('click', '.box a', function(e) {
e.preventDefault();
var rno = $(this).attr("href");
$.ajax({
type : 'post',
url : '/reply/delete',
data : {
"rno" : rno
},
success : function() {
alert("삭제되었습니다.");
getList();
}
})
})
</script>
</html>
- 이제 댓글수의 증감을 나타낼 수 있도록 하겠다.
1. insert시 증가
/ex05/src/main/resources/mapper/Board.xml
.....
<!-- 댓글 수 증감 update -->
<update id="updatereply">
update tbl_board set replycount=replycount + #{amount}
where bno = #{bno}
</update>
</mapper>
/ex05/src/main/java/com/example/mapper/BoardDAO.java
.....
public void updatereply(int bno, int amount);
}
/ex05/src/main/java/com/example/mapper/BoardDAOImpl.java
.....
// add unimplements method(replycount update)
@Override
public void updatereply(int bno, int amount) {
HashMap<String, Object> map = new HashMap<>();
map.put("bno", bno);
map.put("amount", amount);
session.update(namespace + ".updatereply", map);
}
}
/ex05/src/main/java/com/example/service/BoardService.java
package com.example.service;
import com.example.domain.ReplyVO;
public interface ReplyService {
public void insert(ReplyVO vo);
public void delete(int rno);
}
/ex05/src/main/java/com/example/service/ReplyServiceImpl.java
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.domain.ReplyVO;
import com.example.mapper.BoardDAO;
import com.example.mapper.ReplyDAO;
@Service
public class ReplyServiceImpl implements ReplyService {
@Autowired
ReplyDAO rdao;
@Autowired
BoardDAO bdao;
@Transactional
@Override
public void insert(ReplyVO vo) {
rdao.insert(vo);
bdao.updatereply(vo.getBno(), 1);
}
@Override
public void delete(int rno) {
rdao.delete(rno);
}
}
/ex05/src/main/java/com/example/controller/ReplyController.java
package com.example.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.domain.BoardVO;
import com.example.domain.ReplyVO;
import com.example.mapper.BoardDAO;
import com.example.mapper.ReplyDAO;
import com.example.service.ReplyService;
@RestController
@RequestMapping("/reply")
public class ReplyController {
@Autowired
ReplyDAO dao;
@Autowired
ReplyService service;
@Autowired
BoardDAO bdao;
// 3. 댓글 delete
@RequestMapping(value="/delete", method=RequestMethod.POST)
public void delete(int rno){
service.delete(rno);
}
// 2. 댓글 insert
@RequestMapping(value="/insert", method=RequestMethod.POST)
public int insert(ReplyVO vo){
service.insert(vo);
// insert 후 변영된 댓글 수를
BoardVO bvo = bdao.read(vo.getBno());
// vo로 가지고와서 ajax에서 insert시 리턴해서 read에 반영하면 된다.
return bvo.getReplycount();
}
// 1. json list get
@RequestMapping("/list.json")
public List<ReplyVO> list(int bno){
return dao.list(bno);
}
}
/ex05/src/main/webapp/WEB-INF/views/read.jsp
.....
// 등록 버튼을 눌렀을때
$('#btnInsert').on('click', function() {
var reply = $("#txtReply").val();
$.ajax({
type : 'post',
url : '/reply/insert',
data : {
"bno" : bno,
"reply" : reply
},
success : function(data) {
alert("등록되었습니다."+data);
// 댓글란 초기화
$("#txtReply").val("");
// 리턴한 댓글수를 삽입한다.
$("#replycount").html("댓글수:"+data);
getList();
}
})
})
.....
2. delete시 감소
- 삭제할 댓글의 bno를 가지고 오기위해서 read sql을 만든다.
/ex05/src/main/resources/mapper/ReplyMapper.xml
.....
<!-- 삭제할 댓글을 불러오기 -->
<select id="read" resultType="com.example.domain.ReplyVO">
select * from tbl_reply
where rno=#{rno}
</select>
</mapper>
- read 메서드를 인터페이스에 정의한다.
/ex05/src/main/java/com/example/mapper/ReplyDAO.java
.....
public ReplyVO read(int rno);
}
- 인터페이스에 정의한 read 메서드를 임플리먼트에 오버라이딩하여 sql을 실행한다.
/ex05/src/main/java/com/example/mapper/ReplyDAOimpl.java
.....
@Override
public ReplyVO read(int rno) {
return session.selectOne(namespace+".read",rno);
}
}
- sql문을 실행하여 가지고 온 데이터를 vo에 담아 해당 vo에 있는 bno값을 이용해 댓글수를 업데이트할 수 있게 하겠다. 단 트랜잭션 처리를 하여 작업 중 실패시 롤백하여 반영이 안되도록 하겠다.
/ex05/src/main/java/com/example/service/ReplyServiceImpl.java
.....
@Transactional
@Override
public void delete(int rno) {
ReplyVO vo = rdao.read(rno);
//System.out.println(vo.toString());
rdao.delete(rno);
//System.out.println(vo.getBno());
bdao.updatereply(vo.getBno(), -1);
}
}
- 이제 컨트롤러에서 기존 dao가 아닌 service를 이용하여 댓글 삭제 시 댓글 갯수가 업데이트 되도록 하겠다. 댓글이 삭되면 삭제된 게시글의 댓글 수를 불러와 리턴하겠다.
/ex05/src/main/java/com/example/controller/ReplyController.java
.....
@Autowired
ReplyService service;
@Autowired
BoardDAO bdao;
// 3. 댓글 delete
@RequestMapping(value="/delete", method=RequestMethod.POST)
public int delete(int rno){
ReplyVO bvo = dao.read(rno);
service.delete(rno);
BoardVO vo = bdao.read(bvo.getBno());
return vo.getReplycount();
}
.....
- 리턴된 값을 insert와 마찬가지로 read page 댓글 수에 적용되도록 하겠다.
/ex05/src/main/webapp/WEB-INF/views/read.jsp
.....
// 삭제 버튼을 눌렀을때
$('#reply').on('click', '.box a', function(e) {
e.preventDefault();
var rno = $(this).attr("href");
$.ajax({
type : 'post',
url : '/reply/delete',
data : {
"rno" : rno
},
success : function(data) {
alert("삭제되었습니다."+data);
$("#replycount").html("댓글수:"+data);
getList();
}
})
})
</script>
</html>
'ICIA 수업일지' 카테고리의 다른 글
2021.10.13 수업일지(Spring Framework, fileupload) (0) | 2021.10.13 |
---|---|
2021.10.12 수업일지(Spring 연습, fileupload 방법 (0) | 2021.10.12 |
2021.10.07 수업일지(Spring Framework 연습) (0) | 2021.10.07 |
2021.10.06 수업일지(Spring Framework 이용 게시판 만들기) (0) | 2021.10.06 |
2021.10.05 수업일지(Spring Framework 시작) (0) | 2021.10.05 |