본문 바로가기

MyBatis

영속성 프레임워크 / MyBatis란 / MyBatis 설정하기

영속성 프레임워크 (Persistence Framework)

프로그래밍 관점에서 영속성

프로그램이 종료되더라도 사라지지 않는 데이터의 특성을 의미
프로그램은 실행과 동시에 메모리에서 동작,  프로그램이 실행되면서 (CPU적인 관점) 연산된 모든 데이터들은 프로세스가 종료되면서 메모리에서 없어지고 데이터는 사라짐(휘발성)
데이터가 휘발성이 아닌 영속성을 갖기 위해서는 Database에 자료를 저장해야 함

Persistence Framework는 프레임워크 중 데이터베이스와 연동되는 시스템을 빠르게 개발하고 안정적인 구동을 보장해주는 프레임워크 (데이터 영속성 보장과 관련된 프레임워크)

Persistence Framework 구분

SQL Mapper / ORM

  • SQL Mapper 
  • ORM(Obejct Relational Mapping)
     SQL문장을 직접 데이터베이스 데이터로 다루는
     SQL Helper (MyBatis, JDBCTemplate) 

     개발자가 직접 쿼리 조작 
객체를 통해 간접적으로 데이터베이스를 다루는 ORM (Hibernate, JPA)

 

 

 

MyBatis란?

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


SQL Mapping Framework
자바에서 DB에 다녀오기 위해 필요한 JDBC 코드 대체에 사용
SQL 실행 결과를 자바 빈즈 또는 Map 객체에 매핑해 주는 Persistence 솔루션으로 관리
SQL을 소스 코드가 아닌 XML로 분리 (자바코드로부터 SQL문을 xml로 분리해서 관리)
 = >SQL문과 프로그래밍 코드를 분리하여 구현
매개변수 설정과 쿼리 결과를 읽어오는 코드를 제거 
(MyBatis가 자동으로 설정)
데이터소스(DataSource) 기능과 트랜잭션 처리 기능을 제공

 

MyBatis 실행 순서

 

1) sqlMapConfig.xml에 MyBatis에서 사용될 환경을 설정(cacheEnabled, defaultStatementTimeout, 등등)

2) sqlMap.xml에 각 기능별로 실행할 SQL문을 작성

3)애플리케이션에서 데이터베이스와 연동하는 데 필요한 데이터를 각각의 매개변수에 저장후 MyBatis에 전달

4) 애플리케이션에서 요청한 SQL문을 sqlMap.xml에서 선택

5) 전달한 매개변수와 선택한 SQL문을 결합

6) 매개변수와 결합된 SQL문을 DBMS에서 실행

7)DBMS에서 반환된 데이터를 애플리케이션에서 제공하는 적당한 매개변수에 저장 후 반환

 

MyBatis 환경설정

 

SQLSessionFactoryBuilder

MyBatis 환경설정 파일(xml)을 바탕으로 SqlSessionFactory를 생성

// SQLSessionFactory 생성 메소드
SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Configuration config)

// SQLSessionFactory 생성 코드
SqlSessionFactory factory =
  sqlSessionFactoryBuilder.build(reader, props);

// ... or ...

SqlSessionFactory factory =
  new SqlSessionFactoryBuilder.build(reader, environment, props);

SQLSessionFactory

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();

SQLSession 객체를 생성하는 역할. SqlSessionFactory 인스턴스마다 한 개만 사용가능

(데이터베이스별로 하나의 SqlSessionFactory만 사용가능)

복수의 DB 를 연결하고 싶다면 SqlSession을 DB별로 하나씩 생성해야함

 

 

 

SQLSession

SQLSession은 가장 핵심적인 객체로서, SQL문을 실행하고 Transaction을 관리하는데 사용

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

