MyBatis/MyBatis-Plus 使用枚举参数异常, SpringBoot枚举参数异常

视频地址: https://www.bilibili.com/video/BV1kf4y1i761?p=15


在开发中,有很多字段使用枚举类型可以更好地表达我们想要的效果。但在实际使用过程中,却存在两个问题

  1. 枚举参数映射到数据库的时候类型匹配不上
  2. 接收枚举参数的时候也会存在结果不对的情况

一、前提


1.1、StatusEnum

比如我们有一个这样的枚举

import lombok.Getter;
import lombok.ToString;

/**
 * 状态枚举
 */
@Getter
@ToString
public enum StatusEnum {

    ENABLE(1, "启用"),
    DISABLE(0,"禁用")
    ;

    private Integer code;
    private String name;
    
    StatusEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
}

1-2、实体

定义一个这样的实体

public class Menu {

    /**
     * id
     */
    @TableId
    private Integer id;

    /**
     * 菜单名称
     */
    @NotBlank(message = "菜单名称不能为空")
    private String name;

    /**
     * 启用状态
     */
    private StatusEnum status;
}

1-3、使用

其它的参数封装,mapper注入不重要

menuMapper.insert(menu);

1.4、异常

运行上面的代码会报错

### The error occurred while setting parameters
### SQL: INSERT INTO xdx_menu  ( name,   status, create_at, create_by )  VALUES  ( ?,   ?, ?, ? )
### Cause: java.sql.SQLException: #HY000
; uncategorized SQLException; SQL state [HY000]; error code [1366]; #HY000; nested exception is java.sql.SQLException: #HY000

二、解决 MyBatis/MyBatis-Plus 使用枚举参数异常

之所以会发生这样的问题,是因为在MyBatis参数拼接的时候,它不知道你这个枚举参数是要传递什么样的值,并且每个人定义枚举的样式可能都不一样。解决办法:

  1. 写一个 IBaseEnum 接口让所有的枚举都去继承这个接口,这样所有的枚举都有共性了
  2. 自定义一个枚举处理器,继承 BaseTypeHandler 或 实现 TypeHandler 接口
  3. 配置handler default-enum-type-handler

TypeHandler里面有一个方法 setParameter 在进行SQL拼接的时候会调用这个方法获取参数。

这里我的做法是继承 BaseTypeHandler 类,它里面其实也是实现了 TypeHandler 接口,对里面一些方法进行了实现

而在 BaseTypeHandler 方法里面重写了 setParameter 方法,里面去调用了一个抽象方法 setNonNullParameter ,我们的实现方法主要是去重写这个方法就好了。


2-1、IBaseEnum

它里面的内容很简单,我们就是要通过这个接口,知道:

  • 枚举的key是什么类型
  • 枚举的value是什么类型
  • 以及枚举是什么
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.LinkedHashMap;
import java.util.Map;

public interface IBaseEnum<K, V, T extends Enum<?>> {

    K getCode();

    V getMsg();
}

2-2、StatusEnum

StatusEnum改造,实现 通用接口

import lombok.Getter;
import lombok.ToString;

/**
 * 状态枚举
 */
@Getter
@ToString
public enum StatusEnum implements IBaseEnum<Integer, String, StatusEnum>{

    ENABLE(1, "启用"),
    DISABLE(0,"禁用")
    ;

    private Integer code;
    private String name;


    StatusEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public String getMsg() {
        return this.name;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }
}

2-3、EnumTypeHandler

这里面还重写了其它的方法,大家只要看 setNonNullParameter方法即可,有兴趣的可以自行研究其它的

import com.xdx97.blog.common.enums.IBaseEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class EnumTypeHandler<E extends Enum<E> & IBaseEnum> extends BaseTypeHandler<IBaseEnum> {
    private Class<E> type;

    public EnumTypeHandler(Class<E> type) {
        if(type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        } else {
            this.type = type;
        }
    }


    public void setNonNullParameter(PreparedStatement ps, int i, IBaseEnum parameter, JdbcType jdbcType) throws SQLException {
        if(jdbcType == null) {
            ps.setString(i, parameter.getCode().toString());
        } else {
            ps.setObject(i, parameter.getMsg(), jdbcType.TYPE_CODE);
        }
    }

    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        return s == null?null:Enum.valueOf(this.type, s);
    }

    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        return s == null?null:Enum.valueOf(this.type, s);
    }

    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return s == null?null:Enum.valueOf(this.type, s);
    }
}

2-4、application.yml

mybatis-plus:
  configuration:
    # 枚举处理器
    default-enum-type-handler: com.xdx97.blog.common.handler.EnumTypeHandler

三、 SpringBoot枚举参数异常

之所以在接受枚举参数的时候和我们预期的结果不一致是因为系列化和反序列化导致的,SpringBoot默认是使用 jackson 序列化的。我们只需要自定义序列化的过程即可。


3-1、IBaseEnum

在上述的IBaseEnum 中新增2个方法

import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.LinkedHashMap;
import java.util.Map;

public interface IBaseEnum<K, V, T extends Enum<?>> {
    Map<Class<?>, Map<?, ?>> map = new LinkedHashMap();

    default void initMap(K code, T t) {
        if (map.containsKey(t.getClass())) {
            Map<K, T> tmp = (Map)map.get(t.getClass());
            tmp.put(code, t);
            map.put(t.getClass(), tmp);
        } else {
            Map<K, T> tmp = new LinkedHashMap();
            tmp.put(code, t);
            map.put(t.getClass(), tmp);
        }

    }

    @JsonCreator
    static <T extends Enum<?>, K> T get(Class<T> clazz, K code) {
        if (map.get(clazz) == null) {
            return null;
        } else {
            Object _code = code;
            if (code instanceof String) {
                _code = code.toString().trim();
            }
            return (T) map.get(clazz).get(_code);
        }
    }

    K getCode();

    V getMsg();
}

3-2、StatusEnum

在枚举初始化的时候注入到IBaseEnum里面去 initMap, 新增序列化方法

import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Getter;
import lombok.ToString;

/**
 * 状态枚举
 */
@Getter
@ToString
public enum StatusEnum implements IBaseEnum<Integer, String, StatusEnum>{

    ENABLE(1, "启用"),
    DISABLE(0,"禁用")
    ;

    private Integer code;
    private String name;


    StatusEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
        initMap(code, this);
    }

    @Override
    public String getMsg() {
        return this.name;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @JsonCreator
    public static StatusEnum forValue(Integer code) {
        return IBaseEnum.get(StatusEnum.class, code);
    }
}



关注微信公众号(小道仙97)回复:xdxFrameSimple 获取源码。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值