[강의] MyBatis

Jerry·2025년 12월 16일

MyBatis 개념 및 흐름

MyBatis란?

데이터의 입력, 조회, 수정, 삭제(CRUD)를 보다 편하게 하기 위해 xml로 구조화한 Mapper 설정 파일을 통해 JDBC를 구현한 영속성 프레임워크 기존 JDBC를 통해 구현했던 상당 부분의 코드와 파라미터 설정 및 결과 매핑을 xml 설정을 통해 쉽게 구현할 수 있게 함
ORM 기술 중 하나로 대체 기술로는 JPA가 존재하나 여전히 국내에선 MyBatis도 많이 활용됨
MyBatis API Site: https://mybatis.org/mybatis-3/ko/

MyBatis VS JPA

구분MyBatisJPA
개념JDBC Driver 기반으로 Java 코드와 SQL을 분리하고, 반자동 매핑 기능을 제공하는 SQL Mapper 프레임워크객체(Object)와 테이블(Table)을 1:1로 매핑하고, DDL·DML을 자동 생성하는 ORM 표준
특징DB 종속적
SQL 분리 관리
부분 자동 매핑 지원
동적 SQL 작성에 강점
DB 독립성
SQL 자동 생성
객체-테이블 자동 매핑
다중 DB 지원
DB 관리 방식DB별 SQL을 XML로 관리
필요에 따라 Mapper XML을 추가
엔티티 매핑 기반 자동 관리
별도 DB별 설정 불필요
구현 방식XML 기반 SQL과 코드 1:1 관리
Mapper 인터페이스를 통해 호출
엔티티에 어노테이션으로 매핑
JpaRepository 상속 시 CRUD 자동 제공
필요한 기능만 추가 구현
동적 쿼리MyBatis 전용 문법(<if>, <foreach> 등)으로 강력한 동적 쿼리 지원자체적인 동적 쿼리 기능은 제한적
Criteria API, QueryDSL 등 라이브러리 활용 필요
장점복잡한 쿼리·동적 쿼리 작성에 탁월
쿼리 튜닝 및 캐시 전략으로 성능 최적화 용이
DDL·DML 자동 생성으로 생산성 향상
DB 독립적이라 코드 재사용성 높음
단점생산성 저하 가능
DB 종속적이어서 DB별 Mapper 관리 부담
자동 생성 쿼리의 성능 이슈 가능 (튜닝 필요)
복잡한 쿼리 표현에 한계
활용 사례DB 종속 환경에서 복잡한 쿼리·고성능 튜닝이 중요한 경우
(SI, 금융권, 레거시 프로젝트 등)
DB 독립적인 제품·솔루션 개발
개발 생산성이 중요한 환경
(스타트업, 최근 일반적인 Spring Boot 프로젝트)

MyBatis 흐름

이전 JDBC Template을 통해 SQL을 실행하였다면 MyBatis의 구조를 통하여 접근 가능 (실제 사용자는 JDBC를 구현하지 않음)

MyBatis 동작 구조

(참고) MyBatis vs iBatis

Apache에서 ibatis의 개발 팀이 2010년 5월 9일에 Google팀으로 이동하면서 MyBatis로 이름을 변경
MyBatis는 기존 ibatis의 한계점이었던 동적 쿼리와 어노테이션 처리를 보강하여 더 나은 기능 제공
ibatis는 현재 비활성화 상태이며 기존에 ibatis로 만들어진 애플리케이션의 지원을 위해 라이브러리만 제공하고 있으며, 신규 사용은 MyBatis를 권장

차이점

ibatisMyBatis
Java 요구 버전JDK 1.4 이상JDK 1.5이상
패키지 구조 com.ibatis.*org.apache.ibatis.*
사용 용어SqlMapConfig
sqlMap
resultClass
Configuration
Mapper
resultType
동적쿼리/어노테이션XO

MyBatis 내장 별칭

MyBatis 초기 버전에서는 기본형에 대하여 ‘_’ 규칙을 통해 java 호환성 확보를 하였지만, 1.5 버전 이후 부터는 autoboxing이 지원되면서 지키지 않아도 문제 없음

MyBatis 타입(alias)Java 자료형
_bytebyte
byteByte
_shortshort
shortShort
_int, _integerint
int, integerInteger
_longlong
longLong
_floatfloat
floatFloat
_doubledouble
doubleDouble
_booleanboolean
booleanBoolean
stringString
dateDate
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

POOLED와 UNPOOLED 차이점

구분POOLEDUNPOOLED
특징최초 Connection 객체를 생성할 때 해당 정보를 pool 영역에 저장해두고 이후 Connection 객체를 생성할 때 이를 재사용함Connection 객체를 별도로 저장하지 않고 객체 호출 시 매번 생성하여 사용
장점Connection 객체를 생성하여 DataBase와 연결을 구축하는데 걸리는 시간이 단축 됨Connection 연결이 많지 않은 코드를 작성할 때 간단하게 구현 가능
단점단순한 로직을 수행하는 객체를 만들기에는 설정해야 할 정보가 많음매번 새로운 Connection 객체를 생성하므로 속도가 상대적으로 느림

