目录
1.枚举简介
1.1.规范
1.2.枚举类真实的样子
1.3.枚举类的特点
1.4.枚举可以使用的方法
1.4.1.toString()方法
1.4.2.valueOf方法
1.4.3.values方法
1.4.4.ordinal方法
1.5.枚举的用法
1.5.1.常量
1.5.2.switch
1.5.3.枚举中增加方法
1.5.4.覆盖枚举方法
1.5.5.实现接口
1.5.5.1.情况 1:在 enum 类中实现接口
1.5.5.2.情况 2:让枚举类中的对象分别实现接口中的方法
1.5.6.在接口中使用枚举类
1.5.7.使用枚举集合
1.5.7.1.EnumSet
1.5.7.2.EnumMap
2.序列化和反序列化
2.1.什么是序列化和反序列化
2.2.什么是serialVersionUID序列化ID
2.3.什么类型的数据不能被序列化
3.jackson
3.1.jackson序列化writeValueAsString
3.2.jackson反序列化readValue
3.3.集合转换
3.4.@JsonProperty
3.5.ObjectMapper的一些配置
3.6.JsonParser
3.6.1.创建
3.6.2.解析
4.枚举的序列化
4.1.ordinal索引
4.2.时序图
4.3.json枚举序列化/反序列化处理
4.3.1.方法一:使用JsonCreator和JsonValue
4.3.2.方法二:自定义序列化/反序列化方法
4.4.mybatis枚举序列化/反序列化处理
4.4.1.TypeHandler
4.4.2.配置步骤
5.IDEA2019取消枚举提示
6.查看枚举调用的地方
1.枚举简介
枚举类型(enum type)是指由一组固定的常量组成合法的类型。
枚举类格式:
public enum SexEnum {MAN, WOMAN
}
1.1.规范
1.2.枚举类真实的样子
1、使用javac 命令编译,得到.class文件
2、使用命令 javap 对class文件进行反编译
>javac Sex.java
>javap Sex.class
得到如下结果:
Compiled from "Sex.java"
public final class Sex extends java.lang.Enum<Sex> {public static final Sex MAN;public static final Sex WOMAN;public static Sex[] values();public static Sex valueOf(java.lang.String);static {};
}
3、这里其实是创建了2个对象
public final class Sex extends java.lang.Enum<Sex> {public static final Sex MAN = new Sex();public static final Sex WOMAN = new Sex();public static Sex[] values();public static Sex valueOf(java.lang.String);static {};
}
1.3.枚举类的特点
1、枚举类是通过final修饰,不能被继承
2、枚举类默认继承了枚举类型 java.lang.Enum
3、枚举类的第一行罗列的是枚举类对象,并且是常量存储,所以枚举类的第一行写的是常量名称,默认存储了枚举对象。
4、枚举类的构造器是私有的。
5、枚举类相当于多例设计模式
1.4.枚举可以使用的方法
把上面的枚举类加上code和name
public enum SexEnum {MAN(1, "男"),WOMAN(2, "女");private Integer code;private String name;SexEnum(Integer code, String name) {this.code = code;this.name = name;}public Integer getCode() {return code;}public String getName() {return name;}
}
1.4.1.toString()方法
这个方法会返回枚举常量名
// MAN
System.out.println(SexEnum.MAN.toString());
// WOMAN
System.out.println(SexEnum.WOMAN.toString());
1.4.2.valueOf方法
这个方法用于构建枚举类,传入枚举类常量名即可。
SexEnum sexEnum = SexEnum.valueOf("MAN");
// 男
System.out.println(sexEnum.getName());
try {// 报错: java.lang.IllegalArgumentException: No enum constant com.leelen.scd.api.amc.enums.SexEnum.UNKNOWNSystem.out.println(SexEnum.valueOf("UNKNOWN").getName());
} catch (IllegalArgumentException e) {e.printStackTrace();
}
如果不存在传入的枚举常量,那么会报错:
java.lang.IllegalArgumentException:
No enum constant com.leelen.scd.api.amc.enums.SexEnum.UNKNOWN
at java.lang.Enum.valueOf(Enum.java:238)
at com.leelen.scd.api.amc.enums.SexEnum.valueOf(SexEnum.java:3)
at com.leelen.scd.api.amc.enums.UseSex.main(UseSex.java:28)
1.4.3.values方法
使用枚举类名进行调用,会返回所有枚举常量的数组
1.4.4.ordinal方法
这个方法会返回枚举常量在enum中声明的位置,从0开始
SexEnum[] sexEnum = SexEnum.values();
for (SexEnum s : sexEnum) {System.out.println(s.getCode() + "," + s.getName() + "," + s.ordinal());
}
打印结果:
1,男,0
2,女,1
1.5.枚举的用法
1.5.1.常量
public enum ColorEnum {RED, GREEN, BLANK, YELLOW
}
1.5.2.switch
public static void getSexName(SexEnum sexEnum) {switch (sexEnum) {case MAN:System.out.println("男");break;case WOMAN:System.out.println("女");break;default:break;}
}
这里不要使用 SexEnum.MAN, 不然会提示:An enum switch case label must be the unqualified name of an enumeration constant
1.5.3.枚举中增加方法
public class EnumTest {public static void main(String[] args) {ErrorCodeEnum errorCode = ErrorCodeEnum.SUCCESS;System.out.println("状态码:" + errorCode.code() +" 状态信息:" + errorCode.msg());}
}enum ErrorCodeEnum {SUCCESS(1000, "success"),PARAM_ERROR(1001, "parameter error"),SYS_ERROR(1003, "system error"),NAMESPACE_NOT_FOUND(2001, "namespace not found"),NODE_NOT_EXIST(3002, "node not exist"),NODE_ALREADY_EXIST(3003, "node already exist"),UNKNOWN_ERROR(9999, "unknown error");private int code;private String msg;ErrorCodeEnum(int code, String msg) {this.code = code;this.msg = msg;}public int code() {return code;}public String msg() {return msg;}public static ErrorCodeEnum getErrorCode(int code) {for (ErrorCodeEnum it : ErrorCodeEnum.values()) {if (it.code() == code) {return it;}}return UNKNOWN_ERROR;}
}
1.5.4.覆盖枚举方法
我们可以覆盖一些枚举中的方法用于实现自己的业务,比如我们可以覆盖 toString() 方法,实现代码如下:
public class EnumTest {public static void main(String[] args) {ColorEnum colorEnum = ColorEnum.RED;System.out.println(colorEnum.toString());}
}enum ColorEnum {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);// 成员变量private String name;private int index;// 构造方法private ColorEnum(String name, int index) {this.name = name;this.index = index;}//覆盖方法@Overridepublic String toString() {return this.index + ":" + this.name;}
}
1.5.5.实现接口
枚举类可以用来实现接口,但不能用于继承类,因为枚举默认继承了 java.lang.Enum 类,在 Java 语言中允许实现多接口,但不能继承多个父类,实现代码如下:
1.5.5.1.情况 1:在 enum 类中实现接口
public class EnumTest {public static void main(String[] args) {ColorEnum colorEnum = ColorEnum.RED;colorEnum.print();System.out.println("颜色:" + colorEnum.getInfo());}
}interface Behaviour {void print();String getInfo();
}enum ColorEnum implements Behaviour {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}@Overridepublic void print() {System.out.println(this.index + ":" + this.name);}@Overridepublic String getInfo() {return this.name;}
}
1.5.5.2.情况 2:让枚举类中的对象分别实现接口中的方法
public enum ColorEnum implements Behaviour{RED("红色", 1) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, GREEN("绿色", 2) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, BLANK("白色", 3) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}}, YELLOW("黄色", 4) {@Overridepublic void print() {}@Overridepublic String getInfo() {return null;}};private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}
}
1.5.6.在接口中使用枚举类
我们可以在一个接口中创建多个枚举类,用它可以很好的实现“多态”,也就是说我们可以将拥有相同特性,但又有细微实现差别的枚举类聚集在一个接口中,实现代码如下:
public class EnumTest {public static void main(String[] args) {// 赋值第一个枚举类ColorInterface colorEnum = ColorInterface.ColorEnum.RED;System.out.println(colorEnum);// 赋值第二个枚举类colorEnum = ColorInterface.NewColorEnum.NEW_RED;System.out.println(colorEnum);}
}interface ColorInterface {enum ColorEnum implements ColorInterface {GREEN, YELLOW, RED}enum NewColorEnum implements ColorInterface {NEW_GREEN, NEW_YELLOW, NEW_RED}
}
1.5.7.使用枚举集合
在 Java 语言中和枚举类相关的,还有两个枚举集合类 java.util.EnumSet 和 java.util.EnumMap,使用它们可以实现更多的功能。
1.5.7.1.EnumSet
使用 EnumSet 可以保证元素不重复,并且能获取指定范围内的元素,示例代码如下:
public class EnumTest {public static void main(String[] args) {List<ColorEnum> list = new ArrayList<>();list.add(ColorEnum.RED);list.add(ColorEnum.RED); // 重复元素list.add(ColorEnum.YELLOW);list.add(ColorEnum.GREEN);// 去掉重复数据EnumSet<ColorEnum> enumSet = EnumSet.copyOf(list);System.out.println("去重:" + enumSet);// 获取指定范围的枚举(获取所有的失败状态)EnumSet<ErrorCodeEnum> errorCodeEnums = EnumSet.range(ErrorCodeEnum.ERROR, ErrorCodeEnum.UNKNOWN_ERROR);System.out.println("所有失败状态:" + errorCodeEnums);}
}enum ColorEnum {RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4);private String name;private int index;private ColorEnum(String name, int index) {this.name = name;this.index = index;}
}enum ErrorCodeEnum {SUCCESS(1000, "success"),ERROR(2001, "parameter error"),SYS_ERROR(2002, "system error"),NAMESPACE_NOT_FOUND(2003, "namespace not found"),NODE_NOT_EXIST(3002, "node not exist"),NODE_ALREADY_EXIST(3003, "node already exist"),UNKNOWN_ERROR(9999, "unknown error");private int code;private String msg;ErrorCodeEnum(int code, String msg) {this.code = code;this.msg = msg;}public int code() {return code;}public String msg() {return msg;}
}
1.5.7.2.EnumMap
public class EnumTest {public static void main(String[] args) {EnumMap<ColorEnum, String> enumMap = new EnumMap<>(ColorEnum.class);enumMap.put(ColorEnum.RED, "红色");enumMap.put(ColorEnum.GREEN, "绿色");enumMap.put(ColorEnum.BLANK, "白色");enumMap.put(ColorEnum.YELLOW, "黄色");System.out.println(ColorEnum.RED + ":" + enumMap.get(ColorEnum.RED));}
}enum ColorEnum {RED, GREEN, BLANK, YELLOW;
}
2.序列化和反序列化
2.1.什么是序列化和反序列化
序列化过程:是指把一个 Java 对象变成二进制内容,实质上就是一个 byte[]。因为序列化后可以把 byte[] 保存到文件中,或者把 byte[] 通过网络传输到远程(IO),如此就相当于把 Java 对象存储到文件或者通过网络传输出去了。
一个 Java 对象要能序列化,必须实现一个特殊的java.io.Serializable接口,它的定义如下:
package java.io;
public interface Serializable {
}
Serializable 没有定义任何方法,它是一个空接口。这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。
反序列化过程:把一个二进制内容(也就是 byte[])变回 Java 对象。有了反序列化,保存到文件中的 byte[] 又可以“变回” Java 对象,或者从网络上读取 byte[] 并把它“变回” Java 对象。
为什么需要序列化与反序列化?
当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。
当两个 Java 进程进行通信时,需要 Java 序列化与反序列化实现进程间的对象传送。换句话说,一方面,发送方需要把这个 Java 对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出 Java 对象。
代码:
import lombok.Data;
import java.io.Serializable;
@Data
public class Student implements Serializable {private String name;private Integer age;private Integer score;
}
import java.io.*;
public class TypeDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {serialize();deserialize();}/*** 序列化*/public static void serialize() throws IOException {Student student = new Student();student.setName("new");student.setAge(18);student.setScore(100);ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream(new File("student.txt")));objectOutputStream.writeObject(student);objectOutputStream.close();System.out.println("序列化成功!已经生成student.txt文件");System.out.println("============================");}/*** 反序列化*/public static void deserialize() throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream(new File("student.txt")));Student student = (Student) objectInputStream.readObject();objectInputStream.close();System.out.println("反序列化结果为:" + student);}
}
结果:
序列化成功!已经生成student.txt文件
============================
反序列化结果为:Student(name=new, age=18, score=100)
2.2.什么是serialVersionUID序列化ID
private static final long serialVersionUID = -4392658638228508589L;
serialVersionUID 是一个常数,用于唯一标识可序列化类的版本。
从输入流构造对象时,JVM 在反序列化过程中检查此常数。如果正在读取的对象的 serialVersionUID 与类中指定的序列号不同,则 JVM 抛出InvalidClassException。这是为了确保正在构造的对象与具有相同 serialVersionUID 的类兼容。
我们先调用serialize()方法把上面的Student对象序列化进student.txt文件中。然后修改Student类的内容去掉其中一个字段:
@Data
public class Student implements Serializable {private String name;private Integer age;
}
再调用反序列化方法deserialize(),结果报错:
Exception in thread "main" java.io.InvalidClassException: com.codejam.enums.demo.type.Student; local class incompatible: stream classdesc serialVersionUID = -6951954515964250676, local class serialVersionUID = 7327139321132172307
这是因为serialVersionUID 是可选的。如果不显式声明,Java 编译器将自动生成一个。
2.3.什么类型的数据不能被序列化
声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
3.jackson
由于Spring自带的序列化和反序列化使用的是jackson,所以项目不使用fastjson。
3.1.jackson序列化writeValueAsString
先创建对象
@Data
public class Student {private String name;private Integer age;
}
创建保护集合的对象
@Data
public class Room {List<Student> studentList;
}
1、使用writeValueAsString将一个对象序列化为字符串
public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper=new ObjectMapper();Student student=new Student();student.setName("abc");student.setAge(18);String s = objectMapper.writeValueAsString(student);System.out.println(s);
}
打印:
{"name":"abc","age":18}、
2、使用writeValueAsString 将一个包含集合的对象序列化为字符串:
public static void main(String[] args) throws JsonProcessingException {List<Student> list = new ArrayList<>();for (int i = 0; i < 3; i++) {Student student = new Student();student.setName("name" + i);student.setAge(i);list.add(student);}Room classRoom = new Room();classRoom.setStudentList(list);ObjectMapper objectMapper = new ObjectMapper();String s = objectMapper.writeValueAsString(classRoom);System.out.println(s);
}
打印:
{"studentList":[{"name":"name0","age":0},{"name":"name1","age":1},{"name":"name2","age":2}]}
3.2.jackson反序列化readValue
使用readValue将字符串转换为student对象
public static void main(String[] args) throws JsonProcessingException {ObjectMapper objectMapper=new ObjectMapper();String s = "{\"name\":\"abc\",\"age\":18}";Student student = objectMapper.readValue(s, Student.class);System.out.println(student);
}
使用readValue将字符串转为对象(包含集合)
public static void main(String[] args) throws JsonProcessingException {String s = "{\"studentList\":[{\"name\":\"name0\",\"age\":0},{\"name\":\"name1\",\"age\":1},{\"name\":\"name2\",\"age\":2}]}";ObjectMapper objectMapper = new ObjectMapper();Room room = objectMapper.readValue(s, Room.class);System.out.println(room);
}
3.3.集合转换
public static void main(String[] args) throws JsonProcessingException {String listStr = "[{\"name\":\"李四\",\"age\":1},{\"name\":\"张三\",\"age\":2}]";ObjectMapper objectMapper = new ObjectMapper();List<Student> students = objectMapper.readValue(listStr, new TypeReference<List<Student>>() {});System.out.println(students);
}
3.4.@JsonProperty
如果序列化的时候,名称要更改的话,则可以使用@JsonProperty
3.5.ObjectMapper的一些配置
通过以下设置会在序列化和反序列化时忽略无法解析和为空的字段
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
3.6.JsonParser
JsonParser 类是底层 Json解析器。
JsonParser实现相较于 ObjectMapper 更底层,因此解析速度更快,但相对复杂。
3.6.1.创建
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;public static void main(String[] args) throws IOException {String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();JsonParser parser = factory.createParser(carJson);
}
createParser()方法传入 Reader, InputStream, URL, byte[] 或 char[] 参数可以实现解析不同来源 json 数据。
3.6.2.解析
JsonParser 工作方式是将 json 分解成一系列标记 (token) ,逐个迭代这些标记进行解析
public static void main(String[] args) throws IOException {String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";JsonFactory factory = new JsonFactory();JsonParser parser = factory.createParser(carJson);while(!parser.isClosed()){JsonToken jsonToken = parser.nextToken();System.out.println("jsonToken = " + jsonToken);}
}
输出结果:
jsonToken = START_OBJECT
jsonToken = FIELD_NAME
jsonToken = VALUE_STRING
jsonToken = FIELD_NAME
jsonToken = VALUE_NUMBER_INT
jsonToken = END_OBJECT
jsonToken = null
通过 JsonParser 的 nextToken() 方法获得 JsonToken,我们可以检查 JsonToken 实例的类型,JsonToken 类提供了一组常量表示标记类型:
package com.fasterxml.jackson.core;
public enum JsonToken
{NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),START_OBJECT("{", JsonTokenId.ID_START_OBJECT),END_OBJECT("}", JsonTokenId.ID_END_OBJECT),START_ARRAY("[", JsonTokenId.ID_START_ARRAY),END_ARRAY("]", JsonTokenId.ID_END_ARRAY),FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),VALUE_STRING(null, JsonTokenId.ID_STRING),VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),VALUE_TRUE("true", JsonTokenId.ID_TRUE),VALUE_FALSE("false", JsonTokenId.ID_FALSE),VALUE_NULL("null", JsonTokenId.ID_NULL),;
}
如果标记指针指向的是字段,JsonParser 的 getCurrentName() 方法返回当前字段名称。
getValueAsString() 返回当前标记值的字符串类型,同理 getValueAsInt() 返回整型值
其他方法:
public static void main(String[] args) throws IOException {String json = "{ \"name\" : \"tom\", \"age\" : 28, \"height\": 1.75, \"ok\": true}";JsonFactory factory = new JsonFactory();JsonParser parser = factory.createParser(json);while (!parser.isClosed()) {JsonToken token = parser.nextToken();if (JsonToken.FIELD_NAME == token) {String fieldName = parser.getCurrentName();System.out.print(fieldName + ": ");parser.nextToken();switch (fieldName) {case "name":System.out.println(parser.getValueAsString());break;case "age":System.out.println(parser.getValueAsInt());break;case "height":System.out.println(parser.getValueAsDouble());break;case "ok":System.out.println(parser.getValueAsBoolean());break;}}}
}
4.枚举的序列化
4.1.ordinal索引
枚举默认是使用索引ordinal 来进行序列化反序列化操作的,
例如我这边定义了枚举:1, 3 它对应的索引是0,1
@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictionaryEnum {UNIFIED_AUTHORIZATION_MANAGEMENT(1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;
}
前端请求0的时候,对应的是第一个 1, "统一授权管理"。
请求1的时候,对应的是第二个 3, "独立管理"。
但如果我请求2的话,则会报错越界了。
Invalid JSON input: Cannot deserialize value of type `com.leelen.scd.module.amc.enums.AmcManagementModeEnum` from number 2: index value outside legal index range [0..1]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.leelen.scd.module.amc.enums.AmcManagementModeEnum` from number 2: index value outside legal index range [0..1]\n at [Source: (PushbackInputStream); line: 17, column: 31] (through reference chain: com.leelen.scd.base.common.entity.RequestDTO[\"body\"]->com.leelen.scd.base.common.entity.PagerReqDTO[\"params\"]->com.leelen.scd.module.amc.vo.AmcNeighInfoPageReq[\"managementMode\"])
4.2.时序图
4.3.json枚举序列化/反序列化处理
4.3.1.方法一:使用JsonCreator和JsonValue
JsonCreator :标记在反序列化时的初始化函数,入参为对应该枚举类型的值。
JsonVale:标记在序列化时枚举对应生成的值。
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.leelen.scd.base.common.enums.IDictEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;import java.util.Objects;@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictEnum{UNIFIED_AUTHORIZATION_MANAGEMENT( 1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;/*** 处理入参,定义转换函数parse,将code值入参转成对应的枚举类型* 在反序列化的时候Jackson会自动调用这个方法去自动帮我们转换*/@JsonCreatorpublic static AmcManagementModeEnum parse(Integer code) {if (Objects.isNull(code)) {return null;}for (AmcManagementModeEnum item : AmcManagementModeEnum.values()) {if (item.code.equals(code)) {return item;}}return null;}/*** 处理出参,在getter方法标记序列化后的值*/@JsonValuepublic Integer code() {return code;}}
使用枚举:
请求参数:
import com.leelen.scd.module.amc.enums.AmcManagementModeEnum;
import lombok.Data;
@Data
public class AmcNeighInfoPageReq {/*** 小区管理模式*/private AmcManagementModeEnum managementMode;
}
响应参数:
@Data
public class AmcNeighInfoPageRes {/*** 小区管理模式*/private AmcManagementModeEnum managementMode;
}
验证:
@RestController
@RequestMapping("/web/system/community/amc")
public class AmcNeighInfoController {/*** 分页*/@RequestMapping("/page")public ResponseDTO<AmcNeighInfoPageRes> page(@RequestBody final RequestDTO<PagerReqDTO<AmcNeighInfoPageReq>> request) {AmcNeighInfoPageRes res = new AmcNeighInfoPageRes();res.setManagementMode(request.getBody().getParams().getManagementMode());return ResponseHelper.successResponse(request.getHeader(), res);}
}
4.3.2.方法二:自定义序列化/反序列化方法
1、首先定义接口IDictEnum。
这里指定反序列化的方法:EnumJsonDeserializer
这里指定序列化的方法:EnumJsonSerializer
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;@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;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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class EnumJsonSerializer extends JsonSerializer<IDictEnum> {public void serialize(IDictEnum iDictEnum, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {// 序列化只要code的值generator.writeNumber(iDictEnum.code());// 序列化形式: {"code": "", "desc": ""}//generator.writeStartObject();//generator.writeNumberField("code", iBaseDict.code());//generator.writeStringField("desc", iBaseDict.desc());//generator.writeEndObject();}
}
经验证以下这几种情况都能很好的识别成功!!!
@Data
public class AmcNeighInfoPageReq {private AmcManagementModeDictEnum managementMode;private List<AmcManagementModeDictEnum> managementModeList;private AmcNeighInfoPageReq amcNeighInfoPageReq;private Map<Integer, AmcManagementModeDictEnum> map1;private Map<AmcManagementModeDictEnum, Integer> map2;
}
4.4.mybatis枚举序列化/反序列化处理
以上两种方法对于mybatis来说没有起到作用,需要单独进行mybatis自定义序列化
MyBatis内置了两个枚举转换器分别是:org.apache.ibatis.type.EnumTypeHandler和org.apache.ibatis.type.EnumOrdinalTypeHandler。
- EnumTypeHandler是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串。比如有个枚举。
例如 前端输入1的话,则后端insert的时候的值是
字符串“UNIFIED_AUTHORIZATION_MANAGEMENT”
@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum AmcManagementModeEnum implements IDictEnum {UNIFIED_AUTHORIZATION_MANAGEMENT( 1, "统一授权管理"),//SEMI_AUTHORIZED_MANAGEMENT(2, "半授权管理"),INDEPENDENT_MANAGEMENT(3, "独立管理"),;/*** 字典码值*/private Integer code;/*** 字典描述*/private String desc;
}
- EnumOrdinalTypeHandler这个转换器将枚举实例的ordinal属性作为取值(从0依次取值)。还是上面的例子用这种转换器保存在数据库中的值就是0。
如果我们想保存枚举本身所定义的code值呢?这就需要自定义一个类型转换器,自定义一个int类型保存在数据库,即insert时枚举转换为int型数据保存在数据库,select时数据库中的int值转换成实体类的枚举类型。
4.4.1.TypeHandler
TypeHandler,顾名思义类型转换器,就是将数据库中的类型与Java中的类型进行相互转换的处理器。
经常自定义类型转换器方式有两种,实现 TypeHandler 接口, 或继承抽象类 BaseTypeHandle,并且可以指定转换后的字段类型。
- 其实BaseTypeHandler也是继承了TypeHandler接口,在实现的TypeHandler接口的方法中调用的是自身抽象方法
抽象类BaseTypeHandler的抽象方法
// 执行之前,将Java类型转换为对应的jdbc类型,用于赋值sql中参数
public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
// 根据列名从resultSet中获取,将JDBC类型转换为Java类型
public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
// 根据下标从resultSet中获取,将JDBC类型转换为Java类型
public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;
// 用于在执行完存储过程后,将JDBC类型转换为Java类型
public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
4.4.2.配置步骤
1、mybatis自定义枚举类型转换 (这里需要配置@MappedTypes)
import com.leelen.scd.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;@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);}}
}
这里用到了工具类:
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;}
}
2、定义mybatis的typeHandler扫描包路径:
# mybatis配置参数
mybatis:# 定义typeHandler扫描包路径type-handlers-package: com.leelen.scd
5.IDEA2019取消枚举提示
IDEA2019枚举自带入参提示,个人感觉看的比较眼花,建议把他取消掉。
Settings - Editor - Inlay Hints - Java 选择Parameter hints取消勾选Enum constants
效果如下:
6.查看枚举调用的地方
我们通常使用 ctrl + 鼠标左键,来查看方法被哪些地方调用,但是枚举却没法这么使用。
解决方法,使用alt + F7 来间接查看调用的地方,或者右键,选择Find Usage
或者使用快捷键 ctl + alt + F7
也可以在idea配置提示: