MyBatis 枚举存整形字段报错是因为默认不支持枚举与整型的双向转换,需自定义基于码值(非 ordinal)的 TypeHandler 并正确注册,且必须使用 #{} 触发转换。

MyBatis 枚举存整形字段时为什么报错 TypeException: Could not set parameters for mapping
因为 MyBatis 默认不识别 Java 枚举到数据库 TINYINT/SMALLINT 的双向转换,直接传枚举对象会触发类型不匹配。不是配置漏了,是根本没注册转换逻辑。
常见错误现象:org.apache.ibatis.type.TypeException、Cannot convert value of type 'XxxEnum' to required type 'java.lang.Integer',尤其在 <insert> 或 <update> 里用 #{status} 绑定枚举时高频出现。
- 必须自定义
TypeHandler,不能只靠@EnumValue(它只对字符串生效) - 数据库字段类型要是整型(如
TINYINT),不是VARCHAR;否则别走这条路 - 枚举类需明确定义序号或自有码值(推荐用自有码值,避免
ordinal()调整顺序导致数据错乱)
怎么写一个安全的 EnumOrdinalTypeHandler 替代品
别直接继承 EnumOrdinalTypeHandler —— 它硬编码用 ordinal(),一旦枚举加删成员,历史数据全乱。应该基于枚举自身的码值字段做映射。
假设枚举长这样:
public enum OrderStatus {
CREATED(1),
PAID(2),
SHIPPED(3),
CANCELLED(-1);
private final int code;
OrderStatus(int code) { this.code = code; }
public int getCode() { return code; }
public static OrderStatus fromCode(int code) {
for (OrderStatus s : values()) {
if (s.code == code) return s;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
}
对应的 TypeHandler 关键点:
- 实现
setParameter(PreparedStatement, int, E, JdbcType):调用enum.getCode()写入整数 - 实现
getResult(ResultSet, String):用enum.fromCode(rs.getInt())反查 - 务必重写
getNullableResult(ResultSet, int)处理NULL值,否则空值会抛NullPointerException - 不要在 handler 里做异常吞并,
fromCode找不到时该抛就抛,方便定位脏数据
怎么在 MyBatis 中注册这个 TypeHandler
注册方式决定作用域和维护成本。优先选细粒度控制,别一股脑扫包。
- 单个字段绑定:在
<resultMap>里写typeHandler="com.example.OrderStatusTypeHandler" - 全局默认(慎用):在
mybatis-config.xml里加<typeHandlers><typeHandler handler="com.example.OrderStatusTypeHandler" javaType="com.example.OrderStatus"/></typeHandlers> - 注解方式(Mapper 接口方法参数):在参数前加
@Param("status") @TypeHandler(OrderStatusTypeHandler.class) OrderStatus status - 如果用了 MyBatis-Plus,注意它有自己的枚举处理机制(
IEnum),和原生 TypeHandler 不兼容,二者别混用
为什么 #{status} 和 ${status} 在枚举场景下结果完全不同
${} 是字符串拼接,#{} 才走 TypeHandler。新手常在这里翻车。
-
WHERE status = ${status}→ 拼出WHERE status = CREATED,数据库报错或误查(除非字段是字符串型) -
WHERE status = #{status}→ 触发 TypeHandler,实际执行WHERE status = ?,预编译设参为2 - 所有涉及枚举的动态 SQL 参数,必须用
#{},${}是反模式 - 连
<if test="status != null">这种判断都安全,MyBatis 对枚举的==和equals判空有内建支持
最易被忽略的是:枚举类里没写 fromCode 的健壮版本(比如没处理 NULL 或非法码值),线上一遇到脏数据就静默失败或返回 null,查半天发现是 handler 里少了个 if (rs.wasNull()) return null;。










