枚举处理:默认枚举处理器与自定义

Java 枚举在业务里很常见:状态、类型字段,数据库通常存数字或短字符串。MyBatis 默认怎么处理枚举?为什么有人存着存着值就"错位"了?本节讲默认枚举处理器、两个陷阱与稳妥的自定义方案。

默认枚举处理器

不配置任何东西时,MyBatis 内置两种枚举 TypeHandler:

处理器与数据库的映射隐患
EnumTypeHandler(默认)存枚举的 name(),如 ACTIVE枚举改名即历史数据不兼容
EnumOrdinalTypeHandler存枚举的 ordinal() 下标调整枚举顺序/插值就串数据

存字符串默认走 EnumTypeHandler;想让数字列用 ordinal,需要显式指定。正因为 ordinal 太脆弱,官方也不推荐依赖它(行为以官方文档为准)。

默认行为的两个陷阱

  1. name() 即数据库值Status.ACTIVE 存成字符串 "ACTIVE";列若是 TINYINT 会类型不匹配,或依赖隐式转换出问题。
  2. 重排即事故:枚举一旦调整顺序或中间插入新值,旧数据的 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,改枚举时只新增、不重排。

笔记加载中…