源本科技 | 码上会

枚举自动映射

2026/04/02
0
0

前言

MyBatis 原生仅提供2 种基础枚举处理器,无法满足业务中存储枚举编码 / 自定义值的核心需求:

  1. EnumOrdinalTypeHandler:基于枚举 ordinal()(定义顺序:0、1、2...)映射,枚举顺序变更会导致数据错乱

  2. EnumTypeHandler:基于枚举 name()(常量名称:PRIMARY、SECONDARY)映射,存储冗余、可读性差。

MyBatis-Plus 最优解决方案

提供 MybatisEnumTypeHandler(基于枚举自定义属性自动映射),核心规则:

  1. 标记过的枚举 → 自动使用该处理器,映射自定义属性值;

  2. 未标记的枚举 → 沿用 MyBatis 全局默认枚举处理器(默认 EnumTypeHandler);

  3. 全局配置仅对未标记枚举生效,不影响已标记枚举。


枚举标记方式

2 选 1,零配置自动映射

MyBatis-Plus 提供 注解接口 两种标记方式,指定数据库存储的枚举值。

方式1

@EnumValue 注解(推荐,极简)

作用:直接标记枚举字段为 数据库存储值,支持 Integer/String/Byte 任意类型; 核心注解com.baomidou.mybatisplus.annotation.EnumValue

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum GradeEnum {
    PRIMARY(1, "小学"),
    SECONDARY(2, "中学"), // 修正原拼写错误:SECONDORY
    HIGH(3, "高中");

    // 标记:数据库仅存储该字段的值
    @EnumValue
    private final Integer code;
    // 业务描述(仅业务使用,不入库)
    private final String desc;
}

方式2

实现 IEnum 接口(通用适配)

作用:实现 MP 标准接口,重写方法指定数据库存储值,适配框架集成场景; 核心接口com.baomidou.mybatisplus.annotation.IEnum

import com.baomidou.mybatisplus.annotation.IEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
// 泛型:指定数据库存储值的类型
public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");

    private final Integer value;
    private final String desc;

    // 重写方法:返回数据库存储的值
    @Override
    public Integer getValue() {
        return this.value;
    }
}

实体类使用枚举

直接声明枚举类型,无需手动配置 typeHandler,MP 自动完成映射:

import lombok.Data;

@Data
public class User {
    private String name;     // 姓名
    private AgeEnum age;     // 年龄(接口标记枚举)
    private GradeEnum grade; // 年级(注解标记枚举)
}

未标记枚举

全局默认处理器配置

对于 未加注解 / 未实现接口 的枚举,可自定义全局默认处理器,不影响已标记枚举

YML 配置

mybatis-plus:
  configuration:
    # 全局默认枚举处理器(默认:EnumTypeHandler)
    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

Java 配置类

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
        return properties -> {
            var coreConfig = new com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties.CoreConfiguration();
            // 设置全局默认枚举处理器
            coreConfig.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);
            properties.setConfiguration(coreConfig);
            // 关闭 MP 启动 banner(可选)
            properties.getGlobalConfig().setBanner(false);
        };
    }
}

核心原理

自动映射机制

MyBatis-Plus 会自动扫描项目中标记的枚举,自动注册 MybatisEnumTypeHandler,你无需:

  1. resultMap 中手动指定 typeHandler

  2. 在实体字段上加 @TableField(typeHandler = xxx.class)

  3. 手动注册类型处理器。

真正实现 零配置、自动映射


前后端枚举序列化

数据库存储枚举编码,接口需返回编码而非枚举名称,适配主流序列化框架。

最佳实践

双注解共用(统一数据库 + 接口值)

一个字段同时标记 数据库存储接口返回,极简无冗余:

@EnumValue    // 数据库存 code
@JsonValue    // Jackson 序列化返回 code
private final Integer code;

方案 1:Jackson

无需全局配置,仅用 @JsonValue 即可实现 序列化 + 反序列化(前端传 code → 后端自动转枚举)。

备选:全局 toString 配置

import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    }
}

配合枚举重写 toString()

@Override
public String toString() {
    return String.valueOf(this.code);
}

方案 2:Fastjson2

引入依赖

<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.46+</version>
</dependency>

全局配置

import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

@Configuration
public class Fastjson2Config {
    @Bean
    public HttpMessageConverter<?> fastJsonConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        converter.getFastJsonConfig().setWriterFeatures(JSONWriter.Feature.WriteEnumUsingToString);
        return converter;
    }
}

代码示例

Mapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {}

测试用例

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class EnumTest {
    @Autowired
    private UserMapper userMapper;

    // 插入:枚举自动转为 code 存入数据库
    @Test
    void insert() {
        User user = new User();
        user.setName("张三");
        user.setAge(AgeEnum.TWO);   // 入库:2
        user.setGrade(GradeEnum.PRIMARY); // 入库:1
        userMapper.insert(user);
    }

    // 查询:数据库 code 自动转为枚举对象
    @Test
    void select() {
        User user = userMapper.selectById(1);
        System.out.println(user.getAge());   // AgeEnum.TWO
        System.out.println(user.getGrade()); // GradeEnum.PRIMARY
    }
}