BackEnd/Database Programming

[DB Programming] MyBatis, MyBatis 설정, SqlSessionFactory, SqlSession, Mapper XML, Mapper interface

하노정 2022. 12. 9. 21:38

MyBatis

Data Mappers

  • 객체와 데이터베이스 사이에 데이터를 이동시킴 (moves data between objects and a database)
  •  객체와 데이터베이스가 서로 독립적이고 mapper에 대해서도 독립적 (while keeping them independent of each other and the mapper itself)

특징

  • SQL에 대한 고수준 mapping을 지원하는 영속성 프레임워크 (persistence framework)
  • JDBC API를 이용하는 코드들을 생략 가능 (구현 불필요)
  • SQL 질의에 대한 파라미터 설정 및 결과 처리 수행
  • XML과 annotation을 이용한 mapping 설정 방법 지원
  • Java의 기본 타입 값(primitive-type value), Map, Java POJO 객체 등을 데이터베이스의 레코드와 mapping

설치

  • Github repository에서 직접 다운로드
    • mybatis-3.5.x.zip: mybatis-3.5.x.jar 및 의존 라이브러리들을 포함
    • 프로젝트 내에 저장하고 classpath에 포함시켜 사용
  • Maven 이용
    • pom.xml
<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>${mybatis.version}</version>
</dependency>
  • 주의 : 사용할 DBMS의 JDBC Driver는 별도로 설치 필요
    • mybatis와는 별개의 문제로 Oracle DBMS를 사용하려면 Oracle Driver가 필요함

MyBatis Workflow

  1. Java 객체나 값을 SQL statement의 parameter로 전달
    • Parameter는  JavaBeans, Map 또는 primitive(simple) data 이용
    • Parameter는 SQL문이나 stored procedure 호출에서 runtime value로 사용됨
      • Update문의 입력 값, select문의 where 조건 등
  2. Mapped SQL statement 생성 및 실행
    • SQL statement에 대해 JDBC PreparedStatement 생성
    • Parameter 값들을 PreparedStatement 내에 setting
    • SQL 문이나 stored procedure를 데이터베이스에서 실행
  3. 질의 실행 결과(ResultSet)로부터 객체 생성 및 반환
    • Select 질의 : 하나 이상의 객체들을 생성 및 반환
    • Insert/Updat/Delete 문 : 처리된 행(레코드)의 개수 반환

MyBatis 설정

MyBatis 기본 설정 (configuration)

  • Data source(DB 접속 정보), mapper XML file, mapper interface 등에 관한 정보 포함
<?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>
	<settings>
		<setting name="logImpl" value="LOG4J" />
	</settings>
	<typeAliases> // type에 대한 별칭 정의 (전체 경로 사용해도 되지만, Comment 클래스 객체 별칭 지정
		<typeAlias type="model.Comment" alias="Comment" /> // type="패키지.DTO"
		<typeAlias type="model.User" alias="User" />
		<typeAlias type="model.Reply" alias="Reply" />
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" /> // 트랜잭션 처리 방식
			<dataSource type="POOLED"> // data source (DBCP) 설정 - connection pool
				<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
				<property name="url" value="jdbc:oracle:thin:@dblab.dongduk.ac.kr:1521:orcl" />
				<property name="username" value="dbp" />
				<property name="password" value="dbp" />
			</dataSource>
		</environment>
	</environments>
	<mappers> // mapper xml file 또는 mapper interface 설정 - 경로로 위치를 알려줌. 그래야 읽어감
		<mapper resource="repository/mybatis/mapper/CommentMapper.xml" /> // mapper xml file
        <mapper class="repository.mybatis.mapper.CommentMapper2" /> // mapper interface
	</mappers>
</configuration>

Mapper 설정

  • SQL 문들을 포함하는 mapper XML 또는 mapper interface 정의
    • SQL statement 정의
    • Parameter mapping, Result mapping 정의 : SQL 질의에 파라미터 값을 설정하고, 질의 실행 결과를 Java 객체나 Map 등으로 저장하는 규칙 정의
  • mapper XML의 예
    • DAO 메소드가 실행할 쿼리문이 메소드마다 흩어져 있는 게 아니라 한 곳에 모아놓는 것이다.
    • 쿼리문이 각각 식별 가능한 서로 다른 id를 갖고 있어야 한다. 
    • 얼마든지 많은 쿼리문 작성 가능
<?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">
    
