mybatis 中 enumtypehandler 支持枚举与数据库字段双向映射:默认按 name 映射(适用于 varchar 字段),整数字段需自定义 typehandler 按 code 映射;推荐枚举显式定义 code 并实现 ofcode 方法,避免依赖易变的 ordinal()。

在 MyBatis 中,使用 EnumTypeHandler 可以让数据库中的状态字段(如整数或字符串)与 Java 枚举类型自动相互转换,实现状态枚举的双向映射——即查询时把数据库值转成枚举,插入/更新时把枚举转成对应值。
明确枚举设计与数据库字段类型匹配
MyBatis 默认的 EnumTypeHandler(即 org.apache.ibatis.type.EnumTypeHandler)只支持按枚举的 name()(字符串)映射,适用于数据库字段为 VARCHAR 类型、存的是枚举名(如 "PENDING")。若字段是整数(如 tinyint),需自定义处理器,通过 ordinal() 或自定义属性(如 code 字段)映射。
推荐做法:在枚举中显式定义业务码(code),避免依赖 ordinal()(易因增删枚举项而错乱):
public enum OrderStatus {
CREATED(0, "待创建"),
PAID(1, "已支付"),
SHIPPED(2, "已发货"),
COMPLETED(3, "已完成");
private final int code;
private final String desc;
OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
// 提供根据 code 查找枚举的静态方法(后续自定义处理器会用到)
public static OrderStatus ofCode(int code) {
for (OrderStatus status : values()) {
if (status.code == code) return status;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
public int getCode() { return code; }
}
方式一:使用内置 EnumTypeHandler(仅限 name 映射)
如果数据库字段是字符串类型,且存的是枚举名(如 'PAID'),可直接启用 MyBatis 默认枚举处理器:
- 确保
mybatis-config.xml或配置类中开启枚举自动注册(MyBatis 3.4+ 默认开启); - Mapper XML 中字段类型写全限定名即可:
<result column="status" property="status" javatype="com.example.OrderStatus"></result>
MyBatis 会自动调用 Enum.valueOf(Class, String) 将 "PAID" 转为 OrderStatus.PAID;写入时自动调用 enum.name()。
方式二:自定义枚举处理器(推荐,支持 code 映射)
针对整数状态码,需实现 TypeHandler<orderstatus></orderstatus>:
public class OrderStatusTypeHandler implements TypeHandler<orderstatus> {
@Override
public void setParameter(PreparedStatement ps, int i, OrderStatus parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.INTEGER);
} else {
ps.setInt(i, parameter.getCode());
}
}
@Override
public OrderStatus getResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return rs.wasNull() ? null : OrderStatus.ofCode(code);
}
@Override
public OrderStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return rs.wasNull() ? null : OrderStatus.ofCode(code);
}
@Override
public OrderStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return cs.wasNull() ? null : OrderStatus.ofCode(code);
}
}</orderstatus>
然后在 MyBatis 配置中注册该处理器(全局或局部):
- 全局注册(
mybatis-config.xml):
<typehandlers><typehandler handler="com.example.OrderStatusTypeHandler" javatype="com.example.OrderStatus"></typehandler></typehandlers>
- 或在 Mapper XML 的
<resultmap></resultmap>中单独指定:
<result column="status" property="status" javatype="com.example.OrderStatus" typehandler="com.example.OrderStatusTypeHandler"></result>
验证与注意事项
双向映射是否生效,可通过以下方式确认:
- 插入数据时,检查 SQL 日志中参数是否为预期整数(如
1); - 查询结果中,实体的
status字段是否为正确枚举实例(非null且.getCode()匹配); - 若使用 MyBatis-Plus,其
EnumAnnotationTypeHandler支持@EnumValue注解,原理类似,但需配合注解使用; - 务必在自定义
TypeHandler的getResult方法中处理wasNull(),防止空指针; - 多个枚举共用逻辑?可抽象出泛型基类(如
BaseEnumTypeHandler<e extends enum> & CodeEnum></e>)。
不复杂但容易忽略的是:数据库字段类型、枚举属性、TypeHandler 实现三者必须严格对齐。选对映射策略(name 还是 code),再配好处理器,状态枚举就能干净地融入整个持久层。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











