2020년 01월 28일

업데이트:


MyBatis란?

개발자가 지정한 SQL, 저장 프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. 마이바티스는 JDBC로 처리하는 상당부분의 코드와 파라미터 설정및 결과 매핑을 대신해준다. 마이바티스는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO 를 설정해서 매핑하기 위해 XML과 어노테이션을 사용할 수 있다.1




MyBatis 설정

MyBatis_logo
👆🏼 이미지 클릭 시 공식 홈페이지로 이동




라이브러리 다운

홈페이지 사이드 메뉴에서 시작하기를 누른 후 하단에 보이는 mybatis-x.x.x.jar를 클릭한다.
20210128_01



mybatis-3.5.6 zip 파일을 다운한다.
20210128_02



zip 파일 압축을 해제한 후 최상단에 있는 mybatis-3.4.5.jar 파일을 프로젝트 라이브러리 경로에 넣어준다.
20210128_03




source folder 생성

Java Resources > src 내에 새로운 source folder를 만든다.
20210128_04
20210128_05
20210128_06




mybatis-config.xml

XML설정파일에서 지정하는 마이바티스의 핵심이 되는 설정은 트랜잭션을 제어하기 위한 TransactionManager과 함께 데이터베이스 Connection인스턴스를 가져오기 위한 DataSource 를 포함한다.2

mybatis를 사용하기 위해 위에서 만들었던 resoure 폴더에 mybatis-config.xml을 생성한다.
20210128_07
20210128_08

20210128_08

하단의 내용을 config 파일 안에 붙여넣는다. 20210128_10

  • configuration : MyBatis 설정과 관련된 세팅 내용을 선언하는 태그(영역)으로 configuration 내부에 작성되는 태그들의 순서가 중요하다.
  • environments : DB 연결 설정 정보를 작성하는 태그
  • transactionManager : 트랜잭션의 권한이 누구에게 있는지 선언하는 태그 JDBC라고 명시되어 있으면 JAVA에서 수행한다는 의미다.
  • DB 설정
    20210128_11
  • mappers : DB에서 수행될 SQL 구문을 담은 mapper 파일을 등록하는 태그



  • MyBatis 기본 세팅 configuration 바로 밑에 settings 태그를 추가하여 작성한다.
<settings>
<!-- 마이바티스 null 처리 세팅 
    마이바티스는 매핑되는 컬럼에 null 값이 있을 경우 오류를 발생시킨다.
    그래서 값이 null일 때 처리방법을 지정한다.
    value 지정 시 NULL은 반드시 대문자로 작성한다. -> 소문자로 작성 시 오류 발생!
-->
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>




MyBatisTemplate class 생성

Java Resources > src > common 클릭 후 Ctrl+n
20210128_12
20210128_13



class 생성 후 하단 내용을 작성한다.

import org.apache.ibatis.session.SqlSession;

public class MybatisTemplate {

	public static SqlSession getSqlSession() {
		// SqlSession : 확장된 Connection
		SqlSession session = null;
		try {
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return session;
	}
}


그리고 try 부분에 공식 홈페이지에 있는 세줄을 복사해서 붙여넣는다.

20210128_14

에러가 나는 부분은 import가 되지 않아 발생하는 에러이므로 import를 해주면 없어진다. ctrl + shift + s를 눌러 import를 진행하자.

20210128_15

resource의 경우는 제일 첫 줄을 import 하는게 아니므로 주의하자.

20210128_16

import를 끝낸 후 본인의 개발환경에 맞게 복사, 붙여넣기 한 세줄을 수정한다.

String resource = "/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
session = new SqlSessionFactoryBuilder().build(inputStream).openSession(false);


  • SqlSessionFactoryBuilder : SqlSession을 만들어 내는 공장을 만드는 객체
  • SqlSessionFactory : SqlSessionFactoryBuilder.build()의 결과물로 SqlSessionFactory 공장에서 SqlSession을 만든다.
  • openSession(false) : SqlSession을 만들 때 AutoCommit을 false 상태로 만든다.




MyBatis 실습

MyBatis 실습은 기존에 JDBC 형식으로 만들었던 wsp 프로젝트 내 Board 패키지에서 수정하는 방식으로 진행한다.
먼저 기존의 파일들을 보존하기 위해 DAO, Service 파일을 새로 생성한다.
20210128_17

그리고 BoardController에서 전역변수로 선언한 service 관련 코드를 수정한다. BoardService에서도 똑같이 DAO 관련 코드도 수정해주자.

// BoardController
//BoardService service = new BoardService();
BoardService2 service = new BoardService2();

// BoardService
BoardDAO2 dao = new BoardDAO2();



그리고 마지막으로 Service와 DAO 파일 상단에 하단 import 코드를 작성한다.

import static com.kh.wsp.common.MybatisTemplate.*;


게시글 목록 조회

