MyBatis는 Java 객체와 SQL 문 사이에서 자동으로 매핑을 지원하는 ORM(Object Relational Mapping) 프레임워크입니다. 이를 통해 Java 코드에서 SQL을 직접 작성하지 않고도 데이터베이스와 상호 작용할 수 있게 해줍니다.
MyBatis는 다음과 같은 특징이 있습니다.
MyBatis의 아키텍처는 기존 JDBC 프로그래밍과 차이가 있습니다. 기존 JDBC 프로그래밍에서는 Repository(DAO)에서 직접 JDBC API를 통해 데이터베이스에 접근했습니다. 하지만 MyBatis를 사용할 경우, Repository와 JDBC API 사이에 MyBatis가 위치하여 더 편리한 데이터베이스 접근을 제공합니다.
DAO 클래스를 사용하면 SQL문을 수정할 때마다 클래스를 다시 컴파일해야 하고, SQL문을 한 곳에서 통합 관리하기 어렵습니다. MyBatis는 Mapper를 통해 SQL문을 Java 코드로부터 완전히 분리하여 SQL문에 대한 통합 관리를 가능하게 합니다. Mapper는 XML 형식의 문서로 작성되며, 이를 통해 SQL문을 독립적으로 관리할 수 있습니다.
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
프로젝트를 생성하는 방법은 다음과 같습니다.
프로젝트 생성이 완료되었습니다.
JSP 설정과 기본 프로젝트 구성 방법에 대해서는 이 링크를 참조하세요.
프로퍼티 파일에 값을 설정합니다. 다음과 같이 resources/application.properties 파일을 작성합니다.
# Mybatis Mapper의 위치 설정 (src/main/resource 하위에 폴더 생성)
# mybatis.mapper 하위의 모든 폴더에 존재하는 xml 파일을 매퍼로 사용하겠다는 선언
mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml
서비스 인터페이스를 생성합니다. 다음과 같이 com.edu.springboot.jdbc.IMemberService.java 파일을 작성합니다.
package com.edu.springboot.jdbc;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
/* 컨트롤러와 DAO(XML) 사이에서 매개 역할을 하는 인터페이스이다.
* JdbcTemplate에서는 @Service를 사용했지만 mybatis에서는 @Mapper를 사용한다.
* 컨트롤러에서 인터페이스에 정의된 추상메서드를 호출하면 연결된 Mapper의 특정 엘리먼트가 호출되어 실행되는 구조를 가진다. */
@Mapper
public interface IMemberService {
public List<MemberDTO> select();
public int insert(MemberDTO memberDTO);
public MemberDTO selectOne(MemberDTO memberDTO);
public int update(MemberDTO memberDTO);
public int delete(MemberDTO memberDTO);
}
DTO를 생성합니다. 다음과 같이 com.edu.springboot.jdbc.MemberDTO.java 파일을 작성합니다.
package com.edu.springboot.jdbc;
import lombok.Data;
@Data
public class MemberDTO {
private String id;
private String pass;
private String name;
private String regidate;
}
매퍼를 생성합니다. 다음과 같이 resources/mybatis/mapper/MemberDAO.xml 파일을 작성합니다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mapper 파일을 구성하기 위한 Doctype 설정 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 컨트롤러와 Mapper 사이에서 매개 역할을 하는 인터페이스를 namespace로 지정한다.
xml 파일에서는 클래스에 대한 import가 불가능하므로 아래와 같이 패키지의 풀경로를 지정해야 한다. -->
<mapper namespace="com.edu.springboot.jdbc.IMemberService">
</mapper>
해당 파일에서 실행에는 문제가 없으나 'downloading external resource is disabled' 라는 오류와 함께 빨간 줄이 생깁니다. 이는 다음과 같은 방법으로 해결할 수 있습니다.
모든 설정이 완료되면 빨간 줄이 사라집니다. 이제 Mybatis를 사용하기 위한 준비가 완료되었습니다.
홈을 생성합니다. 다음과 같이 webapp/WEB-INF/views/home.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>
<h2>스프링 부트 프로젝트</h2>
<ul>
<li><a href="/">루트</a></li>
</ul>
<h2>Mybatis로 구현한 회원관리</h2>
<ul>
<li><a href="./list.do">회원목록</a></li>
</ul>
</body>
</html>
컨트롤러를 생성합니다. 다음과 같이 com.edu.springboot.MainController.java 파일을 작성합니다.
package com.edu.springboot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.edu.springboot.jdbc.IMemberService;
@Controller
public class MainController {
/* 서비스 인터페이스를 통해 Mapper의 메서드를 호출하므로 여기서 자동주입 받아서 준비한다.
* 해당 인터페이스는 @Mapper 어노테이션이 부착되어 있으므로 컨테이너가 시작될 때 자동으로 빈이 생성된다. */
@Autowired
IMemberService dao;
@RequestMapping("/")
public String home() {
return "home";
}
// 회원목록
@RequestMapping("/list.do")
public String list(Model model) {
/* dao.select() : 서비스 인터페이스의 추상메서드를 호출한다.
* 그러면 인터페이스와 연결된 Mapper 파일에 정의된 특정 엘리먼트가 호출되어 실행된다. */
model.addAttribute("memberList", dao.select());
return "list";
}
}
매퍼를 생성합니다. 다음과 같이 resources/mybatis/mapper/MemberDAO.xml 파일에 코드를 추가합니다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Mapper 파일을 구성하기 위한 Doctype 설정 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 컨트롤러와 Mapper 사이에서 매개 역할을 하는 인터페이스를 namespace로 지정한다.
xml 파일에서는 클래스에 대한 import가 불가능하므로 아래와 같이 패키지의 풀경로를 지정해야 한다. -->
<mapper namespace="com.edu.springboot.jdbc.IMemberService">
<!-- 매퍼에서 쿼리문을 실행하기 위한 엘리먼트
: DML의 4가지 쿼리와 동일한 이름의 엘리먼트가 존재한다.
즉 select 쿼리는 <select> 엘리먼트를 사용하면 된다.
하위 프로퍼티
- id : 컨트롤러에서 호출하기 위한 메서드명을 기술한다. Java와 같이 메서드의 원형을 사용하지 않고, 단지 메서드명만 명시하면 된다.
- resultType : 쿼리문 실행 후 반환할 결과의 타입을 명시한다.
select문의 경우 ResultSet이 반환되므로, 이를 저장하기 위한 DTO를 지정하면 되고 개수가 많은 경우 Mapeer가 알아서 List에 add() 해준다.
insert, delete, update와 같이 행의 변화가 있는 쿼리문을 결과로 int를 반환하므로 resultType은 명시하지 않는다. -->
<!-- 컨트롤러에서 select()를 호출하면 id 속성에 함수명과 동일한 속성값을 가진 엘리먼트가 실행된다. -->
<select id="select"
parameterType="com.edy.sprintboot.jdbc.MemberDTO"
resultType="com.edy.sprintboot.jdbc.MemberDTO">
SELECT * FROM member ORDER BY regidate DESC
</select>
</mapper>
뷰를 생성합니다. 다음과 같이 webapp/WEB-INF/views/list.jsp 파일에 코드를 추가합니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>회원리스트</h2>
<table border="1">
<tr>
<th>아이디</th>
<th>패스워드</th>
<th>이름</th>
<th>가입일</th>
<th></th>
</tr>
<c:forEach items="${ memberList }" var="row" varStatus="loop">
<tr>
<td>${ row.id }</td>
<td>${ row.pass }</td>
<td>${ row.name }</td>
<td>${ row.regidate }</td>
<td>
<a href="edit.do?id=${ row.id }">수정</a>
<a href="delete.do?id=${ row.id }">삭제1</a>
</td>
</tr>
</c:forEach>
</table>
<a href="regist.do">회원등록</a>
</body>
</html>
이제 회원 목록을 조회할 수 있습니다.
다음과 같이 실행됩니다.

