mybatis 中枚举类本身不直接实现 typehandler,而是需单独编写 typehandler 类负责双向转换;枚举应显式定义码值字段(如 code)和静态反查方法(如 of(int)),typehandler 通过继承 basetypehandler 实现泛型复用,重写 setnonnullparameter 和 getnullableresult 方法完成 java 枚举与数据库整型字段的智能无感转换。

MyBatis 中枚举类本身不直接“实现” TypeHandler,而是需要**单独编写一个 TypeHandler 类**,专门负责该枚举与数据库字段(如 INT、VARCHAR)之间的双向转换。枚举类只需提供清晰的码值约定和查找方法,为 TypeHandler 提供支撑。
枚举类要配合 TypeHandler 做好两件事
枚举不是处理器,但它是 TypeHandler 的“数据契约”。为了让自定义 TypeHandler 写得简洁、安全、可复用,枚举应具备以下结构:
-
显式存储数据库值:用私有字段(如
code或value)保存整型或字符串状态码,避免依赖ordinal()(序号易变)或name()(大小写/命名变更风险高) -
提供静态反查方法:如
public static OrderStatus of(int code)或fromCode(int code),TypeHandler 在读取数据库结果时会调用它;该方法需处理非法码值(建议抛IllegalArgumentException或返回默认值) -
可选:实现统一接口(如
IEnum),方便泛型 TypeHandler 统一约束,例如:public interface IEnum { int getCode(); },再让所有状态枚举实现它
写一个通用的枚举 TypeHandler 类
继承 BaseTypeHandler<e extends enum>></e>,用泛型支持任意符合规范的枚举。核心是重写三个方法:
-
setNonNullParameter:把枚举对象转成数据库值,调用parameter.getCode()写入PreparedStatement -
getNullableResult(ResultSet, String):从列名读整数,用枚举的of(code)查找;注意先判断rs.wasNull(),避免空值报错 -
getNullableResult(ResultSet, int):同上,只是按列索引读取,逻辑一致
示例节选(以 OrderStatus 为例):
public class EnumCodeTypeHandler<e extends enum> & IEnum> 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 : findEnumByCode(code);
}
private E findEnumByCode(int code) {
for (E e : type.getEnumConstants()) {
if (e.getCode() == code) return e;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
}</e></e></e></e>
注册 TypeHandler 让它生效
写完处理器后必须注册,MyBatis 才知道“遇到这个枚举该用谁来转”。常用方式有三种:
-
全局包扫描注册:在
application.yml中配置mybatis.type-handlers-package: com.example.handler,MyBatis 会自动扫描该包下带@MappedTypes注解的类 -
XML 显式注册:在
mybatis-config.xml中添加<typehandlers><typehandler handler="com.example.handler.EnumCodeTypeHandler" javatype="com.example.enums.OrderStatus"></typehandler></typehandlers> -
字段级指定(最细粒度):在
@Select或 XML 的<resultmap></resultmap>中写明<result property="status" column="status_code" javatype="com.example.enums.OrderStatus" typehandler="com.example.handler.EnumCodeTypeHandler"></result>
验证是否真正起作用
注册完成后,无需改 DAO 层代码,只要实体类字段类型是该枚举,MyBatis 就会在插入时自动调用 setNonNullParameter,查询时自动调用 getNullableResult。你可以:
- 开启 MyBatis 日志(
logging.level.org.apache.ibatis=DEBUG),观察 SQL 参数绑定和结果映射过程 - 故意传一个数据库中不存在的码值(如 status_code = 999),看是否抛出你定义的异常,确认反查逻辑生效
- 检查查询结果中枚举字段是否非 null 且值正确,而非变成
UNKNOWN或null











