프로젝트와 데이터베이스를 연결하고자 하는 개발자들이라면 떠오르는 프레임워크가 2개 있을 것이다.

 

그것은 바로 MyBatisJPA일 것이라 생각한다.

이번 포스팅은 많은 기업들이 사용하고 있는 SQL Mapper, MyBatis의 이론 및 초기 설정에 대해 작성하려 한다.

 

1. MyBatis란 무엇일까

 

MyBatis는 널리 사용되고 있는 대표적인 SQL Mapper 프레임워크다.

 

Spring Framework와 함께 사용되는 경우가 많고,

아래와 같은 특징으로 인해 많은 개발자들의 선택을 받고 있다.

 

1. JDBC를 추상화(Encapsulation)하여 개발자가 프로젝트 內에서 SQL 쿼리를 작성 및 관리하기 쉽도록 해준다.

2. 응답 결과를 Java 객체로 매핑할 수 있도록 하여, 데이터 처리의 복잡도를 낮춘다.

3. SQL 쿼리문을 별도의 XML 파일에 작성하도록 하여 쿼리문 분리가 용이하다.

 

더보기

💡 JDBC (Java Database Connectivity)

Java와 DB를 연결해주는 API로, DB 접속(Connection) 및 SQL문 실행(Statement) 및 SQL 요청 응답 핸들링(ResultSet) 기능을 제공한다.

 

2. MyBatis를 사용할지, JPA를 사용할지

데이터베이스 매핑에 자주 쓰이는 양대산맥,

MyBatis JPA 중 무엇을 사용해야할지 고민일 것이다.

 

그렇다면 둘의 차이에 대해 알아보도록 하자.

 

1. SQL MapperORM

MyBatisSQL Mapper 기술로, 개발자가 직접 SQL 쿼리문을 작성하고 매개변수 및 반환 객체를 직접 매핑해줄 수 있다.

반면, JPAORM 기술로, 개발자가 객체 모델을 정의하면 JPA가 객체와 DB 테이블을 자동으로 매핑해준다.

 

2. 쿼리문 성능 및 개발 생산성

JPA추상화 수준이 높기 때문에 단순한 쿼리의 자동 생성으로 개발 생산성을 높일 수 있으나, 

복잡한 쿼리가 필요하다면, 성능 면에서는 당연히 쿼리 최적화가 가능MyBatis가 유리하다고 볼 수 있다.

 

즉, 이를 요약하자면 프로젝트의 요구사항에 따라

간단한 CRUD만 필요할 경우 개발 생산성이 좋은 JPA를,

복잡한 쿼리문의 성능 최적화가 필요하다면 직접 SQL 쿼리문을 작성할 수 있는 MyBatis를 사용하는 것이 바람직하다.

 

3.  초기 설정 코드

MyBatis에 대해 이해가 되었고 MyBatis를 사용하기로 결심했다면,

직접 Spring Boot에서 MyBatis를 적용하는 초기 설정 코드를 작성하며 실습을 해보도록 하자.

 

샘플 코드를 참고하여 자신의 프로젝트에 맞게 적용하면 된다.

 

build.gradle

implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' #MyBatis dependency
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' #매핑할 DBMS dependency

 

먼저 MyBatis 적용에 필요한 의존성을 먼저 설치해주어야 한다.

우리에게 필요한 것은 사용할 DBMS의 의존성MyBatis 의존성이다.

 

예시는 MariaDB 기준으로 작성되었는데, 자신이 사용하는 DBMS 의존성을 어떻게 작성해야하는지 모르겠다면

Maven Repository 웹페이지에서 검색해보면 Maven과 Gradle에서의 의존성 작성법을 모두 찾을 수 있다.

 

application.properties

# DB Connection
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://{DB URL}:{DB Port}/{Schema}?serverTimezone=UTC&autoReconnect=true&allowMultiQueries=true&characterEncoding=UTF-8
spring.datasource.username={DB Username}
spring.datasource.password={DB Password}

# MyBatis Mapping
spring.web.resources.add-mappings=true
mybatis.config-location=classpath:mybatis/config/myBatisConfig.xml
mybatis.type-aliases-package=com.sample.academy.model.vo

 

application.properties에서는 DB ConnectionMyBatis Mapping과 관련된 설정을 작성해 준다.

 

  • spring.datasource.driver-class-name: 사용할 데이터베이스 드라이버를 정의한다.
