枚举类型处理
Java 枚举与数据库字段的映射是日常开发中的高频场景。MyBatis 提供了两种内置枚举处理器,同时支持自定义 TypeHandler 实现更灵活的映射策略。
内置枚举处理器
EnumTypeHandler(默认)
MyBatis 默认使用 EnumTypeHandler,它使用枚举的 name() 方法获取枚举名称进行映射:
Java
public enum OrderStatus {
PENDING, // name = "PENDING"
PAID, // name = "PAID"
SHIPPED, // name = "SHIPPED"
COMPLETED // name = "COMPLETED"
}
数据库存储的是字符串:
| id | status |
|---|---|
| 1 | PENDING |
| 2 | PAID |
| 3 | SHIPPED |
无需额外配置即可使用,因为它是默认策略:
XML
<!-- 无需配置,MyBatis 自动处理 -->
<resultMap id="orderResultMap" type="Order">
<result column="status" property="status"/>
</resultMap>
EnumOrdinalTypeHandler
使用枚举的 ordinal() 方法(从 0 开始的索引值)进行映射:
Java
public enum OrderStatus {
PENDING, // ordinal = 0
PAID, // ordinal = 1
SHIPPED, // ordinal = 2
COMPLETED // ordinal = 3
}
数据库存储的是整数:
| id | status |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
需要显式注册:
XML
<!-- 方式一:mybatis-config.xml 全局注册 -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.example.OrderStatus"/>
</typeHandlers>
或在 Mapper XML 中为特定字段指定:
XML
<!-- 方式二:字段级别指定 -->
<resultMap id="orderResultMap" type="Order">
<result column="status" property="status"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</resultMap>
两种内置处理器对比
| 特性 | EnumTypeHandler | EnumOrdinalTypeHandler |
|---|---|---|
| 默认启用 | 是 | 否,需手动注册 |
| 存储值 | 枚举名称(字符串) | 枚举索引(整数) |
| 数据库字段类型 | VARCHAR/CHAR | INT/TINYINT |
| 可读性 | 好,直接看名称 | 差,需要查枚举定义 |
| 枚举顺序变更影响 | 无影响 | 有影响,索引值会变 |
| 存储空间 | 较大(字符串) | 较小(整数) |
警告:使用
EnumOrdinalTypeHandler时,如果修改了枚举项的顺序或增删了枚举项,会导致已有数据索引错乱。生产环境推荐优先使用EnumTypeHandler或自定义 TypeHandler。
自定义枚举 TypeHandler(code 值映射)
当数据库使用自定义 code 值(既不是 name 也不是 ordinal)时,需要自定义 TypeHandler:
Java
public enum OrderStatus {
PENDING("0", "待处理"),
PAID("1", "已支付"),
SHIPPED("2", "已发货"),
COMPLETED("3", "已完成");
private final String code;
private final String label;
OrderStatus(String code, String label) {
this.code = code;
this.label = label;
}
public String getCode() { return code; }
public String getLabel() { return label; }
// 根据 code 反查枚举
public static OrderStatus fromCode(String code) {
for (OrderStatus status : values()) {
if (status.code.equals(code)) {
return status;
}
}
return null;
}
}
编写 TypeHandler:
Java
@MappedTypes({OrderStatus.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class OrderStatusTypeHandler extends BaseTypeHandler<OrderStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
OrderStatus status, JdbcType jdbcType) throws SQLException {
ps.setString(i, status.getCode());
}
@Override
public OrderStatus getNullableResult(ResultSet rs,
String columnName) throws SQLException {
return OrderStatus.fromCode(rs.getString(columnName));
}
@Override
public OrderStatus getNullableResult(ResultSet rs,
int columnIndex) throws SQLException {
return OrderStatus.fromCode(rs.getString(columnIndex));
}
@Override
public OrderStatus getNullableResult(CallableStatement cs,
int columnIndex) throws SQLException {
return OrderStatus.fromCode(cs.getString(columnIndex));
}
}
数据库存储自定义 code:
| id | status |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
注册自定义枚举 TypeHandler
方式一:mybatis-config.xml
XML
<typeHandlers>
<typeHandler handler="com.example.handler.OrderStatusTypeHandler"/>
</typeHandlers>
方式二:包扫描(推荐)
XML
<typeHandlers>
<package name="com.example.handler"/>
</typeHandlers>
配合注解使用,无需 XML 配置:
Java
@MappedTypes({OrderStatus.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class OrderStatusTypeHandler extends BaseTypeHandler<OrderStatus> {
// ...
}
方式三:Mapper XML 字段级指定
XML
<resultMap id="orderResultMap" type="Order">
<result column="status" property="status"
typeHandler="com.example.handler.OrderStatusTypeHandler"/>
</resultMap>
要点总结
EnumTypeHandler是默认策略,使用枚举名称(字符串)进行映射EnumOrdinalTypeHandler使用枚举索引(整数)进行映射,需要显式注册- 枚举顺序变更时,
EnumOrdinalTypeHandler会导致数据索引错乱,生产环境慎用 - 数据库使用自定义 code 值时,需编写自定义 TypeHandler 实现
setNonNullParameter(写)和getNullableResult(读) - 自定义枚举 TypeHandler 推荐继承
BaseTypeHandler减少样板代码 - 注册方式支持 XML 配置、注解
@MappedTypes、包扫描和 XML 字段级指定四种 - 字段级
typeHandler配置优先级高于全局注册
📝 发现内容有误?点击此处直接编辑