Mapper 설정하기

*-mapper.xml 생성 위치

‘resources’ 폴더 안에 ‘mappers’폴더 생성 후 그 안에 식별하기 쉬운 이름을 지어 파일 등록

*-mapper.xml 작성

xml 최상단에 아래와 같이 xml형식을 지정하여 이하의 설정 내용이 MyBatis mapper 설정임을 선언

<?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 속성 기입, 이후 작성될 태그들은 <mapper>태그 안에 기록

<mapper namespace="memberMapper">
  <!-- mapper 내부에 작성될 내용 -->
</mapper>

*-mapper.xml 작성

<resultMap> 태그: 조회한 결과를 객체와 Row간의 1:1 매칭이 아닌 원하는 객체의 필드에 담아 반환하고자할 때 사용

<resultMap Type="Member" id="memberResultSet">
  <!-- property = 자바의 필드 변수 이름 / column = DB의 해당 컬럼 -->
  <!-- id는 primary key / result는 일반 컬럼 -->
  <id property="mid" column="MID"/>
  <result property="userId" column="USER_ID"/>
  <result property="userPwd" column="USER_PWD"/>
  <result property="userName" column="USER_NAME"/>
</resultMap>

<resultMap>의 type 속성은 실제로 구현해 놓은 자바 POJO객체를 사용해야 하며, mybatis-config.xml에서 typeAlias를 지정하지 않은 경우 패키지 명부터 클래스 명까지 모두 기술해야 함

<select> 태그 : SQL의 조회구문을 작성 할 때 사용되는 태그, 해당 쿼리를
외부에서 접근하고자 할 때 namespace.id명을 적어 접근 가능

<select id="memberInfo" parameterType="string" resultType="_int">
  <!-- #{field}는 pstmt의 '?'의 역할이며, 전달된 값을 뜻함
		또한 여러 줄로 줄 바꿈 문자를 섞어 사용도 가능하다.
		단, 쿼리의 마지막을 알리는 세미콜론은 에러를 유발한다. -->
  SELECT *
  FROM MEMBER
  WHERE USER_ID = #{userId}
</select>
속성 명내용
id구문을 찾기 위해 사용될 수 있는 네임스페이스 내 유일한 구분자
parameterType구문에 전달될 파라미터의 클래스 명(패키지 경로 포함)이나 별칭
resultType리턴되는 타입의 패키지 경로를 포함한 전체 클래스 명이나 별칭, collection인 경우 list, arraylist로 설정 가능
resultMap리턴되는 타입의 필드 명이 다를 때 사용하며 직접 이름을 지정하여 매칭

resultMap과 resultType은 둘 모두를 사용할 수 없으며 둘 중 하나만 선언해야 함

<select> 태그 옵션 속성

속성 명내용
flushCache기본 값은 false이며 이 값을 true로 설정하면 구문이 호출 될 때마다 로컬, Second Level 캐시가 초기화 된다.
useCache기본 값은 true이며 이 값을 true로 설정하면 구문의 결과가 Second Level 캐시에 저장
timeout예외가 발생하기 전에 데이터베이스의 요청 결과를 기다리는 최대 시간 설정, 드라이버에 따라 다소 지원되지 않을 수 있음
statementTypeSTATEMENT, PREPARED 또는 CALLABLE 중 하나 선택 가능(기본 값 : PREPARED)
MyBatis에게 Statement, PreparedStatement, CallableStatement를 사용하게 함

(참고) flushCache와 useCache의 개념

MyBatis는 Cache(캐시) 기능 지원하여 최근에 요청한 쿼리와 결과를 저장하고 중복된 쿼리를 요청하면 이를 대신 응답하는 구조로 구성되어 있다. (Commit 기준)
이러한 캐시는 Local Session Cache와 Second Level Cache 두가지로 구성되어 있다.
Local Sesion Cache는 무조건적으로 활성화 되며 SqlSession 객체에 Cache가 되며 Second Level Cache는 Mapper namespace 단위로 Cache가 되며 On/Off 설정이 가능하다.

*-mapper.xml 작성

<insert>, <update>, <delete> 태그 : 해당 태그들은 설정 동일

<insert id="insertMember" parameterType="Member" flushCache="true" statementType="PREPARED", useGeneratedKeys="true" timeout="20">
  INSERT INTO MEMBER VALUES(
      #{userId}, #{userPwd}, #{userName}
  )
</insert>

<insert>, <update>, <delete>태그 주요 속성

