使用枚举Enum代替Integer类型

一、问题

虽然项目使用了枚举Enum,来替代原来的Constant常量类,但DTO对象的入参仍然是Integer类型,顶多再加个 {@link } 说明它是对应哪个枚举。

但这仍然无法限制传入的值不会乱传。要是有人不写{@link } 或者{@link } 写错,那更死。

二、什么是序列化和反序列化

1)序列化过程:把一个 Java 对象变成二进制内容(byte[]数组)存储起来。这里的存储方式可能是存储到磁盘中,也可能是发布到网络中。

一个 Java 对象要能序列化,必须实现一个特殊的java.io.Serializable接口。

Serializable 没有定义任何方法,它是一个空接口。这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

package java.io;public interface Serializable {
}

2) 反序列化过程:把一个二进制内容(byte[]数组)变回 Java 对象。

有了反序列化,保存到文件中的 byte[] 又可以“变回” Java 对象,或者从网络上读取 byte[] 并把它“变回” Java 对象。

三、解决方法

1、首先定义接口IDictEnum(所有枚举类都要去实现该接口)

指定反序列化的方法:EnumJsonDeserializer

指定序列化的方法:EnumJsonSerializer

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;/*** @description: 数据字典公共接口*/
@JsonDeserialize(using = EnumJsonDeSerializer.class)
@JsonSerialize(using = EnumJsonSerializer.class)
public interface IDictEnum {/*** 获得字典码值*/Integer code();/*** 获得字典描述, 这里不能用name, 因为java.lang.Enum已经定义了name*/String desc();}