// namaspace 속성 값으로 쿼리문의 id 값들이 담겨있는 패키지 경로 지정
<mapper namespace="repository.mybatis.mapper.CommentMapper"> 
	<cache />

	// result mapping 정의 - DB column 값을 어떤 변수명으로 저장할지 지정
	<sql id="BaseColumns">
		comment_no AS commentNo,
		user_id AS userId,
		comment_content AS commentContent,
		reg_date AS regDate
	</sql>

	<select id="selectCommentByPrimaryKey" parameterType="long" resultType="Comment"> // 미리 지정한 클래스 이름 별칭 사용
		SELECT <include refid="BaseColumns"/>
		FROM COMMENTS
		WHERE comment_no = #{commentNo} // ? 대신에 클래스 속 변수명으로 구분
	</select>
 
	<select id="selectCommentByCondition" parameterType="hashmap" resultType="Comment">
		SELECT <include refid="BaseColumns"/>
		FROM COMMENTS
		<where>
			<if test="commentNo != null">
				comment_no = #{commentNo}
			</if>
			<if test="userId != null">
				AND user_id = #{userId}
			</if>
		</where>
	</select>

	<insert id="insertComment" parameterType="Comment">
		INSERT INTO COMMENTS (comment_no, user_id, comment_content, reg_date)
		VALUES (#{commentNo}, #{userId}, #{commentContent}, #{regDate}) // 클래스 속 정의된 변수명 -> 보통 private으로 선언되므로 getter 사용하는 게 좋음
	</insert>

	<update id="updateComment" parameterType="Comment">
		UPDATE COMMENTS 
		SET comment_content = #{commentContent}
		WHERE comment_no = #{commentNo}
	</update>
 
	<delete id="deleteComment" parameterType="long"> // parameterType이 객체가 아니라 숫자 값 하나. 하나일 경우 primitive 가능
		DELETE FROM COMMENTS
		WHERE comment_no = #{commentNo}
	</delete>
	
	<delete id="deleteAllComments">
		DELETE FROM COMMENTS
	</delete>
	
	
	<resultMap id="associationResultMap" type="Comment">
		<id column="comment_no" jdbcType="NUMERIC" property="commentNo" />
		<result column="user_id" jdbcType="VARCHAR" property="userId" />
		<result column="comment_content" jdbcType="VARCHAR" property="commentContent" />
		<result column="reg_date" jdbcType="TIMESTAMP" property="regDate" />
		<association column="user_id" property="user" javaType="User">
			<id column="user_id" property="userId" />
			<result column="user_name" property="userName" />
			<result column="phone" property="phone" />
			<result column="address" property="address" />
		</association>
	</resultMap>

	<select id="selectCommentByPrimaryKeyAssociation" parameterType="long" 		
		resultMap="associationResultMap"> 
		SELECT c.comment_no, 
				c.user_id, 
				c.comment_content, 
				c.reg_date, 
				u.user_name, 
				u.phone, 
				u.address
		FROM COMMENTS c, USERS u			
		WHERE c.user_id = u.user_id  
		  AND c.comment_no = #{commentNo} 
	</select>
	
	<select id="selectCommentByPrimaryKeyAssociation2" parameterType="long" 		
		resultType="Comment"> 
		SELECT c.comment_no AS commentNo,
			   c.user_id AS userId,
			   c.comment_content AS commentContent,
			   c.reg_date AS regDate,
			   u.user_id AS "user.userId",	
			   u.user_name AS "user.userName", 			   
			   u.phone AS "user.phone",	
			   u.address AS "user.address"	
		FROM COMMENTS c, USERS u			
		WHERE c.user_id = u.user_id  
		  AND c.comment_no = #{commentNo} 
	</select>
	
	
	<resultMap id="collectionResultMap" type="Comment">
		<id column="comment_no" jdbcType="BIGINT" property="commentNo" />
		<result column="user_id" jdbcType="VARCHAR" property="userId" />
		<result column="comment_content" jdbcType="VARCHAR" property="commentContent" />
		<result column="reg_date" jdbcType="TIMESTAMP" property="regDate" />
		<collection property="replies" ofType="Reply">
			<id column="reply_id" property="replyId" />
			<result column="reply_user_id" property="userId" />
			<result column="reply_content" property="replyContent" />
			<result column="reply_date" property="regDate" />
		</collection>
	</resultMap>
	
	<select id="selectCommentByPrimaryKeyCollection" parameterType="long" 			
		resultMap="collectionResultMap"> 
		SELECT c.comment_no, 
				c.user_id, 
				c.comment_content, 
				c.reg_date, 
				r.reply_id, 
				r.user_id AS reply_user_id, 
				r.reply_content, 
				r.reg_date AS reply_date
		FROM COMMENTS c, REPLY r
		WHERE c.comment_no = r.comment_no 
		  AND c.comment_no = #{commentNo} 
	</select>
	
	<insert id="insertReply" parameterType="Reply">
		INSERT INTO REPLY (reply_id, comment_no, user_id, reply_content, reg_date)
		VALUES (#{replyId}, #{commentNo}, #{userId}, #{replyContent}, #{regDate})
	</insert>

	<delete id="deleteAllReplies">
		DELETE FROM REPLY
	</delete>

</mapper>

 

SqlSessionFactory

  • 데이터베이스 연동을 위한 SqlSession 객체생성하는 factory 객체
  • SqlSessionFactoryBuilder 객체로부터 생성됨
    • build (InputStream is) 메소드 이용
      • InputStream : MyBatis 기본설정 파일에 대한 스트림
      • MyBatis에 대한 초기화 수행 (예: DataSource 생성), SqlSessionFactory 생성
  • SqlSessionFacotory의 Methods
    • 보통 parameter 없는 openSession()으로 SqlSession 객체를 생성하면 SqlSession이 connection 설정
    • session과 connection이 비슷한 느낌
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    throw new IllegalArgumentException(e);
}

// SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
// 위 2줄 체이닝 가능
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

SqlSession

  • Database connection 생성(from DataSource), SQL 질의 실행, transaction 제어(rollback/commit) 등을 실행
public interface SqlSession extends Closeable {
	  <T> T selectOne(String statement); // 제네릭 타입 반환, 질의 실행 후 하나의 결과 객체 반환
      <T> T selectOne(String statement, Object parameter); // 파라미터 객체/값 이용
      
      <E> List<E> selectList(String statement); // 여러 개의 결과 객체들을 반환
      <E> List<E> selectList(String statement, Object parameter);
      
      <K, V> Map<K, V> selectMap(String statement, String mapKey); // 질의 결과 포함하는 Map 반환
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
      
      // 데이터 CRUD
      void select(String statement, Object parameter, ResultHandler handler);
      int 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);
      
      void commit();
      void rollback();
      void close();
      
      Configuration getConfiguration();
      <T> T getMapper(Class<T> type); // T 타입의 Mapper 객체 반환
      Connection getConnection();
}

