【Java】将枚举类转换为Redis字典缓存

管理员

字典翻译框架实现看这篇:

https://www.cnblogs.com/mindzone/p/16890632.html

  

枚举的特性

首先是枚举的一些特性:

1、枚举实例直接在枚举类中声明

2、重载构造器,可以直观表示枚举的属性信息

3、枚举类的方法:

  - values 可以获取枚举类的所有枚举项(数组)

  - 枚举实例 ordinal() 方法获取枚举的下标值

  - 枚举实例 name() 方法 获取你声明的实例名

4、枚举是完全单例的

5、枚举不可以作为通常对象响应给Web传数据,必须自己提取信息转换

package cn.hyite.amerp.common.state;

import cn.hyite.amerp.common.state.intf.IEnumStateConvert;
import lombok.Getter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 盖印状态枚举类
 * @author OnCloud9
 * @version 1.0
 * @project amerp-server
 * @date 2022年11月23日 13:31
 */
@Getter
public enum StampStateEnum implements IEnumStateConvert {
    UNUSED("未盖印", "0"),
    DONE("已盖印", "1"),
    CANCEL("已取消", "2"),
    ;

    public static final String CATE = "STAMP_STATE";
    private final String code;
    private final String name;

    StampStateEnum(String name, String code) {
        this.name = name;
        this.code = code;
    }

    @SuppressWarnings("all")
    @Override
    public Map<String, String> getItem() {
        Map<String, String> instance = new ConcurrentHashMap<>();
        instance.put("instName", name());
        instance.put("instIndex", String.valueOf(ordinal()));
        instance.put("name", name);
        instance.put("code", code);
        return instance;
    }
}

  

基于枚举,我们可以设置字典的属性,相比普通类更直观的表现【字典】

    UNUSED("未盖印", "0"),
    DONE("已盖印", "1"),
    CANCEL("已取消", "2"),

  

我最初的设计几乎和DictDTO没区别,关键字段是 字典编号,字典名称,字典类别

存储方式就直接在应用中,不需要Redis,缓存的抽象设计沿用Redis的方案

package cn.cloud9.server.test.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @author OnCloud9
 * @description
 * @project tt-server
 * @date 2022年11月06日 下午 06:25
 */
@Getter
@AllArgsConstructor
public enum MyType {

    GROUP1_TYPE1("a类型1", 1001, MyType.KEY_GP1),
    GROUP1_TYPE2("a类型2", 1002, MyType.KEY_GP1),
    GROUP1_TYPE3("a类型3", 1003, MyType.KEY_GP1),

    GROUP2_TYPE1("b类型1", 1001,  MyType.KEY_GP2),
    GROUP2_TYPE2("b类型2", 1002,  MyType.KEY_GP2),
    GROUP2_TYPE3("b类型3", 1003,  MyType.KEY_GP2),

    GROUP3_TYPE1("c类型1", 1001, MyType.KEY_GP3),
    GROUP3_TYPE2("c类型2", 1002, MyType.KEY_GP3),
    GROUP3_TYPE3("c类型3", 1003, MyType.KEY_GP3),
    ;

    private final String name;
    private final Integer value;
    private final String cate;

    private static final Map<String, List<MyType>> typeList = new ConcurrentHashMap<>();
    private static final Map<String, String> typeMap = new ConcurrentHashMap<>();

    public static final String SEPARATOR = "@";
    public static final String KEY_GP1 = "GT-1001";
    public static final String KEY_GP2 = "GT-1002";
    public static final String KEY_GP3 = "GT-1003";

    static {
        for (MyType myType : MyType.values()) {
            final String myTypeCate = myType.getCate();
            final String myTypeName = myType.getName();
            final Integer myTypeValue = myType.getValue();

            final String key = myTypeCate + SEPARATOR + myTypeValue;
            typeMap.put(key, myTypeName);

            List<MyType> myTypes = typeList.get(myTypeCate);
            if (CollectionUtils.isEmpty(myTypes)) {
                myTypes = new ArrayList<>();
                typeList.put(myTypeCate, myTypes);
            }
            myTypes.add(myType);
        }
    }

    /**
     * 按枚举类别获取枚举集合
     * @param cate
     * @return
     */
    public static List<MyType> getTypeListByCate(String cate) {
        return typeList.get(cate);
    }

