场景:

MySQL里的某一个字段,比如:status状态,一共有5个状态,我们会在MySQL里 建立 status(int)字段,1、2、3、4、5 来标记5种状态;利用MyBatis在自动代码生成器生成实体类后,status会是integer类型;

而status 只有5种状态,且程序很多地方常用,于是我们建立status枚举,返回给终端的json里,也是返回枚举字符串,只是存MySQL里用int来存,节省空间,提升查询效率;好了多余的废话不说了,直接上代码:

 

1. 建立一个通用的枚举接口:

/**
 * MySQL字段对应的枚举类需要实现这个接口
 */
public interface ISqlEnum {

    /**
     * 获取对应的MySQL里的值
     */
    Integer getSqlInt();
}

 

2.定义一个枚举,实现枚举接口:

/**
 * 平台类型,以后尽量使用这个来取代ShopType,枚举类;
 * ShopType会等待合适的时机变更为:平台内部店铺类型如:专营店、自营店、旗舰店等
 */
public enum PlatformType implements ISqlEnum {

    OFFLINE("线下", 9),

    /**
     * 淘宝/天猫
     */
    TAOBAO("淘宝/天猫", 1),


    /**
     * 京东
     */
    JINGDONG("京东", 2),


    /**
     * 拼多多
     */
    PINDUODUO("拼多多", 3),


    /**
     * 抖音
     */
    DOUYIN("抖音", 4),


    /**
     * 微信视频号
     */
    WEIXIN("微信视频号", 5),


    /**
     * 阿里1688
     */
    ALI1688("阿里1688", 6),


    /**
     * 快手
     */
    KUAISHOU("快手", 7),


    /**
     * 苏宁
     */
    SUNING("苏宁", 8);


    /**
     * 线下
     */



    /**
     * 店铺类型的英文名称就是枚举的小写,中文名称存储一下,方便直接返回给客户端;
     */
    private String cnName;


    /**
     * MySQL里的int值
     * 平台类型:1(淘宝),2(京东),3(拼多多),4(抖音),5(微信视频号),6(阿里1688),7(快手),8(苏宁),9(线下)
     */
    private Integer sqlInt;


    /**
     * 私有构造方法
     */
    private PlatformType(String cnName, Integer sqlInt) {
        this.cnName = cnName;
        this.sqlInt = sqlInt;
    }


    /**
     * 对外公开获取中文名称的方法
     */
    public String getCnName(){
        return this.cnName;
    }

    @Override
    public Integer getSqlInt() {
        return this.sqlInt;
    }

}

 

3. 定义一个类型处理器:类型处理器(typeHandlers),根据mybatis官方的文档,它默认已经有了2个枚举类型处理器:https://mybatis.net.cn/configuration.html#typeHandlers

 

A. EnumTypeHandler 这个官方默认的,通过官方的说明,源码得出结论:就是说 你MySQL里 若这个status字段是varchar类型,你向MySQL里存的是枚举的字符串的话,举例:PlatformType.TAOBAO,

你向MySQL里 存的是 TAOBAO 这个字符串的话,那么你可以直接在实体类里使用PlatformType枚举,而不用再做任何操作,因为TAOBAO这个字符串是可以直接与枚举类互转的;

B. EnumOrdinalTypeHandler 这个官方默认的,意思是:若你向MySQL里存的是枚举类的序号值,MySQL里是int 或兼容int的类型,也是不用做任何操作,在实体类里就可以使用 这个枚举;

    因为序号也是可以直接与枚举互转的;

官方能做的也就A,B这2个了,其它的官方的也无能为力,A 首先不合适,正如官网所说,我们的DBA更倾向于枚举字段,在MySQL里用 int 类型来存储,节省空间,提升查询效率;

接下来说下B,B虽然MySQL里存的是int了,但是也太简陋了,只能按枚举的序号来,那么就意味着枚举类里的顺序 TAOBAO,JINGDONG,PDD你不能改变,一旦变成DOUYIN,TAOBAO,JINGDONG,PDD

则会出大乱子了!所以B还是简陋了,适合刚设计时就永远不会发生变化的枚举类;

细想你会发现官方能做的也只能这样了,因为枚举类是你自由定义的,要想 MySQL -- 枚举类,枚举类 --> Mysql;官方能做的就是 通过默认的字符串和 序号来转化,其它的转化是你自己自定义的,官方又如何能知道呢;

 

