MyBatis自定义TypeHandler用于解决Java与数据库类型不一致问题,推荐继承BaseTypeHandler实现,并支持局部、包扫描、显式三种注册方式,可处理枚举、JSON等复杂类型转换。

MyBatis 的自定义类型处理器(TypeHandler)用于解决 Java 类型与数据库字段类型不一致的问题,比如把 JSON 字符串映射为对象、把枚举存为字符串或序号、处理 LocalDateTime 与字符串/时间戳的转换等。关键在于实现 TypeHandler 接口,并在配置中注册。
实现 TypeHandler 接口
最常用的方式是继承 BaseTypeHandler<T>(推荐),它已帮你处理了 null 值、空值、泛型绑定等逻辑。以将 UserStatus 枚举存为数据库中的字符串为例:
// 自定义枚举类型处理器
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.name()); // 写入时:枚举名存入数据库
}
@Override
public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return value == null ? null : UserStatus.valueOf(value); // 读取时:字符串转枚举
}
@Override
public UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return value == null ? null : UserStatus.valueOf(value);
}
@Override
public UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return value == null ? null : UserStatus.valueOf(value);
}
}
注册类型处理器
有三种方式注册,按优先级从高到低:
立即学习“Java免费学习笔记(深入)”;
-
局部注册(推荐):在
<resultMap>或<parameterMap>中通过typeHandler属性指定,只对当前字段生效
<resultMap id="userMap" type="User"> <id property="id" column="id"/> <result property="status" column="status" typeHandler="com.example.UserStatusTypeHandler"/> </resultMap>
-
包扫描注册:在 MyBatis 配置文件中配置
<typeHandlers>,自动扫描指定包下所有 TypeHandler 实现类
<typeHandlers> <package name="com.example.handler"/> </typeHandlers>
- 显式注册单个类:适合少量 Handler,明确控制映射关系
<typeHandlers> <typeHandler handler="com.example.UserStatusTypeHandler" javaType="com.example.UserStatus"/> </typeHandlers>
处理复杂类型(如 JSON)
若字段是 JSON 字符串,需转为 Java 对象(如 Map 或自定义 DTO),可借助 Jackson 或 FastJSON:
- 读取时:用
ObjectMapper#readValue(String, Class)将 JSON 字符串反序列化 - 写入时:用
ObjectMapper#writeValueAsString(Object)序列化为字符串 - 注意捕获
JsonProcessingException并包装为RuntimeException,MyBatis 会统一处理
示例节选:
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType)
throws SQLException {
try {
ps.setString(i, objectMapper.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize map to JSON", e);
}
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
if (json == null) return null;
try {
return objectMapper.readValue(json, new TypeReference<Map<String, Object>>(){});
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to deserialize JSON to Map", e);
}
}
注意事项和技巧
- 确保数据库字段类型支持存储目标内容(如 JSON 字段建议用
TEXT或JSON类型) - 若使用注解方式(
@Select等),可在@Results中通过@Result(typeHandler = ...)指定 - MyBatis 默认已提供常用类型处理器(如
LocalDateTimeTypeHandler),可直接引用或继承扩展 - 避免在 TypeHandler 中做耗时操作(如远程调用、文件读写),它会在每次参数设置或结果映射时执行


















