본문 바로가기
SpringBoot

[Spring Boot] 10. mybatis camelCase 적용

by 청양호박이 2019. 11. 21.

안녕하세요. 예전 글에서 DB로 부터 Data를 가져와서 브라우저화면에서 표출하는 test를 했었습니다.

 

2019/11/10 - [SpringBoot] - [Spring Boot] 05. MyBatis Basic Full Test

 

[Spring Boot] 05. MyBatis Basic Full Test

이번에는 앞의 DB Connection Pool과 병행하여, 설정이 잘 되었는지 기본 테스트를 진행해 보겠습니다. 가장 기본적인 API형태의 시작이며, 대부분 이 구조의 확장이나 기능의 추가라고 보시면 됩니다. 우선 이전..

ayoteralab.tistory.com

이번에는 Test와 다르게 현실적으로 table을 생성해서 MVC의 전체 로직을 구현하는 방법으로 알아가 보고자 합니다. 전체적인 흐름은 다음과 같습니다.

  • 신규 Table 생성 및 Field 명 변경사항 확인
  • 관련 DTO 및 추가 Controller, Service, Mapper 생성
  • 오류사항 해결
  • mybatis camelCase 적용

 

1. 신규 Table 생성 및 Field 명 변경사항 확인


CREATE TABLE STATION_MISE (
  STATION_NAME VARCHAR(30),
  NO2_VALUE VARCHAR(10),
  O3_VALUE VARCHAR(10),
  PM10_VALUE VARCHAR(10),
  PM25_VALUE VARCHAR(10),
  DATE_TIME TIMESTAMP
);

INSERT INTO STATION_MISE (STATION_NAME, NO2_VALUE, O3_VALUE, PM10_VALUE, PM25_VALUE, DATE_TIME)
VALUES ('han river', '0.025', '0.016', '27', '10', '2019-11-19 23:00');
INSERT INTO STATION_MISE (STATION_NAME, NO2_VALUE, O3_VALUE, PM10_VALUE, PM25_VALUE, DATE_TIME)
VALUES ('songpa', '0.026', '0.017', '37', '15', '2019-11-19 23:00');

생성은 위와 같습니다. 나중에 미세먼지 데이터를 받아서 넣어볼 생각으로 생성하였고, 임시로 데이터를 2개 insert 했습니다. 자세히 살펴보면, 기존 test와 차이점은 Field명이 단순하지 않고 underline ( _ )으로 구분이 되어있습니다. 뭐 별거 있겠어요?? 그냥 느낌대로 가보죠??

 

 

2. 관련 DTO 및 추가 Controller, Service, Mapper 생성


위에 생성한 DB기준으로 DTO를 작성합니다. 이때, DTO의 위치는 기존에 userDTO가 있는 폴더에 작성합니다. 유사한 역할을 하는 아이들은 한곳에 모아줘야겠지요??

 

[StationMiseDTO.java]

public class StationMiseDTO {
	
	private String STATION_NAME;
	private String NO2_VALUE;
	private String O3_VALUE;
	private String PM10_VALUE;
	private String PM25_VALUE;
	private Instant DATE_TIME;
	
	public String getSTATION_NAME() {
		return STATION_NAME;
	}
	public void setSTATION_NAME(String sTATION_NAME) {
		STATION_NAME = sTATION_NAME;
	}
	public String getNO2_VALUE() {
		return NO2_VALUE;
	}
	public void setNO2_VALUE(String nO2_VALUE) {
		NO2_VALUE = nO2_VALUE;
	}
	public String getO3_VALUE() {
		return O3_VALUE;
	}
	public void setO3_VALUE(String o3_VALUE) {
		O3_VALUE = o3_VALUE;
	}
	public String getPM10_VALUE() {
		return PM10_VALUE;
	}
	public void setPM10_VALUE(String pM10_VALUE) {
		PM10_VALUE = pM10_VALUE;
	}
	public String getPM25_VALUE() {
		return PM25_VALUE;
	}
	public void setPM25_VALUE(String pM25_VALUE) {
		PM25_VALUE = pM25_VALUE;
	}
	public Instant getDATE_TIME() {
		return DATE_TIME;
	}
	public void setDATE_TIME(Instant dATE_TIME) {
		DATE_TIME = dATE_TIME;
	}

}

그럼 이제 Controller / Service / Mapper 순서로 쭉 작성해 주면 됩니다. 기존에 작성한 파일들이 있기 때문에 단순하게 추가만으로 테스트가 가능합니다.

 

[UserController.java]

	@ApiOperation(httpMethod = "GET"
			,value = "Station Mise 리스트 조회"
			,notes = "Select Station Mise List"
			,response = UserDTO.class
			,responseContainer = "ArrayList")
	@RequestMapping(value = "/miseList", method = RequestMethod.GET)
	public ResponseEntity<ArrayList<StationMiseDTO>> selectStationMise(){
		
		L.info("Select Station Mise Start");
		
		return ResponseEntity.ok(userService.selectStationMise());
	}

[UserService.java]

	public ArrayList<StationMiseDTO> selectStationMise() {
		return userMapper.selectStationMise();
	}

[UserMapper.java]

	ArrayList<StationMiseDTO> selectStationMise();

[UserMapper.xml]