根据官方的提示,我们参考 官方默认的 两个枚举处理器,自定义一个我们自己的泛型枚举处理器;

 

public class SqlEnumHandler<E extends SqlEnum> extends BaseTypeHandler<E> {

    /**
     * 把枚举实例存入map,而不是官方的数组,因为数组查找的时候需要遍历,
     * 官方使用数组是因为官方按枚举序号来的,即使使用数组也不需要遍历,
     * 而我们不一定,sqlInt我们是自定义的,不是枚举实例的序号;
     */
    private final Map<Integer, E> enumMap;


    /**
     * 官方的框架会自动调用这个方法,来构建枚举实例map
     */
    public SqlEnumHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        //拿到类型实例,写入map
        enumMap = new HashMap<>();
        for (E enumConstant : type.getEnumConstants()) {
            enumMap.put(enumConstant.getSqlInt(), enumConstant);
        }
    }


    /**
     * 1.若MySQL里的字段设置了不为Null,而你传入的枚举是Null,那么应该如何写入MySQL;(这个由你写代码决定,就是本方法)
     * 2.若MySQL里的字段设置了可以为Null,而你传入的枚举也是Null,这种官方自动处理了,自动写入Null,不需要你写额外的代码;
     * --------------------------------------------------------------
     * 我们强制规定,若MySQL里一个字段是枚举,则这个字段【必须】设置成不为Null;
     * 因为MySQL层面的问题,要在MySQL层面解决,举例:status都已经是一个枚举字段了,一共就5种状态,
     * 是定死的,你非要搞成可以null,这就是你自己的脑袋有问题;我们这里不考虑Null的问题;
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getSqlInt());
    }


    /**
     * 1.若MySQL里字段设置了可以为Null,当查询出来的结果为Null,你要转成哪个枚举实例;(需要你写代码来决定,就是本方法)
     * 2.若MySQL里字段设置了可以为Null,查询出来的结果不为Null,这种的也需要你写代码来决定,就是本方法;
     * -----------------------------
     * 我们强制MySQL里的枚举字段不可以为Null,所以不考虑Null的问题,以下都是同理;
     */
    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return enumMap.get(rs.getInt(columnName));
    }


    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return enumMap.get(rs.getInt(columnIndex));
    }


    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return enumMap.get(cs.getInt(columnIndex));
    }
}

 

使用接口的话,也行,我们使用的是接口:

/**
 * mybatis枚举类型处理器
 */
public class SqlEnumHandler<E extends SqlEnum> implements TypeHandler<E> {

    /**
     * 把枚举实例存入Map
     */
    private final Map<Integer, E> enumMap;


    /**
     * 根据枚举类型构建Map
     */
    public SqlEnumHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        //拿到类型实例,写入map
        enumMap = new HashMap<>();
        for (E enumConstant : type.getEnumConstants()) {
            enumMap.put(enumConstant.getSqlInt(), enumConstant);
        }
    }


    /**
     * Java枚举 ---写入---> MySQL Int
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getSqlInt());
    }


    /**
     * MySQL Int ---读取---> Java枚举
     * 强制规定:此MySQL字段必须设置成不为Null,因为MySQL层面的问题,要在MySQL层面解决,
     * 举例:status都已经是一个枚举字段了,一共就5种状态,是定死的,若MySQL里允许为Null,
     * 这就是开发者有问题,需要去修复MySQL,而不是在这里处理Null;这里不考虑Null的问题;
     */
    @Override
    public E getResult(ResultSet rs, String columnName) throws SQLException {
        return enumMap.get(rs.getInt(columnName));
    }

    @Override
    public E getResult(ResultSet rs, int columnIndex) throws SQLException {
        return enumMap.get(rs.getInt(columnIndex));
    }

    @Override
    public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return enumMap.get(cs.getInt(columnIndex));
    }
}

4. spring boot 的 application.properties 加上配置,配置一下枚举类型处理器,你会发现spring boot 会有这个配置,原因是为什么呢,肯定是mybatis官方也知道他提供的那2个枚举类型处理器,弱爆了,

    而这个处理器,只能由用户自己来编写代码,所以他有一个配置来让你指定;

#你指定一下你自己写的那个SqlEnumHandler类就行了;
mybatis.configuration.default-enum-type-handler

 

================================

以上4步搞定后,就可以了,测试:

 

 

posted on 2023-09-03 10:50  del88  阅读(224)  评论(0编辑  收藏  举报