'회원목록' 링크를 클릭하면 데이터베이스에 저장된 회원목록을 확인할 수 있습니다.

회원목록 조회 기능이 성공적으로 구현되었습니다.
컨트롤러를 생성합니다. 다음과 같이 com.edu.springboot.MainController.java 파일에 코드를 추가합니다.
// 회원정보 등록 : 가입폼은 GET 방식, 등록처리는 POST 방식으로 매핑한다.
@GetMapping("/regist.do")
public String regist1() {
// 등록 페이지 포워드만 처리
return "regist";
}
@PostMapping("/regist.do")
public String regist2(MemberDTO memberDTO) {
// 전송된 폼값을 커맨드객체를 통해 일괄적으로 받은 후 DAO의 메서드 호출
int result = dao.insert(memberDTO);
if (result == 1) System.out.println("회원정보가 등록되었습니다.");
// 입력에 성공하면 목록으로 이동한다.
return "redirect:list.do";
}
매퍼를 생성합니다. 다음과 같이 resources/mybatis/mapper/MemberDAO.xml 파일에 코드를 추가합니다.
<!-- 하위 프로퍼티
- paramterType : 메서드 호출 시 파라미터로 사용할 클래스명을 명시한다.
개발자가 직접 정의한 클래스 외에도 Map, List와 같은 컬렉션을 사용할 수 있다.
만약 필요하지 않다면 생략할 수 있다. -->
<!-- 회원등록 : parameterType으로 명시한 DTO 객체의 멤버변수명을 통해 쿼리문의 인파라미터를 설정한다.
EL과 동일하게 DTO에 정의한 getter를 통해 값을 얻어와 세팅한다.
insert의 경우 반환타입은 무조건 정수형이므로 resultType은 생략할 수 있다. -->
<insert id="insert"
parameterType="com.edu.springboot.jdbc.MemberDTO">
INSERT INTO member (id, pass, name) VALUES (#{id}, #{pass}, #{name})
</insert>
뷰를 생성합니다. 다음과 같이 webapp/WEB-INF/views/regist.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>
<h2>회원등록</h2>
<form action="regist.do" method="post">
<table border="1">
<tr>
<th>아이디</th>
<td><input type="text" name="id" value="" /></td>
</tr>
<tr>
<th>패스워드</th>
<td><input type="text" name="pass" value="" /></td>
</tr>
<tr>
<th>이름</th>
<td><input type="text" name="name" value="" /></td>
</tr>
</table>
<input type="submit" value="전송하기" />
</form>
</body>
</html>
이제 회원정보를 등록할 수 있습니다.
다음과 같이 실행됩니다.

'회원등록' 링크를 클릭하면 회원정보를 입력할 수 있는 페이지로 이동합니다.

등록할 회원정보를 입력합니다.

입력한 후 '전송하기' 버튼을 클릭하면 회원정보가 등록된 후 회원목록 페이지로 이동합니다. 목록에서 회원정보가 정상적으로 등록되었음을 확인할 수 있습니다.

콘솔에는 다음과 같이 출력됩니다.
회원정보가 등록되었습니다.
회원정보 등록 기능이 성공적으로 구현되었습니다.
컨트롤러를 생성합니다. 다음과 같이 com.edu.springboot.MainController.java 파일에 코드를 추가합니다.
// 회원정보 수정 1 : 기존 회원정보를 가져와서 수정폼 구성
@GetMapping("/edit.do")
public String edit1(MemberDTO memberDTO, Model model) {
// 기존 레코드를 얻어온 후 View로 전달
memberDTO = dao.selectOne(memberDTO);
model.addAttribute("dto", memberDTO);
return "edit";
}
// 회원정보 수정 2 : 수정된 내용을 통해 레코드 업데이트 처리
@PostMapping("/edit.do")
public String edit2(MemberDTO memberDTO) {
int result = dao.update(memberDTO);
if (result == 1) System.out.println("회원정보가 수정되었습니다.");
return "redirect:list.do";
}
매퍼를 생성합니다. 다음과 같이 resources/mybatis/mapper/MemberDAO.xml 파일에 코드를 추가합니다.
<!-- 회원정보 수정 1 : 회원정보를 select한 후 DTO에 저장 후 반환한다. -->
<select id="selectOne"
parameterType="com.edu.springboot.jdbc.MemberDTO"
resultType="com.edu.springboot.jdbc.MemberDTO">
SELECT * FROM member WEHRE id=#{id}
</select>
<!-- 회원정보 수정 2 : 회원정보를 수정한 결과를 반환하므로 resultType은 명시하지 않는다. -->
<update id="update"
parameterType="com.edu.springboot.jdbc.MemberDTO">
UPDATE member SET pass=#{pass} name=#{name} WHERE id=#{id}
</update>
뷰를 생성합니다. 다음과 같이 webapp/WEB-INF/views/edit.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>
<h2>회원정보 수정</h2>
<form action="edit.do" method="post">
<table border="1">
<tr>
<th>아이디</th>
<td><input type="text" name="id" value="${ dto.id }" readonly /></td>
</tr>
<tr>
<th>패스워드</th>
<td><input type="text" name="pass" value="${ dto.pass }" /></td>
</tr>
<tr>
<th>이름</th>
<td><input type="text" name="name" value="${ dto.name }" /></td>
</tr>
</table>
<input type="submit" value="전송하기" />
</form>
</body>
</html>
이제 회원정보를 조회하여 해당 회원정보를 수정할 수 있습니다.
다음과 같이 실행됩니다.

수정할 회원의 '수정' 링크를 클릭하여 수정할 정보를 입력합니다.

'전송하기' 버튼을 누르면 회원 정보가 수정된 후 회원목록 페이지로 이동합니다. 목록에서 회원정보가 정상적으로 수정되었음을 확인할 수 있습니다.

콘솔에는 다음과 같이 출력됩니다.
회원정보가 수정되었습니다.
회원정보 수정 기능이 성공적으로 구현되었습니다.
컨트롤러를 생성합니다. 다음과 같이 com.edu.springboot.MainController.java 파일에 코드를 추가합니다.
// 회원정보 삭제
@RequestMapping("/delete.do")
public String delete(MemberDTO memberDTO) {
// 아이디를 파라미터로 받은 후 delete 메서드 호출
int result = dao.delete(memberDTO);
if (result == 1) System.out.println("회원정보가 삭제되었습니다.");
return "redirect:list.do";
}
매퍼를 생성합니다. 다음과 같이 resources/mybatis/mapper/MemberDAO.xml 파일에 코드를 추가합니다.
<!-- 회원정보 삭제 -->
<delete id="delete"
parameterType="com.edu.springboot.jdbc.MemberDTO">
DELETE FROM member WHERE id=#{id}
</delete>
이제 회원정보를 삭제할 수 있습니다.
다음과 같이 실행됩니다.

수정할 회원의 '삭제' 링크를 클릭하면 회원 정보가 삭제된 후 회원목록 페이지로 이동합니다. 목록에서 회원정보가 정상적으로 삭제되었음을 확인할 수 있습니다.

콘솔에는 다음과 같이 출력됩니다.
회원정보가 삭제되었습니다.
회원정보 삭제 기능이 성공적으로 구현되었습니다.