Spring4 & MyBatis

[부스트코스] 웹 프로그래밍 기반의 설정에서 추가 설정으로 확장하기 위한 포스팅

  • 목표

    • spring4 pom.xml 설정
    • mybatis dependency 추가
    • mybatis java config
    • domain, mapper, sql 샘플 작성
    • mapper 테스트
  • Spring4의 Java Config 향상 기능을 통해 Mybatis를 구성하는 xml 제외하기

  • mybatis-spring 라이브러리에서 제공하는 @MapperScan 어노테이션을 사용하여 MyBatis Domain Mapper에 대해 패키지단위로 검색이 가능하다.

  • Servlet 3+와 결합하는 경우 XML없이 응용 프로그램을 구성하고 실행할 수 있다.

프로젝트 스펙 및 설정 구조

모듈 버전 설명
Spring Framework 4.3.15.RELEASE
MyBatis 3.5.2
mybatis-spring 2.0.2
- src/main
    - java/com/seok/mybatis/
                    config/
                        * AppConfig.java
                        * DBConfig.java
                        * MybatisConfig.java
                    domain/
                        * Product.java
                    persistence/
                        * ProductMapper.java
    - resources/mapper/
            * ProductMapper.xml
* pom.xml

프로젝트 구조

설정

  • pom.xml

    • MyBatis 사용을 위한 mybatis, mybatis-spring dependency를 추가 한다.

    • 물론 DB를 사용하기 위한 프로젝트이기 때문에 spring-jdbc, mysql-connector-java도 추가

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

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

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.45</version>
    </dependency>

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

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
  • AppConfig.java
    • 스프링 설정 클래스
    • DBConfig와 MyBatisConfig를 분리해서 Import하였다.
    • 같은 클래스안에서 설정해도 무관

@Configuration
@Import({DBConfig.class, MybatisConfig.class})
public class AppConfig {
}
  • DBConfig.java
    • DB 설정관련 클래스
    • java 파일 내에 설정 정보를 넣지 않고 properties 파일을 사용하여 보안 및 배포 관리

@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:db.properties" })
public class DBConfig implements TransactionManagementConfigurer {

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String userName;

    @Value("${spring.datasource.password}")
    private String password;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return transactionManager();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        return dataSource;
    }
}
  • MybatisConfig.java

    1. line 2: MyBatis Mapper가 있는 패키지 설정
    2. line 9: MyBatis.xml 파일에서 resultType으로 사용할 수 있는 도메인 개체를 포함하는 패키지 설정
      (SQL쿼리에서 Java 객체를 반환하는 것은 ORM 문제를 해결)
    • MyBatis를 설정하기 위해 SqlSessionFactoryBean를 사용

@Configuration
@MapperScan("com.seok.mybatis.persistence")
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage("com.seok.mybatis.domain");
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory;
    }
}

  • Product.java
    • Product 도메인 객체는 실제 단순한 POJO

public class Product implements Serializable{

    private static final long serialVersionUID = 3690807750019415954L;

    private int id;
    private int categoryId;
    private String desc;
    private String content;
    private String event;
    // constructor, setter, getter

}
  • ProductMapper.java
    • Mapper 클래스는 해당 mapper.xml에 정의된 sql문과 일치하는 메서드를 정의하는 단순한 인터페이스이다.
    • xml로 SQL을 정의하는 대신 어노테이션으로 간단한 SQL문을 작성하는 것이 가능하지만, query가 번거로워져 복잡한 Query는 사용하지 않는다.

import org.apache.ibatis.annotations.Select;
import kr.or.seok.naver.domain.Product;

public interface ProductMapper {

    public List<Product> getAllProducts();

    // Simple SQL
    @Select("SELECT COUNT(*) totalCnt FROM product")
    public int getTotalCnt();

}

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.seok.mybatis.persistence.ProductMapper">
    <cache />

    <select id="getAllProducts" resultType="Product">
        SELECT 
            id, 
            category_id, 
            description, 
            content, 
            event 
        FROM product
    </select>

</mapper>

테스트

  • TestMyBatis.java
    • Test 코드는 Mockito나 Junit을 사용하는 것이 편리

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestMyBatis {

    @Autowired
    ProductMapper prodMapper;

    @Test
    public void testMapper() {
        // prodMapper.getAllProducts();

    }

    @Test
    public void testTotalProducts() {
        // productMapper.getTotalCnt();
    }
}

결과

  • TotalCnt()
    • Product의 전체 row 갯수를 확인하기 위한 메서드로 50개가 조회되고 있음을 알 수 있다.

23:53:03.208 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - ==>  Preparing: SELECT COUNT(*) totalCnt FROM product 
23:53:03.251 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - ==> Parameters: 
23:53:03.276 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getTotalCnt - <==      Total: 1
23:53:03.282 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3daf7722]
23:53:03.282 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
23:53:03.336 [main] INFO com.seok.mybatis.config.TestMyBatis - 50
  • getAllProducts()
    • Product의 모든 row를 조회하기 위한 테스트 메서드로 50개의 Product리스트가 조회되고 있음을 확인할 수 있다.

23:53:03.348 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - ==>  Preparing: SELECT id, category_id, description, content, event FROM product 
23:53:03.348 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - ==> Parameters: 
23:53:03.372 [main] DEBUG com.seok.mybatis.persistence.ProductMapper.getAllProducts - <==      Total: 50
23:53:03.379 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@52066604]
23:53:03.379 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
23:53:03.419 [main] INFO com.seok.mybatis.config.TestMyBatis - [ {
    "id" : 1,
    "categoryId" : 0,
    "desc" : null,
    "content" : "",
    "event" : ""
    },
    ...
}] 

+ Recent posts