본문 바로가기

좌충우돌 개발기!/DB 설정

[Spring Boot] (2) MyBatis 셋팅 변경

JPA와 MyBatis가 오류없이 잘 작동하는지 테스트를 하기 위해 테스트 코드를 만들어 돌려보았다.

 

package com.springboot2.legacy;


import com.springboot2.legacy.dto.first.PokemonDto;
import com.springboot2.legacy.dto.second.ItemDto;
import com.springboot2.legacy.entity.first.PokemonEntity;
import com.springboot2.legacy.entity.second.ItemEntity;
import com.springboot2.legacy.mapper.first.PokemonMapper;
import com.springboot2.legacy.mapper.second.ItemMapper;
import com.springboot2.legacy.repository.first.PokemonRepository;
import com.springboot2.legacy.repository.second.ItemRepository;
import com.springboot2.legacy.service.first.PokemonService;
import com.springboot2.legacy.service.second.ItemService;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;

import java.util.List;

@SpringBootTest
public class DBConnectTest {

  @Autowired
  PokemonRepository pokemonRepository;

  @Autowired
  PokemonMapper pokemonMapper;

  @Autowired
  ItemRepository itemRepository;

  @Autowired
  ItemMapper itemMapper;

  @Autowired
  PokemonService pokemonService;

  @Autowired
  ItemService itemService;

  @Test
  void firstDB() {
    List<PokemonEntity> all = pokemonRepository.findAll();
    all.forEach(ent -> System.out.println("FIRST DB JPA : " + ent.getPokemonId() + " : " + ent.getName()));

    List<PokemonDto> pokemons = pokemonMapper.getPokemons();
    pokemons.forEach(dto -> System.out.println("FIRST DB MyBatis : " + dto.getPokemonId() + " : " + dto.getName()));
  }

  @Test
  void secondDB() {
    List<ItemEntity> all = itemRepository.findAll();
    all.forEach(ent -> System.out.println("SECOND DB JPA : " + ent.getPrice() + ", " + ent.getStockQuantity()));

    List<ItemDto> items = itemMapper.getItems();
    items.forEach(dto -> System.out.println("SECOND DB MyBatis : " + dto.getPrice() + ", " + dto.getStockQuantity()));
  }
}

 

결과를 확인해 보았더니

 

FIRST DB JPA : 121 : Pikachu
FIRST DB JPA : 122 : Bulbasaur
FIRST DB JPA : 123 : Charmander
FIRST DB JPA : 124 : Squirtle
FIRST DB JPA : 125 : Butterfree
FIRST DB JPA : 126 : Rattata
FIRST DB JPA : 127 : Clefairy
FIRST DB JPA : 128 : Arbok
FIRST DB JPA : 129 : Diglett
FIRST DB JPA : 130 : Kadabra

FIRST DB MyBatis : null : Pikachu
FIRST DB MyBatis : null : Bulbasaur
FIRST DB MyBatis : null : Charmander
FIRST DB MyBatis : null : Squirtle
FIRST DB MyBatis : null : Butterfree
FIRST DB MyBatis : null : Rattata
FIRST DB MyBatis : null : Clefairy
FIRST DB MyBatis : null : Arbok
FIRST DB MyBatis : null : Diglett
FIRST DB MyBatis : null : Kadabra

SECOND DB JPA : 10000, 99
SECOND DB JPA : 20000, 98
SECOND DB JPA : 20000, 197
SECOND DB JPA : 40000, 296

SECOND DB MyBatis : 10000, 0
SECOND DB MyBatis : 20000, 0
SECOND DB MyBatis : 20000, 0
SECOND DB MyBatis : 40000, 0

 

이런 결과가 나왔다.

 

JPA 사용 시에는 값을 제대로 가져오고 있는 반면에 MyBatis 사용 시에는 몇몇 값을 가져오지 못하는 현상이 발견되었다.

 

@Data
public class PokemonDto {

  private Long pokemonId;	// CamelCase

  private String name;

  private String type;

}

 

확인해보니 두 단어 이상으로 이루어져 CamelCase를 사용하는 필드값만 불러오지 못하고 있었다.

 

 

분명 application.yml에 MyBatis 설정에 SnakeCase를 CamelCase로 변경해주는 설정을 true로 해놓았는데 적용되고 있지 않은 듯하다.

 

