Post

(Spring) DB 관련 - Mybatis CustomTypeHandler

jdbcType : nullable column에 null이 들어갈 때?

  • MyBatis는 nullable 컬럼의 parameter로 null이 넘어왔을 때, jdbcType이 명시되어 있지 않으면 TypeException을 던진다.
    • setNull로 해당 타입에 맞는 null값(VARCHAR인 경우 “”) 을 넣어줘야 하는데, 뭘 넣어줄지 모르니까 예외가 발생하는 것
    • The JDBC Type is required by JDBC for all nullable columns, if null is passed as a value. You can investigate this yourself by reading the JavaDocs for the PreparedStatement.setNull() method.
      [www.mybatis.org/mybatis-3/sqlmap-xml.html]
  • 반대로 null이 아닌 값이 넘어왔을 때는 해당 자바 타입의 setNonNullParameter를 호출하는데 여기서 parameter의 타입 -> DB 타입으로 변환해주어야 한다. 적절하게 변환하지 않으면 “부적합한 열 유형” 예외가 발생함.
  • 모든 nullable column에 jdbcType을 명시해주기 귀찮다면, 다음 방법으로 처리할 수 있음.
1
2
3
4
sqlSessionFactory!!.configuration.jdbcTypeForNull=JdbcType.NULL

//xml base 설정이라면
<setting name="jdbcTypeForNull" value="NULL"/>

CustomTypeHandler 정의

1
2
3
4
5
6
7
8
/* myBatis config typeHandler 로 등록되어 Boolean 을 Y, N, null 로 변환해준다. */
@MappedTypes(Boolean.class)
@MappedJdbcTypes(JdbcType.CHAR)
public class BaseBooleanTypeHandler extends BaseTypeHandler<Boolean> {
...


#{param, jdcbType=VARCHAR}
  • https://mybatis.org/mybatis-3/ko/configuration.html#typeHandlers
  • 애너테이션 사용 시 주의. 꼼꼼하게 읽어보고 사용해야 함.
  • 마이바티스는 DB에서 꺼내온 데이터를 어떤 타입핸들러에서 처리할지를, [javaType, jdbcType] 두 개를 이용해 결정한다.
    • jdcbType을 쿼리에 명시하지 않은 경우, javaType=[TheJavaType], jdbcType=null조합을 사용한다.
  • 즉 기본으로 제공되는 AClassTypeHandler<A>와 내가 직접 정의한 AClassTypeHandler<A>가 모두 존재한다면, 후자를 사용하기 위해서는
    • @MappedJdbcTypes(TheJdbcType)을 사용하고, 쿼리 파라미터에 해당 jdbcType을 명시하거나,
      • javaType=[TheJavaType], jdbcType=[TheJdbcType] 에 대해서 내 핸들러를 탄다.
    • 쿼리 파라미터에 아예 typeHandler를 적어서 내 핸들러를 타도록 명시하거나,
      • => 이 경우 typeAliases로 Handler가 등록 되어 있어야 prefix없이 쓴다.
    • @MappedJdbcTypes 애너테이션을 아예 사용하지 않고 @MappedTypes만 쓰거나,
      • 예를 들면, 모든 LocalDateTime 타입은 이 TypeHandler에서만 처리할거라면. 많이 쓰는 방법.
      • => 애너테이션을 안쓰면 AllJdbcType에 대한 핸들러가 정의된다.
      • 따라서 javaType=[TheJavaType], jdbcType=[null&AllType] 이 내 핸들러를 탄다
    • @MappedJdcbTypes(value=TheJdcbType, includeNullJdbcType = true) 로 적어주어야 한다.
      • javaType=[TheJavaType], jdbcType=[null&TheJdbcType] 에 대해서 내 핸들러를 탄다
      • 이건 조금 애매한 방법일 것 같은게… 애초에 핸들러를 2개 이상 유지해야 한다는 것은, jdbcType에 따라 다른 핸들러를 태워서 처리를 다르게 할 필요가 있기 때문이다.
      • db 상에 VARCHAR인 것도 jdbcType 명시 안하면 null로 넘어올거고, DATE인 것도 jdbcType 명시 안하면 null로 넘어오게 될텐데, 이러면 두 jdbcType 모두 includeNullJdbcType = true인 핸들러를 타게 된다.
      • 각기 다른 핸들러를 타기 위해서는 둘 중 하나의 db타입 파라미터에는 jdbcType을 꼭 명시해줘야 되는데…그럴 바에야 두 타입 모두 jdbcType을 명시해주는게 더 나으니까.

CustomTypeHandler for Enum

Enum <> DB 입출력 시 기본적으로 Enum.name()을 이용해 변환하도록 되어 있는데, Enum 필드의 DB 입출력을 Enum.name()이 아니라 별도 필드 값으로 입출력 하고 싶은 경우 어떻게 해야 하는가?

방법 1

방법 2 : interface (추천)

방법 3 : annotation (추천)

  • 근데 interface가 별로 마음에 들지 않는다. annotation을 사용할 수 있는 방법은 없나?
  • ” Mybatis는 ~mapper.xml에 등장하는 모든 Enum을 스캔해서 각각을 파라미터로 기본으로 제공되는 EnumTypeHandler를 생성해준다. " 는 점을 이용하면 별도로 TypeHandler를 정의 할 필요가 없을 것 같은데...
  • Mybatis 3.4.5 부터 기본으로 제공되는 EnumTypeHandler 대신, Custom 타입핸들러를 기본 EnumTypeHandler로 만들 수 있다.
1
2
3
4
5
6
// MyBatis 코드에 다음과 같은 부분이 있음. (>= 3.4.5)
Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));
configuration.setDefaultEnumTypeHandler(typeHandler);
 
// in config.xml
<setting name="defaultEnumTypeHandler" value="~~~.~~~.EnumDbValueTypeHandler"/>
This post is licensed under CC BY 4.0 by the author.