枚举处理:默认枚举处理器与自定义
Java 枚举在业务里很常见:状态、类型字段,数据库通常存数字或短字符串。MyBatis 默认怎么处理枚举?为什么有人存着存着值就"错位"了?本节讲默认枚举处理器、两个陷阱与稳妥的自定义方案。
默认枚举处理器
不配置任何东西时,MyBatis 内置两种枚举 TypeHandler:
| 处理器 | 与数据库的映射 | 隐患 |
|---|---|---|
| EnumTypeHandler(默认) | 存枚举的 name(),如 ACTIVE | 枚举改名即历史数据不兼容 |
| EnumOrdinalTypeHandler | 存枚举的 ordinal() 下标 | 调整枚举顺序/插值就串数据 |
存字符串默认走 EnumTypeHandler;想让数字列用 ordinal,需要显式指定。正因为 ordinal 太脆弱,官方也不推荐依赖它(行为以官方文档为准)。
默认行为的两个陷阱
- name() 即数据库值:
Status.ACTIVE存成字符串"ACTIVE";列若是 TINYINT 会类型不匹配,或依赖隐式转换出问题。 - 重排即事故:枚举一旦调整顺序或中间插入新值,旧数据的 ordinal 全部错位——这就是"枚举编号要对号入座"说法的来源。
自定义:显式编号最稳妥
让枚举自带编号 code,配合专属 TypeHandler。先定义枚举:
public enum Status {
ACTIVE(1), DISABLED(0);
private final int code;
Status(int code) { this.code = code; }
public int getCode() { return code; }
public static Status of(int code) {
for (Status s : values()) {
if (s.code == code) return s;
}
throw new IllegalArgumentException("未知状态: " + code);
}
}
XML 中声明处理器后,全局所有 Status 字段自动转换,无需每条 SQL 标注:
<typeHandlers>
<typeHandler handler="com.demo.handler.EnumCodeTypeHandler"
javaType="com.demo.enums.Status"/>
</typeHandlers>
EnumCodeTypeHandler 继承 BaseTypeHandler<Status>:写入调 ps.setInt(i, parameter.getCode()),读取调 Status.of(rs.getInt(columnName)) 返回(参考 TypeHandler 一章的写法)。
MyBatis-Plus 的简化
MyBatis-Plus 提供 @EnumValue 注解,在枚举值上标注数据库存储值,实体字段自动完成转换(需配置类型枚举扫描,以官方文档为准):
public enum Status {
@EnumValue // 数据库存 1 / 0
ACTIVE(1), DISABLED(0);
// 构造器与 getCode() 同前
}
思路与"显式编号 + 专属处理器"一致,注解只是省掉了样板代码。
核心原则只有一条:枚举与数据库的对应关系必须显式且稳定。依赖 name() 或序号都是隐患,最稳的是 code 编号加专属 TypeHandler,改枚举时只新增、不重排。