MyBatis 枚举映射的实现示例
作者:比特森林探险记
本文主要介绍了MyBatis 枚举映射的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一、MyBatis 枚举映射四大实现方案
1. 基础序数映射(EnumTypeHandler)
// Java 枚举类
public enum OrderStatus {
NEW, PAID, DELIVERED, CANCELLED;
}
// MyBatis实体类映射
public class Order {
private OrderStatus status;
// getters/setters
}Mapper XML 配置:
<resultMap id="orderResultMap" type="Order">
<result property="status" column="status"/>
</resultMap>MySQL 表设计:
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
status TINYINT COMMENT '0-新订单,1-已支付,2-已发货,3-已取消'
);优缺点:
- ✅ 零配置自动生效
- ⚠️ 无法修改枚举顺序
- ⚠️ 数据库可读性差
2. 枚举名称映射(EnumOrdinalTypeHandler)
// MyBatis 配置文件
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
javaType="com.example.OrderStatus"/>
</typeHandlers>
// 或全局配置
<settings>
<setting name="defaultEnumTypeHandler"
value="org.apache.ibatis.type.EnumTypeHandler"/>
</settings>表设计:
ALTER TABLE orders MODIFY COLUMN status VARCHAR(20) COMMENT '订单状态';
适配枚举改动:
public enum OrderStatus {
NEW("新订单"),
PAID("已支付"),
DELIVERED("已发货"),
CANCELLED("已取消");
private final String desc;
OrderStatus(String desc) {
this.desc = desc;
}
}3. 自定义类型处理器(TypeHandler)
3.1 基于字符串的转换
@MappedTypes(OrderStatus.class)
public class OrderStatusHandler extends BaseTypeHandler<OrderStatus> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
OrderStatus status, JdbcType jdbcType) {
ps.setString(i, status.name());
}
@Override
public OrderStatus getNullableResult(ResultSet rs, String columnName) {
String code = rs.getString(columnName);
return OrderStatus.valueOf(code);
}
// 其他getNullableResult方法...
}3.2 基于编码的转换(推荐)
@MappedTypes(UserType.class)
public class UserTypeHandler extends BaseTypeHandler<UserType> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
UserType userType, JdbcType jdbcType) {
ps.setString(i, userType.getCode());
}
@Override
public UserType getNullableResult(ResultSet rs, String columnName) {
String code = rs.getString(columnName);
return UserType.fromCode(code);
}
}注册处理器:
<typeHandlers>
<typeHandler handler="com.example.handler.UserTypeHandler"/>
</typeHandlers>实体类使用:
public class User {
@TableField(typeHandler = UserTypeHandler.class)
private UserType type;
}4. 枚举值关联表方案
CREATE TABLE user_types (
id TINYINT PRIMARY KEY AUTO_INCREMENT,
code CHAR(2) UNIQUE NOT NULL,
name VARCHAR(20) NOT NULL
);
INSERT INTO user_types (code, name) VALUES
('A', '管理员'),
('E', '编辑'),
('U', '普通用户');Mapper XML 查询:
<resultMap id="userResultMap" type="User">
<result property="id" column="id"/>
<association property="type" column="type_code"
select="selectUserTypeByCode"/>
</resultMap>
<select id="selectUserTypeByCode" resultType="UserType">
SELECT code, name AS description
FROM user_types
WHERE code = #[code]
</select>二、MyBatis-Plus 高级枚举映射
1. 内置枚举处理器
public enum ProductStatus implements IEnum<Integer> {
DRAFT(0), PUBLISHED(1), ARCHIVED(2);
private final int value;
ProductStatus(int value) {
this.value = value;
}
@Override
public Integer getValue() {
return this.value;
}
}
// 实体类使用
public class Product {
private ProductStatus status;
}全局配置(application.yml):
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler2. 字段注解映射
public class Product {
@TableField(value = "status",
typeHandler = ProductStatusHandler.class)
private ProductStatus status;
}3. JSON格式存储枚举属性
public class Order {
@TableField(typeHandler = JsonTypeHandler.class)
private Map<OrderStatus, Integer> statusStatistics;
}JSON存储结构:
{
"NEW": 10,
"PAID": 5,
"DELIVERED": 3
}三、枚举映射最佳实践方案
1. 防御式枚举设计
public enum OrderStatus {
NEW("N"), PAID("P"), DELIVERED("D"), CANCELLED("C");
private final String code;
private static final Map<String, OrderStatus> CODE_MAP = new HashMap<>();
static {
for (OrderStatus status : values()) {
CODE_MAP.put(status.code, status);
}
}
public static OrderStatus fromCode(String code) {
OrderStatus status = CODE_MAP.get(code);
if (status == null) {
if (code == null) return null;
String cleanCode = code.trim().toUpperCase();
return Optional.ofNullable(CODE_MAP.get(cleanCode))
.orElseThrow(() -> new IllegalArgumentException(
"无效状态码: " + code));
}
return status;
}
}2. 枚举基类设计
public interface EnumCode {
String getCode();
String getDescription();
}
public abstract class BaseEnumHandler<E extends Enum<E> & EnumCode>
extends BaseTypeHandler<E> {
private final Class<E> type;
private final Map<String, E> enumMap;
public BaseEnumHandler(Class<E> type) {
this.type = type;
this.enumMap = Arrays.stream(type.getEnumConstants())
.collect(Collectors.toMap(EnumCode::getCode, e -> e));
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
E parameter, JdbcType jdbcType) {
ps.setString(i, parameter.getCode());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) {
String code = rs.getString(columnName);
return code == null ? null : enumMap.get(code);
}
}四、实战应用场景
1. 多状态组合(BIT存储)
public enum Permission {
READ(1), WRITE(2), DELETE(4), EXECUTE(8);
private final int bitValue;
// 存储所有权限值到单个整数
public static int encode(Set<Permission> permissions) {
return permissions.stream()
.mapToInt(p -> p.bitValue)
.sum();
}
public static Set<Permission> decode(int value) {
return Arrays.stream(values())
.filter(p -> (value & p.bitValue) != 0)
.collect(Collectors.toSet());
}
}
// MyBatis类型处理器
public class PermissionSetHandler extends BaseTypeHandler<Set<Permission>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Set<Permission> perms, JdbcType jdbcType) {
ps.setInt(i, Permission.encode(perms));
}
@Override
public Set<Permission> getNullableResult(ResultSet rs, String columnName) {
int value = rs.getInt(columnName);
return rs.wasNull() ? Collections.emptySet() : Permission.decode(value);
}
}2. 动态状态机验证
public class OrderService {
@Transactional
public void updateOrderStatus(Long orderId, OrderStatus newStatus) {
Order order = orderMapper.selectById(orderId);
OrderStatus oldStatus = order.getStatus();
if (!oldStatus.isAllowedTransition(newStatus)) {
throw new IllegalStateException("状态转换非法: " +
oldStatus + " -> " + newStatus);
}
order.setStatus(newStatus);
orderMapper.updateById(order);
}
}
// 枚举中定义状态转移规则
public enum OrderStatus {
NEW {
@Override
public boolean isAllowedTransition(OrderStatus newStatus) {
return newStatus == PAID || newStatus == CANCELLED;
}
},
// 其他状态定义...
}五、性能优化技巧
静态映射缓存:
public abstract class CachedEnumHandler<E extends Enum<E> & EnumCode>
extends BaseTypeHandler<E> {
private final Map<String, E> codeEnumMap;
private final Map<E, String> enumCodeMap;
public CachedEnumHandler(Class<E> enumClass) {
E[] enums = enumClass.getEnumConstants();
codeEnumMap = Arrays.stream(enums)
.collect(Collectors.toMap(EnumCode::getCode, e -> e));
enumCodeMap = Arrays.stream(enums)
.collect(Collectors.toMap(e -> e, EnumCode::getCode));
}
}批量处理优化:
@Mapper
public interface BatchMapper {
@Insert("<script>" +
"INSERT INTO users (type, name) VALUES " +
"<foreach item='item' collection='list' separator=','>" +
"(#{item.type, typeHandler=com.example.UserTypeHandler}, #{item.name})" +
"</foreach>" +
"</script>")
int batchInsertUsers(@Param("list") List<User> users);
}数据库约束优化:
-- MySQL ENUM 类型约束
status ENUM('NEW','PAID','DELIVERED','CANCELLED') NOT NULL DEFAULT 'NEW'
COMMENT '订单状态'
-- PostgreSQL检查约束
ALTER TABLE orders
ADD CONSTRAINT status_check
CHECK (status IN ('NEW', 'PAID', 'DELIVERED', 'CANCELLED'));六、错误解决方案手册
1. 序列化问题修复
// 添加枚举序列化配置
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum OrderStatus implements EnumCode {
// ...
}
// 或自定义序列化器
public class EnumCodeSerializer extends StdSerializer<EnumCode> {
public EnumCodeSerializer() {
super(EnumCode.class);
}
@Override
public void serialize(EnumCode value, JsonGenerator gen,
SerializerProvider provider) {
gen.writeStartObject();
gen.writeStringField("code", value.getCode());
gen.writeStringField("description", value.getDescription());
gen.writeEndObject();
}
}2. 国际化方案
public interface LocalizedEnum {
String getCode();
default String getMessage(Locale locale) {
ResourceBundle bundle = ResourceBundle.getBundle(
"enum_messages", locale);
return bundle.getString(this.getClass().getSimpleName() + "." + getCode());
}
}
// 多语言资源文件
// en_US.properties
UserType.A=Administrator
UserType.E=Editor
UserType.U=User
// zh_CN.properties
UserType.A=管理员
UserType.E=编辑
UserType.U=普通用户总结:MyBatis枚举映射决策树
graph TD
A[需要映射枚举] --> B{是否简单状态值?}
B --> |是| C{是否确定永不修改顺序?}
C --> |是| D[使用EnumTypeHandler默认序数映射]
C --> |否| E[使用EnumOrdinalTypeHandler名称映射]
B --> |否| F{是否需要业务编码?}
F --> |是| G[自定义TypeHandler+编码设计]
F --> |否| H{是否多语言/复杂属性?}
H --> |是| I[关联表映射方案]
H --> |否| J[直接使用MyBatis-Plus枚举方案]企业级应用建议:
- 选择自定义编码映射作为默认方案
- 对性能敏感的常量枚举使用序数映射
- 需要多语言支持的使用关联表映射
- 组合状态使用BIT存储+解码方案
遵循这些模式,可构建出健壮且易维护的枚举持久化层,完美平衡数据库高效存储与业务代码的可读性需求。
到此这篇关于MyBatis 枚举映射的实现示例的文章就介绍到这了,更多相关MyBatis 枚举映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