Oracle oracle.jdbc.driver.OracleDriver
MySQL com.mysql.cj.jdbc.Driver
MariaDB org.mariadb.jdbc.Driver
SQL Server (MSSQL) com.microsoft.sqlserver.jdbc.SQLServerDriver
PostgreSQL org.postgresql.Driver
  • spring.datasource.url: 사용할 데이터베이스의 URL을 정의한다.
  • spring.datasource.username: 사용할 데이터베이스의 사용자 이름을 정의한다.
  • spring.datasource.password: 사용할 데이터베이스의 비밀번호를 정의한다.

 

  • spring.web.resources.add-mappings: 웹 리소스 매핑을 활성화 한다. src/main/resources와 하위 레포지토리들의 Path가 /가 된다. Default가 True기 때문에 굳이 명시해주지 않아도 된다.
  • mybatis.config-location: MyBatis Configuration 파일의 경로를 정의한다.
  • mybatis.type-aliases-package: MyBatis에서 사용할 Mapping 타입의 별칭 패키지를 미리 정의해둔다. 해당 레포지토리 하위에 있는 파일은 Mapper 파일에서 별도로 경로를 정의할 필요가 없다.
더보기

🎨 DB 연결 성능을 향상시키기 위한 Hicari CP(Connection Pool) 설정

spring.datasource.hikari.data-source-properties.cachePrepStmts=true #PreparedStatement 캐싱 활성화
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=200 #PreparedStatement 캐시 크기
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048 #PreparedStatement 캐시 SQL 문장 크기 제한
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true #서버 PreparedStatement 사용
spring.datasource.hikari.minimumIdle=5 #최소 유휴 연결 수
spring.datasource.hikari.connectionTimeout=10000 #연결 획득 시간 제한 (ms)
spring.datasource.hikari.maximumPoolSize=10 #최대 연결 풀 크기
spring.datasource.hikari.maxLifeTime=580000 #연결 최대 수명 (ms)
spring.datasource.hikari.autoCommit=true #자동 커밋 모드

 

resources/mybatis/config/myBatisConfig.xml

<?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>
		<!-- 언더바 속성을 CamelCase로 변경 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <mappers>
        <mapper resource="mybatis/mappers/student.xml" />
    </mappers>
</configuration>

 

보통 DB는 대소문자를 구분하지 않아 두 단어를 합칠 땐 create_dt 처럼 언더바를 사용한다.

반면 Java 필드를 작성할 땐 대소문자가 구분되기 때문에 createDt와 같이 카멜 케이스를 사용하는 것이 대다수다.

 

언더바로 명명된 속성을 카멜 케이스로 작성된 객체의 필드와 매핑될 수 있도록 

mapUnderscoreToCamelCase Setting을 True로 설정해주는 것이다.

 

각 매핑할 SQL Mapper 파일마다 mapper 태그를 통해 resource 속성으로 연결 시켜준다.

 

resources/mybatis/mappers/student.xml

<?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="com.sample.academy.model.dao.StudentDao">
    <insert id="upsert" parameterType="StudentVo">
        INSERT INTO STUDENT(`id`, `name`, `age`, `class`)
        VALUES (#{id}, #{name}, #{age}, #{class})
        ON DUPLICATE KEY UPDATE name = #{name}, age = #{age}, class = #{class};
    </insert>
    <select id="selectByName" parameterType="String" resultType="StudentVo">
        SELECT *
        FROM STUDENT
        WHERE name LIKE CONCAT('%', #{name}, '%');
    </select>
</mapper>

 

mapper 태그의 namespace에는 해당 SQL 쿼리문과 매핑할 DAO Interface의 Path를 넣고,

태그 內에 DML 태그를 생성해 SQL 쿼리문을 작성한다.

 

아래는 DML 태그의 필요 속성들이다.

  • id: DAO에서 선언한 메소드명을  정의한다. 해당 메소드를 호출하면 SQL문이 실행된다.
  • parameterType: 전달인자의 자료형을 정의한다.
    전달인자는 #{}, ${}를 이용해 사용할 수 있는데, #{}PreparedStatement방식으로 값을 DB에 전달하여, 보안 면에서 더욱 안전하다.
  • resultType: 반환된 데이터의 매핑될 자료형을 지정해준다. 반환 튜플이 여러 개라면 한 튜플에 대한 자료형만 기입하면 된다. 예를 들어, List<String>이 반환되어야 한다면 String만 작성해도 된다는 것.

이외에 더욱 자세한 작성법을 추후 MyBatis 매핑 XML 작성 게시글에서 다루도록 하려 한다.

 

StudentDao.java

package com.sample.academy.model.dao;

import com.sample.academy.model.vo.BugVo;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface StudentDao {
    void insert(StudentVo studentVo) throws DataAccessException;
    List<StudentVo> selectByName(String name) throws DataAccessException;
}

 

해당 DAO와 연결된 Mapper 파일에서 작성된 DML 태그의 id를 사용하여 DAO Interface를 만든다.

 

따로 실행할 Java 코드 없이 SQL 쿼리문을 실행시키고 값을 받아오는 역할만 하기 때문에

선언형 메소드만이 필요하여 Interface 방식으로 작성하는 것이다.

 

SQL 실행 중 발생하는 Exception을 처리하기 위해  DataAccessException을 서비스에 던져준다.

유영웅