MyBatis 기반 DAO 구현 예

package repository.mybatis;

import java.io.IOException;
import java.io.InputStream;
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 model.Comment;
import model.Reply;

public class CommentSessionRepository {
	private final String namespace = "repository.mybatis.mapper.CommentMapper";

	private SqlSessionFactory sqlSessionFactory = createSqlSessionFactory();

	private SqlSessionFactory createSqlSessionFactory() {
		String resource = "mybatis-config.xml";
		InputStream inputStream;
		try {
			inputStream = Resources.getResourceAsStream(resource);
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
		return new SqlSessionFactoryBuilder().build(inputStream);
	}

	public Comment findCommentByCommentNo(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			// mapped statement에 parameter setting, 질의 실행, 결과 객체 생성, 반환
			return (Comment) sqlSession.selectOne(namespace + ".selectCommentByPrimaryKey", commentNo);
			// xml에 저장했던 쿼리 중 실행하려는 id 지정 & commentNo를 parameter로 넣음.
		} finally {
			sqlSession.close();
		}
	}

	public List<Comment> findCommentByCondition(Map<String, Object> condition) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			return sqlSession.selectList(namespace + ".selectCommentByCondition", condition);
		} finally {
			sqlSession.close();
		}
	}

	public int insertComment(Comment comment) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.insert(namespace + ".insertComment", comment); // id로 쿼리 실행
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int updateComment(Comment comment) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.update(namespace + ".updateComment", comment);
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int deleteComment(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.delete(namespace + ".deleteComment", commentNo);
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int deleteAllComments() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.delete(namespace + ".deleteAllComments");
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public Comment findCommentAndUserByCommentNo(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			return (Comment) sqlSession.selectOne(namespace + ".selectCommentByPrimaryKeyAssociation", commentNo);
		} finally {
			sqlSession.close();
		}
	}

	public Comment findCommentAndUserByCommentNo2(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			return (Comment) sqlSession.selectOne(namespace + ".selectCommentByPrimaryKeyAssociation2", commentNo);
		} finally {
			sqlSession.close();
		}
	}

	public Comment findCommentAndRepliesByCommentNo(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			return (Comment) sqlSession.selectOne(namespace + ".selectCommentByPrimaryKeyCollection", commentNo);
		} finally {
			sqlSession.close();
		}
	}

	public int insertReply(Reply reply) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.insert(namespace + ".insertReply", reply);
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int deleteAllReplies() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.delete(namespace + ".deleteAllReplies");
			if (result > 0) {
				sqlSession.commit();
			}
			return result;
		} finally {
			sqlSession.close();
		}
	}
}

