728x90
myBatis를 사용하면서 resultType을 내가 만든 객체로 설정해서 사용하고 싶었는데 DB에서 조회한 컬럼명과 VO의 변수명이 맞지 않아서 null이 반환되었다. 이 문제의 원인은 VO의 변수명은 카멜 표기법, DB의 컬럼명은 스네이크 표기법을 적용했기 때문이다.
가장 간단한 해결법은 resultType="hashMap"으로 사용하면 된다. 하지만 이러 경우
- DB의 컬럼명이 그대로 노출될 수 있다. (사실 이 부분은 별도의 조치를 하지 않는 이상 스네이크 표기법으로 노출되던게 카멜 표기법으로 노출되도록 변경되는 것 뿐이라 DB의 컬럼명이 노출되는 것은 똑같다. 하지만 DB의 컬럼명이 그대로 노출되는 것을 방지한다는 것에 의미를 두려 한다.)
- SQL 쿼리문 작성할 때 alies를 지정하지 않는 실수를 할 경우 restful API 반환 값의 key 값이 예상한 것과 다르게 설정될 수 있다.
- 맘에 들지 않는 쉬운 해결법이다. (가장 큰 이유)
와 같은 문제가 발생할 수 있다고 생각했다.
그래서 찾아보니 mybatis configuration에 underscore to camelcase 속성이 있었고 이걸 이용해보기로 했다.
VO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BoardVO {
private long seq;
private String title;
private String content;
private String author;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Mapper (SQL)
<select id="selectAllBoard" resultType="BoardVO">
/* QueryID = selectAllBoard */
select seq, title, content, author, created_at
from board
</select>
기존 api response(GET /api/board)
{
"seq": 1,
"title": "restfulboardtitle",
"content": "restfulboardcontent",
"author": "restfulboardauthor",
"createdAt": null, // 해당 값이 문제가 되고 있음
"updatedAt": null
}
해결법 1.
아주 간단한 해결법으로 application.yml 에 속성값을 추가해주는 방법이다.
mybatis:
configuration:
map-underscore-to-camel-case: true
아주 간단한 해결법이지만 내 프로젝트에서는 문제가 해결되지 않았기에 해결법 2도 작성한다.
해결법 2.
mybatis-config.xml 파일을 생성하고 MybatisConfig.java 파일 수정하기
/resource 디렉토리 밑에 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>
그리고 나서 MybatisConfig.java 파일에 해당 파일과 관련된 코드를 작성해주면된다.
@Configuration
@MapperScan(value = "kim.jiwook.playground.mapper", sqlSessionFactoryRef = "SqlSessionFactory")
public class MyBatisConfig {
@Value("${mybatis.mapper-locations}")
String mapperPath;
@Value("${mybatis.type-aliases-package}")
String typeAliasesPackage;
// 추가된 코드
final String mybatisConfigPath = "classpath:/mybatis-config.xml";
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "SqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource,
ApplicationContext applicationContext) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(mapperPath));
sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
// 추가된 코드
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource(mybatisConfigPath));
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "SessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
변경 후 api response(GET /api/board)
{
"seq": 1,
"title": "restfulboardtitle",
"content": "restfulboardcontent",
"author": "restfulboardauthor",
"createdAt": "2023-11-04T00:00:00", // sql 조회 결과와 VO의 변수명이 정상적으로 매핑되어 값이 출력됨
"updatedAt": null
}