如何处理枚举类型(上)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

我们会分为上、下两篇分别介绍DAO及Controller层如何处理枚举。重点不是枚举本身,而是希望帮大家开阔眼界,实际开发手动转换枚举也未尝不可。

不了解枚举的同学请先去阅读小册中与枚举相关的其他章节。另外,本文会用到反射及注解相关知识,不熟悉的同学请戳:

反射

注解

这一篇先介绍DAO中枚举相关的处理。

强调一下,这里我直接使用原生MyBatis,而不是通用Mapper或MyBatis-Plus,意在说明MyBatis本身有注意到枚举转换的问题并预留了接口。

环境准备

SQL(注意,这里rest_day是故意使用varchar的,后面会解释)

CREATE TABLE `t_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) DEFAULT '' COMMENT '姓名',
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
  `rest_day` varchar(20) DEFAULT '' COMMENT '休息日',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

pom.xml

<dependencies>
	<!--SpringBoot Web,下篇会用到-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!--MyBatis依赖-->
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>2.1.3</version>
	</dependency>
	<!--MySQL驱动-->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
	<!--Lombok-->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	<!--测试-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
		<exclusions>
			<exclusion>
				<groupId>org.junit.vintage</groupId>
				<artifactId>junit-vintage-engine</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
</dependencies>

application.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: on

logging:
  level:
    com.example.dao: debug

启动类

/**
 * @author mx
 */
@MapperScan("com.example.dao")
@SpringBootApplication
public class MybatisEnumDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(MybatisEnumDemoApplication.class, args);
	}

}

DO

/**
 * @author mx
 * @date 2023-11-25 09:56
 */
@Data
public class UserDO {
    /**
     * 主键id
     */
    private Long id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 休息日,实际数据库字段是tinyint或varchar
     */
    private WeekDayEnum restDay;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 修改时间
     */
    private Date updateTime;

    /**
     * 是否删除
     */
    private Boolean deleted;
}

WeekDayEnum

/**
 * @author mx
 */
@Getter
public enum WeekDayEnum {
    MONDAY(1,"星期一"),
    TUESDAY(2,"星期二"),
    WEDNESDAY(3,"星期三"),
    THURSDAY(4,"星期四"),
    FRIDAY(5,"星期五"),
    SATURDAY(6,"星期六"),
    SUNDAY(7,"星期日");

    WeekDayEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    private final Integer code;
    private final String desc;
}

UserMapper.java

/**
 * @author mx
 */
public interface UserMapper {

    /**
     * 插入用户
     *
     * @param userDO
     */
    void insertUser(UserDO userDO);

    /**
     * 根据id查询
     * @param id
     * @return
     */
    UserDO selectUserById(@Param("id") Long id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserMapper">
    <resultMap id="BaseResultMap" type="com.example.entity.UserDO">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="age" jdbcType="TINYINT" property="age"/>
        <result column="rest_day" jdbcType="VARCHAR" property="restDay"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
        <result column="deleted" jdbcType="BIT" property="deleted"/>
    </resultMap>

    <insert id="insertUser">
        INSERT INTO t_user (`name`, age, rest_day)
        VALUES(#{name}, #{age}, #{restDay})
    </insert>

    <!-- VALUES(#{name, jdbcType=VARCHAR}, #{age, jdbcType=INTEGER}, #{restDay, jdbcType=VARCHAR})-->

    <select id="selectUserById" resultType="com.example.entity.UserDO">
        SELECT * FROM t_user WHERE id=#{id}
    </select>
</mapper>
@SpringBootTest
class MybatisEnumTest {

	@Autowired
	private UserMapper userMapper;

	@Test
	public void testInsert() {
		UserDO userDO = new UserDO();
		userDO.setName("MyBatis枚举测试");
		userDO.setAge(18);
		userDO.setRestDay(WeekDayEnum.FRIDAY);

		userMapper.insertUser(userDO);
	}

	@Test
	public void testSelect() {
		UserDO userDO = userMapper.selectUserById(1L);
		System.out.println(userDO);
	}

}

插入测试:

查询测试:

至此,我们完成了最简单的环境搭建。

但你们应该会发现一个神奇的现象:

  • 存入时:private WeekDayEnum restDay(内存) --> MyBatis --> "FRIDAY"(数据库)
  • 查询时:private WeekDayEnum restDay(内存) <-- MyBatis <-- "FRIDAY"(数据库)

在Java和数据库之间,MyBatis承担了中间人的角色,存入时会自动将枚举对象转为字符串,而取出时又把字符串转为枚举对象。

怎么做到的呢?

枚举的存入

我们发现,MyBatis默认对枚举的处理是将枚举的名称插入数据库,而枚举的名称其实就是Enum.name,定义在父类Enum中:

那么MyBatis是在哪里调用WeekDayEnum的name()方法进行转换的呢?

注意截图中这个类的名字:EnumTypeHandler。

MyBatis提供了两个枚举转换器,EnumTypeHandler是其中之一,另一个是EnumOrdinalTypeHandler:

通过源码很容易看出两者的区别

  • EnumTypeHandler:取枚举的name作为值插入数据库
  • EnumOrdinalTypeHandler:取枚举的ordinal作为值插入数据库

name和ordinal被定义在Enum抽象类中,而所有枚举类实际上都会继承Enum,所以每一个枚举对象都有name和ordinal。

MyBatis默认的枚举转换器是EnumTypeHandler,所以开头的SQL我故意把rest_day设置为varchar类型,刚好接收被EnumTypeHandler转换后的枚举字符串。

如果我们改为tinyint,就会报错:

DROP TABLE IF	EXISTS t_user;
CREATE TABLE `t_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) DEFAULT '' COMMENT '姓名',
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
  `rest_day` tinyint(1) DEFAULT 1 COMMENT '休息日',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