트랜잭션 처리

트랜잭션 속성 설정

  • Auto-commit mode, isolation level 등 설정
  • SqlSessionFactory를 통해 SqlSession 객체 생성 시 설정 가능
    • SqlSessionFactory#openSession(boolean autoCommit)
    • SqlSessionFactory#openSession(TransactionIsolationLevel level)

트랜잭션 제어

  • SqlSessioinFactory#openSession() 호출 시 새로운 트랜잭션이 시작
  • SqlSessionFactory#commit() 또는 SqlSession#rollback()를 통해 트랜잭션 종료
  • Spring framework 등에서 MyBatis를 사용할 경우, 일반적으로 트랜잭션을 직접 제어하지 않고 framework에게 위임함
    • Framework에 포함된 Transaction Manager 이용

Using Mapper Interfaces

 Interface 및 Annotation 사용

  • Mapper XML 대신 Mapper interface 정의
    • 질의 실행을 위한 메소드를 정의하고 annotation을 통해 SQL문 설정
    • 질의 parameter 및 result mapping은 메소드의 parameter 및 return type으로 나타냄
  • DAO에서는 mapper interface의 메소드를 호출
    • SqlSession#getMapper()를 통해 mapper 객체를 구하고, mapper interface에 정의된 메소드를 호출
    • 주의 : SqlSession의 selectOne(), insert(), update(), delete() 등의 메소드는 사용하지 않음
  • Mapper XML과 Mapper interface 비교
    • <mapper>의 namespace -> Mapper interface의 package 및 interface명
    • <select/insert/update/delete>의 id -> Method 명
    • <select/insert/update/delete>의 parameterType -> Method의 parameter type
    • <select/insert/update/delete>의 resultType -> Method의 return type
    • xml은 컴파일러가 컴파일 안 해주는 단순 설정 파일이다. 잘못된 정보 넣어도 체크 안 해준다. 반환타입도 명시적 type casting을 해야 한다. id로 메소드를 호출하기 때문에 string값을 넣는 것이고, 무슨 글자를 넣어도 string 자체로는 오류를 발견 못 한다. 실행해야 오류를 반견할 수 있다.
    • interface는 컴파일 단계에서 타입 지정 등의 오류를 잡을 수 있다. id로 메소드를 호출하는 게 아니라 메소드 그 자체를 호출하는 것이다.
  • mapper interface 예
package repository.mybatis.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;

import model.Comment;

import org.apache.ibatis.annotations.Delete;

public interface CommentMapper2 {
	@Select("SELECT comment_no AS commentNo," + 
			" 		user_id AS userId," + 
			" 		comment_content AS commentContent," + 
			" 		reg_date AS regDate " +
			"FROM COMMENTS " + 
			"WHERE comment_no = #{commentNo}")
	Comment selectCommentByPrimaryKey(long commentNo); // id 역할을 메소드 이름으로
	
	@Select("SELECT comment_no AS commentNo," + 
			" 		user_id AS userId," + 
			" 		comment_content AS commentContent," + 
			" 		reg_date AS regDate " +
			"FROM COMMENTS " + 
			"WHERE user_id = #{userId}")
	// 주의: @Select annotation으로는 dynamic SQL은 표현 불가 
	//		--> 별도의 클래스 정의 및 @SelectProvider 사용해야 함
	List<Comment> selectCommentByCondition(Map<String, Object> condition);
	
	@Insert("INSERT INTO COMMENTS (comment_no, user_id, comment_content, reg_date) " + 
			"VALUES (#{commentNo}, #{userId}, #{commentContent}, #{regDate})")
	int insertComment(Comment comment);  
 
