目录
4.在application.yml或.properties文件中添加以下配置
引言
在后端开发过程中,常常涉及到很多一些状态处理,理论上我们不能直接将状态值返回给前端,而是要给具体的字段描述,例如,1代表男,2代表女,你在拿到数据的1或者2这种int值时,需要告诉前端人员这代表什么,这就显得十分的麻烦,因此需要后端人员做转换的处理。
案例
此处以SpringBoot+Mybatis项目做案例,介绍如何通过枚举接收从MySQL数据库中查询出来的int类型的字段
1.编写通用枚举接口
@JsonSerialize(using = JsonEnumSerializer.class)
public interface BaseEnum<E extends Enum<?>, T> extends Serializable {
/**
* 获取状态值
*
* @return
*/
T getCode();
/**
* 获取状态描述
*
* @return
*/
String getDesc();
}
此处要注意:要在上面有用于前后端数据交互的通用枚举接口添加@JsonSerialize(using = JsonEnumSerializer.class),后面序列化数据返回前端需要用到
2.编写对应的状态枚举实现枚举接口
@JSONType(serializeEnumAsJavaBean = true)
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SexEnum implements BaseEnum<SexEnum,Integer>{
/** 女 */
FEMALE(0,"女"),
/** 男 */
MALE(1,"男"),
/** 组合 */
COMBINATION(2,"组合"),
/** 未知 */
UNKNOWN(3,"未知");
/** 状态码 */
private Integer code;
/** 描述 */
private String desc;
/** 静态化一个枚举集合,状态码作为对应枚举的索引 */
static Map<Integer,SexEnum> enumMap=new HashMap<Integer, SexEnum>();
// 初始化枚举集合
static{
for(SexEnum sex:SexEnum.values()){
enumMap.put(sex.getCode(), sex);
}
}
SexEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
/**
* 按照状态码返回枚举对象
*
* @param code
* @return
*/
public static SexEnum getEnum(String code) {
return enumMap.get(code);
}
@Override
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
注意:枚举类如何转为json(在一个类的属性中,这个枚举类属性如何直接使用在接收参数和向数据库传递参数时需要自动转化),这里需要用到两个注解: @JSONType(serializeEnumAsJavaBean = true) @sonFormat(shape = JsonFormat.Shape.OBJECT)
3.编写转换器
此处要注意在转换器上要添加注解@MappedTypes(value = {SexEnum.class}),指定需要转换的枚举类
@MappedTypes(value = {SexEnum.class})
public final class UniversalEnumHandler<E extends BaseEnum> extends BaseTypeHandler<E> {
private Class<E> type;
private E [] enums;
/**
* 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
* @param type 配置文件中设置的转换类
*/
public UniversalEnumHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
this.enums = type.getEnumConstants();
if (this.enums == null) {
throw new IllegalArgumentException(type.getSimpleName()
+ " does not represent an enum type.");
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
JdbcType jdbcType) throws SQLException {
//BaseTypeHandler已经帮我们做了parameter的null判断
ps.setObject(i,parameter.getCode(), jdbcType.TYPE_CODE);
}
@Override
public E getNullableResult(ResultSet rs, String columnName)
throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
Integer i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
// 根据数据库中的value值,定位SexEnum子类
return locateEnumStatus(i);
}
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
Integer i = rs.getInt(columnIndex);
if (rs.wasNull()) {
return null;
} else {
// 根据数据库中的value值,定位SexEnum子类
return locateEnumStatus(i);
}
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
Integer i = cs.getInt(columnIndex);
if (cs.wasNull()) {
return null;
} else {
// 根据数据库中的value值,定位SexEnum子类
return locateEnumStatus(i);
}
}
/**
* 枚举类型转换,由于构造函数获取了枚举的子类enums,让遍历更加高效快捷
* @param value 数据库中存储的自定义value属性
* @return value对应的枚举类
*/
private E locateEnumStatus(Integer value) {
for(E e : enums) {
if(e.getCode().equals(value)) {
return e;
}
}
throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对" + type.getSimpleName());
}
}
4.在application.yml或.properties文件中添加以下配置
mybatis.type-handlers-package=自定义枚举转换器所在包
到此,你是可以正常使用枚举字段去接收数据库查询出来的结果
- 1.在用于接收数据的实体中使用枚举定义对应数据库的该字段
![]()
- 2.修改mapper.xml文件
![]()
5.处理实体中的枚举字段,序列化返回前端
/**
* @Todo: 枚举类序列化处理类
* @author: Poison
* @Date:2020/11/18 17:29
*/
public class JsonEnumSerializer extends JsonSerializer<BaseEnum> {
private static Logger log = LoggerFactory.getLogger(JsonEnumSerializer.class);
/**
* 将返回数据中的枚举字段进行序列化处理
*
* @param iBaseEnum
* @param jsonGenerator
* @param serializerProvider
*/
@Override
public void serialize(BaseEnum iBaseEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider){
try {
serializerProvider.defaultSerializeValue(iBaseEnum.getDesc(), jsonGenerator);
} catch (IOException e) {
log.error("枚举类序列化错误", e);
throw new RuntimeException("枚举序列化错误");
}
}
}
此方法是用于处理返回数据中的枚举字段,根据前端数据格式需求,修改上面JsonEnumSerializer的方法,举例:
如果需要返回下面的数据格式,则按照上面JsonEnumSerializer的方法进行编写
![]()
如果需要返回下面的数据格式,则按照下面的方法改写
![]()
@Override public void serialize(BaseEnum iBaseEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider){ Map<String,Object> map=new HashMap<>(); map.put("code",iBaseEnum.getCode()); map.put("desc",iBaseEnum.getDesc()); try { serializerProvider.defaultSerializeValue(map, jsonGenerator); } catch (IOException e) { log.error("枚举类序列化错误", e); throw new RuntimeException("枚举序列化错误"); } }
6.插入数据到数据库时对实体中枚举字段的处理
sex=#{sex,jdbcType=TINYINT,typeHandler=cn.poison.music.common.enums.UniversalEnumHandler},
注意:在#{}内,jdbcType=TINYINT,typeHandler=cn.poison.music.common.enums.UniversalEnumHandler必不可少,否则将报以下错误:
nested exception is org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property=’sex’, mode=IN, javaType=class cn.poison.music.common.enums.SexEnum, jdbcType=null, numericScale=null, resultMapId=’null’, jdbcTypeName=’null’, expression=’null’}. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #2 with JdbcType null . Try setting a different JdbcType for this parameter or a different configuration property. Cause: java.lang.NullPointerException
2020-11-19 10:39:06.535 WARN 14892 — [nio-8888-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property=’sex’, mode=IN, javaType=class cn.poison.music.common.enums.SexEnum, jdbcType