MyBatis 枚举映射的实现示例

 更新时间:2025年06月25日 10:52:46   作者:比特森林探险记  
本文主要介绍了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.MybatisEnumTypeHandler

2. 字段注解映射

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枚举方案]

​企业级应用建议​​:

  1. 选择​​自定义编码映射​​作为默认方案
  2. 对性能敏感的常量枚举使用​​序数映射​
  3. 需要多语言支持的使用​​关联表映射​
  4. 组合状态使用​​BIT存储+解码方案​

遵循这些模式,可构建出健壮且易维护的枚举持久化层,完美平衡数据库高效存储与业务代码的可读性需求。

到此这篇关于MyBatis 枚举映射的实现示例的文章就介绍到这了,更多相关MyBatis 枚举映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • Kotlin委托机制使用方式和原理解析

    Kotlin委托机制使用方式和原理解析

    委托类(代理类)持有真实类的对象,然后委托类(代理类)调用真实类的同名方法,最终真正实现的是方法的是真实类,这其实就是代理模式,下面通过本文给大家介绍Kotlin委托机制使用方式和原理解析,感兴趣的朋友一起看看吧
    2025-06-06
  • Java CompletableFuture实现多线程异步编排

    Java CompletableFuture实现多线程异步编排

    这篇文章主要为大家介绍了Java CompletableFuture实现多线程异步编排,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • JAVA利用泛型返回类型不同的对象方法

    JAVA利用泛型返回类型不同的对象方法

    下面小编就为大家带来一篇JAVA利用泛型返回类型不同的对象方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Java中的Semaphore如何使用

    Java中的Semaphore如何使用

    Semaphore实际上是一种共享锁,因为它允许多个线程并发获取共享的资源,在Semaphore对象创建时必须设置可用令牌的初始数量permits,用于控制并发时同时获取资源权限的线程数量,这篇文章主要介绍了Java中的Semaphore如何使用,需要的朋友可以参考下
    2022-06-06
  • java中http请求之restTemplate配置超时时间问题解决

    java中http请求之restTemplate配置超时时间问题解决

    这篇文章主要介绍了java中http请求之restTemplate配置超时时间,本文给大家分享三种解决方法,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • 浅谈idea live template高级知识_进阶(给方法,类,js方法添加注释)

    浅谈idea live template高级知识_进阶(给方法,类,js方法添加注释)

    下面小编就为大家带来一篇浅谈idea live template高级知识_进阶(给方法,类,js方法添加注释)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Spring Retry重试框架的使用讲解

    Spring Retry重试框架的使用讲解

    重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有,感兴趣的可以了解一下
    2023-01-01
  • Java 中synchronize函数的实例详解

    Java 中synchronize函数的实例详解

    这篇文章主要介绍了Java 中synchronize函数的实例详解的相关资料,希望通过本文能帮助到大家理解使用synchronize函数的使用方法,需要的朋友可以参考下
    2017-09-09
  • Java Swing中的表格(JTable)和树(JTree)组件使用实例

    Java Swing中的表格(JTable)和树(JTree)组件使用实例

    这篇文章主要介绍了Java Swing中的表格(JTable)和树(JTree)组件使用实例,本文同时讲解了表格和树的基本概念、常用方法、代码实例,需要的朋友可以参考下
    2014-10-10
  • MyEclipse如何将项目的开发环境与服务器的JDK 版本保持一致

    MyEclipse如何将项目的开发环境与服务器的JDK 版本保持一致

    我们使用MyEclipse开发Java项目开发中,偶尔会遇到因项目开发环境不协调,导致这样那样的问题,在这里以把所有环境调整为JDK1.6 为例,给大家详细介绍MyEclipse如何将项目的开发环境与服务器的JDK 版本保持一致,需要的朋友参考下吧
    2024-04-04

最新评论