对字典表的数值转换为字符串

导出列表

导出列表的时候,我发现实体类中的性别是存放的字典表的数值,无法转换为字符串,但是客户要求必须显示为男或者女,

@ExcelProperty("性别")
@Schema(description = "性别")
private Integer sex;

我想了好几种办法,最笨的办法就是对查询出来的列表进行遍历,然后把对应的字段值取出来,去数据库中查询对应的字符串,这种方式特别繁琐,而且一个实体类中,可能引用了多种字典表的值,这样就需要挨个去查询数据库,特别繁琐,我有20个表需要导出,我得话费一整天的时间去写导出,写完的导出功能,在性能方面也很慢。数据库大的话导出的时候需要花费几十秒甚至是分钟级别。

二,我也想到从缓存中取值,但是发现缓存中存放的字典值是这种格式

也就是把acceptManage类型字典数据全部放到一个value中,我如果从缓存中拿,我需要根据key查询到对应的value,再对value进行处理,value里面是一个集合,我需要对集合中的字典对象遍历,根据sex的数值,去这个对象中拿到对应的字符串值,这样处理起来也是稍微麻烦,而且性能方面,也会很慢,因为acceptManage这个key中,对应的value,可能包含100个字典对象,我导出的没一条数据,都需要在这100个对象中取遍历取值,我导出100条数据,就需要遍历1万次,效率也是无法提高。

三:我突然看到easyExcel中的注解@ExcelProperty("性别"),心想easyExcel中有没有注解可以对字典表中的值进行转换,查询了一圈,只看有个转换器可以用,但是每一个字段的转换,我都需要创建一个转换器来使用,还是麻烦。

愁死人了

四:我睡了一觉之后,我想到了反射,因为@ExcelProperty这个注解也是基于反射实现的,我也可以,因此我自定义了一个注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictLabel {
   /**
    * 字典类型
    * @return
    */
    String dictType();
}

接下来是对注解的处理

/**
 * 用于处理字典值转换
 */
@Component
public class DictLabelAspect {

   private  SysDictItemService sysDictItemService;
   private HashMap<String, String> dictItemHashMap = new HashMap<>();
   public DictLabelAspect(SysDictItemService sysDictItemService){
      this.sysDictItemService = sysDictItemService;
      constructorDictItem();
   }

   /**
    * 初始化字段表中的数据到本地缓存中
    * @return
    */
   public String constructorDictItem() {
      // 从数据库中获取字典项数据
      List<SysDictItem> dictList = sysDictItemService.list();

      // 使用Stream流处理数据并初始化字典项HashMap
      dictList.stream()
            .forEach(sysDictItem -> {
               String itemValue = sysDictItem.getItemValue();
               String dictType = sysDictItem.getDictType();
               String label = sysDictItem.getLabel();
               dictItemHashMap.put(dictType + ":" + itemValue, label);
            });

      // 返回初始化完成提示信息
      return "字典表数据初始化完成!";
   }

   /**
    * 为对象的带有@DictLabel注解的字段应用字典标签
    *
    * @param obj 要处理的对象
    * @return 处理后的对象
    */
   public Object applyDictLabels(Object obj) {
      if (obj == null) {
         return obj;
      }
      // 用于缓存字典项的哈希映射
      Class<?> clazz = obj.getClass();
      Field[] fields = clazz.getDeclaredFields();

      try {
         for (Field field : fields) {
            field.setAccessible(true);
            // 检查字段是否带有@DictLabel注解
            if (!field.isAnnotationPresent(DictLabel.class)) {
               continue;
            }
            Object value = field.get(obj);
            if (value == null) {
               continue;
            }
            DictLabel annotation = field.getAnnotation(DictLabel.class);
            String dictKey = annotation.dictType() + ":" + value.toString();
            if (dictItemHashMap.containsKey(dictKey)) {
               // 如果缓存中存在对应的字典项值,则直接使用缓存值
               String dictLabelValue = dictItemHashMap.get(dictKey);
               field.set(obj, dictLabelValue);
            } else {
               // 否则查询数据库获取字典项值
               String dictLabelValue = sysDictItemService.findByDictTypeAndItemValue(annotation.dictType(), value.toString());
               if (dictLabelValue != null) {
                  // 将查询结果加入缓存并设置字段值
                  dictItemHashMap.put(dictKey, dictLabelValue);
                  field.set(obj, dictLabelValue);
               }
            }
         }
      } catch (IllegalAccessException e) {
         // 处理反射访问权限异常
         e.printStackTrace();
      }

      // 恢复字段访问权限
      for (Field field : fields) {
         field.setAccessible(false);
      }

      return obj;
   }
}

