注解转换字典表内容

业务介绍

数据库存入的是value值(或者是id),需要将查询出来的数据的字典值与内容进行转换,内容进行展示。

实现方式

1.在sql使用子查询
2.通过springAop切面+注解的方式

优缺点

1.优点:简单,直接 缺点:sql太多,难以维护
2.优点:不需要在每个业务sql中子查询sql,反射与业务交织在一起,代码不好维护(用了大量的递归---为了实现属性中存在对象或者集合对象的情况)

实现思路

通过对返回结果进行处理,查看对应的属性上有没有需要转换注解修饰,有的话则获取,拿到对应的值,通过sql查询出来,在对转换注解修饰的属性进行赋值

代码

转换注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface DataDict {
    /**
     * 赋值的字典字段
     * @return
     */
    String dictField() default "";

    /**
     * 字典code
     * @return
     */
    String dictCode() default "";

}
字典处理类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DictDto {
    /**
     * 字典编码
     */
    private String code;
    /**
     * 当前字段名称
     */
    private String name;

    /**
     * 需要赋值的字段名称
     */
    private String dictField;
}
字典工具类
@Component
public class DictUtil {

    @Resource
    private PlmBaseMapper plmBaseMapper;

    @SneakyThrows
    public Boolean dealDict(List<DictDto> dictParams, Map<String, String> dictInfoMap) {
        if (dictParams.size() == 0) {
            return Boolean.FALSE;
        }
        List<String> codes = getCodes(dictParams);
        //根据code 获取对应的字典内容---可以放在redis中
        List<DictVO> dictList = plmBaseMapper.findDictList(codes);

        if (dictList == null || dictList.isEmpty()) {
            return Boolean.FALSE;
        }
        //先把字典值转成map
        for (DictVO dictVO : dictList) {
            dictInfoMap.put(dictVO.getCode() + "_" + dictVO.getValue(), dictVO.getName());
        }
        return Boolean.TRUE;
    }


    /**
     * 获取实体中所有需要翻译的code
     *
     * @param dictDtos
     * @return
     */
    private List<String> getCodes(List<DictDto> dictDtos) {
        if (CollectionUtil.isEmpty(dictDtos)) {
            return new ArrayList<>();
        }
        List<String> collect = dictDtos.stream()
                .map(dictDto -> dictDto.getCode()).collect(Collectors.toList());
        collect.stream().distinct();
        return collect;
    }


}
参数获取代码
@Slf4j
public class DataParamUtil {
    /**
     * 用户key
     */
    public static final String USER_KEY = "USER_KEY";
    /**
     * 字典key
     */
    public static final String DICT_KEY = "DICT_KEY";

    /**
     * 现在实现了用户和字典,后期有其他的继续往后追加
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, List> getMapping(Object obj) throws IllegalAccessException {
        Map<String, List> resultMap = new HashMap<>(2);

        //字典信息
        List<DictDto> dictDtoList = new ArrayList<>();

        //需要判断是否为集合,集合包含List<List<User>>的情况
        loopMapping(obj, dictDtoList);

        resultMap.put(DICT_KEY, dictDtoList);
        return resultMap;
    }

    private static void loopMapping(Object obj,  List<DictDto> dictDtoList) throws IllegalAccessException {
        Field[] fields;
        if (obj instanceof Collection) {
            Collection olist = ((Collection) obj);
            if (olist == null || olist.size() == 0) {
                return;
            }
            for (Object temp : olist) {
                loopMapping(temp,dictDtoList);
            }
        } else {
            //单对象
            fields = obj.getClass().getDeclaredFields();
            loopFields(fields, obj, null, dictDtoList);
        }
    }

    /**
     * 增加一个前缀,表示属性值中包含集合其他属性的情况
     * 反射性能消耗资源大,多个注解的获取尽量在一次循环中获取(设置值也是一样)
     * 除了获取对应注解的值,也需要判断当前属性类型是否为对象或者是集合,分别进行处理
     *
     * @param fields
     * @param obj
     * @param prefix
     * @param userDtoList
     * @param dictDtoList
     * @throws IllegalAccessException
     */
    private static void loopFields(Field[] fields, Object obj, String prefix,
                                  List<DictDto> dictDtoList) throws IllegalAccessException {

        for (Field field : fields) {
            //去掉安全检查
            field.setAccessible(true);

            if (field.isAnnotationPresent(DataDict.class)) {
                //获取字典信息
                getDictMapping(field, prefix, dictDtoList);
                continue;
            }
            //判断该字段是否为list
            if (Collection.class.isAssignableFrom(field.getType())) {
                //是集合,又需要循环获取每个字段
                Collection olist = (Collection) field.get(obj);
                if (olist == null || olist.size() == 0) {
                    return;
                }
                for (Object temp : olist) {
                    fields = temp.getClass().getDeclaredFields();
                    //循环获取字段值
                    loopFields(fields, temp, field.getName(), dictDtoList);
                }
                continue;
            }

            //属于对象--包含当前包下的数据--写死类型
            boolean isObject = field.getType().toString().contains("com.*.model");
            if (isObject) {
                Class clazz = field.getClass();
                Field[] declaredFields = clazz.getDeclaredFields();
                loopFields(declaredFields, obj, field.getName(),dictDtoList);
            }
        }

    }