  • BoardController
if(command.equals("/list.do")) {
	errorMsg = "게시판 목록 조회 과정에서 오류 발생";

	PageInfo pInfo = service.getPageInfo(cp);

	List<Board> bList = service.selectBoardList(pInfo);
	if(bList != null) {
		List<Attachment> fList = service.selectThumbnailList(pInfo);
	
		if(!fList.isEmpty()) {
			request.setAttribute("fList", fList);
		}
	}
path = "/WEB-INF/views/board/boardList.jsp";
request.setAttribute("bList", bList);
request.setAttribute("pInfo", pInfo);

view = request.getRequestDispatcher(path);
view.forward(request, response);
} 



  • getPageInfo(cp)

기존 코드

public PageInfo getPageInfo(String cp) throws Exception {
	Connection conn = getConnection();
	int currentPage = cp == null ? 1 : Integer.parseInt(cp);
	int listCount = dao.getListCount(conn);
	close(conn);
	return new PageInfo(currentPage, listCount);
}



마이바티스 형식으로 작성된 코드

public PageInfo getPageInfo(String cp) throws Exception {
	SqlSession session = getSqlSession ();
	int currentPage = cp == null ? 1 : Integer.parseInt(cp);
	int listCount = dao.getListCount(session);
	session.close();
	return new PageInfo(currentPage, listCount);
}



  • BoardDAO
public int getListCount(Connection conn) throws Exception {
	int listCount = 0;
	String query = prop.getProperty("getListCount");
	try {
		stmt = conn.createStatement();
		
		rset = stmt.executeQuery(query);
		
		if(rset.next()) {
			listCount = rset.getInt(1);
		}
	}finally {
		close(rset);
		close(stmt);
	}
	return listCount;
}



마이바티스 형식으로 작성된 코드

public int getListCount(SqlSession session) throws Exception {
	return session.selectOne(arg0);
}


selectOne() 메소드의 경우는 조회 결과가 단일 행일때 사용한다. 첫 번째 매개변수에서는 sql이 작성된 mapper의 이름. 태그 아이디를 적고 두 번째 매개변수에는 sql 수행 시 필요한 전달값을 적는다. 두 번째 매개변수는 생략이 가능하다.

아직 SQL을 처리할 수 있는 mapper 파일이 없으므로 새로 생성한다. mapper 파일은 mappers 패키지 안에 board-mapper.xml 이란 이름으로 생성한다.
20210128_22

생성 후 하단의 코드를 붙여 넣는다.
20210128_23

그리고 다음과 같이 수정한다.

<mapper namespace="boardMapper">

<select id="getListCount" resultType="_int">
   SELECT COUNT(*) FROM V_BOARD
	WHERE BOARD_STATUS = 'Y'
</select>
  
</mapper>
  • namespace : mapper의 이름(별칭)
  • resultType : resultSet 결과를 매핑해서 반환되는 타입을 지정. 타입 지정 시 별칭 또는 전체 클래스명(패키지명 + 클래스명)을 작성해야한다.


이제 위에서 작성해놨던 코드를 다음과 같이 수정한다.

public int getListCount(SqlSession session) throws Exception {
	return session.selectOne("boardMapper.getListCount");
}



여기까지 작성한 mapper 파일을 등록하기 위해 mybatis-config.xml로 이동해서 개발 환경에 맞에 mapper 경로를 설정한다.

<mappers>
	<mapper resource="/mappers/board-mapper.xml"/>
</mappers>



이제 Service에서 마이바티스로 변환한 코드가 잘 실행되는지 테스트한다.

int listCount = dao.getListCount(session);
System.out.println("listCount : " + listCount); 



성공!
20210128_24



그럼 이제 목록 조회 나머지 메소들을 모두 마이바티스 형식으로 변경한다.


  • (Service) selectBoardList

기존 코드

public List<Board> selectBoardList(PageInfo pInfo) throws Exception {
	Connection conn = getConnection();
	List<Board> bList = dao.selectBoardList(conn, pInfo);
	close(conn);
	return bList;
}



마이바티스

public List<Board> selectBoardList(PageInfo pInfo) throws Exception {
	SqlSession session = getSqlSession();
	List<Board> bList = dao.selectBoardList(session, pInfo);
	session.close();
	return bList;
}