<select id="selectStationMise" resultType="com.example.ayoteralab.main.dto.StationMiseDTO">
<![CDATA[
	SELECT 
		*
	FROM STATION_MISE
]]>
</select>

과연 결과는 어떻게 나올까요!!!!

성공적으로 불러왔습니다.

 

 

3. 오류사항 해결


DTO를 보면 안타까운 점이 있습니다. 기본적으로 프로젝트를 하게되면 변수명을 정하는 방식을 사전에 정하게 됩니다. 이제는 통상적으로 사용하는것이 바로 camelCase입니다. 간단하게 설명하자면... 단어와 단어의 합성어로 이루어진 변수의 경우 합성되는 단어의 첫 부분을 대문자로 표기하고 나머지는 소문자로 표기하는 방식입니다. 이 모양이 흡사 낙타의 혹과 같다고 해서 지어진 이름이지요.

 

버릇처럼 camelCase를 사용한 DTO는 다음과 같습니다.

 

[StationMiseDTO.java]

public class StationMiseDTO {
	
	private String stationName;
	private String no2Value;
	private String o3Value;
	private String pm10Value;
	private String pm25Value;
	private Instant dateTime;
	
	public String getStationName() {
		return stationName;
	}
	public void setStationName(String stationName) {
		this.stationName = stationName;
	}
	public String getNo2Value() {
		return no2Value;
	}
	public void setNo2Value(String no2Value) {
		this.no2Value = no2Value;
	}
	public String getO3Value() {
		return o3Value;
	}
	public void setO3Value(String o3Value) {
		this.o3Value = o3Value;
	}
	public String getPm10Value() {
		return pm10Value;
	}
	public void setPm10Value(String pm10Value) {
		this.pm10Value = pm10Value;
	}
	public String getPm25Value() {
		return pm25Value;
	}
	public void setPm25Value(String pm25Value) {
		this.pm25Value = pm25Value;
	}
	public Instant getDateTime() {
		return dateTime;
	}
	public void setDateTime(Instant dateTime) {
		this.dateTime = dateTime;
	}

}

결과는 어떻게 될까요?? 

DB로부터 Query는 성공적으로 수행했지만, 결과를 DTO에 넣지 못하여 내용은 null이 되었습니다. 그렇다면 어떤방법이 있을까요?? 가장 단순하게는 mapper query에서 alias를 주는 것입니다.

 

[UserMapper.xml]

<select id="selectStationMise" resultType="com.example.ayoteralab.main.dto.StationMiseDTO">
<![CDATA[
	SELECT 
		STATION_NAME stationName,
  		NO2_VALUE no2Value,
  		O3_VALUE o3Value,
  		PM10_VALUE as pm10Value,
  		PM25_VALUE as pm25Value,
  		DATE_TIME as dateTime
	FROM STATION_MISE
]]>
</select>

이렇게 해주면 되네용~!!

 

 

4. mybatis camelCase 적용


하아... 그렇다면 앞으로 이런경우 무조건 alias를 불편하게 붙여야 하나요?? Spring Boot가 엄청나게 편리한 플랫폼이라면서 이럴수가 있나요...

 

그래서 mybatis에서 제공하는 방법이 있습니다. 바로 mybatis-config.xml을 추가하여 camelCase를 적용하는 방법입니다. 이렇게 하면 필드명에 있는 underline ( _ )을 camelCase로 변경해 줍니다.

  • STATION_NAME -> stationName
  • DATE_TIME -> dateTime

우선 다음의 경로에 다음의 파일을 추가해줍니다.

[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>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="callSettersOnNulls" value="true"/>
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
</configuration>

그리고 해당 파일이 어디있는지 알기 위해서는 명기를 해줘야 합니다. 바로 sessionFactory에 추가를 해줘야 하는데 그 위치는 기존에 mapper영역을 설정한 그 위치입니다.

 

각 설정별 기능은 아래와 같습니다.

  • mapUnderscoreToCamelCase : 이번 적용의 목표
  • callSettersOnNulls : 쿼리 결과 필드가 null인 경우, 누락이 되서 나오는데 누락이 안되게 하는 설정
  • jdbcTypeForNull : 쿼리에 보내는 파라메터가 null인 경우, 오류 발생하는 것 방지  예) #{search.user} 

 

[DatabaseConfiguration.java]

	@Bean(name = "mainSqlSessionFactory")
	public SqlSessionFactory mainSqlSessionFactory(@Qualifier("main") DataSource dataSource) throws Exception{
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource);
		sqlSessionFactoryBean.setTypeAliasesPackage("com.example.ayoteralab.main.dto");
		sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis-config.xml"));
		sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:main_mapper/**/*.xml"));
		return sqlSessionFactoryBean.getObject();
	}

다음과 같이 위치를 지정해주면 아래와 같이 정상적으로 camelCase를 적용하여 불러옵니다.

 

Spring Boot에 application.yml에 위치를적용할 수도... 바로 camelCase를 적용할 수도 있는 것 같지만... 잘 안됩니다. 아무래도 application.yml과 DatabaseConfiguration이 적용되는 시점의 차이가 있어서 안되는 것 같습니다...

mybatis:
  config-location: classpath:mybatis-config.xml
  configuration:
    map-underscore-to-camel-case: true

 

나중에 몽땅 application.yml에 적용해봐야 겠습니다. 

 

 

-Ayotera Lab-

댓글