    /**
     * 获取字典映射信息
     *
     * @param field
     * @param dictDtoList
     * @throws IllegalAccessException
     */
    private static void getDictMapping(Field field, String prefix, List<DictDto> dictDtoList) {
        DataDict dataDict = field.getAnnotation(DataDict.class);
        String curName = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
        dictDtoList.add(new DictDto(dataDict.dictCode(), curName, dataDict.dictField()));
    }
}
设置结果代码
@Component
@Slf4j
public class DataResultUtil {

    @Autowired
    private DictUtil dIctUtil;


    /**
     * 存放对应字典信息
     */
    private Map<String, String> dictInfoMap = new HashMap<>();


    /**
     * 字典参数存放
     */
    private List<DictDto> dictParams = new ArrayList<>();


    /**
     * 思路:获取结果中的数据,然后反射获取对应的注解,获取注解的值数据,通过注解值,查询对应的数据,然后在设置到对应的字段上
     * 结果:分为分页和单结果,主要是为了获取数据的方式不一样
     * 需要考虑:如果结果对象中包含其他对象,包含集合对象的情况,需要将子属性也要处理
     *
     * @param resultR
     * @param annotation
     * @return
     * @throws Throwable
     */
    public Object translation(Object resultR, DataDeal annotation) throws Throwable {

        // 拿到要返回的数据--这里可以打印下看看内容
        if (ObjectUtil.isNull(resultR)) {
            return resultR;
        }

        Object result = ((RestResult) resultR).getData();

        if (result instanceof PageVO) {
            // 分页的情况
            ((RestResult) resultR).setData(translationPage(result, annotation));
            return resultR;
        }
        if (setParamValue(result)) {
            return result;
        }
        result = translate(result, annotation);
        ((RestResult) resultR).setData(result);

        return resultR;
    }

    /**
     * 给字典赋值
     *
     * @param result
     * @return
     * @throws IllegalAccessException
     */
    private boolean setParamValue(Object result) throws IllegalAccessException {
        // 获取所有配置注解的code 和实体字段
        Map<String, List> mapping = DataParamUtil.getMapping(result);
        //字典参数
        dictParams = mapping.get(DataParamUtil.DICT_KEY);

        //两者都没有字段需要转换,直接返回
        if (dictParams.isEmpty() ) {
            return true;
        }
        //字典参数
        Boolean dictPass = dIctUtil.dealDict(dictParams, dictInfoMap);

        if (!dictPass &&) {
            //不通过,直接返回
            return true;
        }
        return false;
    }

    /**
     * 转换单个数据
     *
     * @param result
     * @param annotation
     * @return
     */
    private Object translate(Object result, DataDeal annotation) throws IllegalAccessException {
        //处理数据信息
        if (result instanceof List || result instanceof ArrayList) {
            for (Object entity : (List) result) {
                assign(entity, annotation);
            }
        } else {
            assign(result, annotation);
        }

        return result;
    }


    /**
     * 给字段赋值,这里处理字典和用户参数:后面如果还有处理逻辑,继续往后追加集合
     *
     * @param entity
     * @param annotation
     */
    private void assign(Object entity, DataDeal annotation) {
        int dictSize = dictParams.size();
        int userSize = userParams.size();

        int loopIndex = dictSize > userSize ? dictSize : userSize;

        for (int i = 0; i < loopIndex; i++) {
            //字典处理
            if (dictSize > i && annotation.dict()) {
                setDictParam(entity, dictParams, i);
            }
            //用户处理
            if (userSize > i && annotation.user()) {
                setUserParam(entity, userParams, i);
            }
        }

    }

    /**
     * 设置用户参数
     *
     * @param entity
     * @param userParams
     */
    private void setUserParam(Object entity, List<UserDto> userParams, int i) {
        UserDto userDto = userParams.get(i);
        String userId = userDto.getUserId();
        String userField = userDto.getDictField();
        String name = userDto.getName();
        try {
            String[] names = name.split("\\.");
            loopFields(0, entity, names, userField, userId, userInfoMap);
        } catch (Exception e) {
            log.error("设置值出错", e.getMessage());
        }
    }