속성 명내용
id구문을 식별하기 위한 값으로, 네임스페이스(namespace) 내에서 유일해야 합니다.
parameterTypeSQL 구문에 전달될 파라미터의 클래스 타입을 지정합니다. (패키지 경로 포함 클래스명 또는 Type Alias 사용 가능)
flushCache기본값은 false입니다. true로 설정하면 해당 구문이 호출될 때마다 캐시가 초기화됩니다.
timeout예외가 발생하기 전까지 데이터베이스 요청 결과를 기다리는 최대 시간을 설정합니다. JDBC 드라이버에 따라 지원되지 않을 수 있습니다.
useGeneratedKeys
(insert, update 전용)
DB에서 내부적으로 생성한 키(예: MySQL, SQL Server의 auto-increment)를 가져오기 위해 JDBC의 getGeneratedKeys() 메서드를 사용하도록 설정합니다. (기본값: false)
keyProperty
(insert, update 전용)
getGeneratedKeys() 또는 <selectKey>의 실행 결과를 저장할 객체의 프로퍼티명을 지정합니다. 여러 컬럼을 사용할 경우 콤마(,)로 구분하여 나열합니다.

MyBatis 활용하기

SqlSession 생성하기

싱글톤을 적용한 Template 클래스 생성

mybatis-config.xml, *-mapper.xml 파일 생성을 완료했다면
common패키지를 만들어 싱글톤을 적용한 Template 클래스를 만들고 SqlSession을 반환해주는 static 메소드 작성

SqlSessionFactoryBuilder 메소드

메서드설명
build(InputStream)config.xml 파일만 로드하여 SqlSessionFactory를 생성합니다.
build(InputStream, String)config.xml 파일과 지정한 DB(environment id) 설정을 함께 로드합니다.
build(InputStream, Properties)config.xml 파일과 Properties로 전달된 설정 값을 함께 로드합니다.
${key} 형태로 XML 내에서 참조할 수 있습니다.
build(InputStream, String, Properties)config.xml 파일, 지정한 DB(environment id), Properties 설정 값을 모두 로드합니다.
build(Configuration)이미 구성된 Configuration 객체에 설정된 내용을 기반으로 SqlSessionFactory를 생성합니다.
  • config.xml은 Resource 객체의 getResourceAsStream 메소드를 이용하여 InputStream으로 가져옴

SqlSessionFactory 메소드

메서드설명
openSession()기본 설정값을 사용하여 SqlSession을 생성합니다.
openSession(boolean autoCommit)SqlSession 생성 시 AutoCommit 여부true / false로 지정합니다.
(기본값: true)
openSession(Connection connection)사용자가 직접 생성한 Connection 객체를 사용하여 SqlSession을 생성합니다.
(기본값 없음)
openSession(ExecutorType executorType)쿼리 실행 시 PreparedStatement 재사용 전략을 지정합니다.
(기본값: ExecutorType.SIMPLE)

SqlSession을 통한 쿼리 생성

  1. Service클래스에서 getSqlSession메소드 호출을 통해 SqlSession 생성
  2. DAO클래스의 메소드 호출 시 전달 인자로 SqlSession 객체 전달
  3. DAO클래스의 메소드에서 SqlSession 객체를 통해 쿼리에 접근

SqlSession을 통한 쿼리 실행

메서드반환형설명
selectOne(String statement, Object parameter)Object단일 결과 객체를 조회할 때 사용합니다. 조회 결과가 2건 이상이면 예외가 발생합니다.
selectList(String statement, Object parameter)List<E>조회 결과를 List 형태로 반환합니다. 다건 조회에 사용됩니다.
selectMap(String statement, Object parameter, String mapKey)Map<K, V>조회 결과를 Map 형태로 반환합니다.
마지막 인자로 Map의 Key로 사용할 컬럼명(또는 프로퍼티명)을 지정합니다.
insert(String statement, Object parameter)intDB에 데이터를 삽입할 때 사용합니다.
반환값은 영향 받은 행(row) 수입니다.
update(String statement, Object parameter)intDB의 데이터를 수정할 때 사용합니다.
반환값은 영향 받은 행(row) 수입니다.
delete(String statement, Object parameter)intDB의 데이터를 삭제할 때 사용합니다.
반환값은 영향 받은 행(row) 수입니다.

MyBatis 동적 SQL

DB에서 쿼리는 정적인 성격으로 한번 완성된 쿼리는 일반적으로 재사용하거나 변형하기 어렵다.
웹에서는 경우에 따라 다양한 검색 옵션이 활성화 되는 경우가 있고, 이때 경우의 수에 따라 다수의 쿼리를 작성해야 하거나 통합된 쿼리로 만드는데, 어려움이 발생한다.
동적 SQL이란 다양한 경우의 수를 가지는 쿼리를 if문과 for문과 같은 제어문을 통해 다양한 경우의 수에도 동적으로 변경되는 쿼리를 작성하는 기술이다.

