개발/Mybatis

Mybatis에서 TypeHandler 사용하기

함수형 인간 2025. 1. 24. 07:34

MyBatis에서 TypeHandler는 Java 타입과 JDBC 타입을 변환하는 역할을 한다. 즉, Java 객체의 필드를 데이터베이스 컬럼에 매핑하거나, 데이터베이스 컬럼 값을 Java 객체의 필드로 매핑할 때 데이터 타입 간의 변환을 처리한다.

TypeHandler가 필요한 경우:

  • MyBatis가 기본적으로 지원하지 않는 사용자 정의 타입을 사용할 때
  • 특정 데이터 타입의 변환 방식을 커스터마이징하고 싶을 때 (예: java.util.Date를 yyyyMMdd 형식의 문자열로 변환)
  • 데이터베이스의 특정 데이터 타입(예: JSON, Enum)을 Java 객체로 매핑하고 싶을 때

TypeHandler 구현 방법:

TypeHandler를 구현하려면 org.apache.ibatis.type.TypeHandler 인터페이스를 구현하거나, org.apache.ibatis.type.BaseTypeHandler 추상 클래스를 상속받아야 한다.

1. org.apache.ibatis.type.TypeHandler 인터페이스 구현:

Java
 
public interface TypeHandler<T> {

  void setParameter(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;

}
  • setParameter(): PreparedStatement에 파라미터를 설정한다.
  • getResult(): ResultSet 또는 CallableStatement에서 값을 가져와 Java 객체로 변환한다.

2. org.apache.ibatis.type.BaseTypeHandler 추상 클래스 상속 (권장):

Java
 
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  // ...

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

}
  • BaseTypeHandler를 상속하면 null 값 처리를 MyBatis가 자동으로 처리해주기 때문에, setNonNullParameter()와 getNullableResult() 메서드만 구현하면 된다.

예시: java.time.LocalDate를 위한 TypeHandler 구현:

Java
 
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.*;
import java.time.LocalDate;

public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, LocalDate parameter, JdbcType jdbcType)
            throws SQLException {
        ps.setDate(i, Date.valueOf(parameter));
    }

    @Override
    public LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Date date = rs.getDate(columnName);
        return date != null ? date.toLocalDate() : null;
    }

    @Override
    public LocalDate getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Date date = rs.getDate(columnIndex);
        return date != null ? date.toLocalDate() : null;
    }

    @Override
    public LocalDate getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Date date = cs.getDate(columnIndex);
        return date != null ? date.toLocalDate() : null;
    }
}

TypeHandler 등록:

TypeHandler를 사용하려면 MyBatis 설정 파일(mybatis-config.xml) 또는 Java Config에 등록해야 한다.

1. XML 설정 파일(mybatis-config.xml)에 등록:

XML
 
<configuration>
  <typeHandlers>
    <typeHandler handler="com.example.typehandler.LocalDateTypeHandler" javaType="java.time.LocalDate" jdbcType="DATE"/>
  </typeHandlers>
</configuration>

2. Java Config에 등록:

Java
 
@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        // TypeHandler 등록
        factoryBean.setTypeHandlers(new TypeHandler[]{new LocalDateTypeHandler()});

        return factoryBean.getObject();
    }
}

3. Mapper에서 @TypeHandler 어노테이션으로 등록

Java
 
public class User {
    @TypeHandler(LocalDateTypeHandler.class)
    private LocalDate localDate;
}

4. Result Map에서 typeHandler 속성으로 등록

XML
 
<resultMap id="userResultMap" type="User">
    <result property="localDate" column="ld" typeHandler="com.example.typehandler.LocalDateTypeHandler"/>
</resultMap>

TypeHandler 사용:

TypeHandler를 등록한 후에는, Mapper XML 파일이나 Mapper 인터페이스에서 해당 Java 타입과 JDBC 타입을 사용할 수 있다.

Mapper XML 예시:

XML
 
<insert id="insertUser" parameterType="User">
    INSERT INTO users (name, birth_date)
    VALUES (#{name}, #{birthDate})
</insert>

<select id="selectUser" resultType="User">
    SELECT name, birth_date
    FROM users
    WHERE id = #{id}
</select>

Mapper 인터페이스 예시:

Java
 
public interface UserMapper {
    void insertUser(User user);
    User selectUser(int id);
}

User 클래스 예시:

Java
 
public class User {
    private String name;
    private LocalDate birthDate;

    // getters and setters
}

위 예시에서 birthDate 필드는 java.time.LocalDate 타입이고, LocalDateTypeHandler를 통해 DATE JDBC 타입과 매핑된다.