Custom Type Handlers and Pagination Plugins in MyBatis

Type handlers in MyBatis bridge the gap between JDBC types and Java objects during parameter binding and result mapping. While MyBatis providse built-in handlers for common types, custom handlers enable precise control over serialization and deserialization logic—especially useful when persisting non-standard representations (e.g., storing Date as milliseconds in a BIGINT column).

Implementing a Custom Type Handler

To define a custom handler:

  1. Create a class extending BaseTypeHandler<T>, where T is the target Java type.
  2. Override the four abstract methods: setNonNullParameter (for outbound parameter binding) and three variants of getNullableResult (for inbound result extraction).
  3. Register the handler in MyBatis’ configuration using the <typeHandlers> section.
  4. Verify behavior via integration tests that exercise both insert and select operations.

Below is a refactored implementation that maps java.time.Instant to a BIGINT column representing epoch milliseconds:

public class InstantMillisHandler extends BaseTypeHandler<Instant> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Instant instant, JdbcType jdbcType) 
            throws SQLException {
        ps.setLong(i, instant.toEpochMilli());
    }

    @Override
    public Instant getNullableResult(ResultSet rs, String columnName) throws SQLException {
        long millis = rs.getLong(columnName);
        return rs.wasNull() ? null : Instant.ofEpochMilli(millis);
    }

    @Override
    public Instant getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        long millis = rs.getLong(columnIndex);
        return rs.wasNull() ? null : Instant.ofEpochMilli(millis);
    }

    @Override
    public Instant getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        long millis = cs.getLong(columnIndex);
        return cs.wasNull() ? null : Instant.ofEpochMilli(millis);
    }
}

Registration in mybatis-config.xml:

<typeHandlers>
  <typeHandler handler="com.example.handlers.InstantMillisHandler" 
                javaType="java.time.Instant" 
                jdbcType="BIGINT"/>
</typeHandlers>

Integrating Pagination with PageHelper

The PageHelper plugin simplifies server-side pgaination by transparently rewriting SQL queries and injecting LIMIT/OFFSET clauses (or equivalent dialect-specific constructs). It operates as a MyBatis interceptor and requires minimal setup.

Add dependencies (Maven):

<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.3.2</version>
</dependency>
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>4.7</version>
</dependency>

Configure the interceptor in MyBatis’ main configuration:

<plugins>
  <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!-- Optional: explicitly declare dialect if auto-detection fails -->
    <property name="helperDialect" value="mysql"/>
  </plugin>
</plugins>

Use it programmatically before executing a mapped statement:

try (SqlSession session = sqlSessionFactory.openSession()) {
  UserMapper mapper = session.getMapper(UserMapper.class);
  
  // Trigger pagination: page 1, 5 items per page
  PageHelper.startPage(1, 5);
  
  List<User> pagedUsers = mapper.selectAll();
  
  // PageInfo provides metadata (total count, page numbers, etc.)
  PageInfo<User> pageInfo = new PageInfo<>(pagedUsers);
  System.out.println("Total: " + pageInfo.getTotal());
}

A complete MyBatis configuration file incorporating both features looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
  <properties resource="jdbc.properties"/>

  <typeAliases>
    <typeAlias type="com.example.domain.User" alias="User"/>
  </typeAliases>

  <typeHandlers>
    <typeHandler handler="com.example.handlers.InstantMillisHandler" 
                  javaType="java.time.Instant" 
                  jdbcType="BIGINT"/>
  </typeHandlers>

  <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
      <property name="helperDialect" value="mysql"/>
    </plugin>
  </plugins>

  <environments default="dev">
    <environment id="dev">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>

  <mappers>
    <mapper resource="mappers/UserMapper.xml"/>
  </mappers>
</configuration>

Tags: MyBatis typehandler pagehelper

Posted on Wed, 14 Oct 2026 16:24:05 +0000 by natbrazil