  • (DAO) selectBoardList
    기존 코드
    public List<Board> selectBoardList(Connection conn, PageInfo pInfo) throws Exception{
      List<Board> bList = null;
      String query = prop.getProperty("selectBoardList");
      try {
          // SQL 구문 조건절에 대입할 변수 생성
          int startRow = (pInfo.getCurrentPage() - 1) * pInfo.getLimit() + 1; 
          int endRow = startRow + pInfo.getLimit() - 1; 
          pstmt = conn.prepareStatement(query);
          pstmt.setInt(1, startRow);
          pstmt.setInt(2, endRow);
          rset = pstmt.executeQuery();
          bList = new ArrayList<Board>();
          while(rset.next()) {
              Board board = new Board(
                              rset.getInt("BOARD_NO"), 
                              rset.getString("BOARD_TITLE"), 
                              rset.getString("MEMBER_ID"), 
                              rset.getInt("READ_COUNT"), 
                              rset.getString("CATEGORY_NM"), 
                              rset.getTimestamp("BOARD_CREATE_DT"));
              bList.add(board);
          }
      }finally {
          close(rset);
          close(pstmt);
      }
      return bList;
    }
    



마이바티스

public List<Board> selectBoardList(SqlSession session, PageInfo pInfo) throws Exception{
	List<Board> bList = null;
	// 마이바티스에서는 ROWNUM이 포함된 복잡한 Select문을 사용할 필요가 없도록 RowsBounds 객체를 제공해준다.

	// offset : 시작점
	// limit : 시작점으로부터 몇개까지

	int offset = (pInfo.getCurrentPage() - 1) * pInfo.getLimit();

	RowBounds rowBounds = new RowBounds(offset, pInfo.getLimit());

	// selectList() : 다중 행 결과를 자동적으로 List에 담아서 반환한다.
	// 첫 번째 매개변수 : mapper이름.태그 아이디
	// 두 번째 매개변수 : SQL에서 사용할 전달값 (없으면 null)
	// 세 번째 매개변수 : RowBounds 객체를 참조하는 변수

	bList = session.selectList("boardMapper.selectBoardList", null, rowBounds);


	return bList;
}




  • board-mapper.xml

기존 SQL

<entry key="selectBoardList">
SELECT * FROM 
    (SELECT ROWNUM RNUM, V.*
    FROM 
        (SELECT * FROM V_BOARD WHERE BOARD_STATUS='Y' ORDER BY BOARD_NO DESC) V )
WHERE RNUM BETWEEN ? AND ?
</entry>


마이바티스

<!-- *RowBounds를 사용했기 때문에 SELECT문을 단순하게 작성해도
  		지정된 offset부터 limit 개수만큼의 행만 조회된다.
	** 조회 결과가 다중열인 경우 JAVA 기본 자료형에는 담을 수 없다 -> VO 필요
  		resultType = "VO클래스 풀네임(패키지명 + 클래스명)"
	-> 별칭은 mybatis-config.xml에서 작성한다.
		   -->
<select id="selectBoardList" resultMap="board_rm">
	SELECT * FROM V_BOARD WHERE BOARD_STATUS = 'Y' ORDER BY BOARD_NO DESC
</select>

board_rm을 사용하기 위한 VO 별칭 먼저 작성하기 위해 mybatis-config.xml로 이동한다.





  • mybatis-config.xml

별칭 부분은 settings 태그 밑에 작성한다.

<!-- VO 별칭 지정  -->
<typeAliases>
	<typeAlias type="com.kh.wsp.board.model.vo.Board" alias="Board"/>
</typeAliases>


별칭을 지정해주었으면 resultMap을 만들러 board-mapper.xml로 이동한다.


  • board-mapper.xml

마이바티스에서 가장 강력한 기능 중 하나로 ResultSet에서 데이터를 가져올 때 작성되는 JDBC 코드를 줄여주는 역할을 한다. DB 컬럼명과 VO 필드명이 다를 때 이를 매핑시키는 역할을 한다. DB 컬럼명과 VO 필드명이 같을 경우엔 resultMap 없이 resultType으로 자동 매핑이 된다.

<mapper namespace="boardMapper">
<!-- 이 밑으로 작성한다. -->
<resultMap type="Board" id="board_rm">
	<id property="boardNo" column="BOARD_NO" /> <!-- PK 연결 -->
	
	<!-- PK를 제외한 모든 컬럼들
		property : VO 필드명, column : DB 컬럼명
	-->
	<result property="boardTitle" column="BOARD_TITLE" />
	<result property="boardCount" column="BOARD_COUNT" />
	<result property="memberId" column="MEMBER_ID" />
	<result property="readCount" column="READ_COUNT" />
	<result property="categoryName" column="CATEGORY_NM" />
	<result property="boardCreateDate" column="BOARD_CREATE_DT" />
	<result property="boardModifyDate" column="BOARD_MODIFY_DT" />
	<result property="boardStatus" column="BOARD_STATUS" />
</resultMap>




