⭐
MyBatis 는 개발자가 지정한 SQL, 저장프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. MyBatis 는 JDBC 코드와 수동으로 셋팅하는 파라미터와 결과 매핑을 제거한다. MyBatis 는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO 를 설정하고 매핑하기 위해 XML 과 애노테이션을 사용할 수 있다.
lib 파일에 mybatis.jar 를 넣어 준다. 이러면 이제 mybatis 를 사용 가능하다.
xml의 구조
src 폴데 안에 configuration.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>
<properties resource="board/mybatis/db.properties"/>
<typeAliases>
<typeAlias alias="boardDTO" type="board.dto.BoardDTO"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="board/mybatis/boardMapper.xml"/>
</mappers>
</configuration>
: MyBatis 임을 증명한다
property 를 외부에서 파일로 만들고, xml에서는 파일을 불러다 쓸 수 있게 만든다.
<properties resource="student/mybatis/db/properties"/>
: 확장자이며, 파일은 슬래쉬로 구분하여야 한다.
런타임시 MyBatis 의 행위를 조정하기 위한 중요한 값들이다. 다음표는 셋팅과 그 의미 그리고 디폴트 값을 설명한다.
타입 별칭은 자바 타입에 대한 좀더 짧은 이름이다. 오직 XML 설정에서만 사용되며, 타이핑을 줄이기 위해 존재한다.
Mapper 로 매핑된 SQL 구문을 정의할 수 있다. 먼저 설정을 어디다 둘지 결정하고, 지정하여 협업 시 각자 매퍼 파일만 만들어 놓고 dto만 추가할 수 있도록 만들어 준다.
이제 db 의 세부 설정 파일을 다른 곳으로 빼서 보관한다.
이건 그냥 General 파일을 만들어서 작성해 준다.
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:xe
username=fin01
password=fin01
이렇게 작성해 두면 보안성과 관리가 용이해진다고 한다. 참고로 나 자신을 가르키는 ip 주소는
@localhost / @127.0.0.1 이다.
이 파일은 기존 boardDAO 들의 쿼리들을 옮겨 담고, 그것들을 id로 만들어서 BoardMapper.java 에서 사용할 수 있게 하는 역할을 한다.
중요한 점은 parameterType 이 들어가는 값, 그리고 resultType 이 나오는 값이다.
⭐ 명명공간이란?
-> 누군지 정확하게 이야기를 해 줘서 이름이 같아도 구분할 수 있도록 만듦.
-> selectBlog : 중복되면 안 된다
명명공간(Namespaces)이 이전버전에서는 사실 선택사항이었다. 하지만 이제는 패키지경로를 포함한 전체 이름을 가진 구문을 구분하기 위해 필수로 사용해야 한다.
명명공간은 인터페이스 바인딩을 가능하게 한다. 명명공간을 사용하고 자바 패키지의 명명공간을 두면 코드가 깔끔해지고 MyBatis 의 사용성이 크게 향상될 것이다.
1. 이름 분석(Name Resolution): 타이핑을 줄이기 위해, MyBatis 는 구문과 결과맵, 캐시등의 모든 설정요소를 위한 이름 분석 규칙을 사용한다.
2. “com.mypackage.MyMapper.selectAllThings” 과 같은 패키지를 포함한 전체 경로명(Fully qualified names)은 같은 형태의 경로가 있다면 그 경로내에서 직접 찾는다. “selectAllThings” 과 같은 짧은 형태의 이름은 모호하지 않은 엔트리를 참고하기 위해 사용될 수 있다. 그래서 짧은 이름은 모호해서 에러를 자주 보게 되니 되도록 이면 전체 경로를 사용해야 할 것이다.
-> DTO 에 저절로 들어가진다. 만약 이름이 다르다? 안 들어가진다.
<resultMap id="yourResultMap" type="your.package.YourResultType">
<result property="seq" column="your_column_name_in_database" />
</resultMap>
```java
<?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="student.mybatis.studentMapper">
<select id="listBoard" parameterType="map" resultType="boardDTO">
select * from (select rownum rn, A.* from (select * from board order by re_step asc)A) where rn between #{start} and #{end}
</select>
<update id="plusReadcount" parameterType="int">
update board set readcount = readcount + 1 where num = #{num}
</update>
<select id="getBoard" parameterType="int" resultType="boardDTO">
select * from board where num = #{num}
</select>
<update id="newContent">
update board set re_step = re_step + 1
</update>
<update id="oldContent" parameterType="int">
update board set re_step = re_step + 1 where re_step > #{re_step}
</update>
<insert id="insertBoard" parameterType="boardDTO">
insert into board values(board_seq.nextval, #{writer}, #{email}, #{subject}, #{passwd}, sysdate, 0, #{content}, #{ip}, #{re_step}, #{re_level})
</insert>
<delete id="deleteBoard" parameterType="int">
delete from board where num = #{num}
</delete>
<update id="updateBoard" parameterType="boardDTO">
update board set subject=#{subject}, email=#{email}, content=#{content} where num=#{num}
</update>
<select id="getCount" resultType="int">
select count(*) from board
</select>
<select id="findBoard" parameterType="map" resultType="boardDTO">
select * from board where ${search} like '%'||#{searchString}||'%'
</select>
</mapper>
중요한 점은 List, 단일 객체 반환 등은 java 메서드에서 알아서 하는 거고 여기서는 반환 객체만 명명해 주면 된다는 것이다.
참고로 find 에서 보면 명칭도 다르다.
이제 dao 인 BoardMapper이다. 이게 있으면 굳이 servlet 에 디비를 등록해 줄 필요가 없다. 하지만 개인적으로 DAO가 더 편리하다고 생각 중....
레거시한 곳에 남아 있으면 안 되는데 이미 DAO 와 정이 들어버렸나 보다.
package board.mybatis;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import board.dto.BoardDTO;
public class BoardMapper {
private static SqlSessionFactory sqlSessionFactory;
// sql 세션을 만드는 팩토리 디자인 패턴 "공장"
static {
try {
String resource = "configuration.xml"; //my-batis xml파일의 위치
Reader reader = Resources.getResourceAsReader(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}catch(IOException e) {
throw new RuntimeException("configuration 인스턴스 생성 중 오류 발생!!" + e.getMessage(), e);
}
}
public static List<BoardDTO> listBoard(int start, int end) {
Map<String, Object> params = new HashMap<>();
params.put("start", start);
params.put("end", end);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
List<BoardDTO> list = sqlSession.selectList("listBoard", params);
return list;
}finally {
sqlSession.close();
}
}
public static int getCount() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
int res = sqlSession.selectOne("getCount");
return res;
}finally {
sqlSession.close();
}
}
public static int insertBoard(BoardDTO dto) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
if (dto.getNum() == 0) {
sqlSession.update("newContent");
}else {
sqlSession.update("oldContent", dto.getRe_step());
dto.setRe_step(dto.getRe_step() + 1);
dto.setRe_level(dto.getRe_level() + 1);
}
int res = sqlSession.insert("insertBoard", dto);
sqlSession.commit();
return res;
}finally {
sqlSession.close();
}
}
public static void plusReadcount(int num) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
sqlSession.update("plusReadcount", num);
sqlSession.commit();
}finally {
sqlSession.close();
}
}
public static BoardDTO getBoard(int num, String mode) {
SqlSession sqlSession = sqlSessionFactory.openSession();
if (mode.equals("content")) plusReadcount(num);
try {
BoardDTO dto = sqlSession.selectOne("getBoard", num);
return dto;
}finally {
sqlSession.close();
}
}
protected static boolean isPassword(int num, String passwd) {
BoardDTO dto = getBoard(num, passwd);
if (dto.getPasswd().equals(passwd)) {
return true;
}
return false;
}
public static int deleteBoard(int num, String passwd) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
if (isPassword(num, passwd)) {
int res = sqlSession.delete("deleteBoard", num);
sqlSession.commit();
return res;
}
}finally {
sqlSession.close();
}
return -1;
}
public static int updateBoard(BoardDTO dto) {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
if (isPassword(dto.getNum(), dto.getPasswd())) {
int res = sqlSession.update("updateBoard", dto);
sqlSession.commit();
return res;
}
}finally {
sqlSession.close();
}
return -1;
}
public static List<BoardDTO> findBoard(String search, String searchString) {
Map<String, String> params = new HashMap<>();
params.put("search", search);
params.put("searchString", searchString);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
List<BoardDTO> list = sqlSession.selectList("findBoard", params);
return list;
}finally {
sqlSession.close();
}
}
}
중요한 것은 String 에 sqlSession.delete("deleteStudent", id);
등 seqSession에 값을 넣을 때는 object 타입으로 하나만 받아야 한다는 것이다.
어노테이션으로 넣는 경우도 있다고 하는데 나의 경우 Map을 활용해 넣고, xml 파일에서 map 으로 받아 파라미터 값으로 읽었다.
sqlSession 메서드 종류들.
selectOne(String statement) : Object
selectList(String statement) : List<Object>
selectMap(String statement, String mapKey) : Map<Object, Object>
select(String statement, Object parameter) : List<Object>
insert(String statement) : int
insert(String statement, Object parameter) : int
update(String statement) : int
update(String statement, Object parameter) : int
delete(String statement) : int
delete(String statement, Object parameter) : int
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/board/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean class="board.controller.BoardController"/>
</beans>
최종 servlet.
참고로 디비 등록, dao 등록을 다 날리고도 잘 돌아가는 상태이다.
이제 그런 등록은 myBatis 에게 맡기자.
이제 mall 은 myBatis 로 수정하고 나서 블로그 작성하면 좋을 것 같다.