	@Update("UPDATE COMMENTS " + 
			"SET comment_content = #{commentContent} " + 
			"WHERE comment_no = #{commentNo}")
	int updateComment(Comment comment);
	
	@Delete("DELETE FROM COMMENTS " +
			"WHERE comment_no = #{commentNo}")
	int deleteComment(long commentNo);
	
	@Delete("DELETE FROM COMMENTS")
	int deleteAllComments();
}
  • mappert interface를 이용한 DAO 예
package repository.mybatis;

import java.io.IOException;
import java.io.InputStream;
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 model.Comment;
import repository.mybatis.mapper.CommentMapper2;

public class CommentMapperRepository2 {
	private SqlSessionFactory sqlSessionFactory;
	
	public CommentMapperRepository2() {
		String resource = "mybatis-config2.xml";
		InputStream inputStream;
		try {
			inputStream = Resources.getResourceAsStream(resource);
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	public Comment findCommentByCommentNo(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try { // CommentMapper2라는 인터페이스 타입의 객체를 얻고 바로 메소드를 호출함
			return sqlSession.getMapper(CommentMapper2.class).selectCommentByPrimaryKey(commentNo);		
            // interface에 정의된 메소드를 직접 호출 -> Comment type 객체 반환됨
		} finally {
			sqlSession.close();
		}
	}

	public List<Comment> findCommentByCondition(Map<String, Object> condition) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			return sqlSession.getMapper(CommentMapper2.class).selectCommentByCondition(condition);			
		} finally {
			sqlSession.close();
		}
	}
	
	public int insertComment(Comment comment) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.getMapper(CommentMapper2.class).insertComment(comment);
			if (result > 0) {
				sqlSession.commit();
			} 
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int updateComment(Comment comment) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.getMapper(CommentMapper2.class).updateComment(comment);
			if (result > 0) {
				sqlSession.commit();
			} 
			return result;
		} finally {
			sqlSession.close();
		}
	}

	public int deleteComment(long commentNo) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.getMapper(CommentMapper2.class).deleteComment(commentNo);
			if (result > 0) {
				sqlSession.commit();
			} 
			return result;	
		} finally {
			sqlSession.close();
		}
	}
	
	public int deleteAllComments() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
			int result = sqlSession.getMapper(CommentMapper2.class).deleteAllComments();
			if (result > 0) {
				sqlSession.commit();
			} 
			return result;		
		} finally {
			sqlSession.close();
		}
	}
}

Mapper XML 및 Interface 활용

Mapper XML과 Mapper Interface를 함께 사용

  • SQL statement는 Mapper XML file에서만 정의, Mapper interface에서는 생략
  • 주의 : 서로 매치될 수 있도록 Mapper XML과 Mapper interface의 대응되는 이름들을 서로 일치시켜야 함
    • <mapper>의 namespace = Mapper interface의 package 및 interface명
    • <select/insert/update/delete>의 id = Method 명
    • <select/insert/update/delete>의 parameterType = Method의 parameter type
    • <select/insert/update/delete>의 resultType = Method의 return type
  • DAO에서는 Mapper interface에 정의된 메소드를 호출 (DAO에서는 interface를 사용하는 게 좋아서 interface 쓰고자 하는 것이다)
  • Mapper XML과 Mapper Interface 두 방식의 장점을 모두 활용
  • XML에서 SQL문 정의, interface에서 annotation을 통한 SQL문 정의 생략
package repository.mybatis.mapper;

import java.util.List;
import java.util.Map;

import model.Comment;

public interface CommentMapper {
	Comment selectCommentByPrimaryKey(long commentNo);
	
	List<Comment> selectCommentByCondition(Map<String, Object> condition);
	
	int insertComment(Comment comment);   
 
	int updateComment(Comment comment);
	
	int deleteComment(long commentNo);

	int deleteAllComments();
}

Mapper 정의 방식 비교

  • Mapper XML
    • 장점 : mapper의 모든 기능 사용 가능, iBATIS와 호환
    • 단점 : SQL statement의 id를 문자열로 정의 및 참조해야 하므로 runtime error 발생 가능성 있음
  • Mapper interface
    • 장점 : Interface 및 메소드 호출을 사용하므로 runtime error 가능성 적음, Type 변환 필요 없음
    • 단점 : Annotation 특성상 동적 SQL 작성, 1:N 관계 mapping 등이 어렵거나 불가능함, Result mapping에 제약이 있음