重新执行测试案例:

如果数据库rest_day使用的是tinyint类型,需要将MyBatis的默认枚举转换器切换为EnumOrdinalTypeHandler:

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: on
    # 显式声明Mybatis枚举转换器,默认是EnumTypeHandler
    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

给EnumOrdinalTypeHandler打上断点,再次测试插入:

注意,数据库存的是ordinal,而不是code,Enum的ordinal从0开始,所以4代表FRIDAY

查询:

为什么会打印"FRIDAY"呢?

此时UserDO中的restDay确实是WeekDayEnum对象,打印一个对象通常会调用它的toString(),而Enum父类重写了toString(),实际返回Enum.name,所以打印一个枚举对象最终输出的是Enum.name。

小结:

  • MyBatis默认存入时会使用EnumTypeHandler枚举类型进行转换,调用Enum.name()存入枚举名称
  • 如果希望存入ordinal,可以切换默认枚举转换器为EnumOrdinalTypeHandler

枚举的取出

接着,我们来观察一下数据库中rest_day字段的"FRIDAY"和4被查出来以后如何变成枚举对象FRIDAY的。

同样的,肯定还是EnumTypeHandler和EnumOrdinalTypeHandler帮我们转换的。由于刚才数据库的rest_day已经被我们改为tinyint,所以我们通过EnumOrdinalTypeHandler观察取出时的操作:

好了好了,我知道了,你别说了。我就想知道enums哪来的?

往上看:

在项目启动时MyBatis会初始化EnumTypeHandler,调用构造器时会对private final E[] enums属性赋值。然而神奇的是,type此时enumConstants为null,但从最终结果来看enumConstants是有值的,所以getEnumConstants()内部必然发生了什么。

跟踪进去会发现:

我们之前在设计山寨枚举及反编译枚举时介绍过values()方法和VALUES数组了:

OK,至此EnumOrdinalTypeHandler介绍完了。我们顺便看看EnumTypeHandler:

哦?底层调用了父类Enum的valueOf()方法,根据枚举名称获取枚举实例:

又会跳到Class类的方法中,而且这个方法上面见过了:

