code 관리를 enum으로 할 때 해당 코드들을 웹 화면에 comboBox, radiobutton, checkbox 등등으로 보여줄때 다국어 지원이 안된다. (<form:select> 안에 resourceBundle과 연동되어 처리되는 attribute가 있었으면 좋을련만...) 그럴때 다음과 같이 하면 어떨지... 간단하게 만들어봄.... refactoring은 더 해야 하지만...(DB에는 short type의 column에 숫자 코드값만 들어가는 경우임, DB의 table에 코드성 데이터가 있더라도 동적으로 추가 가능한 기능(페이지)가 없다면 enum으로 관리 할 수도 있을것 같다.
spring context에
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:/message/validation</value>
<value>classpath:/message/label</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8" />
<property name="cacheSeconds">
<value>60</value>
</property>
</bean>
label_ko.properties 에 다음과 같이 WriterTypeCode에 대한 label명을 넣고
label.WriterTypeCode.CUSTOMER = \uACE0\uAC1D
label.WriterTypeCode.MANAGER = \uAD00\uB9AC\uC790
label.WriterTypeCode.SYSTEM = \uC2DC\uC2A4\uD15C
Code interface를 만들고
public interface ICode {
short getCode();
}
사용할 Code enum을 만들고
public enum WriterTypeCode implements ICode {
CUSTOMER((short)1),
MANAGER((short)2),
SYSTEM((short)3);
private short code;
private String codeLabel;
private WriterTypeCode(short code) {
this.code = code;
}
@Override
public short getCode() {
return code;
}
public void setCode(short code) {
this.code = code;
}
public String getCodeLabel() {
return codeLabel;
}
public void setCodeLabel(String codeLabel) {
this.codeLabel = codeLabel;
}
}
controller에서 modelAttribute를 만들고
@Controller
@RequestMapping("/code/*")
public class CodeEnumController {
@Autowired
private MessageSource messageSource;
@ModelAttribute("writerTypeCodes")
public EnumSet<WriterTypeCode> writerTypeCodes() {
EnumSet<WriterTypeCode> writerTypeCodes = EnumSet.allOf(WriterTypeCode.class);
String message = null;
for(WriterTypeCode writerTypeCode : writerTypeCodes) {
message = this.messageSource.getMessage(
"label.WriterTypeCode." + writerTypeCode.name(), null, Locale.getDefault());
writerTypeCode.setCodeLabel(message);
}
return writerTypeCodes;
}
@RequestMapping(value="/code", method=RequestMethod.GET)
public ModelAndView code() {
CodeGroup codeGroup = new CodeGroup();
codeGroup.setName("testName");
codeGroup.setOrder(1);
codeGroup.setWriterTypeCode(WriterTypeCode.MANAGER);
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;
}
}
jsp에서 보여준다.
<form:select path="writerTypeCode" items="${writerTypeCodes}" itemLabel="codeLabel" itemValue="code"></form:select>
<spring:message code="label.WriterTypeCode.${communication.writerTypeCode}"
var="writerTypeCodeValue" />
writerTypeCode [${writerTypeCodeValue}]
post 방식으로 Model에 binding될때는 Controller에 다음의 코드를 넣고
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(WriterTypeCode.class, new CodePropertyEditor(WriterTypeCode.class));
}
@RequestMapping(value="/code", method=RequestMethod.POST)
public ModelAndView codePost(@ModelAttribute CodeGroup codeGroup ) {
System.out.println("################## codeGroupResult[" + codeGroup + "]");
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;
}
CodePropertyEditor 를 만든다.
public class CodePropertyEditor extends PropertyEditorSupport {
@SuppressWarnings("rawtypes")
Class clazz;
@SuppressWarnings("rawtypes")
public CodePropertyEditor(Class clazz) {
this.clazz = clazz;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (!StringUtils.hasLength(text)) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!text.matches("\\d+")) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!ICode.class.isAssignableFrom(this.clazz)) {
super.setValue(null);
throw new IllegalArgumentException();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
EnumSet typeCodes = EnumSet.allOf(this.clazz);
int code = -1;
int parameterCode = Integer.valueOf(text);
for(Object typeCode : typeCodes) {
code = ((ICode)typeCode).getCode();
if (code == parameterCode) {
super.setValue(typeCode);
break ;
}
}
}
}
여기까지 하면 웹에서의 처리는 끝나고 DB쪽에 binding하기 위해서는 myBatis인 경우 다음과 같이 TypeHandler를 만들어서 연동 하면 된다.
다음과 같이 ACodeTypeHandler를 만들고
public abstract class ACodeTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
//** log와 validation 체크 해야 함
private EnumSet<E> enumSet;
public ACodeTypeHandler(Class<E> enumClass){
if(!ICode.class.isAssignableFrom(enumClass))
throw new IllegalArgumentException(); // 메세지 추가하기
this.enumSet = EnumSet.allOf(enumClass);
}
@Override
public E getResult(ResultSet resultSet, String columnName)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnName));
}
@Override
public E getResult(ResultSet resultSet, int columnIndex)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnIndex));
}
@Override
public E getResult(CallableStatement callableStatement, int columnIndex)
throws SQLException {
if (callableStatement.wasNull())
return null;
return valueOf(callableStatement.getShort(columnIndex));
}
@Override
public void setParameter(PreparedStatement preparedStatement, int parameterIndex,
E parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null)
preparedStatement.setNull(parameterIndex, Type.SHORT);
else
preparedStatement.setShort(parameterIndex, ((ICode)parameter).getCode());
}
public E valueOf(short value) {
for(E e: this.enumSet){
if((((ICode)e)).getCode() == value)
return e;
}
return null;
}
}
WriterTypeCodeTypeHandler 를 만들고
public class WriterTypeCodeTypeHandler extends ACodeTypeHandler<WriterTypeCode> {
public WriterTypeCodeTypeHandler() {
super(WriterTypeCode.class);
}
}
mybatis config 파일에 다음의 내용을 넣으면 preparedStatement로 값셑팅하기 전과 ResultSet으로 결과값 Model에 binding 할때 자동으로 변환 된다.
<configuration>
<typeHandlers>
<typeHandler javaType="net.board.type.WriterTypeCode" handler="net.board.type.handler.WriterTypeCodeTypeHandler" />
</typeHandlers>
</configuration>
모든 Controller에서 사용할 수 있도록 AnnotationMethodHandlerAdapter.webBindingInitializer에 등록해도 되고... 이렇게 하면 enum으로 코드 관리가 되지 않을지...
--------------------------------------------------------------------------------------------------
mybatis와의 연동을 위해서는 ICode를 사용해야 하지만... 웹에서 comboBox나 listBox등에 enum의 내용을 출력하고 결과값을 해당 모델에 바인딩할때 ICode를 제거하고 spring:message와 연동해서 roof 돌면서 보여주고 '선택해 주세요'와 같은 것이 제일 상단에 보여지는 경우에는 결과값을 ''으로 해서 처리하면 '@ModelAttribute("writerTypeCodes")'의 소스코드와 initBinder()의 소스코드를 사용하지 않아도 된다. 예로 다음같이 view에서 enum값을 화면에 출력하고
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<t:enumSelect path="modifyTypeCode" type="Header" ></t:enumSelect>
다음과 같은 tag 내용이면 modifyTypeCode용 @ModelAttribute가 필요없고 initBinder()에 소스추가도 필요없게 된다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ attribute name="path" type="java.lang.Object" rtexprvalue="true" required="true" %>
<%@ attribute name="type" type="java.lang.String" rtexprvalue="true" required="false" %>
<spring:eval expression="'${nestedPath}'.substring(0, '${nestedPath}'.length() - 1)" var="basePath"/>
<spring:eval expression="new org.springframework.beans.BeanWrapperImpl(baseBean)" var="beanWrapper"/>
<spring:eval expression="beanWrapper.getPropertyType('${path}')" var="pathClass"/>
<!-- Create a select option for each of the enum constants. -->
<form:option value=""><spring:message code="${pathClass.simpleName}.Header"/></form:option>
how to bind using enum.valueOf(). -->
<!-- Label is pulled from propfile as simpleName.constant, e.g Color.RED -->