목차
Mybatis란?
개발자가 지정한 sql 저장 프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다.
-
JDBC에서 처리하는 코드와 파라미터 설정 및 결과 매핑을 대신 해준다.
-
데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO를 설정해서 매핑하기 위해 xml과 어노테이션을 사용할 수 있다.
사용법
1. xml에서 SqlSessionFactory 빌드하기
-
모든 Mybatis 어플리케이션은 SqlSessionFactory 인스턴스를 사용한다.
-
SqlSessionFactory 인스턴스는 SqlSessionFactoryBuilder를 사용하여 만들 수 있다.
-
SqlSessionFactoryBuilder는 XML설정파일에서 SqlSessionFactory 인스턴스를 빌드 할 수 있다.
ex) SqlSessionFactory인스턴스를 빌드하기
import java.util.Objects;
import java.util.Properties;
String resource= "org/mybatis/exam/mybatis-config.xml";
Properties properties = new Properties();
properties.setProperty("driver", Objects.requireNonNull(System.getenv("DB_DRIVER")));
properties.setProperty("url", Objects.requireNonNull(System.getenv("DB_URL")));
properties.setProperty("username", Objects.requireNonNull(System.getenv("DB_USER")));
properties.setProperty("password", Objects.requireNonNull(System.getenv("DB_PASSWORD")));
SqlSessionFactory sqlSessionFactory;
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);
}
ex) mybatis-config.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/exam/HelloMapper.xml"/>
</mappers>
</configuration>
ex) HelloMapper.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="org.mybatis.exam.HelloMapper">
<select id="selectHello" resultType="org.mybatis.exam.Hello">
SELECT id, msg FROM hello WHERE id = #{id}
</select>
</mapper>
2. SqlSessionFactory에서 SqlSession만들기
- SqlSession SQL 명령어를 실행하기 위해 필요한 모든 메서드를 가지고 있고, SqlSession인스턴스를 통해 직접 SQL 구문을 수행할 수 있다.
ex_1)
SqlSession session = sqlSessionFactory.openSession();
try {
Hello hello = session.selectOne("org.mybatis.exam.HelloMapper.selectHello", 100);
}finally {
session.close();
}
- 주어진 SQL 구문의 파라미터와 리턴 값을 설명하는 인터페이스를 사용하여 문자열 처리 오류나 캐스팅 오류 없이 좀더 타입에 안전하고 깔끔하게 실행 할 수 있다.
ex_2)
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
HelloMapper mapper = sqlSession.getMapper(HelloMapper.class);
Hello hello = mapper.selectHello(100);
}finally {
sqlSession.close();
}
3. Namespaces(네임스페이스)
- 인터페이스 바인딩을 가능하게 하고, 패키지 경로를 포함한 전체 이름을 가진 구문을 구분하기 위해 사용한다.
ex) 아래 방식처럼 xml에 매핑되지 않고 자바 어노테이션을 이용하여 사용할 수 있다.
public interface HelloMapper {
@Select("SELECT msg FROM hello WHERE id = #{id}")
String selectHello(int id);
}
구문은 깔끔하지만 복잡한 구문을 사용한다면 xml매핑을 사용하는 것이 낮다.
4. 스코프와 생명주기
- SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder는 인스턴스화 되어 사용되고 던져질 수 있고, 사용 후에는 유지할 필요는 없다. 가장 좋은 스코프는 메소드 스코프(메서드 지역변수)이다.
- SqlSessionFactory
-
SqlSessionFactory은 어플리케이션이 실행되는 동안 존재해야하므로, 삭제되거나 재생성 할 필요가 없다.
-
어플리케이션이 실행되는 동안 여러차례 SqlSessionFactory를 다시 빌드하지 하지 않는 것이 좋다.
-
SqlSessionFactory를 유지하기 위한 방법은 Singleton, static Singleton 패턴을 이용하거나 아님 static Map에 담아서 사용할 수도 있다.
- SqlSession
-
SqlSession은 스레드 간 공유하지 않아야 한다. 스레드 안전한 객체로 취급하면 안 된다.
-
static 필드나 클래스의 인스턴스 필드로 지정하면 안된다.
-
HttpSession과 같은 관리 스코프에 둬서는 안된다.
-
SqlSession을 닫는 것은 중요하므로 finally 블록에서 종료한다.
ex_)
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
- Mapper 인스턴스
-
매핑된 구문을 바인딩하기 위해 만들어야 할 인터페이스로 SqlSession에서 생성한다.
-
가장 좋은 스코프는 메서드 스코프이다.
-
사용할 메서드가 호출되면 생성되고 끝나므로 명시적으로 닫을 필요 없다.
조회 한 건으로 설정을 검증하기
예제 XML의 namespace는 매퍼 인터페이스의 전체 이름과 일치해야 한다. selectHello는 인터페이스 메서드 이름과 일치해야 한다. resultType에 쓰는 클래스는 클래스패스에서 찾을 수 있어야 한다. 이 세 곳 중 하나라도 다르면 단순한 조회도 매핑 단계에서 실패한다. 먼저 DB에 hello(id, msg) 테이블과 ID 100의 테스트 행을 준비한 뒤, 직접 JDBC 조회와 매퍼 조회 결과를 비교하면 연결 실패와 매핑 실패를 분리하기 쉽다.
flowchart LR
A["XML 설정과 외부 접속 값"] --> B["SqlSessionFactory"]
B --> C["작업별 SqlSession"]
C --> D["HelloMapper 메서드"]
D --> E["SQL과 #{id} 바인딩"]
E --> F["DB 결과 행"]
F --> G["Hello 객체"]
위 그림은 Java SE에서 MyBatis를 직접 구성할 때의 흐름이다. SqlSessionFactory는 재사용하지만 SqlSession은 작업마다 열고 닫는다. 앞 코드의 접속 값은 Properties로 전달되며 필수 환경 변수가 없으면 시작 단계에서 실패한다. 운영에서는 어떤 변수가 빠졌는지 설명하는 검증을 추가하고 비밀번호 값을 로그로 출력하지 않는다.
쓰기 작업과 SQL 안전성
조회만 할 때는 세션을 닫으면 된다. INSERT·UPDATE·DELETE를 수행한다면 성공 시 commit(), 예외 시 rollback()을 분명히 해야 한다. 세션을 닫는 것만으로 변경이 커밋되었다고 가정하면 안 된다.
try (SqlSession session = sqlSessionFactory.openSession()) {
try {
HelloMapper mapper = session.getMapper(HelloMapper.class);
mapper.updateMessage(100, "변경한 메시지");
session.commit();
} catch (RuntimeException ex) {
session.rollback();
throw ex;
}
}
updateMessage는 매퍼 인터페이스와 SQL에 별도로 정의해야 하는 예시 메서드다. 중요한 점은 트랜잭션 소유자가 직접 만든 SqlSession이라는 것이다. Spring에서 SqlSessionTemplate을 쓴다면 Spring의 트랜잭션 경계가 연결과 커밋·롤백을 관리하므로 같은 코드를 그대로 섞지 않는다.
#{id}는 값 바인딩이고, ${column}은 문자열을 SQL에 치환한다. 사용자 입력을 ${}에 넣으면 SQL 구문 자체가 바뀔 수 있다. 정렬 컬럼처럼 SQL 식별자를 바꿔야 한다면 코드가 정한 허용 목록에서 선택한다. 조회 결과를 받을 때는 SELECT *보다 필요한 컬럼을 명시하면 스키마 변경과 불필요한 데이터 전송을 줄이는 데 도움이 된다.
| 증상 | 확인 순서 |
|---|---|
| 설정 XML을 못 찾음 | 리소스 경로와 빌드 결과물 |
| 매핑된 문장이 없음 | namespace, statement ID, 매퍼 인터페이스 |
| 조회 결과가 null | 실제 DB·스키마·ID, 커밋 여부 |
| 쓰기 후 데이터가 없음 | commit() 호출과 예외·롤백 경로 |
| 동시 요청에서 값이 섞임 | SqlSession을 공유 필드에 저장했는지 |