  • selectThumbnailList()

마지막으로 썸네일 조회를 위한 메소드를 마이바티스 형식으로 바꾼다. 기존 코드와 다른 부분이 많아 Contorller 먼저 수정한다.

  • BoardContorller
if(bList != null) {
	// 추가
	List boardNoList = new ArrayList();
	
	for(Board b : bList) {
		boardNoList.add(b.getBoardNo() + "");
	}
	
	String boardNoStr = String.join(", ", boardNoList);
	// 추가 끝
															// 수정
	List<Attachment> fList = service.selectThumbnailList(boardNoList);
	
	if(!fList.isEmpty()) {
		request.setAttribute("fList", fList);
	}
}
  • (service) selectThumbnailList()

기존 코드

public List<Attachment> selectThumbnailList(PageInfo pInfo) throws Exception {
	Connection conn = getConnection();
	List<Attachment> fList = dao.selectThumbnailList(conn, pInfo);
	close(conn);
	return fList;
}


마이바티스

public List<Attachment> selectThumbnailList(String boardNoStr) throws Exception {
	SqlSession session = getSqlSession();
	List<Attachment> fList = dao.selectThumbnailList(session, pInfo);
	session.close();
	return fList;
}



  • (DAO) selectThumbnailList()

기존 코드

public List<Attachment> selectThumbnailList(Connection conn, PageInfo pInfo) throws Exception{
	List<Attachment> fList = null;
	String query = prop.getProperty("selectThumbnailList");
	try {
		int startRow = (pInfo.getCurrentPage() -1) * pInfo.getLimit() + 1;
		int endRow = startRow + pInfo.getLimit() - 1;
		pstmt = conn.prepareStatement(query);
		pstmt.setInt(1, startRow);
		pstmt.setInt(2, endRow);
		rset = pstmt.executeQuery();
		
		fList = new ArrayList<Attachment>();
		
		while(rset.next()) {
			Attachment at = new Attachment();
			at.setFileName(rset.getString("FILE_NAME"));
			at.setParentBoardNo(rset.getInt("PARENT_BOARD_NO"));
			fList.add(at);
		}
	}finally {
		close(rset);
		close(pstmt);
	}
	return fList;
}


마이바티스

public List<Attachment> selectThumbnailList(SqlSession session, String boardNoStr) throws Exception{
	return session.selectList("boardMapper.selectThumbnailList", boardNoList);
}



  • mybatis-config.xml

사용하기 편하게 VO에 별칭을 지정해준다.

<!-- 추가 -->
<typeAlias type="com.kh.wsp.board.model.vo.Attachment" alias="Attachment" />



  • board-mapper.xml

SQL과 resultMap을 작성한다.

<!-- 썸네일 목록 조회(select / 다중 행 다중 열) -->
<select id="selectThumbnailList" resultMap="attachment_rm">
	SELECT * FROM aTTACHMENT
	WHERE PARENT_BOARD_NO
	IN (${boardNoStr})
	AND FILE_LEVEL = 0
</select>

얻어온 매개변수가 작성될 위치와 형태에 따라 #{매개변수명}, ${매개변수명} 으로 필요한 위치에 작성할 수 있다.

  • #{매개변수명}
    • Statement, 전달 받은 값을 모양 그대로 사용하고자 할때 사용한다.
  • ${매개변수명}
    • PreparedStatement, 위치홀더(?)에 매개변수를 값으로써 지정할 때 사용한다.
    • 주의사항 : 전달 받은 값이 String 형태이면 값 앞, 뒤에 ‘‘(작은따옴표)가 붙는다.


<resultMap type="Attachment" id="attachment_rm">
	<id property="fileNo" column="FILE_NO"/>
	
	<result property="filePath" column="FILE_PATH"/>
	<result property="fileName" column="FILE_NAME"/>
	<result property="fileLevel" column="FILE_LEVEL"/>
	<result property="parentBoardNo" column="PARENT_BOARD_NO"/>
</resultMap>




제대로 조회가 되면 성공적으로 바뀐것이다!
image




마이바티스가 대략 하루 반 정도 진도를 더 나갔는데 이렇게 기존에 작성했던 JDBC 메소드를 바꾸는 형식이라 따로 포스팅,,하진,,,않,,,,

태그: , ,

카테고리:

업데이트:

댓글남기기