하지만, PaymentMethodType 과 같은 Enum 클래스에 아무런 조치를 취하지 않는다면, JSON Object와 DB에서는 Enum 클래스의 Value를 아래와 같이 인식을 하게 된다.
Enum
Value
CREDIT
CREDIT
PREPAID
PREPAID
WALLET
WALLET
이는, JSON Object 에서 Object로 맵핑하거나 또는 DB 에서는 paymentMethod 를 DATA TYPE의 길이를 2로 정의했을때 오류가 발생하는 것을 쉽게 볼수 있을 것이다.
TypeHandler 의 정의 및 리팩토링
TypeHandler는 myBatis가 PreparedStatement에 파라미터를 설정하고 ResultSet 에서 값을 가져올때마다 적절한 자바 타입의 값을 가져오거나, INSERT시에 PreparedStatement에 적절한 자바 타입을 값을 set 할때 사용한다.
PaymentMethodType과 같은 Enum클래스를 예를들면, “CREDIT” 이라는 Value 대신에 “00” 이라는 VALUE가 INSERT / SELECT 되기를 기대하면 된다. TypeHandler에 대한 myBatis의 Reference는 아래의 링크를 참조하면 되며, 이 문서에서는 Enum을 위해 TypeHandler 클래스를 어떻게 정의하고 활용하는지 살펴보도록 하겠다.
위와 같은 목적으로 클래스를 정의하기 위해서는 TypeHandler (org.apache.ibatis.type.TypeHandler) 인터페이스를 구현해야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13
package org.apache.ibatis.type; ..... publicinterfaceTypeHandler<T> { voidsetParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)throws SQLException; T getResult(ResultSet rs, String columnName)throws SQLException; T getResult(ResultSet rs, int columnIndex)throws SQLException; T getResult(CallableStatement cs, int columnIndex)throws SQLException; }
필자는 위와 같은 이슈가 발생하는 Enum 클래스마다 TypeHandler 를 구현하는 것은 비효율적이기 때문에 아래와 같은 구조로 TypeHandler 클래스를 정의하였다.
문자열인 코드를 반환하기 위한 인터페이스
1 2 3
publicinterfaceCodeEnum{ public String getCode(); }
최종적인 PaymentMethodType Enum 클래스는 다음과 같다. PaymentMethodType 과 같은 이슈를 가진 Enum 클래스 내부에 TypeHandler와 같은 static 클래스를 정의하며, 이 클래스는 CodeEnumTypeHandler 를 상속하여 공통적인 비지니스 로직을 처리하게 하였다.
Java Config로 myBatis의 TypeHandler를 설정하기 위해서는 @MappedTypes(PaymentMethodType.class) 와 같이 명시적으로 정의해주어야 합니다.
Spring Boot에서 TypeHandler 등록
myBatis 설정시 SqlSessionFactoryBean 클래스에 아래와 같이 TypeHandler 등록이 가능하다. 이렇게 myBatis에서 Enum 클래스를 관리하는 방법을 알아보았고, 요구사항에 맞게 특정 코드성 값을 JSON Object 또는 DB 에서 사용할 수 있게 되었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Bean public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource)throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setTypeAliasesPackage(MyBatisProperties.TYPE_ALIASES_PACKAGE); sessionFactoryBean.setConfigLocation(applicationContext.getResource(myBatisProperties.getConfigLocation())); sessionFactoryBean.setMapperLocations(applicationContext.getResources(myBatisProperties.getMapperLocations())); sessionFactoryBean.setTypeHandlers(new TypeHandler[] { new BooleanTypeHandler(), new PaymentMethodCodeType.TypeHandler() }); return sessionFactoryBean.getObject(); }