自定义 TypeHandler:类型与字段互转
Java 类型与 JDBC 类型不会总是一一对应:比如把 List<String> 存成 VARCHAR 里的 JSON,或把数据库整型映射成自定义对象。MyBatis 用 TypeHandler(类型处理器)负责"Java 类型 ↔ JDBC 类型"的双向转换,内置覆盖了基础类型,特殊类型就要自定义。本节讲实现、注册与使用。
TypeHandler 的职责
一次读写由四个方法分工,通常继承 BaseTypeHandler<T> 只覆写需要的几个:
| 方法 | 时机 | 作用 |
|---|---|---|
| setNonNullParameter | 写库 | 把 Java 参数设进 PreparedStatement |
| getNullableResult(rs, columnName) | 查库 | 按列名读值并转成 Java 对象 |
| getNullableResult(rs, columnIndex) | 查库 | 按下标读值(按列顺序取值时用) |
| getNullableResult(cs, columnIndex) | 调用存储过程 | 从 CallableStatement 读值 |
自定义:JSON 字段处理示例
需求:tags 列存 JSON 文本,Java 侧是 List<String>。继承 BaseTypeHandler,用 Jackson 完成序列化与反序列化:
public class JsonListTypeHandler extends BaseTypeHandler<List<String>> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
List<String> parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, MAPPER.writeValueAsString(parameter));
} catch (JsonProcessingException e) {
throw new SQLException(e);
}
}
@Override
public List<String> getNullableResult(ResultSet rs, String columnName)
throws SQLException {
String json = rs.getString(columnName);
if (json == null || json.isEmpty()) return Collections.emptyList();
try {
return MAPPER.readValue(json, new TypeReference<List<String>>() {});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 还需实现 getNullableResult(ResultSet, int) 与 CallableStatement 版本,逻辑同上
}
null 值由 BaseTypeHandler 的 setParameter 兜底处理,不必在 setNonNullParameter 里判空;真实代码三个 getNullableResult 都要补全,具体 API 以官方文档为准。
注册与使用
XML 配置里声明类型处理器,也可配置包扫描自动注册(如 type-handlers-package: com.demo.handler):
<typeHandlers>
<typeHandler handler="com.demo.handler.JsonListTypeHandler"
javaType="java.util.List" jdbcType="VARCHAR"/>
</typeHandlers>
使用位置三选一:
- resultMap:
<result column="tags" property="tags" typeHandler="JsonListTypeHandler"/>; - 写入参数:
#{tags, typeHandler=JsonListTypeHandler}; - 实体字段注解
@TableField(typeHandler = ...)(MyBatis-Plus 写法,以官方文档为准)。
注册后 Java 类型匹配即自动生效;查询映射、写入参数两条路径都能命中。写完建议补单测,覆盖 null、空串、非法 JSON 三个边界。
自定义 TypeHandler 是 MyBatis 的标准扩展点:先登记 javaType/jdbcType,再在 resultMap 或参数里启用。JSON、枚举、加解密字段这类"Java 与库表长不一样"的场景,一套处理器通吃。