1. MyBatis

  • MyBatis: Sql Mapping Framework라고 표현됨, Sql Mapping이란 SQL의 실행 결과를 객체 지향으로 매핑해준다는 뜻
  • MyBatis 사용 시, 기존 SQL을 그대로 사용할 수 있고 다음의 장점이 존재
    • PreparedStatement / ResultSet의 처리: 기존 프로그램 작성 시 하나씩 처리해야 하는 파라미터나 ResultSet의 getXXX()를 MyBatis가 알아서 처리해 주어 많은 양의 코드를 줄일 수 있음
    • Connection / PreparedStatement / ResultSet의 close() 처리: MyBatis와 스프링을 연동해서 사용하는 방식을 이용하면 자동으로 close() 처리 가능
    • SQL의 분리: MyBatis를 사용하면 별도의 파일이나 어노테이션 등을 이용해서 SQL을 선언, 파일을 이용하는 경우 SQL을 별도의 파일로 분리해서 운영 가능

 

 1) MyBatis와 스프링의 연동 방식

  • MyBatis는 단독으로 실행이 가능하지만, 스프링 프레임워크는 MyBatis와 연동을 쉽게 처리할 수 있는 라이브러리와 API를 제공
    • MyBatis를 단독으로 개발하고 스프링에서 DAO를 작성해서 처리하는 방식
      • 기존 DAO에서 SQL 처리를 MyBatis를 이용하는 구조
      • 완전히 MyBatis와 스프링 프레임워크를 독립적인 존재로 바라보고 개발
    • MyBatis와 스프링을 연동하고 Mapper 인터페이스만 이용하는 방식
      • 스프링과 MyBatis 사이에 'mybatis-spring'이라는 라이브러리 이용
      • 스프링이 데이터베이스 전체를 처리
      • MyBatis는 일부 기능 개발에만 활용하는 방식
      • 개발 시에는 Mapper 인터페이스라는 방식을 이용해서 인터페이스만으로 모든 개발이 가능한 방식

 

  • MyBatis를 위한 라이브러리들
    • 스프링 관련: spring-jdbc, spring-tx
    • MyBatis 관련: mybatis, mybatis-spring
dependencies {
    
    ...

    // spring 관련 라이브러리
    implementation group: 'org.springframework', name: 'spring-jdbc', version: '5.3.19'
    implementation group: 'org.springframework', name: 'spring-tx', version: '5.3.19'

    // MyBatis 관련 라이브러리
    implementation group: 'org.mybatis', name: 'mybatis', version: '3.5.6'
    implementation group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.6'
}

 

  • MyBatis를 위한 스프링의 설정 - SqlSessionFactory
    • MyBatis를 이용하기 위해 스프링에서 설정해둔 HikariDataSource를 이용해서 SqlSessionFactory라는 Bean을 설정
    •  
<!-- root-context.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context = "http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <context:component-scan base-package = "org.zerock.springex.sample"></context:component-scan>

    ...

    <!-- 이 부분 추가 -->
    <bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean">
        <property name = "dataSource" ref = "dataSource" />
    </bean>
    <!-- 여기까지 -->
    
</beans>

 


실습

 1) Mapper 인터페이스 활용하기

  - MyBatis는 SQL 파일을 별도로 처리할 수 있지만, 인터페이스와 어노테이션만으로도 처리 가능

  - 프로젝트에 mapper라는 패키지 구성 > 현재 시간을 처리하는 TimeMapper 인터페이스 선언

// TimeMapper
package org.zerock.springex.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {

  @Select("select now()")
  String getTime();
  
}

  - 데이터베이스의 현재 시각을 문자열로 처리하도록 구성

  - @Select 어노테이션으로 쿼리 작성 가능(';' 사용 x)

  - 작성된 인터페이스를 매퍼 인터페이스라고 하며, root-context.xml에 어떤 매퍼 인터페이스 설정했는지 등록해야함

<!-- root-context.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans ...

       xmlns:mybatis = "http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

   ...

    <mybatis:scan base-package = "org.zerock.springex.mapper"></mybatis:scan>
</beans>

 

  - 테스트 코드를 통해 확인

  - Test > java > org.zerock.springex > mapper 패키지 > TimeMapperTests

// TimeMapperTests
package org.zerock.springex.sample.mapper;

import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.zerock.springex.mapper.TimeMapper;

@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TimeMapperTests {

  // required가 fales이면 해당 객체를 주입 받지 못하더라도 예외가 발생 x
  @Autowired(required = false)
  private TimeMapper timeMapper;

  @Test
  public void testGetTime() {
    log.info(timeMapper.getTime());
  }
}

정상적으로 현재 시간이 출력됨

  - MyBatis와 스프링을 연동하고 매퍼 인터페이스를 활용하는 방식은 개발자가 실제 동작하는 클래스와 객체를 생성하지 않고, 스프링에서 자동 생성되는 방식 이용

  - 스프링에서 자동 생성되어 개발자가 직접 코드를 수정할 수 없다는 단점이 있지만, 인터페이스만으로 개발을 완료할 수 있다는 장점

 

 2) XML로 SQL 분리하기

  - SQL을 @Select 어노테이션으로 써도 되지만, SQL이 길어지면 처리가 복잡해지고, 어노테이션이 변경되면 프로젝트 전체를 다시 빌드하는 작업이 필요하기 때문에 단순 파일(XML)로 사용하는 것이 편리

  - XML과 매퍼 인터페이스 결합 과정

  • 매퍼 인터페이스 정의하고 메서드 선언
  • 해당 XML 파일 작성(파일이름과 매퍼 인터페이스 이름 같게), <select>와 같은 태그 이용해서 SQL 작성
  • <select>, <insert> 등 태그에 id 속성 값을 매퍼 인터페이스의 메서드 이름과 같에 작성

 

  - main > org.zerock.springex > mapper > TimeMapper2 파일 생성

// TimeMapper2
package org.zerock.springex.mapper;

public interface TimeMapper2 {

  String getNow();

}

  - main > source > mappers 패키지 생성 > TimeMapper2.xml 파일 생성(파일 이름을 매퍼 인터페이스와 같게)

<!-- TimeMapper2.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.zerock.springex.mapper.TimeMapper2">

    <!-- select의 id 속성값을 매터 인터페이스의 메서드 이름과 같게 설정 -->
    <!-- select 태그는 반드시 resultType이나 resultMap 속성을 지정해야 함 -->
    <!-- resultType은 select문이 결과를 어떤 타입으로 처리할 지에 대한 설정 -->
    <select id = "getNow" resultType = "string">
        select now()
    </select>

</mapper>

  - 마지막으로 root-context.xml 파일의 MyBatis 설정에 XML 파일들을 인식하도록 설정을 추가

<!-- root-context.xml -->
...

<bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean">
        <property name = "dataSource" ref = "dataSource" />
        <!-- mapperLocations는 XML 매퍼 파일의 위치 -->
        <property name = "mapperLocations" value = "classpath:/mappers/**/*.xml"></property>
    </bean>
    
...

 

  - 테스트 코드로 정상적으로 동작하는지 확인

// TimeMapperTests
package org.zerock.springex.sample.mapper;

import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.zerock.springex.mapper.TimeMapper2;

@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
public class TimeMapperTests {

  @Autowired(required = false)
  private TimeMapper2 timeMapper2;

  @Test
  public void testNow() {
    log.info(timeMapper2.getNow());
  }
}

정상적으로 현재 시간이 출력됨

+ Recent posts