    /**
     * 循环递归设置list中的数据
     *
     * @param curIndex
     * @param entity
     * @param names
     * @param field
     * @param key
     * @param map
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private void loopFields(int curIndex, Object entity, String[] names, String field, String key, Map<String, String> map)
            throws NoSuchFieldException, IllegalAccessException {

        Class c = entity.getClass();
        if (null == c) {
            return;
        }

        if (curIndex == names.length - 1) {
            setFieldValue(curIndex, entity, names, field, key, map);
            return;
        }

        //下面还有数据
        Field f = c.getDeclaredField(names[curIndex]);
        f.setAccessible(true);

        Object objList = f.get(entity);
        if (Objects.isNull(objList)) {
            return;
        }

        objList = getObj(objList);

        if (Objects.isNull(objList)) {
            return;
        }
        loopFields(++curIndex, objList, names, field, key, map);

    }

    /**
     * 设置字段值
     *
     * @param curIndex
     * @param entity
     * @param names
     * @param field
     * @param key
     * @param map
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private void setFieldValue(int curIndex, Object entity, String[] names, String field, String key, Map<String, String> map)
            throws NoSuchFieldException, IllegalAccessException {
        Class c = entity.getClass();
        //需要赋值了
        if (c != null) {
            Field f = c.getDeclaredField(names[curIndex]);
            f.setAccessible(true);
            Object preValue = f.get(entity);
            if (ObjectUtil.isNotNull(preValue)) {
                // 需要赋值的字段
                Field fvalue = c.getDeclaredField(field);
                fvalue.setAccessible(true);
                //如果字段值不为空得话,就直接返回
                if (null != fvalue.get(entity)) {
                    return;
                }
                //注意key拼接的格式
                String fieldValue = map.getOrDefault(key + "_" + preValue.toString(), null);
                fvalue.set(entity, fieldValue);
            }
        }
    }


    /**
     * 设置字典参数
     *
     * @param entity
     * @param dictParams
     * @param i
     */
    private void setDictParam(Object entity, List<DictDto> dictParams, int i) {
        DictDto dictDto = dictParams.get(i);
        String code = dictDto.getCode();
        String name = dictDto.getName();
        String dictField = dictDto.getDictField();
        try {
            String[] names = name.split("\\.");
            loopFields(0, entity, names, dictField, code, dictInfoMap);
        } catch (Exception e) {
            log.error("设置值出错", e.getMessage());
        }
    }


    /**
     * 转换多个数据
     *
     * @param resultR
     * @param annotation
     * @return
     */
    private Object translationPage(Object resultR, DataDeal annotation) throws Throwable {
        long s = System.currentTimeMillis();
        log.info("开始处理字典....");


        if (resultR == null) {
            return resultR;
        }
        PageVO page = (PageVO) resultR;
        Object result = page.getData();
        if (ObjectUtil.isNull(result)) {
            return resultR;
        }
        List dealList = (List) result;

        List resultList = new ArrayList();

        if (setParamValue(result)) {
            return result;
        }

        for (Object temp : dealList) {
            resultList.add(translate(temp, annotation));
        }

        page.setData(resultList);


        // 返回处理后的数据
        log.info("字典处理完毕....结束时间为:{}毫秒", System.currentTimeMillis() - s);

        return page;
    }

    /**
     * 判断单个数据是集合还是一个对象,只获取一个对象
     *
     * @param result
     * @return
     */
    public static Object getObj(Object result) {
        Object obj = null;
        if (result instanceof Collection) {
            Collection olist = ((Collection) result);
            if (olist == null || olist.size() == 0) {
                return null;
            }
            Iterator iterator = olist.iterator();
            while (iterator.hasNext()) {
                obj = iterator.next();
                break;
            }
        } else {
            obj = result;
        }
        return obj;
    }

}

切面注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataDeal {
    /**
     * 字典处理
     * @return
     */
    boolean dict() default false;

    /**
     * 用户处理
     * @return
     */
    boolean user() default false;
}
切面处理类
@Component
@Aspect
public class DataAspect {

    @Autowired
    private DataResultUtil dataResultUtil;


    @AfterReturning(pointcut = "@annotation(com.*.common.annotation.DataDeal)", returning = "result")
    public Object dealData(JoinPoint pjp, Object result) throws Throwable {

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        DataDeal annotation = method.getAnnotation(DataDeal.class);
        if (annotation == null || (!annotation.dict() && !annotation.user())) {
            return result;
        }
        //存在注解,判断注解的值然后调用对应的方法
        return dataResultUtil.translation(result, annotation);
    }

}

具体使用情况

image.png

备注

我是实现了用户转换的,代码中有关于用户信息的,请自行去掉
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容