    /**
     * 按枚举类别获取枚举集合(响应用)
     * @param cate
     * @return
     */
    public static List<Map<String, String>> getItemListByCate(String cate) {
        final List<MyType> myTypes = typeList.get(cate);
        if (CollectionUtils.isEmpty(myTypes)) return Collections.EMPTY_LIST;
        final ArrayList<Map<String, String>> items = new ArrayList<>(myTypes.size());
        for (MyType myType : myTypes) {
            items.add(myType.getItem());
        }
        return items;
    }

    /**
     * 按枚举类别和code获取名称
     * @param cate
     * @param code
     * @return
     */
    public static String getNameBy(String cate, Integer code) {
        String key =  cate + SEPARATOR + code;
        return typeMap.get(key);
    }

    public Map<String, String> getItem() {
        Map<String, String> item = new ConcurrentHashMap<>();
        item.put("instName", name());
        item.put("instIndex", String.valueOf(this.ordinal()));
        item.put("name", this.name);
        item.put("value", String.valueOf(this.value));
        return item;
    }
}

  

但是老板认为一个枚举类不应该枚举所有的状态种类,一个枚举类就只负责一个特定状态类别:

例如:

1、审批状态

2、盖印状态

3、借还状态

翻译交给另外一个工具类来完成

加上我们已经习惯字典翻译框架的开发方式

在尽可能不动框架的情况下,就通过工具类来完成:

package cn.hyite.amerp.common.util;

import cn.hyite.amerp.common.state.ApproveStateEnum;
import cn.hyite.amerp.common.state.LendStateEnum;
import cn.hyite.amerp.common.state.StampStateEnum;
import cn.hyite.amerp.system.common.dict.dto.DictDTO;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 枚举缓存工具类,沿用Redis字典缓存方式初始化缓存
 * @author OnCloud9
 * @version 1.0
 * @project amerp-server
 * @date 2022年11月24日 09:42
 */
public class StateEnumUtil {
    private static final String DICT_CACHE_LISTS = "dict_cache_lists";
    private static final String DICT_CACHE_KEYS = "dict_cache_keys";
    private static final String SEPARATOR = "|";
    private static final String DEFAULT_TABLE = "sys_co_dict";
    private static final List<DictDTO> enumList = new ArrayList<>();

    static {
        List<DictDTO> approveStateList = transToDictList(ApproveStateEnum.values(), ApproveStateEnum::getCode, ApproveStateEnum::getName, ApproveStateEnum.CATE);
        List<DictDTO> stampStateList = transToDictList(StampStateEnum.values(), StampStateEnum::getCode, StampStateEnum::getName, StampStateEnum.CATE);
        List<DictDTO> lendStateList = transToDictList(LendStateEnum.values(), LendStateEnum::getCode, LendStateEnum::getName, LendStateEnum.CATE);

        enumList.addAll(approveStateList);
        enumList.addAll(stampStateList);
        enumList.addAll(lendStateList);
    }

    public static void initialize() {
        /* 1、获取RedisTemplate */
        StringRedisTemplate stringRedisTemplate = SpringContext.getBean(StringRedisTemplate.class);
        HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();

        /* 2、准备redis装载容器 */
        Map<String, String> tmpMap = new HashMap<>();
        Map<String, List<DictDTO>> tmpListMap = new HashMap<>();

        /* 3、将枚举按字典的方式拼装 */
        for (DictDTO dto : enumList) {
            String diCateIdent = dto.getDiCateIdent();
            String diCode = dto.getDiCode();

            /* hKey -> 字典表名 | 代码类别 | 代码编号  */
            String key = DEFAULT_TABLE + SEPARATOR + diCateIdent + SEPARATOR + diCode;
            tmpMap.put(key.toUpperCase(), GsonUtil.toJson(dto));

            /* hKey -> 字典表名 | 代码类别 */
            String key2 = DEFAULT_TABLE + SEPARATOR + diCateIdent;
            List<DictDTO> list = tmpListMap.get(key2.toUpperCase());
            if (CollectionUtils.isEmpty(list)) {
                list = new ArrayList<>();
                tmpListMap.put(key2.toUpperCase(), list);
            }
            list.add(dto);
        }

        /* 4、推入Redis中,不做删除操作,共用缓存Key */
        opsForHash.putAll(DICT_CACHE_KEYS, tmpMap);
        opsForHash.putAll(DICT_CACHE_LISTS, toJsonMap(tmpListMap));
        LogUtil.COUNT_LOG.info("加载枚举缓存字典到Redis完毕...");
    }

