해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.
이번 시간에는 코드를 이용해서 CRUD 작업을 진행해본다.
Persistence 계층의 작업은 다음 순서로 진행한다.
-. 테이블의 컬럼 구조를 반영한 VO(Value Object) 클래스의 생성
-. MyBatis의 Mapper 인터페이스의 작성 / XML 처리
-. 작성한 Mapper 인터페이스 테스트
1. Persistence(영속) 계층의 구현 준비
1) VO 클래스의 작성
<현재 테이블의 구성>
프로젝트에 org.zerock.domain 패키지를 생성하고, BoardVO 클래스를 정의한다.
BoardVO.java
: 생성한 테이블과 동일한 타입/명칭으로 정의한다.
package org.zerock.domain;
import java.util.Date;
import lombok.Data;
@Data
public class BoardVO {
private Long bno;
private String title;
private String content;
private String writer;
private Date cdate;
private Date udate;
}
2) Mapper 인터페이스와 Mapper XML
: 우선, Mapper 패키지를 스캔(조사)할 수 있도록 root-context.xml을 아래와 같이 수정한다.
root-context.xml 일부
<mybatis-spring:scan base-package="org.zerock.mapper"/>
Mapper 인터페이스 작성하기
: 프로젝트에 org.zerock.mapper 패키지를 생성하고, BoardMapper 인터페이스를 정의한다.
BoardMapper.java
package org.zerock.mapper;
import java.util.List;
import org.zerock.domain.BoardVO;
public interface BoardMapper {
//게시글 전체 조회
public List<BoardVO> getList();
}
Mapper XML 파일 작성하기
: src/main/resources 내에 패키지와 동일한 org/zerock/mapper 단계의 폴더를 생성하고 BoardMapper.xml 파일을 작성한다. (폴더는 한 번에 생성하지 말고 하나씩 생성하도록 하자.)
BoardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mtbatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
<!-- 게시글 전체 조회 -->
<select id="getList" resultType="org.zerock.domain.BoardVO">
<![CDATA[
select * from tbl_board where bno > 0
]]>
</select>
</mapper>
<작성시 유의 사항>
-. <mapper>의 namespace 속성 값을 Mapper 인터페이스와 동일한 이름을 지정해야 한다.
-. <select> 태그의 id 속성 값은 메소드의 이름과 일치해야 한다.
-. resultType 속성의 값은 select 쿼리의 결과를 특정 클래스의 객체로 만들기 위해서 설정한다.
-. XML에 사용한 CDATA 부분은 XML에서 부등호를 사용하기 위해 사용한다.
테스트 수행하기
: org.zerock.mapper.BoardMapperTests 클래스
BoardMapperTests.java
package org.zerock.mapper;
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.zerock.domain.BoardVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
@Setter(onMethod_ = @Autowired)
private BoardMapper mapper;
@Test
public void testGetList() {
mapper.getList().forEach(board -> log.info(board));
}
}
테스트 결과
2. Persistence(영속) 영역의 CRUD 구현
: MyBatis는 내부적으로 JDBC의 PreparedStatement를 활용하고 필요한 파라미터를 처리하는 '?'에 대한 치환은 '#{속성}'을 이용해서 처리한다.
BoardMapper.java
package org.zerock.mapper;
import java.util.List;
import org.zerock.domain.BoardVO;
public interface BoardMapper {
//게시글 전체 조회
public List<BoardVO> getList();
//게시글 삽입 : insert 처리 후 생성된 PK값을 알 필요 없는 경우
public void insert(BoardVO board);
//게시글 삽입 : insert 처리 후 생성된 PK값을 알아야 할 경우
public void insertSelectKey(BoardVO board);
//게시글 읽기 : 게시글 번호를 통해서 1개의 글 읽어오기
public BoardVO read(Long bno);
//게시글 삭제 : 특정 게시글을 삭제하기
//삭제하고자하는 게시글이 있다면 1, 없으면 0 반환
public int delete(Long bno);
//게시글 수정 : 특정 게시글 수정하기
//수정하고자하는 게시글이 있다면 1, 없으면 0 반환
public int update(BoardVO board);
}
BoardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mtbatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
<!-- 게시글 전체 조회 -->
<select id="getList" resultType="org.zerock.domain.BoardVO">
<![CDATA[
select * from tbl_board where bno > 0
]]>
</select>
<!-- 게시글 삽입 : insert 처리 후 생성된 PK값을 알 필요 없는 경우 -->
<insert id="insert">
insert into tbl_board (bno, title, content, writer)
values (seq_board.nextval, #{title}, #{content}, #{writer})
</insert>
<!-- 게시글 삽입 : insert 처리 후 생성된 PK값을 알아야 할 경우 -->
<insert id="insertSelectKey">
<!-- @SelectKey라는 MyBatis의 어노테이션을 이용한다.
미리(Before) SQL을 통해 처리해주고 특정한 이름으로 결과를 보관하는 방식 -->
<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>
<!-- 게시글 읽기 : 게시글 번호를 통해서 1개의 글 읽어오기 -->
<select id ="read" resultType="org.zerock.domain.BoardVO">
select * from tbl_board where bno = #{bno}
</select>
<!-- 게시글 삭제 : 특정 게시글을 삭제하기 -->
<delete id="delete">
delete from tbl_board where bno = #{bno}
</delete>
<!-- 게시글 수정 : 특정 게시글 수정하기 -->
<update id="update">
update tbl_board
set title=#{title},
content=#{content},
writer=#{writer},
udate=sysdate
where bno=#{bno}
</update>
</mapper>
BoardMapperTests.java (테스트 코드)
package org.zerock.mapper;
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.zerock.domain.BoardVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
@Setter(onMethod_ = @Autowired)
private BoardMapper mapper;
@Test
public void testGetList() {
mapper.getList().forEach(board -> log.info(board));
}
@Test
public void testInsert() {
BoardVO board = new BoardVO();
board.setTitle("새로 작성하는 글 테스트");
board.setContent("새로 작성하는 내용 테스트");
board.setWriter("newbie");
mapper.insert(board);
log.info(board);
}
@Test
public void testInsertSelectKey() {
BoardVO board = new BoardVO();
board.setTitle("새로 작성하는 글 테스트 Select Key");
board.setContent("새로 작성하는 내용 테스트 Select Key");
board.setWriter("newbie");
mapper.insertSelectKey(board);
log.info(board);
}
@Test
public void testRead() {
//존재하는 게시글 번호로 테스트 진행 필수
BoardVO board = mapper.read(1L);
log.info(board);
}
@Test
public void testDelete() {
//존재하는 게시글 번호로 테스트 진행 필수
//삭제하고자하는 게시글이 있다면 1, 없으면 0 반환
log.info("DELETE COUNT: " + mapper.delete(22L));
}
@Test
public void testUpdate() {
BoardVO board = new BoardVO();
board.setBno(29L);
board.setTitle("수정한 글 입니다.");
board.setContent("수정하는 내용 테스트");
board.setWriter("user00");
int count = mapper.update(board);
log.info("UPDATE COUNT: " + count);
}
}
테스트 수행 결과
public void insert(BoardVO board); 수헹
public void insertSelectKey(BoardVO board); 수행
: bno 값이 먼저 구해지고, insert 할 때 미리 구한 값을 이용하는 것을 볼 수 있음.
public BoardVO read(Long bno); 수행
public int delete(Long bno); 수행
public int update(BoardVO board); 수행
다음 시간에는 Business 계층의 로직을 좀 더 구체화 해보도록 한다!
'개발 > [Spring] 블로그 만들기' 카테고리의 다른 글
[코드로 배우는 스프링 웹 프로젝트] 10강. 프레젠테이션(웹) 계층의 CRUD 구현 (0) | 2021.12.30 |
---|---|
[코드로 배우는 스프링 웹 프로젝트] 9강. 비즈니스(Business) 계층 구현하기 (0) | 2021.12.30 |
[코드로 배우는 스프링 웹 프로젝트] 7강. 스프링 MVC 프로젝트의 기본 구성 (0) | 2021.12.21 |
[코드로 배우는 스프링 웹 프로젝트] 6강. 스프링 MVC의 Controller 2 (Model, Controller의 Return / Exception 처리) (0) | 2021.12.17 |
[코드로 배우는 스프링 웹 프로젝트] 6강. 스프링 MVC의 Controller 1 (어노테이션, @RequestMapping 활용, 파라미터 수집) (0) | 2021.12.11 |