处理逻辑是:先根据反射,读取这个类上的字段是否加上了DictLabel注解,没有加上的去下一个字段查找。

找到之后取出来DictLabel注解里面对应的dictType值,这个就是字段表里的值

/**
	* 性别
	*/
	@DictLabel(dictType = "gender")
	@ExcelProperty("性别")
    private String gender;

这个sex的值就是字典表中的item_value值,我要得到对应的lable值

查出来这俩值后,去数据库查询对应的lable值

	@Override
	public String findByDictTypeAndItemValue (String dictType , String itemValue) {
		QueryWrapper< SysDictItem > sysDictItemQueryWrapper = new QueryWrapper<>();
		sysDictItemQueryWrapper.eq("item_value",itemValue);
		sysDictItemQueryWrapper.eq("dict_type",dictType);
		return this.getOne(sysDictItemQueryWrapper).getLabel();
	}

之后把这个值设置到sex中

field.set(obj, dictLabelValue);

完成之后,发现代码写起来不多,但是导出性能还是差。

缓存不能用,直接数据查询也不行。

只要在本地自己建一个缓存,于是自己创建了一个hashMap,用来把数据库查到的值直接放入到缓存中,这样下次导出的时候就不会查询数据库了,后来一想,用户在导出的时候,谁会进行重复导出了,一般只导出一遍

接下来我就想到能不能本地缓存预热,项目在启动的时候,就把字典表中的数据全部放入到缓存中,这样导出的时候,直接再本地内存操作,不需要走数据库了。

因此我在构造函数里,直接先查出来,并放入缓存。这时候,导出的时候的性能大幅度提升

这是接口

@ResponseExcel
	@GetMapping("/export")
	@PreAuthorize("@pms.hasPermission('cbResident_cbResident_export')")
	public List<WxCbResidentExcel> export(WxCbResidentEntity wxCbResident, Long[] ids) {
		List< WxCbResidentEntity > list = wxCbResidentService.list(Wrappers.lambdaQuery(wxCbResident).in(ArrayUtil.isNotEmpty(ids) , WxCbResidentEntity::getId , ids));
		// 使用 Stream API 简化代码,并显式指定泛型类型
		List< WxCbResidentExcel > WxCbResidentExcels = BeanUtil.copyToList(list , WxCbResidentExcel.class);
		List< WxCbResidentExcel > residentList = WxCbResidentExcels.stream()
				.map((wx) -> (WxCbResidentExcel) dictLabelAspect.applyDictLabels(wx))
				.collect(Collectors.toList());
		return residentList;

	}

这个接口中,先会查询出来对应的数据,接下来需要转换为excel的实体类,为什么要转换呢?

因为实体类中,有的人会把sex的类型写成Integer类型,这样的话,在反射过程中,是无法给sex字段赋值的。所以再excel的实体类中,所有的字段的整型类型应该全部写为String,这样在导出的时候反射可以实现。

我知道可以把自己写的注解@DictLabel(dictType = "gender")按照@ExcelProperty("性别")的处理方式来处理,但是我不会找他的源码来查看,我这个注解是通过方法调用来实现,我觉得可以按照aop代理的方式,但是时间来不及了,得先实现功能了,后序有时间了再优化吧

代码逻辑可能写的不好,欢迎大家指导,谢谢

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值