TypeHandler 接口
TypeHandler 是 MyBatis 中负责 Java 类型与 JDBC 类型之间双向转换的核心接口。当数据库字段类型与 Java 类型不一致时,TypeHandler 负责完成自动转换。
TypeHandler 接口定义
Java
package org.apache.ibatis.type;
public interface TypeHandler<T> {
// 设置 PreparedStatement 参数
void setParameter(PreparedStatement ps, int i,
T parameter, JdbcType jdbcType) throws SQLException;
// 从 ResultSet 按列名获取结果
T getResult(ResultSet rs, String columnName) throws SQLException;
// 从 ResultSet 按列索引获取结果
T getResult(ResultSet rs, int columnIndex) throws SQLException;
// 从 CallableStatement 获取存储过程结果
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
四个方法覆盖了两个方向:
| 方向 | 方法 | 场景 |
|---|---|---|
| Java -> 数据库 | setParameter | INSERT/UPDATE 时设置参数 |
| 数据库 -> Java | getResult(ResultSet, String) | SELECT 查询按列名读取 |
| 数据库 -> Java | getResult(ResultSet, int) | SELECT 查询按列索引读取 |
| 数据库 -> Java | getResult(CallableStatement, int) | 存储过程返回值 |
BaseTypeHandler 抽象实现
直接实现 TypeHandler 接口需要处理大量 null 判断的样板代码。MyBatis 提供了 BaseTypeHandler 抽象类,封装了 null 值处理逻辑:
Java
public abstract class BaseTypeHandler<T> extends TypeReference<T>
implements TypeHandler<T> {
@Override
public void setParameter(PreparedStatement ps, int i,
T parameter, JdbcType jdbcType) {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("...");
}
ps.setNull(i, jdbcType.TYPE_CODE);
} else {
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
@Override
public T getResult(ResultSet rs, String columnName) {
T result = rs.getObject(columnName);
if (result == null && rs.wasNull()) {
return null;
}
return getNullableResult(rs, columnName);
}
// 其他 getResult 方法同理...
// 子类只需实现以下四个非空方法
public abstract void setNonNullParameter(...) 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;
}
推荐:自定义 TypeHandler 时继承
BaseTypeHandler,只需关注非空情况下的转换逻辑,减少样板代码。
内置 TypeHandler 概览
MyBatis 在 org.apache.ibatis.type 包下提供了大量内置 TypeHandler:
基本类型处理器
| TypeHandler | Java 类型 | JDBC 类型 |
|---|---|---|
| IntegerTypeHandler | Integer | INTEGER |
| LongTypeHandler | Long | BIGINT |
| StringTypeHandler | String | VARCHAR |
| BooleanTypeHandler | Boolean | BOOLEAN |
| DoubleTypeHandler | Double | DOUBLE |
| FloatTypeHandler | Float | FLOAT |
| ByteTypeHandler | Byte | TINYINT |
| ShortTypeHandler | Short | SMALLINT |
| BigDecimalTypeHandler | BigDecimal | DECIMAL/NUMERIC |
日期时间处理器
| TypeHandler | Java 类型 | JDBC 类型 |
|---|---|---|
| DateTypeHandler | java.util.Date | TIMESTAMP |
| SqlDateTypeHandler | java.sql.Date | DATE |
| SqlTimeTypeHandler | java.sql.Time | TIME |
| SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
Java 8 时间 API 处理器
| TypeHandler | Java 类型 | JDBC 类型 |
|---|---|---|
| InstantTypeHandler | Instant | TIMESTAMP |
| LocalDateTypeHandler | LocalDate | DATE |
| LocalTimeTypeHandler | LocalTime | TIME |
| LocalDateTimeTypeHandler | LocalDateTime | TIMESTAMP |
枚举处理器
| TypeHandler | Java 类型 | 存储方式 |
|---|---|---|
| EnumTypeHandler | Enum | 枚举名称(字符串) |
| EnumOrdinalTypeHandler | Enum | 枚举索引(整数) |
其他处理器
| TypeHandler | Java 类型 | JDBC 类型 |
|---|---|---|
| ByteArrayTypeHandler | byte[] | BLOB/LONGVARBINARY |
| BlobTypeHandler | Blob | BLOB |
| ClobTypeHandler | Clob | CLOB |
| ObjectTypeHandler | Object | 任意类型 |
注册 TypeHandler 的两种方式
方式一:mybatis-config.xml 配置注册
XML
<configuration>
<typeHandlers>
<!-- 注册单个 TypeHandler -->
<typeHandler handler="com.example.handler.JsonTypeHandler"/>
<!-- 包扫描自动注册 -->
<package name="com.example.handler"/>
</typeHandlers>
</configuration>
方式二:注解注册(推荐)
在 TypeHandler 类上添加注解:
Java
@MappedTypes({UserConfig.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class JsonTypeHandler extends BaseTypeHandler<UserConfig> {
// 实现...
}
| 注解 | 作用 |
|---|---|
@MappedTypes | 指定该 TypeHandler 处理的 Java 类型 |
@MappedJdbcTypes | 指定该 TypeHandler 对应的 JDBC 类型 |
注解方式更加简洁,推荐在大多数场景下使用。XML 配置方式适合集中管理或第三方 TypeHandler 的注册。
TypeHandler 的自动注册机制
MyBatis 在启动时会自动注册 TypeHandler:
- 先注册内置 TypeHandler(基本类型、日期、枚举等)
- 再注册配置文件中指定的 TypeHandler
- 最后通过注解
@MappedTypes和@MappedJdbcTypes注册的 TypeHandler
当存在多个 TypeHandler 可处理同一类型时,后注册的会覆盖前面的,即局部配置优先于全局默认。
完整自定义 TypeHandler 示例
Java
package com.example.handler;
import org.apache.ibatis.type.*;
import java.sql.*;
@MappedTypes({com.example.model.Status.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StatusTypeHandler extends BaseTypeHandler<com.example.model.Status> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
com.example.model.Status status, JdbcType jdbcType) throws SQLException {
ps.setString(i, status.getCode());
}
@Override
public com.example.model.Status getNullableResult(
ResultSet rs, String columnName) throws SQLException {
return Status.fromCode(rs.getString(columnName));
}
@Override
public com.example.model.Status getNullableResult(
ResultSet rs, int columnIndex) throws SQLException {
return Status.fromCode(rs.getString(columnIndex));
}
@Override
public com.example.model.Status getNullableResult(
CallableStatement cs, int columnIndex) throws SQLException {
return Status.fromCode(cs.getString(columnIndex));
}
}
要点总结
- TypeHandler 接口定义了 4 个核心方法,覆盖参数设置和结果读取两个方向
BaseTypeHandler封装了 null 值处理逻辑,自定义时继承它只需实现非空方法- MyBatis 内置了基本类型、日期、枚举、JSON 等数十种 TypeHandler
- 注册方式有两种:XML 配置注册和注解
@MappedTypes+@MappedJdbcTypes注册 - TypeHandler 注册具有优先级,后注册的覆盖先注册的,字段级别优先于全局级别
- 自定义 TypeHandler 的核心是
setNonNullParameter(写)和getNullableResult(读)
📝 发现内容有误?点击此处直接编辑