T[] universe就是T[] values,而且准备了一个名为m的Map,把枚举的名称作为key,枚举对象本身作为value,把枚举存了起来(你看,又是实用小算法)。

最终Enum.valueOf()其实就是传入枚举名称,然后从Map中得到对应的枚举实例:

所以数据库的"FRIDAY"会被转为FRIDAY对象。

对MyBatis默认提供的枚举转换器的介绍就到这里了。

但不论EnumTypeHandler还是EnumOrdinalTypeHandler,其实都不好用。实际开发中,我们往往使用的不是ordinal或name,而是自己定义的枚举字段,比如code、desc。

默认的两个枚举转换器,一个针对name,另一个针对ordinal,这两个字段属于抽象父类Enum,会在初始化时赋值,而子类特有的code和desc却没用到。

简单版枚举转换器

核心思想是,照着EnumOrdinalTypeHandler抄,搞一个山寨的,然后让MyBatis用我们的Handler转换。

自定义枚举转换器分3步:

  • 编写枚举转换类,实现MyBatis提供的TypeHandler接口
  • 指定type-handlers-package,告诉MyBatis在哪里可以找到我们自定义的转换器
  • 在转换器上用@MappedTypes({WeekDayEnum.class})指定用来处理哪个枚举

TypeHandler是个接口,啥都没有,白手起家太难了:

MyBatis另外提供了BaseTypeHandler让我们继承,EnumOrdinalTypeHandler也是这么干的:

要想自定义枚举转换器,最快的办法是“抄袭”EnumOrdinalTypeHandler:

再把里面的内容全部拷过来:

为了方便确认最终起作用的是我们自定义的MyEnumTypeHandler,稍作修改:

如果最终插入的值会在原来的基础上加100,就说明走了我们自定义的转换器。

然后加上@MappedTypes({WeekDayEnum.class})注解指定处理WeekDayEnum:

最后告诉MyBatis我们自定义的转换器包路径:

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: on
    # 显式声明Mybatis默认枚举转换器(默认EnumTypeHandler)
    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
  # 指定自定义的枚举转换器路径
  type-handlers-package: com.example.handlers

传入的是4,经过我们的Handler后实际插入104,说明自定义转换器成功了!但别高兴得太早,上面仅仅是拷贝EnumOrdinalTypeHandler,最终插入的还是ordinal,并不是自定义的枚举字段code。

什么是JdbcType

在正式改代码之前,我们先来解决一个疑惑:JdbcType是什么?

不论是EnumTypeHandler还是EnumOrdinalTypeHandler,setNonNullParameter()的参数列表都有JdbcType:

而且EnumTypeHandler的setNonNullParameter()内部还对JdbcTye做了判断。

所以,什么是JdbcType呢?

MyBatis在org.apache.ibatis.type包下定义了一个JdbcType枚举,用来定义数据库的字段类型,与JdbcType对应的还有JavaType。

如果你跟着上面的代码做了实验,会发现不论怎么修改UserDO,type始终是null:

JdbcType是在SQL中规定的,而不是UserDO中。

其实,只要大家仔细回想,就会发现以前在写SQL语句时好像会指定JdbcType:

