枚举字段映射
枚举字段映射是将数据库中的枚举字段(字符串或整数)映射为 Java 枚举类型的过程。MyBatis 提供了三种层级的配置方式:全局注册、Mapper XML 字段级指定和注解配置。
数据库枚举字段的两种存储方式
字符串存储(VARCHAR)
| id | status | created_at |
|---|---|---|
| 1 | PENDING | 2026-05-01 10:00:00 |
| 2 | APPROVED | 2026-05-02 14:30:00 |
| 3 | REJECTED | 2026-05-03 09:15:00 |
对应的 Java 枚举:
Java
public enum ApprovalStatus {
PENDING, APPROVED, REJECTED
}
整数存储(INT/TINYINT)
| id | status | created_at |
|---|---|---|
| 1 | 0 | 2026-05-01 10:00:00 |
| 2 | 1 | 2026-05-02 14:30:00 |
| 3 | 2 | 2026-05-03 09:15:00 |
对应同一个枚举,但使用 EnumOrdinalTypeHandler 处理。
方式一:Mapper XML 字段级指定(推荐)
在 resultMap 中为特定字段指定 TypeHandler:
XML
<resultMap id="approvalResultMap" type="Approval">
<id column="id" property="id"/>
<result column="status" property="status"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
<result column="created_at" property="createdAt"/>
</resultMap>
<select id="selectById" resultMap="approvalResultMap">
SELECT * FROM approval WHERE id = #{id}
</select>
这种方式可以为单个字段覆盖全局配置,非常灵活。
优先级规则:resultMap 中字段级的
typeHandler配置优先级最高,参数级次之,mybatis-config.xml 全局注册最低。
方式二:mybatis-config.xml 全局注册
XML
<configuration>
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.example.ApprovalStatus"/>
</typeHandlers>
</configuration>
注册后,所有涉及 ApprovalStatus 的字段都会自动使用 EnumOrdinalTypeHandler,无需在每个 Mapper XML 中重复指定。
方式三:注解配置
Java
@MappedTypes({ApprovalStatus.class})
@MappedJdbcTypes({JdbcType.INTEGER})
public class ApprovalStatusHandler extends BaseTypeHandler<ApprovalStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
ApprovalStatus status, JdbcType jdbcType) throws SQLException {
ps.setInt(i, status.ordinal());
}
@Override
public ApprovalStatus getNullableResult(ResultSet rs,
String columnName) throws SQLException {
int ordinal = rs.getInt(columnName);
return rs.wasNull() ? null : ApprovalStatus.values()[ordinal];
}
@Override
public ApprovalStatus getNullableResult(ResultSet rs,
int columnIndex) throws SQLException {
int ordinal = rs.getInt(columnIndex);
return rs.wasNull() ? null : ApprovalStatus.values()[ordinal];
}
@Override
public ApprovalStatus getNullableResult(CallableStatement cs,
int columnIndex) throws SQLException {
int ordinal = cs.getInt(columnIndex);
return cs.wasNull() ? null : ApprovalStatus.values()[ordinal];
}
}
三种配置方式对比
| 方式 | 作用范围 | 优先级 | 适用场景 |
|---|---|---|---|
| resultMap 字段级指定 | 单个字段 | 最高 | 少数特殊字段需要单独处理 |
| mybatis-config.xml 全局注册 | 全项目 | 最低 | 所有同类型枚举使用统一策略 |
| 注解 @MappedTypes | 全项目 | 中等 | 推荐,配合包扫描自动注册 |
null 值处理
数据库中的枚举字段可能为 null,TypeHandler 必须正确处理:
Java
@Override
public ApprovalStatus getNullableResult(ResultSet rs,
String columnName) throws SQLException {
int ordinal = rs.getInt(columnName);
if (rs.wasNull()) {
return null; // 数据库字段为 null 时返回 Java null
}
// ordinal 可能超出枚举范围,需要容错
ApprovalStatus[] values = ApprovalStatus.values();
if (ordinal < 0 || ordinal >= values.length) {
return null; // 越界视为无效数据
}
return values[ordinal];
}
注意:继承
BaseTypeHandler时,getNullableResult方法只在非 null 情况下被调用。但如果使用ResultSet.getInt()读取整数,null 值会返回 0(Java int 默认值),需要通过rs.wasNull()判断。
自定义 code 值映射
当数据库使用自定义 code 值而非枚举名称或索引时:
Java
public enum PaymentMethod {
ALIPAY("alipay", "支付宝"),
WECHAT("wechat", "微信支付"),
BANK_CARD("bankcard", "银行卡");
private final String code;
private final String label;
PaymentMethod(String code, String label) {
this.code = code;
this.label = label;
}
public String getCode() { return code; }
public static PaymentMethod fromCode(String code) {
if (code == null) return null;
for (PaymentMethod method : values()) {
if (method.code.equals(code)) {
return method;
}
}
return null; // 未知 code 返回 null
}
}
对应的 TypeHandler:
Java
@MappedTypes({PaymentMethod.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class PaymentMethodHandler extends BaseTypeHandler<PaymentMethod> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
PaymentMethod method, JdbcType jdbcType) throws SQLException {
ps.setString(i, method.getCode());
}
@Override
public PaymentMethod getNullableResult(ResultSet rs,
String columnName) throws SQLException {
return PaymentMethod.fromCode(rs.getString(columnName));
}
@Override
public PaymentMethod getNullableResult(ResultSet rs,
int columnIndex) throws SQLException {
return PaymentMethod.fromCode(rs.getString(columnIndex));
}
@Override
public PaymentMethod getNullableResult(CallableStatement cs,
int columnIndex) throws SQLException {
return PaymentMethod.fromCode(cs.getString(columnIndex));
}
}
INSERT/UPDATE 中的枚举参数
在 INSERT 或 UPDATE 语句中设置枚举参数:
XML
<!-- 方式一:使用字段级 typeHandler -->
<insert id="insertApproval">
INSERT INTO approval (status, created_at)
VALUES (#{status, typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler},
#{createdAt})
</insert>
<!-- 方式二:全局注册后直接使用 -->
<insert id="insertApproval">
INSERT INTO approval (status, created_at)
VALUES (#{status}, #{createdAt})
</insert>
要点总结
- 数据库枚举字段可存储为字符串(VARCHAR)或整数(INT),分别对应
EnumTypeHandler和EnumOrdinalTypeHandler - resultMap 中字段级的
typeHandler配置优先级最高,允许单个字段覆盖全局配置 - 全局注册通过
mybatis-config.xml或注解@MappedTypes实现,推荐注解配合包扫描 - null 值处理需要特别注意:
ResultSet.getInt()的 null 返回 0,必须用wasNull()判断 - 自定义 code 值映射需要在枚举中实现
fromCode()反查方法和 TypeHandler 的双向转换 - TypeHandler 配置遵循"就近原则",字段级 > 参数级 > 全局级
📝 发现内容有误?点击此处直接编辑