MyBatis
XML bean 등록과 mapper scanner 설정 예시는 source의 Spring configuration 관례를 보존한 것이다. 핵심은 mapper boundary, parameter binding, dynamic SQL이며, 현재 application의 configuration recipe를 그대로 지시하지 않는다.
MyBatis는 Java Object와 SQL 문 사이의 자동 매핑을 지원하는 Persistence Framework다. 핵심은 SQL을 Java 코드 밖의 별도 파일(Mapper XML)로 분리하고, Java 객체와 SQL 파라미터/결과 사이의 매핑 작업을 대신 처리하는 것이다.[1] Spring과 함께 쓸 때는 MyBatis 관련 Bean을 Spring Container에 등록하고, Spring의 트랜잭션 기능을 함께 쓰면 Spring Transaction의 @Transactional 기반으로 트랜잭션을 처리할 수 있다.[1:1] 이를 Spring Framework Core의 PSA/DI 사례로 보고 JPA·MyBatis 어느 쪽을 쓰든 @Transactional이 동일하게 트랜잭션을 관리한다고 연결하는 것은 이 page의 해석이다.
MyBatis vs JPA/Hibernate
| 구분 | MyBatis | JPA/Hibernate |
|---|---|---|
| 패러다임 | SQL Mapper — SQL을 직접 작성 | ORM — SQL을 자동 생성 |
| SQL 제어 | 개발자가 완전 제어 | Framework가 생성 |
| 학습 곡선 | 낮음(익숙한 SQL 그대로) | 높음(새 패러다임) |
| 쿼리 튜닝 | 직접 SQL 작성이라 쉬움 | JPQL/Criteria 작성, 생성 SQL 이해 필요 |
| 국내 점유율 | 매우 높음(SpringBoot + MyBatis 조합) | 상대적으로 낮음 |
- MyBatis는 Hibernate/JPA처럼 새 ORM 패러다임을 깊게 익혀야 하는 부담이 상대적으로 적다. 개발자가 익숙한 SQL을 그대로 사용하면서 JDBC 반복 작업을 줄인다.[2]
- JDBC의 기능을 대부분 제공하면서 복잡한 JDBC 코드를 걷어내고 소스 코드를 간결하게 유지한다. 수동 파라미터 설정이나 ResultSet 객체 매핑 코드를 줄여 준다.[3]
- SQL과 프로그래밍 코드의 분리: SQL이 바뀌어도 Java 코드 수정·재컴파일이 불필요하다. SQL 작성·관리·검토를 DBA 등 다른 역할과 분리할 수 있고, SQL을 명시적으로 관리하므로 쿼리 튜닝이 비교적 쉽다.[4]
MyBatis 3 주요 컴포넌트
flowchart TB Config["SqlSessionFactoryBuilder
→ Configuration (XML/Annotation)"] Factory["SqlSessionFactory
→ SqlSession 생성"] Session["SqlSession
→ SQL 실행 + 매핑"] Mapper["Mapper Interface
→ type-safe SQL 호출"] DB["Database"] Config --> Factory --> Session Session --> Mapper Mapper --> DB
| 컴포넌트 | 역할 |
|---|---|
| SqlSessionFactoryBuilder | Configuration 파일(XML)을 읽어 SqlSessionFactory 생성[5] |
| SqlSessionFactory | SqlSession 생성 팩토리. 애플리케이션에서 재사용되는 객체[5:1] |
| SqlSession | SQL 실행과 Transaction 관리를 담당하는 핵심 클래스[5:2] |
| Mapper Interface | namespace와 메서드명 매핑으로 type-safe SQL 호출 |
Mapper Interface와 namespace 매핑
Mapper Interface는 XML mapper의 namespace와 인터페이스 FQCN(Fully Qualified Class Name)이 일치하고, 메서드명과 SQL id가 일치하면 자동 매핑된다.[6]
// Mapper Interface
public interface BoardMapper {
BoardDto findById(int id); // → XML: <select id="findById" resultType="BoardDto">
}
// 호출
BoardDto board = sqlSession.getMapper(BoardMapper.class).findById(1);
Mapper Interface를 XML mapper와 연결하려면 다음 규칙을 맞춰야 한다.
| 항목 | 규칙 |
|---|---|
namespace |
패키지를 포함한 Mapper Interface의 전체 이름 |
SQL id |
매핑할 Interface 메서드 이름 |
| 최종 매핑 키 | packageName.InterfaceName.methodName |
- XML 없이 Annotation으로도 SQL을 정의할 수 있다(
@Select,@Insert,@Update,@Delete). - Mapper Interface를 쓰면
sqlSession.selectOne("namespace.id", param)같은 문자열 기반 호출보다 오타를 줄일 수 있고, IDE의 code assist(자동완성)를 활용하기 쉽다. 문자열 기반 호출에서는 오타가 런타임 버그로 이어지기 쉽다.[6:1]
MyBatis-Spring 연동
MyBatis-Spring은 Spring 애플리케이션과 MyBatis 3 사이를 연결하는 역할을 한다.[7] 핵심 Bean은 다음과 같다.
| Bean | 역할 |
|---|---|
| DataSource | DB 연결(HikariCP 등) |
| SqlSessionFactoryBean | SqlSessionFactory를 Spring Bean으로 생성. DataSource + ConfigLocation(mybatis-config.xml) + MapperLocations(mapper XML) 설정[8] |
MapperScannerConfigurer / <mybatis-spring:scan> |
지정 패키지의 Mapper Interface를 스캔해 Bean으로 등록[9] |
| SqlSessionTemplate | Thread-safe SqlSession 래퍼. Spring 트랜잭션과 연동[9:1] |
다음 Java Config 예시(@MapperScan 포함)는 원문에 없는 이 page의 보충 예시이며, 원문은 XML bean 등록과 <mybatis-spring:scan>을 사용한다.[9:2]
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/**/*.xml"));
return factory;
}
}
공식 문서 기준 최소 연동 모델은 다음과 같다.
- MyBatis-Spring을 쓰려면 Spring application context에
SqlSessionFactory와 적어도 하나의 mapper interface가 필요하다.[10] SqlSessionFactoryBean은 Spring의DataSource를 받아SqlSessionFactory를 만들고, mapper interface는 Spring Bean으로 주입되어 호출된다.[10:1]- Spring transaction이 진행 중이면 세션은 해당 transaction 완료 시 함께 commit 또는 rollback되며, 예외는 Spring
DataAccessException으로 번역된다.[10:2] 즉 mapper가 별도의 JDBC transaction을 새로 시작하는 것이 아니라 Spring transaction lifecycle에 참여한다.
실패 모드
- Mapper Interface의 namespace·메서드명이 XML의 namespace/id와 불일치해
BindingException이 발생하는 경우. SqlSession을 Spring 트랜잭션과 연동하지 않고 직접 생성해 트랜잭션이 묶이지 않는 경우.- 동적 SQL을 문자열 연결로 작성해 SQL Injection 위험이나 가독성 저하를 일으키는 경우 —
<if>,<choose>,<foreach>같은 동적 SQL 태그 사용이 권장된다. - Mapper XML에
resultType을 잘못 지정해 매핑이 실패하는 경우. - N+1 쿼리 문제를 인지하지 못하고 대량 조회 시마다 개별 쿼리가 나가는 경우 —
join또는 배치 fetch 검토가 필요하다.
JPA와의 선택 기준
복잡한 통계 쿼리, DB 벤더 특화 기능, 다중 JOIN이 많은 도메인에서는 MyBatis가 단순·유리하고, 단순 CRUD·도메인 중심 설계에서는 JPA의 객체-관계 매핑 이점이 크다고 판단한다. 이 선택 기준 자체는 이 페이지가 위 특징 비교를 근거로 종합한 해석이며, source가 직접 제시하는 결론은 아니다.
관련
- Spring Framework Core — MyBatis-Spring이 PSA/DI 위에서 동작.
- Spring Transaction — MyBatis/JPA 관계없이
@Transactional로 동일한 트랜잭션 관리(PSA 적용 사례).
출처
테스트 질문
- MyBatis가 JPA/Hibernate와 다른 점과, 어떤 상황에서 MyBatis가 더 적합한가?
- Mapper Interface의 namespace·메서드명 매핑 규칙은 무엇이며, 이 규칙이 문자열 기반 호출의 어떤 위험을 줄이는가?
- MyBatis-Spring 연동의 핵심 Bean(SqlSessionFactoryBean, SqlSessionTemplate) 역할은?
- "SQL과 프로그래밍 코드의 분리"가 쿼리 튜닝·DBA 역할 분리에 어떻게 기여하는가?
Mybatis.md — "MyBatis는 Java Object와 SQL 문 사이의 자동 매핑을 지원하는 Persistence Framework다. 핵심은 SQL을 Java 코드 밖의 별도 파일로 분리하고, Java 객체와 SQL 파라미터/결과 사이의 매핑 작업을 대신 처리해 주는 것이다."(L31, L33), "Spring과 함께 사용할 때는 MyBatis 관련 Bean을 Spring Container에 등록해서 사용한다. 이때 Spring의 트랜잭션 기능을 함께 사용하면
@Transactional기반으로 트랜잭션을 처리할 수 있다."(L316) ↩︎ ↩︎Mybatis.md — "MyBatis는 Hibernate나 JPA처럼 새로운 ORM 패러다임을 깊게 익혀야 하는 부담이 상대적으로 적다. 개발자가 익숙한 SQL을 그대로 사용하면서도 JDBC 코드의 반복 작업을 줄이고..." ↩︎
Mybatis.md — "JDBC의 기능을 대부분 제공하는 간단한 Persistence Framework로 볼 수 있다.", "복잡한 JDBC 코드를 걷어내고 소스 코드를 간결하게 유지한다.", "수동으로 파라미터를 설정하거나 ResultSet을 객체에 직접 매핑하는 코드를 줄여 준다." ↩︎
Mybatis.md — "SQL 변경이 있을 때 Java 코드를 수정하거나 다시 컴파일하지 않아도 된다."(L49), "SQL 작성, 관리, 검토를 DBA 등 다른 역할과 분리할 수 있다.", "SQL 자체를 명시적으로 관리하기 때문에 쿼리 튜닝과 검토가 비교적 쉽다." ↩︎
Mybatis.md — MyBatis 3 주요 컴포넌트 표: "SqlSessionFactoryBuilder | MyBatis 설정 파일을 바탕으로 SqlSessionFactory 생성", "SqlSessionFactory | SqlSession 생성", "SqlSession | SQL 실행과 Transaction 관리를 담당하는 핵심 클래스", "
SqlSessionFactory는 애플리케이션에서 재사용되는 객체다."(L228) ↩︎ ↩︎ ↩︎Mybatis.md — "Mapper Interface를 제대로 연결하려면 다음 규칙을 맞춰야 한다."(L302), "
namespace| 패키지를 포함한 Mapper Interface의 전체 이름", "SQLid| 매핑할 Interface 메서드 이름"(L306-307), "문자열로 SQL ID를 직접 조합하는 방식보다 오타를 줄일 수 있고, IDE의 code assist를 활용하기 쉽다."(L278), "이 방식은 문자열 오타가 런타임 버그로 이어지기 쉽고, IDE 자동완성의 도움을 받기 어렵다."(L290) ↩︎ ↩︎Mybatis.md — "MyBatis-Spring은 Spring 애플리케이션과 MyBatis 3 사이를 연결하는 역할을 한다." ↩︎
Mybatis.md — "SqlSessionFactoryBean | MyBatis 설정을 바탕으로 SqlSessionFactory를 생성하는 Spring Bean" ↩︎
Mybatis.md — "SqlSessionTemplate | SQL 실행과 트랜잭션 처리를 담당하며 SqlSession 인터페이스를 구현", "SqlSessionTemplate은 Thread-safe하므로 Spring Bean으로 등록해서 공유할 수 있다.", Mapper Scanner 사용: "
mapperScannerConfigurer를 사용해 지정 패키지 하위 Mapper Interface를 자동 검색 및 등록"(L427), Mapper Scanner 예시:<mybatis-spring:scan base-package="com.ssafy.*.model.mapper"/>(L448) ↩︎ ↩︎ ↩︎mybatis-spring-getting-started.md — "To use MyBatis with Spring you need at least two things defined in the Spring application context: an SqlSessionFactory and at least one mapper interface.", "If there is a Spring transaction in progress, the session will also be committed or rolled back when the transaction completes. Finally, any exceptions will be translated into Spring DataAccessExceptions." ↩︎ ↩︎ ↩︎