지원 구문 종류

  1. If
  2. choose (when, otherwise)
  3. trim (where, set)
  4. foreach

#{…}, ${…} 파라미터 표현

Mybatis에서는 파라미터 표현법으로 EL 표현법과 유사하게 활용한다.
#{param}의 경우 문자열과 숫자를 구분하여 문자의 경우 ‘(싱글쿼테이션)을 자동으로 추가한다.
ex) #{param} -> ‘param’, #{}은 쿼리에 보안적으로 안전하게 문자열을 주입됨으로 권장되는 표현
${param}의 경우 파라미터를 바로 주입되는 표현법이다. ex) ${param} -> param
${} 표현법의 경우 그대로 주입됨에 따라 SQL injection을 유의해 한다.

<![CDATA[]]>구문

XML 고유 문법으로 XML 태그 내부에 XML 표현법으로 활용되는 특수문자를 표현할 때 활용된다.
주로 태그 표현(<b></b>)와 같은 표현법을 XML 내부 컨텐츠로 활용하기 위해 활용된다.
Mybatis의 경우 SQL 자체에 >,<를 대소 비교 시 활용됨에 따라 간혹 CDATA 구문이 활용된다.

if 구문

동적 쿼리를 구현할 때 가장 기본적으로 사용되는 구문으로 특정 조건을 만족할 경우 안의 구문을 쿼리에 포함 시킴

다중 if 구문

필요로 하는 조건이 1개 이상일 시 if구문을 여러 개 사용 가능

chhose, when, otherwise 구문

자바의 if-else, switch, JSTL의 choose 구문과 유사하며 주어진 구문 중 한 가지만 수행하고자 할 때 사용

불완전한 동적 SQL로 발생하는 에러 케이스


위와 같은 동적 SQL을 작성하는 경우 경우에 따라 SQL 에러가 발생하는 케이스가 존재

  • 경우 1 : if 어느 조건도 만족하지 못 했을 경우
    SELECT * FROM BOARD
    WHERE
  • 경우 2 : 두 번째 if 조건만 만족시켰을 경우
    SELECT * FROM BOARD
    WHERE AND TITLE = ‘OOO’

trim, where, set 구문

불완전한 동적 SQL 구문을 해결하기 위해 Mybatis에서 지원하는 문법
<trim> : 쿼리 구문의 특정 부분을 없앨 때
<where> : 기존 쿼리의 WHERE절을 동적으로 구현할 때
<set> : 기존의 UPDATE SET절을 동적으로 구현할 때
또는 아래와 같은 WHERE 1=1 AND … 구문으로도 해결 가능하다. (실제 필드에서 가장 많이 활용하는 문법)

SELECT * FROM BOARD
WHERE 1=1
<if test="writer != null">
    AND ID LIKE '%${writer}%'
</if>
<if test="title != null">
    AND TITLE LIKE '%${title}%'
</if>

where 구문

단순히 WHERE만을 추가하지만 만일 태그 안의 내용이 AND나 OR로 시작할 경우 ‘AND’ 또는 ‘OR’ 제거

trim 구문을 통해 where 구현

태그 안의 내용이 완성될 때 처음 시작할 단어(prefix)와 시작 시 제거해야 할 단어(prefixOverrides)를 명시해 where와 같은 내용으로 구현

set 구문

UPDATE하고자 하는 컬럼을 동적으로 포함시키기 위해 사용하며 SET 키워드를 붙이고 불필요한 콤마 제거

trim 구문을 통해 set 구현

WHERE와 흡사하나 suffixOverrides속성을 ‘,’로 설정하여 구문의 마지막에 제거할 값 명시

foreach 구문

동적 쿼리를 구현할 때 collection에 대한 반복처리 제공

foreach 속성

속성 명설명
item반복 시 각 요소에 접근할 때 사용할 변수명입니다.
index반복되는 인덱스 값을 가리키는 변수입니다. 0부터 순차적으로 증가합니다.
collectionMapper로 전달받은 파라미터 중 반복에 사용할 컬렉션 객체입니다.
List 또는 배열(Array) 타입만 사용할 수 있습니다.
open반복 구문이 시작될 때 앞에 삽입할 문자열입니다.
separator반복되는 요소 사이에 삽입할 구분자 문자열입니다.
close반복 구문이 종료될 때 뒤에 삽입할 문자열입니다.

bind 구문

특정 문장을 미리 생성하여 쿼리에 적용해야 할 경우 사용
_parameter를 통해 전달받은 값에 접근하여 구문 생성

include 구문

<sql></sql>태그를 통해 미리 만들어 놓은 쿼리를 다른 쿼리에 주입 가능
이때 <include>를 활용하며, 만일 다른 값을 주입할 경우 <property> 활용

profile
Backend engineer

0개의 댓글