typehandler 是 mybatis 中实现 java 枚举与数据库整型字段双向转换的核心桥梁;通过自定义泛型 enumcodetypehandler,结合枚举的 getcode() 和 of(int) 方法,支持全局或局部注册,确保类型安全与复用性。

理解 TypeHandler 的核心作用
MyBatis 的 TypeHandler 是连接 Java 类型与 JDBC 类型的桥梁。当数据库字段是整型(如 TINYINT、INT),而 Java 侧使用枚举(如 OrderStatus)时,MyBatis 默认无法自动识别转换逻辑。自定义 TypeHandler 就是告诉 MyBatis:“遇到这个枚举,读写时请按我指定的规则转成/解析为整数”。
编写枚举类并约定值映射
推荐在枚举中显式定义数据库存储的整数值,并提供静态查找方法,便于 TypeHandler 复用:
public enum OrderStatus {
CREATED(1),
PAID(2),
SHIPPED(3),
COMPLETED(4),
CANCELLED(5);
private final int code;
OrderStatus(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static OrderStatus of(int code) {
for (OrderStatus status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("Unknown order status code: " + code);
}
}
实现泛型安全的枚举 TypeHandler
继承 BaseTypeHandler<e extends enum>></e> 可复用通用逻辑,避免为每个枚举重复写 handler:
- 重写
setNonNullParameter():将枚举对象的code写入 PreparedStatement - 重写
getNullableResult():从 ResultSet 中读取整数,用枚举的of()方法反查 - 确保空值处理健壮(如返回
null或默认枚举项)
示例实现:
public class EnumCodeTypeHandler<e extends enum> & CodeEnum> extends BaseTypeHandler<e> {
private final Class<e> type;
public EnumCodeTypeHandler(Class<e> type) {
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return rs.wasNull() ? null : getCodeEnum(code);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return rs.wasNull() ? null : getCodeEnum(code);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return cs.wasNull() ? null : getCodeEnum(code);
}
private E getCodeEnum(int code) {
try {
Method method = type.getMethod("of", int.class);
return (E) method.invoke(null, code);
} catch (Exception e) {
throw new RuntimeException("Failed to resolve enum " + type.getSimpleName() + " for code " + code, e);
}
}
}</e></e></e></e>
其中 CodeEnum 是一个标记接口(含 int getCode() 方法),让枚举统一契约,提升类型安全。
注册与使用方式
有两种主流注册方式,按需选择:
-
全局注册:在 MyBatis 配置文件或
Configuration中注册,对所有匹配字段生效 -
局部注册:在
@Results或 XML 的<result></result>中通过typeHandler属性指定,精准控制字段级行为
XML 示例(局部):
<result column="status" property="status" typehandler="com.example.EnumCodeTypeHandler<com.example.OrderStatus>"></result>
注解示例(配合 @Select):
@Results({
@Result(property = "status", column = "status", typeHandler = EnumCodeTypeHandler.class)
})
若用全局注册,需在 mybatis-config.xml 中配置:
<typehandlers><typehandler handler="com.example.EnumCodeTypeHandler" javatype="com.example.OrderStatus" jdbctype="INTEGER"></typehandler></typehandlers>
注意事项与避坑点
实际使用中容易忽略的关键细节:
- 确保枚举的
of()方法能处理非法码值(如数据库脏数据),否则查询会抛异常 - MyBatis 3.4+ 支持泛型 TypeHandler,但 Spring Boot 整合时注意版本兼容性(建议 3.4.6+)
- XML 中引用泛型 TypeHandler 时,不能直接写带泛型的全限定名(JVM 泛型擦除),应改用非泛型基类或通过别名注册
- 若枚举无参构造或静态方法命名不一致,反射调用会失败——建议统一用
of(int)命名
不复杂但容易忽略。只要枚举有清晰的码值契约,再配一个可复用的 TypeHandler,就能彻底告别 DAO 层手动 switch 转换。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











