package com.gonabi.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Criteria {
//getter,setter 는 lombok이 자동으로 만들어준다.
private int pageNum;//페이지 번호
private int amount;//한 페이지당 몇 개의 데이터를 보여줄 것인지 값
public Criteria() { //생성자 constructor
this(1,10);//public Criteria(int pageNum, int amount) 호출, 기본값을 1페이지, 10개의 데이터
}
public Criteria(int pageNum, int amount) { //생성자 오버로딩
this.pageNum = pageNum;
this.amount = amount;
}
}
/*
Criteria 클래스의 용도는 pageNum과 amount 값을 같이 전달하는 용도지만 생성자를 통해서 기본값을 1페이지, 10페이지 10개로 지정해서 처리합니다.
*/
package com.gonabi.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import com.gonabi.domain.BoardVO;
import com.gonabi.domain.Criteria;
public interface BoardMapper {
//@Select("select * from tbl_board where bno > 0")
public List<BoardVO> getList();
public List<BoardVO> getListWithPaging(Criteria cri);
//create
public void insert(BoardVO board);
public void insertSelectKey(BoardVO board);
//read, detail, 조회
public BoardVO read(Long bno);
//delete
public int delete(Long bno);
/*
delete()의 메서드의 리턴 타입은 int로 지정해서 만일 정상적으로 데이터가 삭제되면 1이상의 값을 가지도록 작성합니다.
만일 해당 번호의 게시물이 없다면 0이 출력됩니다.
*/
public int update(BoardVO board);
}
<?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.gonabi.mapper.BoardMapper">
<!--
resultType 속성은 인터페이스에 선언된 메서드의 리턴 타입과 동일하게 작성
-->
<select id="getList" resultType="com.gonabi.domain.BoardVO">
<![CDATA[
select * from tbl_board where bno > 0
]]>
</select>
<!--
ROWNUM과 인라인뷰
ROWNUM은 쉽게 생각해서 SQL이 실행된 결과에 넘버링을 해준다고 생각하면 됩니다.
모든 select문에는 rownum이라는 변수를 이용해서 해당 데이터가 몇 번째로 나오는지 알아낼 수 있습니다.
rownum은 실제 데이터가 아니라 테이블에서 데이터를 추출한 후에 처리되는 변수이므로 상황에 따라서 그 값이 매번 달라질 수 있습니다.
XML의 CDATA 처리가 들어갑니다. CDATA 섹션은 XML에서 사용할 수 없는 부등호를 사용하기 위함인데,
XML을 사용할 경우에는 '<,>'는 태그로 인식하는데, 이로 인해 생기는 문제를 막기 위함입니다. <나 >와 같은 특수 문자를 사용할 수도 있긴 합니다.
인라인뷰에서 BoardVO를 구성하는데 필요한 모든 컬럼과 ROWNUM을 RN이라는 가명을 이용해서 만들어 주고 바깥쪽 SQL에서는 RN컬럼을 조건으로 처리합니다.
11부터 20까지
select bno, title, content, writer, regdate, updatedate
from(
select rownum rn, bno, title, content, writer, regdate, updatedate
from tbl_board
where rownum <= 20
)
where rn > 10
-->
<select id="getListWithPaging" resultType="com.gonabi.domain.BoardVO">
<![CDATA[
select bno, title, content, writer, regdate, updatedate
from(
select rownum rn, bno, title, content, writer, regdate, updatedate
from tbl_board
where rownum <= #{pageNum} * #{amount}
)
where rn > (#{pageNum} -1) * #{amount}
]]>
</select>
<!-- create sequence seq_board;
insert만 처리되고 생성된 PK 값을 알 필요가 없는 경우
insert문이 실행되고 생성된 PK 값을 알아야 하는 경우
-->
<insert id="insert">
insert into tbl_board(bno,title,content,writer)
values( seq_board.nextval, #{title}, #{content}, #{writer} )
</insert>
<insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE" resultType="long">
select seq_board.nextval from dual
</selectKey>
insert into tbl_board(bno, title, content, writer)
values( #{bno}, #{title}, #{content}, #{writer} )
</insert>
<select id="read" resultType="com.gonabi.domain.BoardVO">
select * from tbl_board where bno = #{bno}
</select>
<delete id="delete">
delete tbl_board where bno=#{bno}
</delete>
<update id="update">
update tbl_board
set title=#{title},content=#{content},writer=#{writer}, updatedate = sysdate
where bno = #{bno}
</update>
</mapper>
<!--
BoardMapper의 insert()는 단순히 시퀀스의 다음 값을 구해서 insert할 때 사용합니다.
insert문은 몇 건의 데이터가 변경되었지만을 알려주기 때문에 추가된 데이터의 PK값을 알 수 없지만,
1번의 SQL 처리만으로 작업이 완료되는 장점이 있습니다.
insertSelectKey()는 @SelectKey라는 MyBatis의 어노테이션을 이용합니다.
@SelectKey는 주로 PK 값을 미리(before) SQL을 통해서 처리해 두고 특정한 이름으로 결과를 보관하는 방식입니다.
-->
package com.gonabi.controller;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
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 org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
import com.gonabi.mapper.BoardMapperTests;
import com.gonabi.service.BoardService;
@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 BoardControllerTests {
@Setter(onMethod_ = {@Autowired})
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
/*
@Test
public void testList() throws Exception{
log.info(
mockMvc.perform(MockMvcRequestBuilders.get("/board/list"))
.andReturn()
.getModelAndView()
.getModelMap()
);
}
*/
/*
@Test
public void testRegister() throws Exception{
String resultPage = mockMvc.perform(
MockMvcRequestBuilders.post("/board/register")
.param("title","월요일")
.param("content", "안녕하세요")
.param("writer", "gonabi")
).andReturn().getModelAndView().getViewName();
log.info(resultPage);
}
*/
/*
@Test
public void tetGet() throws Exception{
log.info(
mockMvc.perform(
MockMvcRequestBuilders
.get("/board/get")
.param("bno", "83")
).andReturn()
.getModelAndView().getModelMap()
);
}
*/
/*
@Test
public void testModify() throws Exception{
String resultPage = mockMvc
.perform(
MockMvcRequestBuilders.post("/board/modify")
.param("bno", "81")
.param("title", "듀란트")
.param("content", "듀란트는 어디로 갈까요?")
.param("writer", "gonabi")
).andReturn().getModelAndView().getViewName();
log.info(resultPage);
}
*/
/*
@Test
public void testRemove() throws Exception{
//삭제전 데이터베이스 게시물 번호 확인
String resultPage = mockMvc.perform(
MockMvcRequestBuilders.post("/board/remove")
.param("bno", "64")
).andReturn().getModelAndView().getViewName();
log.info(resultPage);
}
*/
@Test
public void testListPaging() throws Exception{
log.info(mockMvc.perform(
MockMvcRequestBuilders.get("/board/list")
.param("pageNum", "2")
.param("amount", "50")
).andReturn().getModelAndView().getModelMap());
}
}
/*
테스트할 때 MockMvcRequestBuilders의 post()를 이용하면 POST 방식으로 데이터를 전달할 수 있고,
param()을 이용해서 전달해야 하는 파라미터들을 지정할 수 있습니다.
input 태그와 유사한 역할
이러한 방식으로 코드를 작성하면 최초 작성 시에는 일이 많다고 느껴지지만 매번 입력할 필요가 없기 때문에 오류가 발생하거나
수정하는 경우 반복적인 테스트가 수월해 집니다.
테스트 클래스의 선언부에는 @WebAppConfiguration 어노테이션을 적용합니다.
@WebAppConfiguration은 Servlet의 ServletContext 를 이용하기 위해서인데,
스프링에서는 WebApplicationContext라는 존재를 이용하기 위해서입니다.
@Before어노테이션이 적용된 setUp()에서는 import 할때 JUnit을 이용해야 합니다.
@Before가 적용된 메서드는 모든 테스트 전에 매번 실행되는 메서드가 됩니다.
MockMvc는 말 그대로 '가짜 mvc'라고 생각하면 됩니다.
가짜로 URL과 파라미터 등을 브라우저에서 사용하는 것처럼 만들어서 Controller를 실행해 볼 수 있습니다.
testList()는 MockMvcRequestBuilders라는 존재를 이용해서 Model에 어떤 데이터들이 담겨 있는지 확인합니다.
*/