실습 코드 참조
moonhy7/SpringFramework: Spring Framework 실습 코드 정리 (github.com)
1.1절 Mybatis 프레임워크 특징
1. Mybatis
- 의미 : Mybatis는 원래 Apache에서 Ibatis였으나 구글로 넘어가면서 바뀐 프레임워크
- 첫 번째 특징 : 한두 줄의 자바 코드로 DB 연동을 처리한다.
- 두 번째 특징 : SQL 명령어를 자바 코드에서 분리하여 XML 파일에 따로 관리한다.
2. JDBC 기반 DB 연동 (비교)
BoardDAO
- JDBCUtil 클래스를 이용하여 DB 커넥션 획득과 해체 작업을 처리해서 그나마 코드가 간결해진 것임
package com.springbook.biz.board.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;
//@Repository("boardDAO")
public class BoardDAO {
//JDBC 관련 변수
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
//SQL 관련 명령어
//private final String BOARD_INSERT = "INSERT INTO BOARD(SEQ, TITLE, WRITER, CONTENT) "
// + "VALUES((SELECT NVL(MAX(SEQ), 0) + 1 FROM BOARD), ?, ?, ?)";
private final String BOARD_INSERT = "INSERT INTO studydb.BOARD(SEQ, TITLE, WRITER, CONTENT) "
+ "VALUES((SELECT IFNULL(MAX(SEQ), 0) + 1 FROM BOARD A), ?, ?, ?)";
// private final String BOARD_INSERT = "INSERT INTO studydb.BOARD(SEQ, TITLE, WRITER, CONTENT) "
// + "VALUES((SELECT IFNULL(MAX(SEQ), 0) + 1 FROM BOARD A), vo.getTitle(), vo.getWriter(), vo.getContent())";
// Object[] args = {vo.getTitle(), vo.getWriter(), vo.getContent()};
// jdbcTemplate.update(BOARD_INSERT, args);
private final String BOARD_UPDATE = "UPDATE studydb.BOARD SET TITLE = ?, CONTENT = ?, WRITER = ? WHERE SEQ = ?";
private final String BOARD_DELETE = "DELETE FROM studydb.BOARD WHERE SEQ = ?";
private final String BOARD_GET = "SELECT * FROM studydb.BOARD WHERE SEQ = ?";
private final String BOARD_LIST = "SELECT * FROM studydb.BOARD ORDER BY SEQ DESC";
private final String BOARD_LIST_T = "SELECT * FROM BOARD WHERE TITLE LIKE CONCAT_WS('%', ?, '%') ORDER BY SEQ DESC";
private final String BOARD_LIST_C = "SELECT * FROM BOARD WHERE CONTENT LIKE CONCAT_WS('%', ?, '%') ORDER BY SEQ DESC";
//CRUD 기능의 메소드 구현
//글 등록
public void insertBoard(BoardVO vo) {
System.out.println("====> JDBC로 insertBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_INSERT);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getWriter());
stmt.setString(3, vo.getContent());
stmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
//글 수정
public void updateBoard(BoardVO vo) {
System.out.println("====> JDBC로 upadateBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_UPDATE);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getContent());
stmt.setString(3, vo.getWriter());
stmt.setInt(4, vo.getSeq());
stmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
//글 삭제
public void deleteBoard(BoardVO vo) {
System.out.println("====> JDBC로 deleteBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_DELETE);
stmt.setInt(1, vo.getSeq());
stmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
//글 상세 조회
public BoardVO getBoard(BoardVO vo) {
System.out.println("====> JDBC로 getBoard() 기능 처리");
BoardVO board = null;
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_GET);
stmt.setInt(1, vo.getSeq());
rs = stmt.executeQuery();
if(rs.next()) {
board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
}
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, stmt, conn);
}
return board;
}
//글 목록 조회
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("====> JDBC로 getBoard() 기능 처리");
List<BoardVO> boardList = new ArrayList<BoardVO>();
try {
conn = JDBCUtil.getConnection();
if(vo.getSearchCondition().equals("TITLE")) {
stmt = conn.prepareStatement(BOARD_LIST_T);
} else if(vo.getSearchCondition().equals("CONTNET")) {
stmt = conn.prepareStatement(BOARD_LIST_C);
}
stmt.setString(1, vo.getSearchKeyword());
rs = stmt.executeQuery();
while(rs.next()) {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
boardList.add(board);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, stmt, conn);
}
return boardList;
}
}
3. Mybatis로 변경한 코드
BoardDAO
- Mybatis는 XML 파일에 저장된 SQL 명령어를 대신 실행하고 실행 결과를 VO 같은 자바 객체에 자동으로 매핑까지 해줌
- 그래서 Mybatis 프레임워크를 데이터 맵퍼라고 부른다.
- Mybatis 프레임워크를 이용하여 DB 연동을 처리하면 대부분 한두 줄의 자바 코드만으로도 원하는 DB 연동 로직을 처리할 수 있게 된다.
- 또한 SQL 명령어에 대한 통합 관리를 위해 자바 소스에서 SQL 구문을 분리한다.
package com.springbook.biz.board.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.util.SqlSessionFactoryBean;
public class BoardDAO {
private SqlSession mybatis;
public BoardDAO() {
mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
}
public void insertBoard(BoardVO vo) {
mybatis.insert("BoardDAO.insertBoard", vo); // 네임스페이스.아이디 형태로 mapper에 있는 쿼리문을 호출할 수 있다.
mybatis.commit(); //HashMap, Map, List, Integer, String
}
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard", vo);
mybatis.commit();
}
public void deleteBoard(BoardVO vo) {
mybatis.delete("BoardDAO.deleteBoard", vo);
mybatis.commit();
}
public BoardVO getBoard(BoardVO vo) {
return (BoardVO) mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
4. XML 파일에 분리된 SQL구문
- 게시글 목록 검색에 사용될 SQL
<?xml version="1.0" encoding="UTF-8" ?>
<!-- mapper DTD 선언 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="BoardDAO">
<select id="getBoardList" resultMap="boardResult">
SELECT * FROM BOARD
WHERE TITLE LIKE CONCAT_WS('%', #{searchKeyword}, '%')
ORDER BY SEQ DESC
</select>
</mapper>
1.2절 Java ORM Plugin 설치
1. Java ORM
- 스프링에서도 STS 플러그인 프로그램을 이용하면 스프링 환경설정 파일을 비롯하여 다양한 기능을 사용했듯이 Mybatis 역시 Java ORM이라는 플러그인 프로그램이 있다.
- Java ORM 플러그인을 이용하면 Mybatis와 관련된 복잡한 XML 설정 파일들을 자동으로 만들고 관리할 수 있다.
2. Java ORM 플러그인 설치 순서
1. javaORMPlugin 압축해제
2. C:\DEV211\sts-3.9.15.RELEASE\features로 이동
3. me.karthy.plugin.java.orm.feature_1.0.0.201411180016 폴더 생성
4. 압축 해제한 javaORMPlugin > fetures의 me.karthy.plugin.java.orm.feature_1.0.0.201411180016.jar 파일 생성한 폴더로 복사
5. C:\DEV211\sts-3.9.15.RELEASE\plugins로 이동
6. javaORMPlugin > plugins의 JavaORMPlugin_1.0.0.201411180016.jar 복사
7. javaORMPlugin의 artifacts.jar, content.jar 복사
8. C:\DEV211\sts-3.9.15.RELEASE\configuration\org.eclipse.equinox.simpleconfigurator로 이동
9. bundles.info파일 vscode로 열기
10. #version=1 밑에 아래의 내용 붙여넣고 저장 후 종료
1.0.0.201411180016.jar,plugins/JavaORMPlugin_1.0.0.201411180016.jar,4,false
3. Java ORM 플러그인 설치 결과
- 다음과 같이 세 가지 플러그인이 검색되면 성공
1.3절 프로젝트 생성 ( _074_MybatisProject )
1. Spring Legacy Project 선택
2. 프로젝트 생성
3. 패키지명 설정
4. Java 버전 설정
5. Mybatis, Ibatis 라이브러리 내려받기
- pom.xml에 엘리먼트 추가
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<!-- Ibatis -->
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
- Maven Dependencies에서 확인이 가능하다.
1.4절 VO 클래스 작성
BoardVO.java
- XML 파일에 저장된 SQL 명령어에 사용자가 입력한 값을 전달하고 실행 결과를 매핑할 VO 클래스 작성하기
package com.springbook.biz.board;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import org.springframework.web.multipart.MultipartFile;
public class BoardVO {
private int seq;
private String title;
private String writer;
private String content;
private Date regDate;
private int cnt;
private String SearchCondition;
private String SearchKeyword;
private MultipartFile uploadFile;
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
public String getSearchCondition() {
return SearchCondition;
}
public void setSearchCondition(String searchCondition) {
SearchCondition = searchCondition;
}
public String getSearchKeyword() {
return SearchKeyword;
}
public void setSearchKeyword(String searchKeyword) {
SearchKeyword = searchKeyword;
}
@Override
public String toString() {
return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regDate="
+ regDate + ", cnt=" + cnt + "]";
}
}
1.5절 SQL Mapper XML 파일 작성
1. SQL Mapper XML 파일 역할
- SQL Mapper XML에서 DB 연동에 필요한 SQL 명령어들이 저장되므로 가장 중요한 파일이다.
2. SQL Mapper XML 파일 생성
1) Mybatis Mapper XML 선택
- 이렇게 Java ORM 플러그인을 통해 파일을 간단히 생성 가능
2) 폴더 이동
src/main/resources 소스 폴더에 mappings라는 패키지 만들고 거기로 파일 이동
3. SQL Mapper XML 파일 수정
- <mapper>를 루트 엘리먼트로 사용한다.
- insert, update, delete, select 엘리먼트를 이용하여 필요한 SQL 구문들을 등록한다.
<?xml version="1.0" encoding="UTF-8" ?>
<!-- mapper DTD 선언 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper 자체가 DAO 분리되어 나온 개념이기 때문에 namespace는 DAO 명을 따라가는 경우가 일반적임 -->
<mapper namespace="BoardDAO">
<resultMap type="board" id="boardResult">
<id property="seq" column="SEQ"/>
<result property="title" column="TITLE"/>
<result property="writer" column="WRITER"/>
<result property="content" column="CONTENT"/>
<result property="regDate" column="REGDATE"/>
<result property="cnt" column="CNT"/>
</resultMap>
<insert id="insertBoard">
<!-- <selectKey keyProperty="seq" resultType="int">
SELECT BOARD_SEQ.NEXTVAL AS SEQ FROM DUAL
</selectKey>
// #{seq} : 사용자 입력 값 or VO에서 가져온 값을 의미 -->
<![CDATA[
INSERT INTO BOARD(
SEQ
, TITLE
, WRITER
, CONTENT
)
VALUES(
#{seq}
, #{title}
, #{writer}
, #{content}
)
]]>
</insert>
<update id="updateBoard">
<![CDATA[
UPDATE BOARD
SET
TITLE = #{title}
, CONTENT = #{content}
WHERE SEQ = #{seq}
]]>
</update>
<delete id="deleteBoard">
<![CDATA[
DELETE FROM BOARD
WHERE SEQ = #{seq}
]]>
</delete>
<select id="getBoard" resultType="board">
<![CDATA[
SELECT * FROM BOARD
WHERE SEQ = #{seq}
]]>
</select>
<!-- <select id="getBoardList" resultType="board" parameterType="com.springbook.biz.board.BoardVO"> -->
<!-- 파라미터타입 : sql-map에서 typeAlias를 주었기 때문에 그냥 board라고 써도 됨 -->
<!-- <select id="getBoardList" resultType="board" parameterType="board"> -->
<select id="getBoardList" resultMap="boardResult">
<![CDATA[
SELECT * FROM BOARD
WHERE TITLE LIKE CONCAT_WS('%', #{searchKeyword}, '%')
ORDER BY SEQ DESC
]]>
</select>
</mapper>
1.6절 Mybatis 환경설정 파일
1. Mybatis Configuration XML 파일 생성
2. 생성된 파일들 확인
- 두 파일의 위치를 리소스 폴더로 이동
3. db.properties 수정
- 데이터베이스 커넥션 관련 프로퍼티 정보가 등록되어있음
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/studydb?serverTimeZone=UTC
jdbc.username=study
jdbc.password=study
4. sql-map-config.xml 파일 수정
1) properties 엘리먼트 : XML 설정에서 사용할 프로퍼티를 선언하거나 참조할 때 사용한다.
2) typeAliases 엘리먼트 : 별칭을 선언하여Sql Mapper에서 설정을 간단히 처리하는데 사용할 수 있다.
3) environments 엘리먼트 : 커넥션을 획득하고 DB 연동을 처리하기 위한 Datasource를 설정한다.
4) mappers 엘리먼트 : SQL 명령어들이 저장된 SQL 파일들을 등록할 수 있다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- Properties 파일 설정 -->
<properties resource="db.properties" />
<!-- Alias 설정 -->
<typeAliases>
<typeAlias type="com.springbook.biz.board.BoardVO" alias="board"></typeAlias>
</typeAliases>
<!-- DataSource 설정 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- Sql Mapper 설정 -->
<mappers>
<mapper resource="mappings/board-mapping.xml" />
<!-- <mapper rewource="mappings/yser-mapping.xml" /> -->
</mappers>
</configuration>
1.7절 SqlSession 객체 생성하기
1. SqlSessionFactoryBean 생성하는 이유
- Mybatis를 이용하여 DAO를 구현하려면 SqlSession 객체가 필요하다.
- SqlSession 객체를 얻으려면 SqlSessionFactory 객체가 필요하다.
- 따라서 DAO 클래스를 구현하기에 앞서 SqlSessionFactory 객체를 생성하는 유틸리티 클래스를 작성한다.
2. SqlSessionFactoryBean.java 코드
package com.springbook.biz.util;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionFactoryBean {
private static SqlSessionFactory sessionFactory =null;
static {
try {
if(sessionFactory == null) {
Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSessionInstance() {
return sessionFactory.openSession();
}
}
3. 핵심 코드 설명
Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
- 첫번째 줄 : Mybatis 메인 설정 파일인 sql-map-confi.xml 파일로부터 설정 정보를 읽어들이는 입력 스트림 생성
- 두번째 줄 : 입력 스트림을 통해 sql-map-confi.xml 파일을 읽어 SqlSessionFactory 객체를 생성
public static SqlSession getSqlSessionInstance() {
return sessionFactory.openSession();
}
- getSqlSessionInstance() 메소드는 SqlSessionFactory 객체로부터 SqlSession 객체를 얻어내어 리턴하는 메소드
- 이 메소드를 이용하여 SqlSession 객체가 필요한 DAO 클래스를 구현하면 된다.
1.8절 DAO 클래스 작성
1. BoardDAO.java
- BoardDAO 클래스는 Mybatis를 이용하여 데이터베이스 연동을 처리해준다.
- 생성자에서 SqlSessionFactoryBean을 이용하여 SqlSession 객체를 얻어낸다.
- 그리고 이 SqlSession 객체의 메소드를 이용하여 CRUD 기능의 메소드를 모두 구현하고 있다.
package com.springbook.biz.board.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.util.SqlSessionFactoryBean;
public class BoardDAO {
private SqlSession mybatis;
public BoardDAO() {
mybatis = SqlSessionFactoryBean.getSqlSessionInstance();
}
public void insertBoard(BoardVO vo) {
mybatis.insert("BoardDAO.insertBoard", vo);
mybatis.commit();
}
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard", vo);
mybatis.commit();
}
public void deleteBoard(BoardVO vo) {
mybatis.delete("BoardDAO.deleteBoard", vo);
mybatis.commit();
}
public BoardVO getBoard(BoardVO vo) {
return (BoardVO) mybatis.selectOne("BoardDAO.getBoard", vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
}
2. 두 개의 인자 역할
- 첫 번째 인자 : 실행될 SQL의 id정보이다. Sql Mapper에 선언된 네임스페이스와 아이디를 조합하여 아이디를 지정
- 두 번째 인자 : parameterType 속성으로 지정된 파라미터 객체이다.
mybatis.delete("BoardDAO.deleteBoard", vo);
1.9절 테스트 클라이언트 작성 및 실행
1. BoardServiceClient.java
- BoardDAO 클래스의 메소드를 테스트하는 클라이언트 프로그램이다.
package com.springbook.biz.board;
import java.sql.SQLException;
import java.util.List;
import com.springbook.biz.board.impl.BoardDAO;
public class BoardServiceClient {
public static void main(String[] args) throws SQLException {
BoardDAO boardDAO = new BoardDAO();
BoardVO vo = new BoardVO();
vo.setTitle("Mybatis 제목");
vo.setWriter("홍길동");
vo.setContent("Mybatis 내용입니다....");
boardDAO.insertBoard(vo);
vo.setSearchCondition("TITLE");
vo.setSearchKeyword("");
List<BoardVO> boardList = boardDAO.getBoardList(vo);
for(BoardVO board : boardList) {
System.out.println("---->" + board.toString());
}
}
}
2. 실행 결과
- 어떤 SQL 명령어들이 실행되었고 실행 시 어떤 파라미터값들이 SQL에 바인딩되었는지 알 수 있다.
- 실행 결과도 간단하게 출력되는 것을 볼 수 있음
3. 전체 소스 구성
'👨💻 2. 웹개발_Back end > 2-6 Spring' 카테고리의 다른 글
[Spring] 3장 스프링과 MyBatis 연동 - 라이브러리 설정 및 DAO 클래스 구현하는 두 가지 방법 / 스프링과 MyBatis 연동 테스트 / Dynamic SQL 적용 (0) | 2021.11.02 |
---|---|
[Spring] 2장 Mapper XML 파일 설정 - Mybatis 구조와 Mapper XML 파일 구조 및 엘리먼트(select, insert, update, delete), 속성들(parameterType, resultType, resultMap) (0) | 2021.11.02 |
[Spring] 7장 데이터 변환 (0) | 2021.11.01 |
[Spring] 6장 다국어 처리 (0) | 2021.11.01 |
[Spring] 5장 파일 업로드 (0) | 2021.11.01 |
댓글