포스팅의 목차는 다음과 같다.
1. 개요 및 설치
1) 개요
2) 설치
2. 사용
1) mapper.xml
2) sql-map-config.xml
3) SqlSessionFactory
4) DAO로직 파일 생성
3. Spring 적용
1) 라이브러리 변경
2) Mappings 파일 위치 이동
3) ApplicationContext.xml 내용 추가
4) Sql-map-config.xml 내용 변경
5) DAO3 내용 변경
1. 개요 및 설치
1) 개요
Mybatis를 사용하는 이유는 JDBCTemplate와 비슷하다.
1. 자바코드의 개입을 줄일 수 있다. (Pstmt, conn, execute, resultSet 등)
2. SQL명령어를 자바코드에서 분리 시킬 수 있다.
-> SQL명령어를 XML파일에서 별도로 관리함으로써 결합도가 낮아진 것이다. (이게 jdbc랑 가장 큰 차이)
3. 실행 결과를 VO 자바 객체로 매핑하는 설정이 가장 핵심 수행 동작이다.
-> 설정이 잘 되어있으면 매핑이 자동으로 잘 될 것이다.
검색에 함께 나올 가능성이 있는 Ibatis는 회사에 인수 되기 전 옛날 버전으로, 동일한 것으로 보아도 무관하다.
2) 설치
이클립스 상단의 help탭에서 Eclips Marketplace를 들어간다.
검색해서 나오는 mybatis관련 프로그램 2개를 전부 설치한다.
한개 설치할 때마다 우측 하단의 초록색 로딩바가 100%가 되고 이클립스 restart가 뜨기 전까지 아무 행동도 하지 않는것이 오류를 방지하는 가장 큰 방법이다. 설치 과정에서 나오는 모든 내용을 동의하고, selectALL해야한다.
pom.xml에 라이브러리를 추가해야한다.
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
아래 사진이 첨부된 부분들을 확인했을때 일치하면 정상 설치 된 것이다.
new를 했을때 mybatis관련한 두 종류의 파일 생성이 가능해지면 정상 설치 된 것이다.
2. 사용
1) mapper.xml
아래의 위치에 sql문과 자바 vo객체를 mapping해주는 xml파일을 생성한다. 이때 mapping을 위한 설정파일은 보편적으로 한개의 테이블당 한개의 xml파일로 구성한다. 허나 경우에 따라 상이할 수 있다.
① namespace
mapper태그에 사용된 namespace속성은, mapper속성 내부에 선언된 속성들을 사용할 객체이름정도로 생각해두면 이해가 쉽다. "BoardDAO.getBoard"이런 형식으로 매핑된 sql문이 적절한 메서드 내부에서 사용될 예정이다.
<?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="BoardDAO">
<select id="getBoard">
SELECT * FROM BOARD WHERE BID=#{bid}
</select>
<select id="getBoardList">
SELECT * FROM BOARD WHERE TITLE LIKE '%'||#{searchKeyword}||'%' ORDER BY BID DESC
</select>
<insert id="insertBoard">
INSERT INTO BOARD VALUES((SELECT NVL(MAX(BID),0)+1 FROM BOARD),#{title},#{writer},#{content},#{filename})
</insert>
<update id="updateBoard">
UPDATE BOARD SET TITLE=#{title},CONTENT=#{content} WHERE BID=#{bid}
</update>
<delete id="deleteBoard">
DELETE BOARD WHERE BID=#{bid}
</delete>
</mapper>
- <select>, <insert>, <update>, <delete>태그들을 사용할 SQL문의 성격에 맞게 매핑해주고, id속성을 통해 해당 sql문을 무어라고 명시할지를 작성해줄 수 있다.
- 이때 기존 DAO의 SQL문에서는 pstmt를 통해 채워넣을 멤버변수(자바 VO 객체)를 ?로 표시하였으나, #{멤버변수명}과 같은 형식으로 변경해준다. 이를 통해 pstmt.set어쩌구(int 혹은 String)(?의 순서, vo.get멤버변수명()); 의 행동을 대체할 수 있다.
- 자바코드와 SQL문이 한 줄에 선언되어있어 구분이 어려우므로 일반적으로는 SQL문을 대문자, JAVA를 소문자로 작성하는 식으로 가독성을 높힌다. 이때 word에 문장을 붙여넣고 SHIFT + F3을 누르면 전체 문장을 대문자로 쉽게 변경할 수 있다.
② resultType
select류에만 나오는 속성으로, 결과를 매핑해주는 속성이다. 이 이름으로 어떤 vo를 참조할건지를 정한 설정 파일을 매핑할 수 있다. 이 설정으로 사용 예시는 아래와 같다. resultType에 작성된 value에 관한 자세한 내용은 다음 단락인 sql-map-config.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="BoardDAO">
<select id="getBoard" resultType="board">
SELECT * FROM BOARD WHERE BID=#{bid}
</select>
<select id="getBoardList" resultType="board">
<![CDATA[
SELECT * FROM BOARD WHERE TITLE LIKE '%'||#{searchKeyword}||'%' ORDER BY BID DESC
]]>
</select>
<insert id="insertBoard">
INSERT INTO BOARD VALUES((SELECT NVL(MAX(BID),0)+1 FROM BOARD),#{title},#{writer},#{content},#{filename})
</insert>
<update id="updateBoard">
UPDATE BOARD SET TITLE=#{title},CONTENT=#{content} WHERE BID=#{bid}
</update>
<delete id="deleteBoard">
DELETE BOARD WHERE BID=#{bid}
</delete>
</mapper>
이 ResultType속성의 설정을 통해 아래의 예시와 같은 작업이 진행된다고 이해할 수 있다.
rs = pstmt.executeQuery();
if(rs.next()) {
vo.setAid(rs.getInt("aid"));
vo.setUid(rs.getString("uid"));
}
③ resultMap
vo멤버변수와 column명이 다를때, 혹은 지정된 그대로가 아니라 본인이 설정하는 대로 mapping되게 할 목적으로 사용한다.
<mapper namespace="BoardDAO">
<!-- type="이 파일이 참조할 설정" id="이 설정정보를 무어라고 명시할지" -->
<resultMap type="board" id="boardResult">
<!-- property="VO 멤버변수 명" column="해당 테이블 속성명" -->
<id property="bid" column="bid" />
<result property="title" column="title" />
<result property="writer" column="writer" />
<result property="content" column="content" />
<result property="filename" column="filename" />
</resultMap>
<select id="getBoard" resultMap="boardResult">
SELECT * FROM BOARD WHERE BID=#{bid}
</select>
</mapper>
④ CDATA Section
캐릭터 데이터라고도 하며 SQL문 내부에 꺽새표시( '>' 혹은 '<' )가 사용될때 쓰이는 개념이다. xml파일의 파서는 꺽새가 사용될때 "태그"로 인식하기 때문에 태그가 아닌 크기를 비교할 목적으로 꺽새를 사용할 예정이라면 해당 꺽새를 "문자열"로 xml파서가 인식할 수 있도록 별도 설정이 필요하게 된다. 이때 해당 설정을 도와주는 명령어가 바로 CDATA이다. 사용 예시는 아래와 같다.
<?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="BoardDAO">
<resultMap type="board" id="boardResult">
<id property="bid" column="bid" />
<result property="title" column="title" />
<result property="writer" column="writer" />
<result property="content" column="content" />
<result property="filename" column="filename" />
</resultMap>
<select id="getBoard" resultMap="boardResult">
<![CDATA[
SELECT * FROM BOARD WHERE BID=#{bid}
]]>
</select>
<select id="getBoardList" resultType="board">
<![CDATA[
SELECT * FROM BOARD WHERE TITLE LIKE '%'||#{searchKeyword}||'%' ORDER BY BID DESC
]]>
</select>
<insert id="insertBoard">
INSERT INTO BOARD VALUES((SELECT NVL(MAX(BID),0)+1 FROM BOARD),#{title},#{writer},#{content},#{filename})
</insert>
<update id="updateBoard">
UPDATE BOARD SET TITLE=#{title},CONTENT=#{content} WHERE BID=#{bid}
</update>
<delete id="deleteBoard">
DELETE BOARD WHERE BID=#{bid}
</delete>
</mapper>
금액검색을 사용하게 될지도 모른다는 가정으로 작업을 진행하였다. 이때 꺽새가 사용되지 않을 예정이라도 select문에는 <![CDATA[ SQL문 ]]>을 작업해주는 것이 좋다.
나중에 추가될 수도 있다는 가정도 존재하며, 추후에 추가할때 피치못할 에러가 발생할 가능성을 줄이기 위함이다.
⑤ Dynamic SQL
유사성이 존재하는 SQL문이 사용자의 입력에 따라 다르게 사용되어야 할때 사용하는 방식이다.
입력에 따라 SQL문이 다르게 사용된다는 경우의 예시는 아래와 같다.
이때 한개의 메서드에서 두개의 SQL문이 사용되는 트랜잭션의 경우와는 다른 관점으로 보아야한다.
Dynamic SQL문을 적용한 예시는 아래와 같다. 이때 CDATA의 영향으로 인해 <if>를 작성하기 위한 태그가 인식되지 않을 수 있으므로 CDATA를 사용해야한다면 <if>태그에 영향이 미치지 않도록 내부에 작성하는 방법으로 진행하여야한다. 또한 if가 시작할때 and로 문장을 연결해줄 수 있도록 한다.
<?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="BoardDAO">
<select id="getBoardList" resultType="board">
SELECT *
FROM BOARD
WHERE 1=1
<if test="searchCondition == 'title'">
AND TITLE LIKE '%'||#{searchKeyword}||'%'
</if>
<if test="searchCondition == 'writer'">
AND WRITER LIKE '%'||#{searchKeyword}||'%'
</if>
<if test="searchCondition == 'content'">
AND CONTENT LIKE '%'||#{searchKeyword}||'%'
</if>
ORDER BY BID DESC
</select>
</mapper>
sql문은 라인별로 해석되는 언어이기 때문에, 줄을 중간에 끊을 수 없이 바로 연결해주어야하며,
항상 참이 되는 조건식(ex. 1=1)이 필요하다!
2) sql-map-config.xml
Mybatis가 참조할 xml파일이다. resultType의 속성이 참조하는 xml파일이기도 하다. 파일명은 일반적으로 아래와 같이 설정한다.
파일 초기 생성시 아래와 같은 내용이 자동으로 작성되어 있겠지만, 사용할 내용과 상이하므로 수정이 필요하다.
수정을 적용한 코드를 첨부하였다.
<?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>
</configuration>
① typeAliases : 별칭설정
어느 위치에 있는 파일을 무어라고 부를 것인지를 설정하는 속성이다. 설정 예시는 아래와 같다.
<?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>
<!-- 별칭 설정 -->
<typeAliases>
<!-- type="참조 파일 위치" alias="무어라고 부를지, 이곳에서 설정한 이름이 mapper.xml의 resultType의 value가 된다." -->
<typeAlias type="com.ham.app.vo.ArticleVO" alias="article"/>
<typeAlias type="com.ham.app.vo.ArticleSet" alias="articleSet"/>
<typeAlias type="com.ham.app.vo.CommentVO" alias="comment"/>
<typeAlias type="com.ham.app.vo.FavVO" alias="fav"/>
<typeAlias type="com.ham.app.vo.UsersVO" alias="users"/>
</typeAliases>
</configuration>
② Data Source설정
어떤 데이터 베이스를 사용할지를 설정할 수 있다. 이때 Mybatis를 어디에 연결하느냐에 따라 내부 내용은 변경되므로 내용 없이 틀만 작성하였다.
<?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>
<!-- 별칭 설정 -->
<typeAliases>
<typeAlias type="com.ham.app.vo.ArticleVO" alias="article"/><!-- 어디 위치의 누구를 뭐라고 부를 것인지! -->
<typeAlias type="com.ham.app.vo.ArticleSet" alias="articleSet"/>
<typeAlias type="com.ham.app.vo.CommentVO" alias="comment"/>
<typeAlias type="com.ham.app.vo.FavVO" alias="fav"/>
<typeAlias type="com.ham.app.vo.UsersVO" alias="users"/>
</typeAliases>
<!-- Data Source설정(DB연동) : 어디(클라이언트, 자바 웹 어플리케이션)에 연결을 하는지에 따라 내용은 변경 사항이 존재한다. -->
<environments default="">
<environment id="">
<transactionManager type=""/> <!-- 트랜잭션 매니저 설정 -->
<dataSource type="">
<property name="driver" value=""/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
</configuration>
3) SqlSessionFactory
Mybatis프레임워크는 sqlSession 객체를 활용한다. 이때 "팩토리(객체를 생성해서 반환하는 주체) 패턴_객체 생성해서 반환하는 로직을 캡슐화 하는 패턴"을 활용하며 이를 통해 SqlSessionFactory라는 클래스가 sqlSession 객체를 생성하여 반환할 것임을 추측 가능하다
# 싱글톤 패턴 : 메모리에 동일한 객체를 하나만 생성하여 재사용하는 패턴
# 팩토리 패턴 : 객체를 반환하는 로직이 캡슐화되어 해당 객체를 별도 설정 없이 바로 반환이 가능한 패턴(게임에 많이 사용)
파일 생성 위치와 이름은 아래와 같다.
코드의 첨부
package com.ham.app.common;
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;
// spring과 연동하는 순간 제공 되는 친구를 사용하기 때문에 존재의 이유 없어짐
public class SqlSessionFactoryBean_ {
private static SqlSessionFactory sessionFactory=null;
static {
try {
if(sessionFactory==null) { // null일때 단 한번 수행된다! 두 번째부터는 아래의 xml파일을 통해 커넥션이랑 전부 되어있으니, 알아서 쓸 수 있음이다.
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();
}
}
4) DAO로직 파일 생성
내용은 아래와 같다. 만일 한개의 메서드 내에서 두개의 SQL문을 수행하는 "트랜잭션"관련 로직을 구현하고 싶다면, 메서드 내부에서 mybatist의 메서드를 두 번 실행하면 된다.
package com.test.app.board.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.test.app.board.BoardVO;
import com.test.app.common.SqlSessionFactoryBean;
// @Repository는 사용 가능하다
public class BoardDAO3 {
// @Autowired의 사용이 불가하다.
private SqlSession mybatis;
public BoardDAO3() { // 의존성 주입을 위해 "생성자 주입"방식을 활용하고 있다.
mybatis=SqlSessionFactoryBean.getSqlSessionInstance();
}
public void insertBoard(BoardVO vo) {
System.out.println("insertBoard() 호출됨");
mybatis.insert("BoardDAO.insertBoard",vo);
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("getBoard() 호출됨");
return (BoardVO)mybatis.selectOne("BoardDAO.getBoard",vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("getBoardList() 호출됨");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard",vo);
}
public void deleteBoard(BoardVO vo) {
mybatis.delete("BoardDAO.deleteBoard",vo);
}
}
3. Spring 적용
1) 라이브러리 변경 (pom.xml)
Spring에 Mybatis를 적용하기 위해서는 해당 포스팅에서 추가했던 라이브러리를 변경해줄 필요가 있다. 본래 Spring과 연결을 위한 라이브러리만 추가되면 되는게 맞지만 간혹 몇대의 PC에서 버전 충돌로 인한 에러가 발생하는 경우가 있기 때문에 필요하지 않은 라이브러리와 필요한 라이브러리를 교차하여 추가해준다. 코드는 아래와 같다.
<!-- ================ Mybatis ================== -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.4</version>
</dependency>
<!-- ============================================== -->
2) Mappings 파일 위치 이동
spring 컨테이너가 알아서 참조할 수 있는 위치로 Mapping 설정 정보가 담긴 파일을 옮겨주는 것이 좋다. 이때 내용이 중복될 필요는 없기 때문에 기존 위치에 존재하던 파일은 삭제한다.
3) ApplicationContext.xml 내용 추가
SqlSessionFactory라는 클래스가 sqlSession 객체를 생성하여 반환하도록 팩토리 패턴을 활용하여 작업했었는데, 이 SqlSessionFactory클래스를 직접 생성하지 않고 프레임워크에서 지원해주는 객체로 대체하여 사용할 수 있다. 이때 DI객체가 존재하기에 아래와 같이 설정을 마친다.
<!-- Mybatis 설정 -->
<!-- SqlSession을 뽑아낼 Factory -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 우리가 아까 작성해서 쓴 친구를 이제는 제공받아서 써보자! -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:sql-map-config.xml" /> <!-- 입력 스트림을 뽑아낼 설정 정보 -->
</bean>
<!-- DAO3파일에서 멤버변수로 필요한 SqlSessionTemplate를 객체화 한다 -->
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSession"></constructor-arg>
</bean>
4) Sql-map-config.xml 내용 변경
해당 설정을 보고 팩토리가 session을 만들어낼 수 있게 된다.
<?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>
<!-- 별칭 설정 -->
<typeAliases>
<typeAlias type="com.ham.app.vo.ArticleVO" alias="article"/><!-- 어디 위치의 누구를 뭐라고 부를 것인지! -->
<typeAlias type="com.ham.app.vo.ArticleSet" alias="articleSet"/>
<typeAlias type="com.ham.app.vo.CommentVO" alias="comment"/>
<typeAlias type="com.ham.app.vo.FavVO" alias="fav"/>
<typeAlias type="com.ham.app.vo.UsersVO" alias="users"/>
</typeAliases>
<!-- SQL mapper연결 -->
<mappers>
<mapper resource="mappings/article-mapper.xml"/><!-- 어디 파일을 참조할 것인지 설정 -->
</mappers>
</configuration>
기존에 작업되어있던 DataSource와 관련된 코드가 없는 이유는, 이미 applicationContext.xml에서 객체화가 완료된 DataSource의 설정정보가 존재하기 때문에 두 번 작업할 필요성이 없기 때문이다.
5) DAO3 내용 변경
이전 순서에서 생성한 SqlSessionTmaplate클래스를 멤버변수로 가지고 있으며, @Autowired를 통해 DI하고 있는데, 이때 해당 설정으로 인해 타입만으로 객체를 참조하여 초기화 할 수 있게 된다.
package com.test.app.board.impl;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.test.app.board.BoardVO;
@Repository("boardDAO")
public class BoardDAO3 {
@Autowired
private SqlSessionTemplate mybatis;
public void insertBoard(BoardVO vo) {
System.out.println("insertBoard() 호출됨");
mybatis.insert("BoardDAO.insertBoard",vo);
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("getBoard() 호출됨");
return (BoardVO)mybatis.selectOne("BoardDAO.getBoard",vo);
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("getBoardList() 호출됨");
return mybatis.selectList("BoardDAO.getBoardList", vo);
}
public void updateBoard(BoardVO vo) {
mybatis.update("BoardDAO.updateBoard",vo);
}
public void deleteBoard(BoardVO vo) {
mybatis.delete("BoardDAO.deleteBoard",vo);
}
}
'Spring' 카테고리의 다른 글
[Boot] Spring Boot 설치 (0) | 2022.04.23 |
---|---|
[JPA] JPA를 활용한 DAO버전관리_4 (0) | 2022.04.21 |
다국어 처리 : 국제화 (0) | 2022.04.13 |
이미지 업로드 + 예외처리 (0) | 2022.04.12 |
[레이어] 비즈니스 컴포넌트 (0) | 2022.04.11 |