为实体提供枚举类型的支持

为实体提供枚举类型的支持,第1张

为实体提供枚举类型的支持 使用

一般而言,Java 的实体(Bean/Pojo)很少涉及“枚举(Enum)”类型,例如男女,分别用 int 表示 1= 男/2=女 就好了。Bean 写法如下。

private Integer sex;

public Integer getSex() {
	reutrn sex;
}

public void setSex(Integer sex) {
	this.sex = sex;
}

这是最正常不过了。但是,为了提高语义程度,我们希望把枚举的概念利用起来,那样程序代码的好处是更清晰了。使用枚举类型,是这样子的。

private Gender gender;

public Gender getGender() {
	return gender;
}

public void setGender(Gender gender) {
	this.gender = gender;
}

必须承认,为了换取可读性和语义性,那是有代价的,它会适当增加你的代码复杂度。但实际上,笔者觉得这是一个更细致深入的建模过程,抛弃掉以往那种定义常量的方式。

public static interface Gender {
	public static final int MALE  = 1;
	public static final int FEMALE= 2;
}

而采用 Java Enum:

import com.ajaxjs.framework.IntegerValueEnum;


public enum Gender implements IntegerValueEnum {
	
	UNKNOW,

	
	MALE,

	
	FEMALE;

	@Override
	public Integer getValue() {
		return ordinal();
	}
}
序列化与反序列化

这仅仅是 Java 层面的,如果涉及“序列化与反序列化”,也就是考虑:数据库与 Json 两处地方,该如何保存枚举呢?

首先数据库仍旧不变,还是保持 int/string。虽然 MySQL 支持 enum 类型字段,但为跨数据库而言,不考虑特殊类型的字段去使用。存储方面照旧,而问题在于读取的时候,怎么把 int/string 变为 Java 的枚举类型。

其二,输出 Json,怎么把 Java 的枚举类型变为 Json 输出呢?

综上所述,解决的一个思路就是,凡是遇到实体中的枚举类型,就作适当的转换,跟以前 Integer/String/Boolean 类型针对性处理的手法一样。当然那需要我们额外的 *** 作,也就是前面所说的“代价”,你要在你的 ORM 框架(如 Mybatis)或者 Json 序列库(Jackson)中,当遍历 Bean 各字段时候,处理好 Enum 类型。

虽然不同框架的遍历切入的方法不一样,要看它具体如何开放出来,但是针对枚举的转换却是可以共性的。这点我们最后再讲。

如何定义枚举

既然我们说要识别枚举、处理枚举,那么需要将枚举类用一个接口简单标识一下,说明它是为 Bean 服务。我们知道,Java 中的枚举可以实现接口(但不能继承)。于是设计一个 baseEnmu,表示,POJO 中可以读取的枚举,以及可以获取枚举值,

import java.io.Serializable;


public interface baseEnmu {
	Serializable getValue();
}

其实每个枚举类都继承 Enum——要看它是不是枚举,可以简单用 instanceOf Enum 一下,但为了更方便的识别,获取 value 值,最好还是设置一个 baseEnmu 接口。

关于枚举值,就是保存到数据库或者输出到 Json 的那个值。不是方便人类阅读的(例如 0/1……),——在 Java 中使用枚举才是方便阅读的(例如 Male/Female……)。所以它肯定可以返回一个值,即 Serializable getValue();。Serializable 类型的用意是兼容了 Number/String 的值。

如何安排值?一般是 MALE(1), FEMALE(2),甚至不写,用系统索引序号也可以。至于 String 类型也行,看你需求。使用系统索引序号有个问题,下面会讲。

当然,除了值你还可以在枚举中加入丰富的内容,相当于横向扩充说明,这就有点像 Java 程序里面的数据字典了,也可以被其他代码所引用。例如这个 Status 笔者加入的 desc 说明。

public enum Status implements IntegerValueEnum, DescEnum {
	
	ONLINE(1, "上线"),

	
	OFFLINE(0, "下线"),

	
	DELETED(-1, "已删除");

	
	Status(Integer value, String desc) {
		this.value = value;
		this.desc = desc;
	}

	private Integer value;

	@Override
	public Integer getValue() {
		return value;
	}

	private String desc;

	@Override
	public String getDesc() {
		return desc;
	}

	@Override
	public boolean isUsingOrdinal() {
		return false;
	}
}
枚举的类型

一般我们都是用 int 即可,但不排除用字符串的。是 int 还是 String 我们要让程序知道,于是派生两个接口 IntegerValueEnum 和 StringValueEnum。

public interface IntegerValueEnum extends baseEnmu {
	
	public Integer getValue();

	
	default boolean isUsingOrdinal() {
		return true;
	}
}
public interface StringValueEnum extends baseEnmu {
	
	String getValue();
}

Enum 的 ordinal() 能返回枚举在数组中的索引,这个允许我们无须定义枚举值,按照枚举出现的顺序就好了。但它是从零开始算的,——万一我常量没有 0,而是从 1 开始算的——那就对不上了。于是我们提供一个标识,说明使用了 ordinal() 机制。

default boolean isUsingOrdinal() {
	return true;
}

默认是使用的。如果你自己定义 value,那么就要把这个值设为 false。

枚举的转换

前面说到序列化与反序列化的问题,这里谈谈具体如何解决。首先说说 Json 输出的,很简单,就是遇到枚举读取其值即可。


如截图,在不同类型的转换中,判断是否 baseEnum,然后转换即可,——一句两句代码的事情。如下图所示,是返回的 Json。


接着是,如何从数据库的值,转换到 Enum 呢?这个相对比较“烧脑”但其实也不难。笔者直接贴代码好了。

@SuppressWarnings({ "unchecked", "rawtypes" })
private static Object dbValue2Enum(Class propertyType, Object _value) {
	if (_value != null) {
		boolean isNumber = _value instanceof Number, isString = _value instanceof String; // 写在这里不用在 for 里面每次判断

		for (Object e : propertyType.getEnumConstants()) {
			if (isNumber) {
				IntegerValueEnum _e = (IntegerValueEnum) e;
				Integer _int = ((Integer) _value) - (_e.isUsingOrdinal() ? 1 : 0);

				if (_int == _e.getValue()) {
					return e;
				}
			} else if (isString)
				return Enum.valueOf((Class) propertyType, _value + "");
		}
	}

	return null;
}

这里笔者自研的 ORM 框架,在遍历数据库字段转换到 Java Bean 时候,调用 dbValue2Enum()。


Java 反射很方便的提供了 isEnum() 和 getEnumConstants() 使得可以让输入值与枚举值匹配上,——不然我真不知道该如何处理,使用注解说明哪个枚举类?太复杂了恐怕得不偿失。

小结

最后希望你会喜欢这套机制,或者给你带来一点启发。不足之处,敬请提出!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5691568.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存