<insert id="insertUser">
    INSERT INTO t_user (`name`, age, rest_day)
    VALUES(#{name, jdbcType=VARCHAR}, #{age, jdbcType=INTEGER}, #{restDay, jdbcType=INTEGER})
</insert>

如果不指定,MyBatis会自己判断。

现在我把restDay设为jdbcType=INTEGER,再次启动程序就会发现:

注意,这里的4可不是FRIDAY,而是INTEGER:

意思是把Object类型转为INTEGER插入。特别注意,由于上面ps.setObject()方法中传入的是parameter.name(),也就是Enum.name(),所以实际传入的String类型的"FRIDAY"。

当然,此时会报错(实际参数是String,你偏要转为Integer存入):

Cause: java.sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException - For input string: "FRIDAY"

在追踪源码的过程中,有一点很不解:枚举类型最终被归为DECIMAL_UNSIGNED...

但本文不是研究MyBatis源码的,就此打住。总之,必须在MyEnumTypeHandler中对WeekDayEnum进行转换。

注解+反射实现枚举自动类型转换

由于这是demo,且平常我都不写JdbcType,所以我们不考虑type!=null的情况:

/**
 * 标记需要转换的枚举字段
 *
 * @author mx
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumValue {
}
@Getter
public enum WeekDayEnum {
    MONDAY(1,"星期一"),
    TUESDAY(2,"星期二"),
    WEDNESDAY(3,"星期三"),
    THURSDAY(4,"星期四"),
    FRIDAY(5,"星期五"),
    SATURDAY(6,"星期六"),
    SUNDAY(7,"星期日");

    WeekDayEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // 标记最终把code作为枚举的值插入数据库
    @EnumValue
    private final Integer code;
    private final String desc;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    if (jdbcType == null) {
        // 获取WeekDayEnum的所有字段并循环,找到带有@EnumValue注解的字段
        Field[] declaredFields = type.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            // 是否有@EnumValue注解
            EnumValue enumValue = declaredField.getAnnotation(EnumValue.class);
            if (enumValue != null) {
                Object fieldValue = null;
                try {
                    // 反射获取标记了@EnumValue注解的字段的value
                    declaredField.setAccessible(true);
                    fieldValue = declaredField.get(parameter);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                // 设置值
                ps.setObject(i, fieldValue);
                return;
            }
        }
    } else {
		// 不考虑jdbcType!=null的情况
        ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);
    }
}

大家自己测试,一般来说是没问题的:

==>  Preparing: INSERT INTO t_user (`name`, age, rest_day) VALUES(?, ?, ?)

==> Parameters: MyBatis枚举测试(String), 18(Integer), 5(Integer)

<==    Updates: 1

注意,存入的是WeekDayEnum.FRIDAY,而数据库显示5,说明这次不是ordinal,而是code。

当然,你也可以把数据库rest_day字段改回VARCHAR,然后把@EnumValue注解加在private String desc上。

还是不要高兴得太早,我们来测一下查询:

是的,MyEnumTypeHandler在取出时会把5当做ordinal解析,所以最终得到的是SATURDAY。

为什么呢?因为当初照抄EnumOrdinalTypeHandler,我们只改了存入的逻辑。

取出的逻辑就不详细说了,大家复制过去看看即可:

@MappedTypes({WeekDayEnum.class})
public class MyEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

    private final Class<E> type;
    private final E[] enums;

    public MyEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null) {
            throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
            // 获取WeekDayEnum的所有字段并循环,找到带有@EnumValue注解的字段
            Field[] declaredFields = type.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 是否有@EnumValue注解
                EnumValue enumValue = declaredField.getAnnotation(EnumValue.class);
                if (enumValue != null) {
                    Object fieldValue = null;
                    try {
                        // 反射获取标记了@EnumValue注解的字段的value
                        declaredField.setAccessible(true);
                        fieldValue = declaredField.get(parameter);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    // 设置值
                    ps.setObject(i, fieldValue);
                    return;
                }
            }
        } else {
            // 不考虑jdbcType!=null的情况
            ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 定义一个变量,接收从数据库查出的rest_day
        Object valueFromDB = null;

        // 确定当初存入时指定了哪个字段
        Field enumValueField = null;
        Field[] declaredFields = type.getDeclaredFields();
        for (Field field : declaredFields) {
            // 是否有@EnumValue注解
            EnumValue enumValue = field.getAnnotation(EnumValue.class);
            if (enumValue != null) {
                // 找到带有@EnumValue的字段
                enumValueField = field;
                // 数据库返回了ResultSet,也即是查询结果集,我们可以从中获取restDay的值
                valueFromDB = rs.getObject(columnName, enumValueField.getType());
                break;
            }
        }
        
        if (enumValueField == null) {
            // 如果没有标注@EnumValue,还是按默认的解析返回
            return getResultByOrdinal(rs, columnName);
        }
        
        // 遍历WeekDayEnum的所有实例,反射获取每个实例中标注了@EnumValue的字段值并比较
        enumValueField.setAccessible(true);
        for (E weekday : enums) {
            Object value = null;
            try {
                value = enumValueField.get(weekday);
                if (valueFromDB.equals(value)) {
                    // 值相等,返回对于的枚举对象
                    return weekday;
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private E getResultByOrdinal(ResultSet rs, String columnName) throws SQLException {
        int ordinal = rs.getInt(columnName);
        if (ordinal == 0 && rs.wasNull()) {
            return null;
        }
        return toOrdinalEnum(ordinal);
    }

    private E toOrdinalEnum(int ordinal) {
        try {
            return enums[ordinal];
        } catch (Exception ex) {
            throw new IllegalArgumentException("Cannot convert " + ordinal + " to " + type.getSimpleName() + " by ordinal value.", ex);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) {
        return null;
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) {
        return null;
    }

}

MyBatis-Plus对枚举的处理

有时候就是这么巧,万万没想到MyBatis-Plus的处理方式和我们惊人地相似。

3.1.0开始,如果你无需使用原生枚举,可配置默认枚举来省略扫描通用枚举配置

  • 升级说明:
    3.1.0 以下版本改变了原生默认行为,升级时请将默认枚举设置为EnumOrdinalTypeHandler
  • 影响用户:
    实体中使用原生枚举
  • 其他说明:
    配置枚举包扫描的时候能提前注册使用注解枚举的缓存

声明通用枚举属性

方式一: 使用 @EnumValue 注解枚举属性

public enum GradeEnum {
    PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");
    GradeEnum(int code, String descp) {
        this.code = code;
        this.descp = descp;
    }
    @EnumValue//标记数据库存的值是code
    private final int code;
    //。。。
}

方式二: 枚举属性,实现 IEnum 接口如下:

public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");
    
    private int value;
    private String desc;
    
    @Override
    public Integer getValue() {
        return this.value;
    }
}

实体属性使用枚举类型

public class User {
    /**
     * 名字
     * 数据库字段: name varchar(20)
     */
    private String name;
    
    /**
     * 年龄,IEnum接口的枚举处理
     * 数据库字段:age INT(3)
     */
    private AgeEnum age;
        
        
    /**
     * 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
     * 数据库字段:grade INT(2)
     */
    private GradeEnum grade;
}