// 커서는 List와 동일한 역할을 하지만, Iterator를 반환합니다
try (Cursor<MyEntity> entities = session.selectCursor(statement, param)) {
   for (MyEntity entity : entities) {
      // process one entity
   }

 

 

 

transactionManager

 

  • JDBC - 간단하게 JDBC 커밋과 롤백을 처리하기 위해 사용
  • 트랜잭션의 스코프를 관리하기 위해 dataSource 로 부터 커넥션을 가져옴
    <transactionManager type="JDBC">
      <property name="skipSetAutoCommitOnClose" value="true"/>
    </transactionManager>
  • MANAGED - 커밋이나 롤백을 하지 않음. 컨테이너가 트랜잭션의 모든 생명주기를 관리
  • 디플트로 커넥션을 닫으므로, 커넥션이 자동으로 close되는 것을 원하지 않는다면closeConnection”프로퍼티를 false로 설정해야함

     

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>

typeAliases

자바 타입에 대한 별칭 .  XML 설정에서만 사용되며, 타이핑을 줄이기 위해 사용

 

mappers

매핑된 SQL 구문을 정의 

상대경로/ 절대경로 /  url통해 지정가능

 

 

 

xml (class.xml) 에서 <mapper>태그안에 sql문 각각의 태그(select, insert, delete, update)와 id (resultType, resultMap, etc)를 지정하고, mapper를 통해 프로그래밍에서 사용

메소드 기능
List selectList(query_id) id에 대한 select 문을 실행한 후 여러 레코드를 List로 반환
List selectList(query_id, 조건) id에 대한 select 문을 실행하면서 사용되는 조건도 전달
T selectOne(query_id) id에 대한 select 문을 실행한 후 지정된 타입으로 한 개의 레코드 반환
T selectOne(query_id, 조건) id에 대한 select 문을 실행하면서 사용되는 조건도 전달
Map<K,V> selectMap(query_id, 조건) id에 대한 select 문을 실행하면서 사용되는 조건도 전달. Map 타입으로 레코드 반환
int insert(query_id, Object obj) id에 대한 insert문을 실행하면서 객체의 값을 테이블에 추가
int update(query_id, Object obj) obj 객체의 값을 조건문의 수정 값으로 사용해 id데 대한 update문 실행
int delete(query_id, Object obj) obj 객체의 값을 조건문의 조건 값으로 사용해 id데 대한 delete문 실행

 

 

 

Mapper XML 파일

 

속성설명

id 매칭되는 mapper구문을 찾기 위해 사용될 수 있는 구분자
parameterType mapper구문에 전달될 파라미터의 패키지 경로를 포함한 전체 클래스명(TypeAlias태그에서 type과 alias를 지정한 경우 alias 로 풀클래스명 대체가능)
resultType mapper구문 실행결과 return되는 클래스의 풀클래스명 or alias
resultMap 참조할 외부 resultMap의 id 값 

 

 

Result Maps

resultMap엘리먼트는 마이바티스에서 가장 중요하고 강력한 엘리먼트

ResultSet에서 데이터를 가져올때 작성되는 JDBC코드를 대부분 줄여주는 역할을 담당

ResultMap은 간단한 구문에서는 매핑이 필요하지 않고 복잡한 구문에서 관계를 서술하기 위해 필요

(resultType으로 대체가능)

명시적인 resultMap을 가지지 않는 간단한 매핑 구문

<select id="selectUsers" resultType="map">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

컬럼명과 resultType객체의 필드명이 일치하는 구문에선s HashMap에서 키 형태로 자동 매핑

(컬럼을 자바빈 필드를 기준으로 매핑하여 resultMap을 자동으로 생성)

 

만약 칼럼명이 프로퍼티명과 다르다면 SQL구문에 별칭을 지정할 수 있음 ( 컬럼A as "a", 컬럼B as "b") 

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password     as "hashedPassword"
  from some_table
  where id = #{id}
</select>

컬럼명과 필드명이 다른 경우 명시적인 resultMap 을 선언할 수 있음

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>

 

 

Auto-mapping

이전의 절에서 이미 본것처럼 간단한 경우 마이바티스는 결과를 auto-mapping 할 수 있으며,

직접 resultMap을 선언할 수 있음

자동매핑의 처리 로직

 

결과를 자동매핑할때 마이바티스는 컬럼명을 읽은 후, resultType의 객체에 이와 일치하는 이름의 필드를 찾음

컬럼명이 ID라면 id 이름의 프로퍼티를 찾은 후, 마이바티스는 ID 칼럼값을 사용해서 id 프로퍼티를 설정

  * 대개 데이터베이스 컬럼은 대문자를 사용해서 명명하고 단어 사이사이에는 _를 넣음

 * 자바 필드는 일반적으로 CamelCase를 사용하여 정의

이런 둘사이의 자동매핑을 가능하게 하기 위해서는 mapUnderscoreToCamelCase 를 true로 설정하면 됨

 

자동매핑은 결과매핑을 선언한 경우에도 동작

이런 경우 각각의 결과매핑에서 ResultSet의 모든 칼럼은 자동매핑이 아니라 수동매핑으로 처리한

다음 샘플에서 id  userName 칼럼은 자동매핑되고 hashed_password 칼럼은 수동으로 매핑

<select id="selectUsers" resultMap="userResultMap">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password
  from some_table
  where id = #{id}
</select>
<resultMap id="userResultMap" type="User">
  <result property="password" column="hashed_password"/>
</resultMap>

자동매핑에는 3가지가 있음

  • NONE - 자동매핑을 사용하지 않는다. 오직 수동으로 매핑한 프로퍼티만을 설정할것이다.
  • PARTIAL - 조인 내부에 정의한 내포된 결과매핑을 제외하고 자동매핑한다.
  • FULL - 모든것을 자동매핑한다.

디폴트값은 PARTIAL이며, FULL 을 사용할때 자동매핑은 조인결과나 같은 레코드의 여러가지 다른 엔터티를 가져올때 예상치 못한 문제를 야기할 수 있음

 

 

동적 SQL

 

마이바티스의 가장 강력한 기능 중 하나

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

 

if

 

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

 

choose, when, otherwise

자바의 switch 구문과 유사 

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

 

 

trim, where, set

앞서 예제는 악명높게 다양한 엘리먼트가 사용된 동적 SQL 이다. “if” 예제를 사용해보자.

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

 

foreach

 

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  <where>
    <foreach item="item" index="index" collection="list"
        open="ID in (" separator="," close=")" nullable="true">
          #{item}
    </foreach>
  </where>
</select>

 

 

[mybatis-config.xml]

 
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
  
  
  <!-- 이 문서의 형식이 configuration 임을 알려줌
  	==> configuration태그가 전체를 감싸고 있음
  	DTD : 유효성 검사 (내부 태그들이 configuration태그 내 존재가능한지 체크)
  
  -->
  
  
<configuration>
	<!-- setting: MyBatis 구동시 선언할 설정 작성 영역-->
	<!-- 단수형을 쓰려면 복수형 settings가 감싸고 있어야 한다. 복수형 태그 내 단수형이 있어야 인식가능 -->
	<settings>
		<!-- null데이터 전달시 빈 칸이 아닌 null로 인식하겠다 (무조건 대문자 NULL로 작성) -->
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
	<!-- typeAlias: 컨트롤러에서 값을 받아서 db로 이동할 클리스(vo/dto)들의 풀 클래스명이 너무 길어서 이걸 단순한 클래스명으로 사용하기 위해서 별칭 등록할 수 있는 영역 -->
	
	<typeAliases>
		<typeAlias type="com.kh.mybatis.member.model.vo.Member" alias="member"/>
		<typeAlias type="com.kh.mybatis.board.model.vo.Board" alias="board"/>
		<typeAlias type="com.kh.mybatis.board.model.vo.Reply" alias="reply"/>
	</typeAliases>

	<!-- 
	 	environment: MyBatis에서 연동할 DB 정보들을 등록하는 영역 
	 	(여러개 DB정보 등록가능)
	 	=> default 속성으로 여러개 아이디 중 어떤 DB를 기본DB로 사용할지 지정해야함
			 -->
	<environments default="development">
		<environment id="development" >
			<!-- 
				* transactionManager: 두개중 하나 고룰 수 있음, JDBC, MANAGED 둘 중 하나 선택가능
				- JDBC: 트랜잭션 직접 관리 (수동 COMMIT)
				- MANAGED: 개발자가 트랜잭션에 어떤 영향도 행사하지 않음(자동commit)				
			 -->
			 <transactionManager type="JDBC"/>
			 <!-- 
			 	* dataSource로는 POOL/UNPOOLED 둘 중 하나 선택가능
			 	(ConnectionPool 사용여부)
			     - ConnectionPool: Connection객체를 담아둘 수 있는 영역
			      한 번 생성된 Connection객체를 담아두면 재사용 가능
			    POOLED: ConnectionPool을 사용하겠다
			    UNPOOLED: ConnectionPool을 안쓰겠다
			   
			   -->
			 <dataSource type="POOLED">
			 	<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
			 	<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
			 	<property name="username" value="MYBATIS"/>
			 	<property name="password" value="MYBATIS"/>
			 </dataSource> 
			 
		</environment>
	</environments>	
	
	<!-- mapper: 실행할 sql문들을 기록해둔 mapper파일 등록 영역 -->	
	<mappers>
		<mapper resource="/mappers/member-mapper.xml"/>
		<mapper resource="/mappers/board-mapper.xml"/>
	</mappers>
</configuration>

 

 

package com.cc.mybatis.common.template;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Template {
	/*
	 * 기존 jdbc

		public static Connection getConnection(){
		
		driver.properties을 읽어들여서
		해당 DB와 접속된 Connection 객체를 생성하여 반환
		
		public static void close(JDBC용 객체){
		
		
		
		전달받은 jdbc용 객체를 예외처리 후 반납
		
		
		}
		pubilc static void commit/rollback(
	*/
	/* MyBatis
	 * 앞으로 Connection의 역할은 SqlSession이 대신할 것
	 * 
	*/
	
	public static SqlSession getSqlSession() {
		// mybatis-config.xml파일을 읽어들여서
		// 해당 DB와 접속된 SqlSession 객체를 생성하여 반환
		
		SqlSession sqlSession = null;
		// 모든 소스폴더의 최상위 폴더는 classes
		// 경로를 classes부터로 지정해야함
		String resource ="/myBatis-config.xml";
		// "/"는 모든 src폴더의 최상위 폴더들을 의미
		
		try {
			InputStream stream = Resources.getResourceAsStream(resource);
		    // 1단계) new SqlSessionFactoryBuilder(): SqlSessionFactoryBuilder 객체 생성
			// 2단계) .build(stream) : 통로로부터 mybatis-config.xml파일을 읽어들이면서
			// sqlSessionFactory객체를 만들겠다
			// 3단계) .openSession(): SqlSession객체 생성
			// .openSession(false)=> 개발자가 직접 commit을 하겠다
			// .openSession(true) => autocommit이 되어버림
			
			sqlSession = new SqlSessionFactoryBuilder().build(stream).openSession(false);
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return sqlSession;
				
	}
}

 


[web.xml]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>MybatisProject</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

[index.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	 -->
	<!-- index페이지가 로딩되자마자 바로main.jsp페이지로 바로 포워딩하려면 -->
	<jsp:forward page="WEB-INF/views/main.jsp"></jsp:forward>
	<!--forward태그 특징:  url은 그대로지만 실제 화면은 forward한 페이지가 뜸 -->
	<!-- 서블릿단에서 우회하여 forward할 필요가 없 다 -->
</body>
</html>

[main.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	안녕 나는 메인이야
	<!-- http://localhost:8004/WEB-INF/views/main.jsp -->
	<!-- 이렇게 들어가면 아무것도 안나온다 왜..?  -->
	<!-- WEB-INF 아래 있는 폴더에는 직접접근 할 수가 없다 (보안상) -->
	<!-- 클래스파일들 다 노출될 수 있으니까ㅣ -->
	
	<jsp:include page="common/menubar.jsp"/>
	
</body>
</html>

[menubar.jsp]

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1 align="center">Welcome to my page</h1>
	<br>
	<div class="login-area" align="right">
		<!-- case1. 로그인 전 -->
		<form action="" method="post">
			<c:choose>
				<c:when test="${loginUser eq null }">
                    <table>

                        <tr>
                            <td>아이디</td>
                            <td><input type="text" name="userId" required></td>
                            <td rowspan="2"><button type="submit" style="height:60px">로그인</button></td>
                        </tr>
                        <tr>
                            <td>비밀번호</td>
                            <td><input type="password" name="userPwd" required></td>
                        </tr>
                        <tr>
                            <td colspan="3" align="center">
                                <a href="enrollForm.me">회원가입</a></td>
                        </tr>
                    </table>
		</form>
		</c:when>
		<c:otherwise>
		<!-- case2. 로그인 후 -->
		<div>
			<table>
				<tr>
					<td colspan=""2>
						<h3>누구누구님 환영합니다</h3>

					</td>
				</tr>
				<tr>
					<td> <a href="">마이페이지</a></td>
					<td><a href="">로그아웃</a></td>
				</tr>
			</table>
            </div>	
		</c:otherwise>
	</c:choose>
	
	</div>
	<br>
	<div class="nav-area" align="center">
		<div class="menu">Home</div>
		<div class="menu">공지사항</div>
		<div class="menu">게시판</div>
		<div class="menu">etc</div>
	</div>

</body>
</html>

 

 

[MemberService]

package com.kh.mybatis.member.model.service;


import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.common.template.Template;
import com.kh.mybatis.member.model.dao.MemberDao;
import com.kh.mybatis.member.model.vo.Member;

public class MemberServiceImpl implements MemberService{
	
	private MemberDao memberDao= new MemberDao();
	

	@Override
	public int insertMember(Member m) {
		/*
		Connection conn = JDBCTemplate.getConnection();
		int result = new MemberDao().insertMember(conn, m);
		
		이제 SqlSession이 connection대체
		*/
		
		SqlSession sqlSession = Template.getSqlSession();	
		// sqlSession이 Connection 역할을 대신함
		// sqlSession은 DB접속정보를 담고있음
		// sqlSession을 DAO에 전달하기O
		
		
		int result = memberDao.insertMember(sqlSession, m);
		
		
		if(result>0) {
			sqlSession.commit();
		} 
		// insert자체가 이루어지지 않으면 rollback을 할 필요가 사실 없었다..!
		// insert가 안되었는데 왜 rollback을 해야하느냐
		
		sqlSession.close();
		return result;
		
		
	}

	@Override
	public Member loginMember(Member m) {
		SqlSession sqlSession = Template.getSqlSession();
		Member loginUser = memberDao.loginMember(sqlSession, m);
		sqlSession.close();
		
		return loginUser;
		
	}


	
}

 

[MemberDao]

package com.kh.mybatis.member.model.dao;

import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.member.model.vo.Member;

public class MemberDao {

	public int insertMember(SqlSession sqlSession, Member m) {
		/*
			int result=	0;
			PreparedStatement pstmt = null;
			String sql= prop.getProperty("insertMember");
			
			try{
				pstmt = conn.prepareStatement(sql);
			
			} catch {
			}
		*/
		// sqlSession이 connection, prepareStatement 모두 대신
		// sqlSession제공 메소드를 통해 sql문을 실행 및 결과반환이 가능
		// sql문 종류에 맞는 메소드 호출
		// sqlSession insert메소드에 넘길 수 있는 최대 객체는 1개
		// insert를 하려면 메퍼파일에 먼저 접근해야함 (nameSpace로 식별)
		// sqlSession.sql문 종류에 맞는 메소드("메퍼파일의 nameSpace.해당sql문의 고유한 id", 전달할 객체);
		
		// sqlSession.insert(arg0, arg1)
		return sqlSession.insert("memberMapper.insertMember", m);
		
		
	}

	public Member loginMember(SqlSession sqlSession, Member m) {
		// selectOne메소드는 조회결과가 없다면 null을 반환함
		return sqlSession.selectOne("memberMapper.loginMember", m);
		
		// config파일에 등록한 메퍼명 : ~~~.xml
		// ~~.xml 파일에 <mapper namespace="">
		// nameSpace.id 
		// select메소드도 최대 1개 객체까지만 전달할 수 있으며 이러한 객체의 가공은 controller단에서 해야함
		
		
		
		// config 파일에 alias type, resource 등 태그를 작성할 때
		// 존재하지 않는 파일이나 타입인 경우 에러가 뜸 , 반드시 먼저 만들고 config에 작성해야함 
	}

}
<?xml version="1.0" encoding="UTF-8"?>


<!-- mapper파일 유효성 검사
 -->

<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="memberMapper">
<!-- namespace: 해당 mapper파일의 고유한 별칭 (중복불가), 전체가 mapper태그로 감싸져있어야 한다-->
<!--
	* DML 구문일 경우
	<insert id="각 sql문의 식별자" parameterType="전달받을 자바타입을 적는데 풀클레스 명(or별칭)으로 적는다">
		쿼리문작성
	</insert>
	<update></update>
	<delete></delete>
	
	
	sqlSession의 insert/delete/update 메소드 반환형은 int형임
	parameterType은 파라미터가 없을 경우 생략가능
	
	

	* select문일 경우 
	<select id="각 sql문의 식별자" parameterType="전달받을 자바타입(풀클래스명) 혹은 별칭">
  	resultType="조회결과를 반환하고자하는 자바타입"
 	resultMap =" 조회결과를 뽑아서 매핑할 resultMap의 id"
 	select 문은 resultType(자바에서 제공하는 자료형)이나 resultMap(내가만든 vo클래스) 중 하나를 무조건 기술해야함
 	=> select문의 결과는 항상 어떤 select문이냐에 따라서 다를 수 있기 떄문에
 	(예: count를 쓰면 정수 / name만 조회하면 문자열)
	
	쿼리문 작성
	
	그동안은 pstmt객체를 사용하여 위치홀더를 기술하였지만
	앞으로는 위치홀더 대신 해당 sql문에 전달된 객체로부터 값을 꺼내서 사용
	꺼내는 방법 #{필드명 or 변수명 or map의 키 값}
	</select>
 -->
	
<!-- 	<insert id="insertMember" parameterType="com.kh.mybatis.member.model.vo.Member"> -->	
	<insert id="insertMember" parameterType="member">	
	INSERT
	  INTO
	 		MEMBER
	  		(
	  		USER_NO,
	  		USER_ID,
	  		USER_PWD,
	  		USER_NAME,
	  		EMAIL,
	  		BIRTHDAY,
	  		GENDER,
	  		PHONE,
	  		ADDRESS
	  		)
	  VALUES
	  		(
	  		SEQ_UNO.NEXTVAL,
	  		#{userId},
	  		#{userPwd},
	  		#{userName},
	  		#{email},
	  		#{birthDay},
	  		#{gender},
	  		#{phone},
	  		#{address}
	  		)
	  		<!--  내부적오로 getter메소드를 호출하여 값을 얻는다 -->
	  		<!-- 반드시getter메소드가 존재해야 한다. 자바 빈 규약 -->
	  		
	</insert>


	<!-- 
		*resultMap:resultMap엘리먼트는 마이바티스에서 가장 중요하고 강력한 엘리먼트이다.
		ResultSet에서 데이터를 가져올때 작성되는 JDBC코드를 대부분 줄여주는 역할을 담당한다. 
		사실 join매핑과 같은 복잡한 코드는 굉장히 많은 코드가 필요하다. 
		ResultMap은 간단한 구문에서는 매핑이 필요하지 않고 복잡한 구문에서 관계를 서술하기 위해 필요하다.
		컴퓨터는 각 컬럼의 값이 Result로 받을 Member 객체의 어느 필드에 대응되는지 알지 못한다
		이것을 매핑하는 작 업 !
		resultMap: 마이바티스 핵심기능 중 하나
		ResultSet으로부터 조회된 컬럼값을 하나씩 뽑아서 내가 지정한 VO 객체 각 필드에 담는 jdbc 코드
		
		<resultMap id="식별자" type="조회된 결과를 담아 반환하고자하는 vo타입 또는 별칭">
			<result column="조회결과를 뽑고자하는 db컬럼명" property="결과를 담을 필드명>
			<result column="조회결과를 뽑고자하는 db컬럼명" property="결과를 담을 필드명>
			<result column="조회결과를 뽑고자하는 db컬럼명" property="결과를 담을 필드명>
		</resultMap>
 	-->
	<!-- 내부적으로 필드에 대한 setter메소드를 호출  -->
	<!-- 따라서 무조건 setter메소드가 존재해야 한 다   -->
	<resultMap id="memberResultSet" type="member">
		<result column="USER_NO" property="userNo"/>
		<result column="USER_ID" property="userId"/>
		<result column="USER_PWD" property="userPwd"/>		
		<result column="USER_NAME" property="userName"/>
		<result column="EMAIL" property="email"/>
		<result column="BIRTHDAY" property="birthDay"/>
		<result column="GENDER" property="gender"/>
		<result column="PHONE" property="phone"/>
		<result column="ADDRESS" property="address"/>
		<result column="ENROLL_DATE" property="enrollDate"/>
		<result column="MODIFY_DATE" property="modifyDate"/>
		<result column="STATUS" property="status"/>
		
	</resultMap>

	
<!-- 
	<select id="loginMember" parameterType="member" resultMap="memberResultSet">
		SELECT
				USER_NO,
				USER_ID,
				USER_PWD,
				USER_NAME,
				EMAIL,
				BIRTHDAY,
				GENDER,
				PHONE,
				ADDRESS,
				ENROLL_DATE,
				MODIFY_DATE,
				STATUS
		FROM
				MEMBER
		WHERE
				USER_ID = #{userId}
		AND
				USER_PWD = #{userPwd}
		AND 	
				STATUS = 'Y'			
		
	</select>
	
	
	 -->
	<select id="loginMember" parameterType="member" resultType="member">
		SELECT
				USER_NO userNo,
				USER_ID userId,
				USER_PWD userPwd,
				USER_NAME userName,
				EMAIL email,
				BIRTHDAY birthDay,
				GENDER gender,
				PHONE phone,
				ADDRESS address,
				ENROLL_DATE enrollDate,
				MODIFY_DATE modifyDate,
				STATUS status
		FROM
				MEMBER
		WHERE
				USER_ID = #{userId}
		AND
				USER_PWD = #{userPwd}
		AND 	
				STATUS = 'Y'			
		
	</select>
	
	
</mapper>

 

 

[boardService]

package com.kh.mybatis.board.model.Service;

import java.util.ArrayList;
import java.util.HashMap;

import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.board.model.vo.Reply;
import com.kh.mybatis.common.model.vo.PageInfo;

public interface BoardService {
	
	//  게시글 관련 서비스
	int selectListCount();
	
	ArrayList<Board> selectList(PageInfo pi);

	int increaseCount(int boardNo);
	
	Board selectBoard(int boardNo);
	
	// 댓글 관련 서비스
	
	ArrayList<Reply> selectReply(int boardNo);
	
	// 댓글관련
	
	// 게시글 검색
	int selectSearchCount(HashMap<String, String> map);
	ArrayList<Board> selectSearchList(HashMap<String, String > map, PageInfo pi);
	
}

[boardServiceImpl]

package com.kh.mybatis.board.model.Service;

import static com.kh.mybatis.common.template.Template.getSqlSession;

import java.util.ArrayList;
import java.util.HashMap;

import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.board.model.dao.BoardDao;
import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.board.model.vo.Reply;
import com.kh.mybatis.common.model.vo.PageInfo;

public class BoardServiceImpl implements BoardService{

	private BoardDao boardDao = new BoardDao();
	
	@Override
	public int selectListCount() {

		SqlSession sqlSession = getSqlSession();
		int listCount = boardDao.selectListCount(sqlSession);
		//return boardDao.selectListCount(sqlSession);
		// 그런데 sqlSession을 닫아야하므로 이렇게슨 못씀
		
		sqlSession.close();
		return listCount;
	}

	@Override
	public ArrayList<Board> selectList(PageInfo pi) {
		SqlSession sqlSession = getSqlSession();
		ArrayList<Board> list = boardDao.selectList(sqlSession, pi);
		sqlSession.close();
		
		return list;
	}

	@Override
	public int increaseCount(int boardNo) {
		SqlSession sqlSession = getSqlSession();
		int result = boardDao.increaseCount(sqlSession, boardNo);
		if(result>0) {
			sqlSession.commit();
		}
		sqlSession.close();
		return result;
	}

	@Override
	public Board selectBoard(int boardNo) {
		SqlSession sqlSession=getSqlSession();
		Board b = boardDao.selectBoard(sqlSession, boardNo);
		
		sqlSession.close();
		return b;
	}

	@Override
	public ArrayList<Reply> selectReply(int boardNo) {
		SqlSession sqlSession = getSqlSession();
		ArrayList<Reply> list = boardDao.selectReply(sqlSession, boardNo);
		sqlSession.close();
		return list;
	}

	@Override
	public int selectSearchCount(HashMap<String, String> map) {
		SqlSession sqlSession = getSqlSession();
		int result = boardDao.selectSearchCount(sqlSession,map);
		
		
		sqlSession.close();
		
		return result;
	}

	@Override
	public ArrayList<Board> selectSearchList(HashMap<String, String> map, PageInfo pi) {
		SqlSession sqlSession = getSqlSession();
		ArrayList<Board> list = boardDao.selectSearchList(sqlSession, map, pi);
		
		sqlSession.close();
		
		return list;
	}

}

 

[boardDao]

 

package com.kh.mybatis.board.model.dao;

import java.util.ArrayList;
import java.util.HashMap;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import com.kh.mybatis.board.model.vo.Board;
import com.kh.mybatis.board.model.vo.Reply;
import com.kh.mybatis.common.model.vo.PageInfo;

public class BoardDao {

	public int selectListCount(SqlSession sqlSession) {
		return sqlSession.selectOne("boardMapper.selectListCount");
		
	}

	public ArrayList<Board> selectList(SqlSession sqlSession, PageInfo pi) {
		//sqlSession.selectList("boardMapper.selectList", arg1);
		// 마이바티스에서는 페이징 처리를 위해 RowBounds라는 클래스를 제공
		// *offset: 몇개의 게시물을 건너 뛰고 조회할건지
		/*
			ex)boardLimit 5일 경우
			offset (건너뛸 숫자) 
			                                  offset  boardLimit
			currentPage : 1 (게시글 1~5)	     0             5
			currentPage : 2( 6~10)	         5             5
			currentPage : 3( 11~15)          10           5
			
		
		*/
		int offset = (pi.getCurrentPage() -1)*pi.getBoardLimit();
		RowBounds rowBounds = new RowBounds(offset, pi.getBoardLimit());
		//return sqlSession.selectList("boardMapper.selectList", null, rowBounds);
		//Type mismatch: cannot convert from List<Object> to ArrayList<Board>
		return (ArrayList)sqlSession.selectList("boardMapper.selectList", null, rowBounds);
	
		
		
		// rowBounds객체를 넘겨야 할 경우 selectList메소드 오버로딩 된 형태 중 매개변수 3개 메소드를 사용해야함
		// 만약 두번째 인자값을 생략하게 되면 rowBounds를 넘겨받는 객체로 인식하므로
		// 반드시 두번째에 넘겨주는 객체가 들어가야하는데
		// 만약 넘겨줄 객체가 없을 경우, null을 기술
	
		
		
		
	}

	public int increaseCount(SqlSession sqlSession, int boardNo) {
		return sqlSession.update("boardMapper.increaseCount", boardNo);		
	}

	public Board selectBoard(SqlSession sqlSession, int boardNo) {
		return sqlSession.selectOne("boardMapper.selectBoard", boardNo);
	} 

	public ArrayList<Reply> selectReply(SqlSession sqlSession, int boardNo) {
		return (ArrayList)sqlSession.selectList("boardMapper.selectReply", boardNo);
		
		
	}

	public int selectSearchCount(SqlSession sqlSession, HashMap<String, String> map) {
		
		return sqlSession.selectOne("boardMapper.selectSearchCount", map);	
	}

	public ArrayList<Board> selectSearchList(SqlSession sqlSession, HashMap<String, String> map, PageInfo pi) {
		int offset = (pi.getCurrentPage()-1)*pi.getBoardLimit();
		RowBounds rowBounds = new RowBounds(offset, pi.getBoardLimit());
		
		return (ArrayList)sqlSession.selectList("boardMapper.selectSearchList", map, rowBounds);
	
	}

}

[boardMapper]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="boardMapper">

	<resultMap id="boardResultSet" type="board">
		<result column ="BOARD_NO" property="boardNo"/>
		<result column ="BOARD_TITLE" property="boardTitle"/>
		<result column ="USER_ID" property="boardWriter"/>
		<result column ="COUNT" property="count"/>
		<result column ="CREATE_DATE" property="createDate"/>
		<result column ="BOARD_CONTENT" property="boardContent"/>
		<!--여기에 없는 컬럼만 불러도 됨, 여기있는 컬럼을 써도되고 안써도 된다~-->
		<!--널포인터 걱정 노노~-->
		
	</resultMap>
	
	
	<select id="selectListCount" resultType="_int">
	<!--int라고만 적어도 잘 돌아가기는 함 -->
		SELECT 
				COUNT(*)
		FROM
				BOARD
		WHERE
				STATUS = 'Y'	
	</select>
	<select id="selectList" resultMap="boardResultSet" >
	SELECT 
	        BOARD_NO,
	        BOARD_TITLE,
	        USER_ID,
	        COUNT,
	        CREATE_DATE
	        
     FROM
        	BOARD B
     JOIN 	MEMBER ON(BOARD_WRITER = USER_NO)
    WHERE
        	B.STATUS ='Y'
    ORDER 
       BY 
    		BOARD_NO 
    DESC
	
	</select>
	<update id="increaseCount" parameterType="_int">
	UPDATE
			BOARD
	  SET
	  		COUNT = COUNT+1
	  WHERE
	  		BOARD_NO = #{boardNo}
	  AND
	  		STATUS='Y'		
	  		<!-- #{내가 전달하는 변수명}  -->	
	</update>
	
	
	<select id="selectBoard" resultMap="boardResultSet" parameterType="_int">
	SELECT 
	        BOARD_NO,
	        BOARD_TITLE,
	        USER_ID,
	        COUNT,
	        CREATE_DATE,
	        BOARD_CONTENT
     FROM
        	BOARD B
     JOIN 	MEMBER ON(BOARD_WRITER = USER_NO)
    WHERE
        	B.STATUS ='Y'
	AND 
			BOARD_NO = #{boardNo}
	  		<!-- #{내가 전달하는 변수명}  -->	
	  		
	</select>
	
	<select id="selectReply" resultType="reply" parameterType="_int">
	SELECT 	
			REPLY_NO replyNo,
			REPLY_CONTENT replyContent,
			REF_BNO refBno,
			USER_ID replyWriter,
			CREATE_DATE createDate
	 FROM
	 		REPLY
	 JOIN	
	 		MEMBER
	 ON 	(REPLY_WRITER=USER_NO)
	 WHERE
	 		REPLY.STATUS = 'Y'
	 AND 	
	 		REF_BNO = #{boardNo} 
	 ORDER
	 	BY	
			CREATE_DATE DESC
	</select>
	<select id="selectSearchCount" resultType="_int" parameterType="hashmap">
	select 
	        count(*)
	from
	        board
	join
	        member 
	 on     (board_writer =user_no)
	 where  board.status='Y'
	 
	 <if test="condition=='writer'">
	 and   user_id	
	 </if>
	 <if test="condition=='content'">
	 and board_content
	 </if>
	 <if test="condition=='title'">
	 and board_title
	 </if>	
		like  '%'|| #{keyword}|| '%' 

	</select>
		  <!-- test에 HashMap의 키값만 적으면 됨!! 똑똑해..소름... -->
	<!--mybatis를 통해 동적 으로 sql문 실행이 가능해진다 -->
	
	<select id="selectSearchList" parameterType="hashmap" resultMap="boardResultSet">
	select
			board_no,
			board_title,
			user_id,
			count,
			create_date
	from
			board
	join	member
	 on 	(board_writer = user_no)
	 where 	board.status ='Y'
	 <choose>
	 	<when test="condition == 'writer'">
	 	 and user_id
	 	</when>
	 	<when test="condition =='title">
	 	and board_title
	 	</when>
	 	<otherwise>
	 		and board_content
	 	</otherwise>
	 </choose>
	 	like '%' || #{keyword} || '%'
	 order
		 by
		 	board_no desc
	 
	 
	</select>
	
</mapper>

 

 

 

 

 

 

 

 

---------------------

MyBatis 이클립스 세팅하기

 

 

 

 

 

 

 

 

 

 

 

 

https://github.com/mybatis/mybatis-3/releases

 

Releases · mybatis/mybatis-3

MyBatis SQL mapper framework for Java. Contribute to mybatis/mybatis-3 development by creating an account on GitHub.

github.com

 

mybatis관련 파일은 resources폴더에 넣음

myBatis설정파일 먼저 만들자

resources소스폴더 하단 myBatis-config폴더 만들기

<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">

  

 

 

 

 

 

 

 


 

Reference

 

https://dadmi97.tistory.com/66?category=828767 

 

[ MyBatis ] MyBatis Framework란?

1. MyBatis란? 1.1 등장 배경 기존의 JDBC에서는 아래 예시와 같이 수많은 Boilerplate Code와 SQL문이 프로그래밍 코드에 섞여 코드를 복잡하게 하고 사용 및 유지 보수가 어려웠습니다. public class Test { publ

dadmi97.tistory.com

 

 

마이바티스란?

https://mybatis.org/mybatis-3/ko/index.html

 

MyBatis – 마이바티스 3 | 소개

마이바티스는 무엇인가? 마이바티스는 개발자가 지정한 SQL, 저장프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. 마이바티스는 JDBC로 처리하는 상당부분의 코드와

mybatis.org