Java枚举"已过时"-改用“数据字典”

Java枚举的一大缺点:增加一个枚举项,需要重新发版,不易扩展

数据字典:可以存储key=value形式的任何数据,变更不需要发版,易扩展


实现原理


Java枚举"已过时"-改用“数据字典”

字典的工作原理图


数据库表设计

CREATE TABLE `system_dict_node`  (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`pid` bigint(20) NOT NULL DEFAULT 0 COMMENT '上级ID',
`code` varchar(20) NOT NULL COMMENT '字典code 保存前统一转为大写/小写', --只能包含字典、数字、下划线
`title` varchar(20) NOT NULL DEFAULT '' COMMENT '字典值',
`leaf` tinyint(1) NOT NULL DEFAULT 0 COMMENT '末级 0-是 1-否',
`remark` varchar(255) DEFAULT NULL COMMENT '说明',
`status` tinyint(1) NULL DEFAULT NULL COMMENT '0-禁用 1-启用',
`order` int(5) NOT NULL COMMENT '显示顺序',
`time` int(10) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_pid`(`pid`) USING BTREE
) ENGINE = InnoDB COMMENT = '节点树';
CREATE TABLE `system_dict_value`
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`nid` bigint(20) NOT NULL COMMENT '节点ID',
`code` varchar(255) NOT NULL COMMENT '健',
`value` varchar(2048) NOT NULL COMMENT '值',
`remark` varchar(255) NULL DEFAULT NULL COMMENT '说明',
`status` tinyint(1) NULL DEFAULT NULL COMMENT '0-禁用 1-启用',
`order` int(5) NOT NULL COMMENT '索引 显示顺序 值越大越靠前',
`time` int(10) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_nid`(`nid`) USING BTREE
) ENGINE = InnoDB COMMENT = '节点键值对';


管理效果图


Java枚举"已过时"-改用“数据字典”

字典管理界面

说明:

节点支持多级

每个节点中可以有任意多个有顺序的“键值”对

数据变更后,点击“更新缓存”,则会把数据库中所有的 节点数据 和 键值数据 以json的形式存到 缓存(Redis中),并更新字典的最后变更时间到缓存(Redis)中


增加字典公共类

下面代码是SpringBoot实现,并封装到Starter中 (不明白什么是SpringBoot Starter可以参考我的其它文章)

@Configuration
@ConditionalOnProperty(prefix = "codeyyy.dict", name = "enable", matchIfMissing = true) //是否启用字典
@ConditionalOnClass({ EnableScheduling.class, RedisTemplate.class })
@EnableScheduling
@AutoConfigureAfter(RedisAutoConfiguration.class) //以来缓存(Redis)
public class DictConfiguration {

public DictConfiguration() {
log.info("======= SpringBootStarter ======= {}", "dict");
}

@Bean
public DictCache dictCache(RedisTemplate<String,String> redisTemplate) {
return new DictCache(redisTemplate);
}

}
public class DictCache {

private final String KEY_COMMON_DICT_NODE = "common:dict:node:data"; //保存的是节点数据列表
private final String KEY_COMMON_DICT_VALUE = "common:dict:value:data"; //保存的是键值对数据列表
private final String KEY_COMMON_DICT_TIME = "common:dict:time"; //保存的是字典最后变更的时间

protected static Map<String, Map<String, String>> cache = new HashMap(); //字典本地缓存(速度更快)

private String lastUpdateTime = "";
private RedisTemplate<String,String> systemRedisTemplate;

public DictCache(RedisTemplate<String,String> redisTemplate) {
this.systemRedisTemplate = redisTemplate;
reload();
}

//开放接口:通过节点 和 键 获取 值
public String getDictNodeValue(String dict, String code) {
if(cache.containsKey(dict)) {
return cache.get(dict).get(code);
}else{
return null;
}
}

//通过节点获取键值对列表(有序)
public Map<String, String> getDictNodeList(String dict) {
if(cache.containsKey(dict)) {
return cache.get(dict);
}else{
return new HashMap();
}
}

//定时监控字典数据是否变更
@Scheduled(fixedDelay = 1000)
public synchronized void reload() {
String value = systemRedisTemplate.opsForValue().get(KEY_COMMON_DICT_TIME);
if(value == null || lastUpdateTime.equals(value)) return; //字典数据是否变更
this.id = value;

Map<String, Map<String, String>> cache = new HashMap();
//临时的字典缓存
Map<String, JSONObject> dictCache = new HashMap();
String json = systemRedisTemplate.opsForValue().get(KEY_COMMON_DICT_VALUE);
if(json == null) {
log.warn("Dict Tree Data IS NULL.");
return;
}
JSONArray data = JSONArray.parseArray(json);
for(int i = 0; i < data.size(); i++) {
JSONObject item = data.getJSONObject(i);
dictCache.put(item.getString("id"), item);
}

//dict_node节点缓存
String did = null;
Map<String, String> codeCache = new HashMap();
json = systemRedisTemplate.opsForValue().get(KEY_COMMON_DICT_NODE);
if(json == null) {
log.warn("Dict Node Data IS NULL.");
return;
}
JSONArray nodeList = JSONArray.parseArray(json);
for(int i = 0; i < nodeList.size(); i++) {
JSONObject item = nodeList.getJSONObject(i);
String code = "";
did = item.getString("tid");
if(!codeCache.containsKey(did)) {
code = "";
JSONObject temp = null;
while(dictCache.containsKey(did)) {
temp = dictCache.get(did);
did = temp.getString("pid");
code = temp.getString("code") + "." + code;
}
if(code.length() > 0) {
code = code.substring(0, code.length() - 1);
}
codeCache.put(did, code);
}else{
code = codeCache.get(did);
}

if(!cache.containsKey(code)) {
cache.put(code, new LinkedHashMap());
}

cache.get(code).put(item.getString("code").trim(), item.getString("value").trim());
}

DictCache.cache = cache;
log.info("Reload Dict Data Success.");
}

}


项目中的使用

action/service中注入dictCache

@Autowired
private DictCache dictCache;

获取某一节点下的所有键值,如下:

Map<String,String> qqLoginSdkData = dictCache.getDictNodeList("SDK.QQ.LOGIN");


获取某一节点下 某一键 的值,如下:

String appid = dictCache.getDictNodeValue("SDK.QQ.LOGIN", "appid");


应用实例:

  1. 存放第三方的 appid和appkey等信息
  2. 存放 状态信息(0-禁用 1-启用)、打开方式(_blank-新窗口 _self-当前窗口...)、... 可以直接返回一个节点列表给前端,展示给用户,让其选择
  3. ...


Java枚举"已过时"-改用“数据字典”

实例效果图

java枚举
分享到:

您可能还会对下面的文章感兴趣: