[Spring] MyBatis 연동

msriver·2020년 6월 18일
0

Spring

목록 보기
7/16

MyBatis란?

SQL Mapping 프레임워크로 흔히 분류된다.
개발자들은 JDBC 코드의 복잡하고 지루한 작업을 피하기 위해 사용한다.

전통적인 JDBC프로그램과 MyBatis의 구조를 비교해본다.

  • 전통적인 JDBC프로그램
    • 직접 Connection을 맺고 마지막에 close()
    • PreparedStatement 직접 생성 및 처리
    • PreparedStatement의 setXXX() 등
    • Select 경우 ResultSet 직접 처리

  • MyBatis
    • 자동으로 Connection close() 가능
    • MyBatis 내부적으로 PreparedStatement 처리
    • #{prop} 과 같이 속성 지정하여 자동 처리
    • 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리

스프링은 다른 프레임워크들과의 연동을 쉽게 하는 추가적인 라이브러리 들이 많다. MyBatis 또한 mybatis-spring 이라는 라이브러리를 통해 쉽게 연동이 가능하다.

Spring(mybatis-spring) --- MyBatis --- DB

MyBatis 관련 라이브러리 추가

pom.xml에 추가해야할 라이브러리들은 다음과 같다.

  • spring-jdbc/spring-tx : 스프링에서 데이터베이스 처리와 트랜잭션 처리(이름만 보면 전혀 상관없어보이긴한다.)

  • mybatis/mybatis-spring : MyBatis와 스프링 연동

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

SQLSessionFactory

MyBatis에서 가장 핵심적인 객체는 SQLSession이라는 녀석과 SQLSessionFactory 인터페이스 이다.

SQLSessionFactory는 이름만 보아도 SQLSession을 만들어 내는 공장이라는 것을 알 수 있다.

SQLSession은 개발자가 이를 통해 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴 받는 구조로 작성하게 된다.

root-context.xml에 다음을 추가한다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
</bean>

테스트 코드

따로 작성할 필요 없이 커넥션 풀 연동할때 사용한 테스트 코드에 그냥 추가해보자

@Setter(onMethod_ = @Autowired)
private SqlSessionFactory sqlSessionFactory;
	
@Test
public void testMyBatis() {
	try(SqlSession session = sqlSessionFactory.openSession();
		Connection conn = session.getConnection();) {
			log.info(session);
			log.info(conn);
		} catch(Exception e) {
			fail(e.getMessage());
		}
	}

스프링과 연동

  • SQLSessionFactory를 이용하여 직접 Connection을 얻고 JDBC 코딩이 가능은 하다.
  • 그러나 좀더 편하게 작업하기 위해 SQL을 어떻게 처리할지에 대한 별도의 설정을 따로 분리 + 자동으로 처리되는 방식을 이용한다.
  • 이를 위해 MyBatis의 Mapper 라는 녀석을 작성한다.

Mapper는 SQL과 그에 대한 처리를 지정해주는 역할을 한다.
MyBatis-Spring을 이용한다면 Mapper를 XML과 인터페이스 + 어노테이션 형태로 작성 가능

Mapper 인터페이스

Mapper을 작성하는 것은 XML을 이용할 수도 있지만 인터페이스로도 가능하다.

src/main/java에 TimeMapper라는 인터페이스를 추가했다.

package com.minsung.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
	@Select("SELECT sysdate FROM dual")
	public String getTime();
}

MyBatis가 동작할 때 이 Mapper를 인식할 수 있도록 root-context.xml에 설정을 해야한다. 가장 간단한 방법은 <mybatis:scan> 태그를 이용하는 것.

root-context.xml > Namespaces 항목 > mybatis-spring 탭 선택
아래 코드 작성한다.

<mybatis-spring:scan base-package="com.minsung.mapper"/>

base-package속성은 지정된 패키지의 모든 MyBatis 관련 어노테이션을 찾아 처리한다.

Mapper테스트

mybatis-spring은 mapper인터페이스를 이용해 실제 SQL 처리가 되는 클래스를 자동으로 생성해준다.

package com.minsung.persistence;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.minsung.mapper.TimeMapper;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class TimeMapperTests {
	@Setter(onMethod_ = @Autowired)
	private TimeMapper tmapper;
	
	@Test
	public void testGetTime() {
		log.info(tmapper.getClass().getName());
		log.info(tmapper.getTime());
	}
}

INFO : com.minsung.persistence.TimeMapperTests - com.sun.proxy.$Proxy22
INFO : com.minsung.persistence.TimeMapperTests - 2020-06-19 17:15:59

위와 같은 로그 메세지를 포함하여 여러 줄들이 나올 것이다.
위 코드에서는 timeMapper.getClass().getName() 을 통해 실제 동작하는 클래스의 이름을 확인해준다.

나는 분명 인터페이스만 만들고 그 인터페이스 타입의 레퍼런스변수만 선언해주었는데 내부적으로 TimeMapper타입의 스프링 객체(빈)이 생성됬다는 것이다.

XML 매퍼와 같이 쓰기

위 예제에선 TimeMapper 인터페이스에 어노테이션으로 select문을 작성했었다. 아주 간편하긴 하지만 만약 SQL문이 복잡하거나 길어진다면 XML을 이용해 작성하는 것이 더 좋을 수 있다.

XML파일의 위치
1. Mapper인터페이스가 있는 곳에 같이 작성
2. src/main/resources 에 폴더들을 만들어서 작성.

책은 2번째 방식을 택해서 보여준다.
src/main/resources에 하나씩 com/minsung/mapper 폴더들을 만들어준다. 반드시 한번에 하나씩 폴더를 만들것.(한번에 다 만들면 인식이 안될수도)

이름에 대한 규칙도 딱히 없지만 가독성을 위해 매칭되는 인터페이스와 같은 이름을 사용하는 것이 좋다.

일단 TimeMapper인터페이스에 메서드를 하나 추가한다.

package com.minsung.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
	
	@Select("SELECT sysdate FROM dual")
	public String getTime();
	
	//Mapper인터페이스와 XML을 같이 사용하는 메서드. 기능은 위와 동일
	public String getTime2();
}

XML파일에는 MyBatis의 XML매퍼에서 이용하는 태그에 대한 설정이 필요
https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#Auto-mapping 참고

  • TimeMapper.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.minsung.mapper.TimeMapper">
  <select id="getTime2" resultType="string">
    SELECT sysdate FROM dual
  </select>
</mapper>

<mapper>태그의 namespaces 속성값은 해당 Mapper인터페이스이다.
MyBatis는 Mapper인터페이스와 XML을 인터페이스의 이름과 namespaces 속성값을 가지고 판단한다.
실제로 com.minsung.mapper.TimeMapper 인터페이스가 존재하고,
namespaces 속성값이 일치한다면 병합하여 처리한다.

다시말해서 위 예제의 경우에는

  • 메서드 선언은 인터페이스에서
  • SQL에 대한 처리는 XML을 이용

select 태그의 id속성값은 매칭되는 인터페이스의 메서드 이름과 동일해야한다. resultType속성은 인터페이스에 선언된 메서드의 리턴타입과 동일해야 한다.

테스트

//TimeMapperTests에 작성
@Test
	public void testGetTime2() {
		log.info("getTime2");
		log.info(tmapper.getTime2());
	}

결과 로그

INFO : com.minsung.persistence.TimeMapperTests - getTime2
INFO : com.minsung.persistence.TimeMapperTests - 2020-06-19 17:54:01

profile
NOBODY

0개의 댓글