配置扫描通用枚举

mybatis-plus:
    # 支持统配符 * 或者 ; 分割
    typeEnumsPackage: com.baomidou.springboot.entity.enums
  ....

没想到,我的构思被MyBatis-Plus抄袭了。

一些说明

其实数据库有一种枚举字段类型,大家可以了解下,本文并没有介绍,个人不建议使用。

另外,MyEnumTypeHandler还有很多不足,最大的不足就是仍然不够通用。

假设现在系统新增InvitationStatusEnum:

DROP TABLE IF	EXISTS t_user;
CREATE TABLE `t_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(50) DEFAULT '' COMMENT '姓名',
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
  `rest_day` tinyint(1) DEFAULT 1 COMMENT '休息日',
	`invitation_status` varchar(50) DEFAULT '' COMMENT '面试状态',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
@Getter
public enum InvitationStatusEnum {

    WAIT_FOR_DEAL("wait_for_deal", "等待处理"),
    SUITABLE("suitable", "合适"),
    ;

    @EnumValue
    private final String value;
    private final String desc;

    InvitationStatusEnum(String value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

你会发现又不行了。除非在@MappedTypes的属性中另外指定InvitationStatusEnum.class:

也就是说,作为通用组件的MyEnumTypeHandler还是无法避免被反复修改,不如MyBatis-Plus来得优雅。有兴趣的同学可以自行研究(意义不大)。

 

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值