    private static Map<String, String> toJsonMap(Map<String, ?> tmpListMap) {
        Map<String, String> jsonList = new HashMap<>(tmpListMap.size());
        for (Map.Entry<String, ?> entry : tmpListMap.entrySet()) {
            jsonList.put(entry.getKey(), GsonUtil.toJson(entry.getValue()));
        }
        return jsonList;
    }

    /**
     * 将枚举类集合转换为字典集合
     * @param eArr 枚举类数组
     * @param diCodeFunc 设置diCode取值方法
     * @param diNameFunc 设置diName取值方法
     * @param diCateIdent 设置diCateIdent值
     * @return List<DictDTO>
     * @author OnCloud9
     * @date 2022/11/24 10:40
     */
    private static <StateEnum extends Enum, FieldType> List<DictDTO> transToDictList(StateEnum[] eArr, Function<StateEnum, FieldType> diCodeFunc, Function<StateEnum, FieldType> diNameFunc, String diCateIdent) {
        List<DictDTO> dictList = new ArrayList<>(eArr.length);
        for (StateEnum e : eArr) {
            DictDTO dict = new DictDTO();
            /* 1、设置字段编码 */
            FieldType diCode = diCodeFunc.apply(e);
            dict.setDiCode(String.valueOf(diCode));

            /* 2、设置字段名称 */
            FieldType diName = diNameFunc.apply(e);
            dict.setDiName(String.valueOf(diName));

            /* 3、设置字段类别 */
            dict.setDiCateIdent(diCateIdent);

            /* 4、装载 */
            dictList.add(dict);
        }
        return dictList;
    }
}

  

在处理的钩子方法这里加上工具类的初始化:

package cn.hyite.amerp.common.cache;

import cn.hyite.amerp.common.cache.impl.RedisCacheDictServiceImpl;
import cn.hyite.amerp.common.cache.intf.CacheDictService;
import cn.hyite.amerp.common.util.SpringContext;
import cn.hyite.amerp.common.util.StateEnumUtil;

/**
 * 业务数据缓存管理
 *
 * @version 1.0
 * @project portal-server
 * @author lianss
 * @date 2019年2月25日 下午6:19:36
 */
public abstract class CacheManager {

    /**
     * 刷新缓存
     *
     * @author lianss
     * @date :2019年2月25日 下午6:24:49
     */
    public static void refreshCache() {
        Thread refreshThread = new Thread(new RefreshCache());
        refreshThread.start();
    }
}

class RefreshCache extends Thread {
    @Override
    public void run() {
        CacheDictService cacheDictService = SpringContext.getBean(RedisCacheDictServiceImpl.class);
        cacheDictService.init();
        StateEnumUtil.initialize();
    }
}

  

这里的难题主要是有各种各样的枚举类,需要有一个统一的方法来转换

提供枚举类,和字典属性的取值方法,可以生成字典集合

但是我不会写Lambda方法入参啊。。。,然后参考了一下MybatisPlus的lambdaQuery()源码,写出来了,

    /**
     * 将枚举类集合转换为字典集合
     * @param eArr 枚举类数组
     * @param diCodeFunc 设置diCode取值方法
     * @param diNameFunc 设置diName取值方法
     * @param diCateIdent 设置diCateIdent值
     * @return List<DictDTO>
     * @author OnCloud9
     * @date 2022/11/24 10:40
     */
    private static <StateEnum extends Enum, FieldType> List<DictDTO> transToDictList(StateEnum[] eArr, Function<StateEnum, FieldType> diCodeFunc, Function<StateEnum, FieldType> diNameFunc, String diCateIdent) {
        List<DictDTO> dictList = new ArrayList<>(eArr.length);
        for (StateEnum e : eArr) {
            DictDTO dict = new DictDTO();
            /* 1、设置字段编码 */
            FieldType diCode = diCodeFunc.apply(e);
            dict.setDiCode(String.valueOf(diCode));

            /* 2、设置字段名称 */
            FieldType diName = diNameFunc.apply(e);
            dict.setDiName(String.valueOf(diName));

            /* 3、设置字段类别 */
            dict.setDiCateIdent(diCateIdent);

            /* 4、装载 */
            dictList.add(dict);
        }
        return dictList;
    }