그동안 JPA 로만 공부하다가 이번 팀원끼리 하는 사이드 프로젝트에서는 Mybatis를 사용하기로 하여 Mybatis 를 공부해보려고 합니다.
CREATE TABLE tb_board (
idx INT NOT NULL AUTO_INCREMENT COMMENT '번호 (PK)',
title VARCHAR(100) NOT NULL COMMENT '제목',
content VARCHAR(3000) NOT NULL COMMENT '내용',
writer VARCHAR(20) NOT NULL COMMENT '작성자',
view_cnt INT NOT NULL DEFAULT 0 COMMENT '조회 수',
notice_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '공지글 여부',
secret_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '비밀글 여부',
delete_yn ENUM('Y', 'N') NOT NULL DEFAULT 'N' COMMENT '삭제 여부',
insert_time DATETIME NOT NULL DEFAULT NOW() COMMENT '등록일',
update_time DATETIME NULL COMMENT '수정일',
delete_time DATETIME NULL COMMENT '삭제일',
PRIMARY KEY (idx)
) COMMENT '게시판';
DESC tb_board
,SHOW TABLES
명령어를 입력합니다.게시판 테이블(TB_BOARD)의 구조화 역할을 하는 클래스를 생성합니다.
보통, 테이블 구조화 클래스는 xxxVO,xxxDTO로 네이밍 합니다.
저는 입력받은 데이터의 저장 및 전송을 의미하는 xxxDTO로 네이밍을 하겠습니다.
우선 com.codej.board 패키지에 controller,domain,mapper,service 네 개의 패키지를 추가해줍니다.
다음으로 domain 패키지에 BoardDTO 클래스를 추가하고,다음의 코드를 작성해줍니다.
@Getter
@Setter
public class BoardDTO {
/** 번호 (PK) */
private Long idx;
/** 제목 */
private String title;
/** 내용 */
private String content;
/** 작성자 */
private String writer;
/** 조회 수 */
private int viewCnt;
/** 공지 여부 */
private String noticeYn;
/** 비밀 여부 */
private String secretYn;
/** 삭제 여부 */
private String deleteYn;
/** 등록일 */
private LocalDateTime insertTime;
/** 수정일 */
private LocalDateTime updateTime;
/** 삭제일 */
private LocalDateTime deleteTime;
}
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.board.domain.BoardDTO;
@Mapper
public interface BoardMapper {
public int insertBoard(BoardDTO params);
public BoardDTO selectBoardDetail(Long idx);
public int updateBoard(BoardDTO params);
public int deleteBoard(Long idx);
public List<BoardDTO> selectBoardList();
public int selectBoardTotalCount();
}
List<BoardDTO>
와 같이 "< >"안에 타입을 파라미터로 갖는 형ㅌ태를 제네릭 타입이라고 합니다.<?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.board.mapper.BoardMapper">
<sql id="boardColumns">
idx,
title,
content,
writer,
view_cnt,
notice_yn,
secret_yn,
delte_yn,
insert_time,
update_time,
delte_time
</sql>
<insert id="insertBoard" parameterType="BoardDTO">
INSERT INTO tb_board(
<include refid="boardColumns"/>
)VALUES (
#{idx},
#{title},
#{content},
#{writer},
0,
IFNULL(#{notice_yn},'N'),
IFNULL(#{secret_yn},'N'),
'N',
NOW(),
NULL,
NULL
)
</insert>
<select id="selectBoardDetail" parameterType="Long" resultType="BoardDTO">
SELECT
<include refid="boardColumns"/>
FROM
tb_board
WHERE
delete_yn = 'N'
AND
idx = #{idx};
</select>
<update id="updateBoard" parameterType="BoardDTO">
UPDATE tb_board
SET
update_time = NOW(),
title = #{title},
content = #{content},
writer = #{writer},
notice_yn = IFNULL(#{noticeYn},'N'),
secret_yn = IFNULL(#{secretYn},'N')
WHERE
idx =#{idx}
</update>
<update id="deleteBoard">
UPDATE tb_board
SET
delete_yn = 'Y',
delete_time = NOW()
WEHRE
idx = #{idx}
</update>
<select id="selectBoardList" parameterType="BoardDTO" resultType="BoardDTO">
SELECT
<include refid="boardColumns"/>
FROM
tb_board
WEHRE
delete_yn = 'N'
ORDER BY
notice_yn ASC,
idx DESC,
insert_time DESC
</select>
<select id="selectBoardToTalCount" parameterType="BoardDTO" resultType="int">
SELECT
COUNT(*)
FROM
tb_board
WHERE
delete_yn = 'N'
</select>
</mapper>
<mapper>
태그를 여는 부분의 네임스페이스 속성에는 BoardMapper 인터페이스의 경로가 지정되어 있습니다.<sql>
태그를 이용하여 태그의 시작과 끝 사이에 공통으로 사용되거나, 반복적으로 사용되는 SQL 조각을 정의할 수 있습니다.boardColumns
라는 이름으로 사용합니다.<sql>
태그에 정의한 boardColumns SQL 조각의 인클루드에 사용되는 태그입니다.<sql>
태그의 id를 적어줍니다.CASE WHEN ex1 = ex2 THEN NULL ELSE ex1 END
와 같습니다.notice_yn AS noticeYn
와 같이 별칭처리를 하지 않고, 테이블의 컬럼명과 같이 언더바(_)로 연결하는 스네이크 케이스를 사용합니다.#MyBatis
mybatis.configuration.map-underscore-to-camel-case=true
mport javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@PropertySource("classpath:/application.properties")
public class DBConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariConfig hikariConfig() {
return new HikariConfig();
}
@Bean
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mappers/**/*Mapper.xml"));
factoryBean.setTypeAliasesPackage("com.board.domain");
factoryBean.setConfiguration(mybatisConfg());
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSession() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
public org.apache.ibatis.session.Configuration mybatisConfg() {
return new org.apache.ibatis.session.Configuration();
}
}
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.board.domain.BoardDTO;
import com.board.mapper.BoardMapper;
@SpringBootTest
class MapperTests {
@Autowired
private BoardMapper boardMapper;
@Test
public void testOfInsert() {
BoardDTO params = new BoardDTO();
params.setTitle("1번 게시글 제목");
params.setContent("1번 게시글 내용");
params.setWriter("테스터");
int result = boardMapper.insertBoard(params);
System.out.println("결과는 " + result + "입니다.");
}
}
@Test
public void testOfSelectDetail() {
BoardDTO board = boardMapper.selectBoardDetail((long) 1);
try {
//String boardJson = new ObjectMapper().writeValueAsString(board);
String boardJson = new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(board);
System.out.println("=========================");
System.out.println(boardJson);
System.out.println("=========================");
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}