Understanding and Customizing MyBatis TypeHandler: Configuration, Implementation, and Execution

This article explains what MyBatis TypeHandler is, how to configure the environment, create custom handlers, register them via Spring Boot or XML, and details the internal execution flow for converting parameters and results between Java and JDBC types.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Understanding and Customizing MyBatis TypeHandler: Configuration, Implementation, and Execution

MyBatis uses TypeHandler to map between Java types and JDBC types, enabling seamless data conversion when persisting objects. The article begins by assuming familiarity with MyBatis and introduces the concept of the "black box" TypeHandler.

Environment setup requires MyBatis 3.5 and Spring Boot 2.3.3.RELEASE. The built‑in TypeHandlers cover most common cases, but custom handlers can be created when special conversion logic is needed.

The TypeHandler interface defines four methods: setParameter for converting Java to JDBC, and three getResult overloads for converting JDBC results back to Java. The source code of the interface is shown below:

public interface TypeHandler<T> {
  // set Java parameter to JDBC
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  // get result from ResultSet by column name
  T getResult(ResultSet rs, String columnName) throws SQLException;
  // get result from ResultSet by column index
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  // get result from CallableStatement
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

To illustrate a custom handler, the article defines GenderTypeHandler that maps the Chinese strings "男"/"女" to integer values 1/2 in the database. The handler extends BaseTypeHandler and overrides setNonNullParameter and the three getNullableResult methods.

@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(String.class)
public class GenderTypeHandler extends BaseTypeHandler {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, StringUtils.equals(parameter.toString(), "男") ? 1 : 2);
    }
    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getInt(columnName) == 1 ? "男" : "女";
    }
    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getInt(columnIndex) == 1 ? "男" : "女";
    }
    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getInt(columnIndex) == 1 ? "男" : "女";
    }
}

The annotations @MappedTypes and @MappedJdbcTypes specify the Java and JDBC types the handler works with. Registration can be done in two ways:

Method 1: Add the package containing the handler to mybatis.type-handlers-package in application.properties.

Method 2: Manually create a SqlSessionFactoryBean, instantiate the handler, and set it via sqlSessionFactoryBean.setTypeHandlers(...).

mybatis.type-handlers-package=cn.cb.demo.typehandler
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    // other configuration ...
    GenderTypeHandler genderTypeHandler = new GenderTypeHandler();
    bean.setTypeHandlers(new TypeHandler[]{genderTypeHandler});
    return bean.getObject();
}

In XML mapper files, the custom handler is referenced by its fully‑qualified class name using the typeHandler attribute on #{} placeholders for inserts/updates, and on result elements within a resultMap for queries.

<insert id="insertUser">
  INSERT INTO user_info(user_id, his_id, name, gender, password, create_time)
  VALUES(#{userId,jdbcType=VARCHAR}, #{hisId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
        #{gender,jdbcType=INTEGER,typeHandler=cn.cb.demo.typehandler.GenderTypeHandler},
        #{password,jdbcType=VARCHAR}, NOW())
</insert>

<resultMap id="userResultMap" type="cn.cb.demo.domain.UserInfo">
  <id column="id" property="id"/>
  <result column="gender" property="gender" typeHandler="cn.cb.demo.typehandler.GenderTypeHandler"/>
  <result column="name" property="name"/>
  <result column="password" property="password"/>
</resultMap>

Internally, when a statement is executed, PreparedStatementHandler.parameterize() delegates to DefaultParameterHandler.setParameters(), which calls typeHandler.setParameter(...) to convert Java values to JDBC. For query results, ResultSetHandler.handleResultSets() eventually invokes typeHandler.getResult(...) inside DefaultResultHandler.getPropertyMappingValue() to perform the reverse conversion.

The article concludes by summarizing the steps to create, register, and use a custom TypeHandler in MyBatis, and invites readers to follow the author for more technical content.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaMyBatisORMJDBCSpringBootTypeHandler
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.