2、反序列化方法:EnumJsonDeserializer

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;import java.io.IOException;
import java.util.Arrays;/*** @description: 数据字典枚举反序列化* @see <a href="https://blog.51cto.com/u_15782374/5666254">springboot项目中枚举类型的最佳实践</a>*/
public class EnumJsonDeSerializer extends JsonDeserializer<IDictEnum> implements ContextualDeserializer {private Class<? extends IDictEnum> clazz;public EnumJsonDeSerializer() {}public EnumJsonDeSerializer(Class<? extends IDictEnum> clazz) {this.clazz = clazz;}@Overridepublic IDictEnum deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {String param = jsonParser.getText();IDictEnum[] enumConstants = clazz.getEnumConstants();JsonStreamContext parsingContext = jsonParser.getParsingContext();IDictEnum iDictEnum = Arrays.stream(enumConstants).filter(x -> {//x.toString(),取枚举的具体值,如:xxx.enums.share.DelFlagEnum 枚举里的“NOT_DELETE”//从而使得两种形式都能识别String enumCodeStr = x.toString();return enumCodeStr.equals(param) || param.equals(x.code() + "");}).findFirst().orElse(null);/*if (null == iEnum) {String msg = String.format("枚举类型%s从%s未能转换成功", clazz.toString(), param);throw new Exception(msg);}*/return iDictEnum;}@Overridepublic Class<?> handledType() {return IDictEnum.class;}@SuppressWarnings({"unchecked"})@Overridepublic JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)throws JsonMappingException {JavaType type = property.getType();// 如果是容器,则返回容器内部枚举类型while (type.isContainerType()) {type = type.getContentType();}return new EnumJsonDeSerializer((Class<? extends IDictEnum>) type.getRawClass());}
}

3、序列化方法:EnumJsonSerializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;import java.io.IOException;/*** @description: 数据字典枚举序列化* @see <a href="https://blog.51cto.com/u_15782374/5666254">springboot项目中枚举类型的最佳实践</a>*/
public class EnumJsonSerializer extends JsonSerializer<IDictEnum> {@Overridepublic void serialize(IDictEnum iDictEnum, JsonGenerator generator, SerializerProvider provider) throws IOException {// 序列化只要code的值generator.writeNumber(iDictEnum.code());// 序列化形式: {"code": "", "desc": ""}//generator.writeStartObject();//generator.writeNumberField("code", iBaseDict.code());//generator.writeStringField("desc", iBaseDict.desc());//generator.writeEndObject();}@Overridepublic Class handledType() {return IDictEnum.class;}
}

4、现在我们定义一个 枚举类,去实现 IDictEnum接口

import com.aa.docking.base.common.enums.IDictEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum ManagementModeEnum implements IDictEnum {UNIFIED_AUTHORIZATION_MANAGEMENT( 1, "模式一"),SEMI_AUTHORIZED_MANAGEMENT(2, "模式二"),INDEPENDENT_MANAGEMENT(3, "模式三"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;}

5、经验证以下几种写法都可以成功识别:

@Data
public class NeighInfoPageReq {private ManagementModeDictEnum managementMode;private List<ManagementModeDictEnum> managementModeList;private NeighInfoPageReq amcNeighInfoPageReq;private Map<Integer, ManagementModeDictEnum> map1;private Map<ManagementModeDictEnum, Integer> map2;
}

使用postman传参, 传Integer类型就行

{

   “managementMode”: 1,

   "managementModeList": [1, 2, 3]

}

6、假如枚举值传错了要怎么判断呢?加上validation的@NotNull即可。此时如果传“managementMode”: 4 就会报错。

import lombok.Data;
import javax.validation.constraints.NotNull;@Data
public class NeighInfoPageReq {@NotNull(message = "模式不能为空或码值不正确")private ManagementModeDictEnum managementMode;
}

四、mybatis枚举序列化和反序列化处理

1、mybatis有自己的一套序列化反序列化规则,所以我们还需要使用@MappedTypes,单独针对IDictEnum定义一套规则。

package com.aa.docking.base.common.enums;import com.aa.docking.base.common.util.EnumUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** @description: mybatis自定义枚举类型转换* @see <a href="https://blog.csdn.net/weixin_30273931/article/details/94930515">springboot + mybatis 自定义枚举类型转换</a>*/
@MappedTypes({IDictEnum.class})
public class EnumTypeHandler<E extends Enum<?> & IDictEnum> extends BaseTypeHandler<IDictEnum> {private Class<E> type;public EnumTypeHandler(Class<E> type) {if (type == null) {throw new IllegalArgumentException("Type argument cannot be null.");}this.type = type;}/*** 用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型*/@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, IDictEnum parameter, JdbcType jdbcType)throws SQLException {ps.setInt(i, parameter.code());}/*** 用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(ResultSet rs, String columnName) throws SQLException {int code = rs.getInt(columnName);return rs.wasNull() ? null : codeOf(code);}/*** 用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int code = rs.getInt(columnIndex);return rs.wasNull() ? null : codeOf(code);}/*** 用定义调用存储过程后,如何把数据库类型转换为对应的Java类型*/@Overridepublic E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {int code = cs.getInt(columnIndex);return cs.wasNull() ? null : codeOf(code);}private E codeOf(int code) {try {return EnumUtil.getEnumByCode(type, code);} catch (Exception ex) {throw new IllegalArgumentException("Cannot convert " + code + " to " + type.getSimpleName() + " by code value.", ex);}}
}

这里的EnumUtil:

import com.aa.docking.base.common.enums.IDictEnum;
import org.apache.commons.lang3.StringUtils;/*** @description: 公共枚举方法* @see <a href="https://blog.csdn.net/qq_37953002/article/details/120352298">枚举Enum使用范例-公共枚举方法</a>*/
public class EnumUtil {/*** 根据code获取枚举*/public static <T extends IDictEnum> T getEnumByCode(Class<T> tClass, Integer code) {if (code != null) {for (T t : tClass.getEnumConstants()) {if (t.code().equals(code)) {return t;}}}return null;}/*** 根据desc获取枚举*/public static <T extends IDictEnum> T getEnumByDesc(Class<T> tClass, String desc) {if (StringUtils.isNotBlank(desc)) {for (T t : tClass.getEnumConstants()) {if (t.desc().equals(desc)) {return t;}}}return null;}/*** 根据code获取desc*/public static <T extends IDictEnum> String getDescByCode(Class<T> tClass, Integer code) {T t = getEnumByCode(tClass, code);if (null != t) {return t.desc();}return null;}/*** 根据desc获取code*/public static <T extends IDictEnum> Integer getCodeByDesc(Class<T> tClass, String desc) {T t = getEnumByDesc(tClass, desc);if (null != t) {return t.code();}return null;}
}

2、配置文件里面还需要定义mybatis的typeHandler扫描包路径:

# mybatis配置参数
mybatis:# 定义typeHandler扫描包路径type-handlers-package: com.aa.docking

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/153299.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

host配置

配置host文件的作用主要是用于自定义域名与IP地址之间的映射关系。Host文件是一个操作系统用于将人类可读的域名&#xff08;例如&#xff1a;www.example.com&#xff09;映射到IP地址&#xff08;例如&#xff1a;192.168.1.1&#xff09;的文件。当你在浏览器中输入一个网址…

【小工具-生成合并文件】使用python实现2个excel文件根据主键合并生成csv文件

1 小工具说明 1.1 功能说明 一般来说&#xff0c;我们会先有一个老的文件&#xff0c;这个文件内容是定制好相关列的表格&#xff0c;作为每天的报告。 当下一天来的时候&#xff0c;需要根据新的报表文件和昨天的报表文件做一个合并&#xff0c;合并的时候就会出现有些事新增…

H5逆向之远程RPC

引言前一讲说过H5 怎么去抓包,逆向分析。其中说到RPC。这一节详细讲一下。有一种情况,JS 比较复杂,混淆的厉害。 这个时候就用到RPC。原理就是,hook web 浏览器,直接调用js 里边的方法。 Node 服务。为什么用到Node 服务,先来看下这架构 Node 对外提供各种接口,外部可以…

基于乌燕鸥优化的BP神经网络(分类应用) - 附代码

基于乌燕鸥优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于乌燕鸥优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.乌燕鸥优化BP神经网络3.1 BP神经网络参数设置3.2 乌燕鸥算法应用 4.测试结果&#x…

【pycharm】sqlite3:Driver class ‘org.sqlite.JDBC‘ not found

pycharm 连接sqlite3的时候&#xff0c;点击&#xff1a;Test Connection 提示&#xff1a;Driver class ‘org.sqlite.JDBC’ not found. 点击&#xff1a;Download missing driver files 点击&#xff1a;Test Connection 这样就ok了&#xff1b;

VR全景拍摄酒店,为用户消除“不透明度”

近日在各大社交平台上&#xff0c;出现了不少吐槽国庆期间酒店价格太贵的帖子&#xff0c;而一些热门旅游地的度假酒店、网红民宿的热门房型已经“一房难求”&#xff0c;这就出现酒店房型与预定房型不同的现象出现&#xff0c;VR全景拍摄技术同酒店行业的结合&#xff0c;就可…

linux中Crontab定时参数

注&#xff1a;图片转载于 点我进入图片出处 * * * * * sh /data/var/test.sh >> test_crontab_log.log分钟 0~59 0表示没分周 小时 0~23 0表示每小时 天 1~31 *表示每天 月 1~12 *表示每月 周 0~7 */0/7表示每周

PDF编辑和OCR文字识别工具ABBYY FineReader PDF

ABBYY FineReader PDF是一款专业的OCR文字识别和PDF编辑工具&#xff0c;可以帮助用户更好地处理和管理PDF文档。以下是ABBYY FineReader PDF的一些特点&#xff1a; 1. 文字识别精准&#xff1a;ABBYY FineReader PDF具有强大的OCR文字识别功能&#xff0c;可以将PDF中的文字…

【香橙派-OpenCV-Torch-dlib】TF损坏变成RAW格式解决方案及python环境配置

前言 本文将介绍在香橙派&#xff08;Orange Pi&#xff09;开发板上进行软件配置和环境搭建的详细步骤&#xff0c;以便运行Python应用程序。这涵盖了以下主要内容&#xff1a; 获取所需软件&#xff1a;提供了香橙派操作系统和balenaEtcher工具的下载链接&#xff0c;以确保…

Netty通信在中间件组件中的广泛使用-Dubbo3举例

Netty是一个高性能异步IO通信框架&#xff0c;封装了NIO&#xff0c;对各种bug做了很好的优化解决。所以很多中间件底层的通信都会使用Netty&#xff0c;比如说&#xff1a;Dubbo3&#xff0c;rocketmq&#xff0c;ElasticSearch等。 比方说&#xff0c;我们使用dubbo作为rpc跨…

flutter出现entrypoint isn‘t within the current project

更新了android studio版本&#xff0c;打开一个老的flutter项目时&#xff0c;无法运行&#xff0c;打开configuration配置&#xff0c;提示错误entrypoint isn’t within the current project. 解决办法 1、删掉目录.idea, .gradle, .dart_tool退出重新打开项目 2、选中根目…

国内首档大模型技术直播专栏重磅推出!

你会好奇AI应用背后的故事吗&#xff1f;它的功能如何实现、如何实现最佳效果&#xff1f;或许大家都曾有过这样的好奇。 每一段代码&#xff0c;都被开发者赋予灵魂&#xff0c;每一个应用背后&#xff0c;都蕴含着长久的思考和深厚的技术基础。我们希望能在开发者之间建立交流…

【深度学习实验】卷积神经网络(七):实现深度残差神经网络ResNet

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. Residual&#xff08;残差连接&#xff09; __init__&#xff08;初始化&#xff09; forward&#xff08;前向传播&#xff09; 2. resnet_block&#xff08;残…

HTML基础入门01

目录 1.HTML基础 1.1HTML标签 1.2HTML 文件基本结构 1.3标签层次结构 1.4快速生成代码框架 2.HTML 常见标签 2.1注释标签 2.2标题标签: h1-h6 2.3段落标签: p 2.4.换行标签: br 3.综合案例: 展示博客 1.HTML基础 1.1HTML标签 HTML 代码是由 "标签" 构成…

0基础学习VR全景平台篇 第104篇:720全景后期软件安装

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 摄影进入数码时代&#xff0c;后期软件继承“暗房工艺”&#xff0c;成为摄影师表达内在情感的必备工具。 首先说明&#xff0c;全景摄影与平面摄影的一个显著的区别是全景图片需…

MyCat-web安装文档:安装Zookeeper、安装Mycat-web

安装Zookeeper A. 上传安装包 zookeeper-3.4.6.tar.gzB. 解压 #解压到当前目录&#xff0c;之后会生成一个安装后的目录 tar -zxvf zookeeper-3.4.6.tar.gz#加上-c 代表解压到指定目录 tar -zxvf zookeeper-3.4.6.tar.gz -C /usr/local/C. 在安装目录下&#xff0c;创建数据…

CasA:用于点云 3D 目标检测的级联注意力网络

论文摘要 LiDAR 收集的数据通常表现出稀疏和不规则的分布。 3D 空间中的 LiDAR 扫描并不均匀。近处和远处的物体之间存在巨大的分布差距。 CasA(Cascade Attention) 由 RPN&#xff08;Region proposal Network&#xff09;和 CRN&#xff08;cascade refinement Network&…

uni-app:引入echarts(使用renderjs)

效果 代码 <template><view click"echarts.onClick" :prop"option" :change:prop"echarts.updateEcharts" id"echarts" class"echarts"></view> </template><script>export default {data()…

用netty实现简易rpc

文章目录 rpc介绍&#xff1a;rpc调用流程:代码&#xff1a; rpc介绍&#xff1a; RPC是远程过程调用&#xff08;Remote Procedure Call&#xff09;的缩写形式。SAP系统RPC调用的原理其实很简单&#xff0c;有一些类似于三层构架的C/S系统&#xff0c;第三方的客户程序通过接…

谷歌 Chrome 浏览器正推进“追踪保护”功能

导读近日消息&#xff0c;根据国外科技媒体 Windows Latest 报道&#xff0c;谷歌计划在 Chrome 浏览器中推进“追踪保护”&#xff08;Tracking Protection&#xff09;功能&#xff0c;整合浏览器现有隐私功能&#xff0c;保护用户被网站跟踪。 根据一项 Chromium 提案&…