알고보니 DB를 하나만 사용할 경우, application.yml 설정을 바로 사용하지만 다중 DB를 사용할 경우에는 추가로 설정해주어야했다.

 

이를 위해 MyBatis 공통 부분에 추가 셋팅을 하였다.

 

package com.springboot2.legacy.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Map;

@Configuration
public class DBConfig {

  @Autowired
  private HibernateProperties hibernateProperties;

  @Autowired
  private JpaProperties jpaProperties;
  
  // MyBatis 프로퍼티 객체 추가
  @Autowired
  private MybatisProperties mybatisProperties;

  protected void setConfigEntityManagerFactory(LocalContainerEntityManagerFactoryBean factory) {
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
      jpaProperties.getProperties(), new HibernateSettings()
    );

    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaPropertyMap(properties);
    factory.afterPropertiesSet();
  }

  protected void setConfigSqlSessionFactory(SqlSessionFactoryBean sessionFactoryBean, DataSource dataSource) throws Exception {
    sessionFactoryBean.setDataSource(dataSource);
//    sessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:/mybatis/config/mybatis-config.xml"));
    sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mapper/*.xml"));
    // yml로 사용하도록 추가
    sessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
    
    // Configuration에 MyBatis 프로퍼티를 셋팅
    org.apache.ibatis.session.Configuration configuration = sessionFactoryBean.getObject().getConfiguration();
    // SnakeCase -> CamelCase 설정
    configuration.setMapUnderscoreToCamelCase(mybatisProperties.getConfiguration().isMapUnderscoreToCamelCase());
    
    sessionFactoryBean.setConfiguration(configuration);
  }
}
# Mybatis 설정
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    call-setters-on-nulls: true
  mapper-locations: classpath:/mybatis/mapper/*.xml
#  config-location: classpath:/mybatis/config/mybatis-config.xml
  type-aliases-package: "com.springboot2.legacy.dto"	# 추가

추가를 하면서 MyBatis 설정을 전부 yml로 컨트롤하도록 변경했다.

 

 

FIRST DB JPA : 121 : Pikachu
FIRST DB JPA : 122 : Bulbasaur
FIRST DB JPA : 123 : Charmander
FIRST DB JPA : 124 : Squirtle
FIRST DB JPA : 125 : Butterfree
FIRST DB JPA : 126 : Rattata
FIRST DB JPA : 127 : Clefairy
FIRST DB JPA : 128 : Arbok
FIRST DB JPA : 129 : Diglett
FIRST DB JPA : 130 : Kadabra

FIRST DB MyBatis : 121 : Pikachu
FIRST DB MyBatis : 122 : Bulbasaur
FIRST DB MyBatis : 123 : Charmander
FIRST DB MyBatis : 124 : Squirtle
FIRST DB MyBatis : 125 : Butterfree
FIRST DB MyBatis : 126 : Rattata
FIRST DB MyBatis : 127 : Clefairy
FIRST DB MyBatis : 128 : Arbok
FIRST DB MyBatis : 129 : Diglett
FIRST DB MyBatis : 130 : Kadabra

SECOND DB JPA : 10000, 99
SECOND DB JPA : 20000, 98
SECOND DB JPA : 20000, 197
SECOND DB JPA : 40000, 296

SECOND DB MyBatis : 10000, 99
SECOND DB MyBatis : 20000, 98
SECOND DB MyBatis : 20000, 197
SECOND DB MyBatis : 40000, 296

이후 테스트 결과, 값을 전부 잘 가져오는 것을 확인했다.

 

 

 

해당 프로퍼티가 추가되면, DBConfig에도 변경이 일어난다.
DBConfig에도 추가해주어야 한다.

그러나 이 설정에는 한가지 단점이 있는데, 만약 MyBatis에서 사용하는 프로퍼티가 늘어나는 경우에 DBConfig와 application.yml 두 군데를 수정해야한다는 것이다. 해당 문제를 풀어보려했지만 방법을 찾지 못하여, 결국 MyBatis의 설정은 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>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="jdbcTypeForNull" value="NULL"/>
    </settings>
    <typeAliases>
        <package name="com.springboot2.legacy.dto"/>
    </typeAliases>
</configuration>

 

 

이 문제는 MyBatis 3이 나오면서 해결할 수 있었다. 다음 편에 해결 방법에 대해 적어보겠다.