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

news/2024/5/19 1:34:56 标签: spring boot, java, 后端, mybatis, 枚举

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


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

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

一、前提


1.1、StatusEnum

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

java">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、实体

定义一个这样的实体

java">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

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

java">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改造,实现 通用接口

java">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方法即可,有兴趣的可以自行研究其它的

java">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个方法

java">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, 新增序列化方法

java">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 获取源码。


http://www.niftyadmin.cn/n/774889.html

相关文章

RabbitMQ实现延迟消息【死信队列实现、插件实现】

视频地址 之前一直没使用过RabbitMQ&#xff0c;最近有一个需求需要用到延迟消息&#xff0c;就简单的使用了一下&#xff0c;发现还是有蛮多坑的。 此篇文章只是RabbitMQ延迟消息相关内容&#xff0c;至于安装RabbitMQ等其它操作&#xff0c;参考百度。 一、什么是延迟消息 …

MySQL分组后取最大一条数据【最优解】

视频地址 有一个简单而又常见的需求:分组后取每组的最大一条数据 一、表结构 xdx_user CREATE TABLE xdx_user (id int(10) NOT NULL AUTO_INCREMENT COMMENT 自增id,name varchar(255) NOT NULL COMMENT 名称,PRIMARY KEY (id) USING BTREE ) ENGINEInnoDB AUTO_INCREMENT1 …

基于ThreadLocal和JWT登录的问题,微服务登录架构解决方案

https://www.bilibili.com/video/BV1f3411G7xk 公司之前是以JWT ThreadLocal 做的登录系统&#xff0c;在使用的过程发现了如下的问题&#xff0c;下面我们一起来看看&#xff0c;后面也会给出更好的解决方案。 一、基于JWT ThreadLocal实现登录 1-1、JWT 所谓的JWT是 json …

MySQL之监控binlog日志,解决系统响应慢的问题【maxwell】

视频地址&#xff1a; https://www.bilibili.com/video/BV1PY4y1s7a1 一、场景 不知道你是否有无数次吐槽公司的架构设计&#xff0c;比如某一个关键的列表&#xff0c; join 了无数张表 &#xff08;join 代表了left join、 inner join 等&#xff09; 做了一个新功能&#x…

Deadlock found when trying to get lock; try restarting transaction 【MySQL死锁问题解决】

视频地址&#xff1a; https://www.bilibili.com/video/bv1zY411N7tB 最近在调试接口的时候遇到了MySQL死锁问题&#xff0c;我自己测试的时候一切都好好的&#xff0c;但在并发下&#xff0c;就死锁了 其实死锁问题&#xff0c;并没有一个类似“万金油”的解决办法&#xff0…

MySQL 日期【加号/+】条件筛选问题

今天在修改一个SQL的时候发现一个关于时间格式的问题&#xff0c;原SQL如下&#xff08;已简化&#xff09; SELECT COUNT(*) FROM im_first_statistics WHERE prj_code IN(yxjt3311) AND created_at BETWEEN 2022-07-2000:00:00 AND 2022-07-2023:59:59SQL运行也没有问题&…

Redis分布式锁进阶之事物分布式锁

视频地址 https://www.bilibili.com/video/BV1et4y1P73Q redis分布式锁&#xff0c;大家肯定不陌生&#xff0c;也应该都用过&#xff0c;主要作用是防止并发产生的数据问题&#xff0c;下面是一段redis锁的伪代码 public void fun() {try {String key "";// 获取锁…

记一次服务宕机、优化全流程(以后也可以装X了)

视频地址&#xff1a; https://www.bilibili.com/video/BV1924y1y7jN 221115上午10点的时候客户反应进入小程序慢&#xff0c;打开监控发现服务pv已经超过了历史之最&#xff08;印象中最高的是100w&#xff09;&#xff0c;这次到了400w。原因是因为推广了一个发红包的活动。 …