VUE+ElementUI+springboot项目中 数据字典表全面解析与使用场景 PO转VO时进行转义 令前端下拉列表动态生成 前端VUE表格列的动态转义
1 产生数据字典表的原因
现在有这么一张表,其中的大部分字段都是固定值,比如订单分类、类型、动作、状态、业务类型
如果都以字符串的形式存入数据库中,就存在数据冗余的情况,而且如果希望修改某一字段的字符串(比如从“来自网络的订单”改为“网络订单”),那就不是修改一条,而是需要修改许多条
因此通常以数字的格式存入表中,此时原表的数据如图
而每个字段的数字与字符串的对应关系单独建一张表进行管理,示意图如下
但是如果字段很多,比如上面有5个字段,那么进行转义时就要连接6张表进行查询,而这并不是为了业务进行连表,只是单纯需要转义,这就非常麻烦了
每个字段分别建一个表也很繁琐,不方便管理,此时就可以考虑把这些字段全部统合在一张表里——数据字典表
数据字典表 能起到减少代码量以及灵活修改固定值的作用
2 数据字典表建表代码
2.1 一张表
如图,就是数据字典表,每个字段根据type进行区分,同一type的数据再根据type_value进行区分,并与type_text构成对应关系用于转义
数据字典表建表语句一览
CREATE TABLE `sales_dictionary` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) DEFAULT NULL COMMENT '类型 1.订单动作 2 订单状态 3 业务类型 4 订单分类 5 订单类型 6 配送方式 7 支付方式',
`type_value` int(11) DEFAULT NULL COMMENT '值',
`type_text` varchar(255) DEFAULT NULL COMMENT '文本',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
2.2 两张表
上面是简单的建表,将类型、key、value统合在一张表,方便直接查询使用,但很明显,时间久了谁记得type中的数字对应什么,如果直接改用字符串,又会陷入“改一个字符串就要修改多条记录”的情况
因此实际上应该拆为两张表。
两张表的建表方式以及查询方式具体见SpringBoot+Redis实现数据字典_蹊源的奇思妙想的博客-CSDN博客_springboot缓存数据字典
下面是摘录:
3 查询数据库的数据字典表并缓存到集合中
3.1 步骤一 建一个与字典对应的实体类
如图,为单张数据字典表对应的实体类,姑且可以理解为PO,如果是两张数据字典表,那么就是两表联查的PO
3.2 步骤二 写一个mapper 查询数据字典表
3.2.1 单张数据字典表
在后端写一个mapper,查询数据库的字典表
3.2.2 两张数据字典表
相比起一张表,两张表管理更为清晰,只是在后端mapper查询数据库表时,需要两张表进行联查
参考SpringBoot+Redis实现数据字典_蹊源的奇思妙想的博客-CSDN博客_springboot缓存数据字典
3.3 步骤三 编写词典全局缓存方法
3.3.1 存放位置
直接复制下面的代码,粘贴到IDEA中
3.3.2 代码
注意根据步骤一和二 修改一下声明的mapper接口以及集合中的元素类型
@Component
public class InitContext {
/**
* 词典全局缓存对象
* key : type 表示哪个类型
* value中的hashMap中的key : 表示value value表示text文本
*/
public static final Map<Integer,LinkedHashMap<Integer,String>> dic = new HashMap<>();
//ProjectApplication是当前springboot模块的启动类,Logger类是一个日志对象 作用是在缓存好词典后,在控制台提示一下
Logger logger = LoggerFactory.getLogger(ProjectApplication.class);
@Resource
private SalesDictionaryMapper salesDictionaryMapper;
/**
* 容器创建自动执行
*/
@PostConstruct
public void init(){
initDictionary();
}
/**
* 初始化词典
*/
public void initDictionary(){
List<SalesDictionary> all = salesDictionaryMapper.findAll();
all.forEach(sysDictionary -> {
LinkedHashMap<Integer, String> kv = dic.get(sysDictionary.getType());
if(kv == null){
kv = new LinkedHashMap<>();
}
kv.put(sysDictionary.getTypeValue(),sysDictionary.getTypeText());
dic.put(sysDictionary.getType(),kv);
});
//缓存完毕,logger对象在控制台打印下面的语句进行说明,可以不要
logger.debug("词典初始化完成");
}
}
3.3.3 几个知识点
3.3.3.1 Logger,是一个日志对象
java日志LoggerFactory.getLogger最全讲解使用方法_滑稽的鼠标的博客-CSDN博客_loggerfactory.getlogger
下图划线代码就是在springboot的启动类下创建一个日志对象,其实不是必须的
这个对象要做的就是在springboot启动完毕并且词典缓存后,在控制台提示一下,这不是必须要做的
3.3.3.2 @PostConstruct 这个注解注释的方法会在整个类被注入spring容器后自动执行
springboot 启动时加载数据(字典)到内存 - 简书 (jianshu.com)
主要看文章中对 “@PostConstruct”的介绍 以及 存在“BiMap”这种东西,能把map的value也唯一化,不过我这里没有用
下面是部分摘录
@PostConstruct
spring中Constructor、@Autowired、@PostConstruct的顺序(网上解释比较清楚的版本)
要将对象p注入到对象a,那么首先就必须得生成对象p与对象a,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowired注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
Constructor >> @Autowired >> @PostConstruct
3.3.3.3 字典的数据类型 Map<Integer,LinkedHashMap<Integer,String>> dic
这是一个嵌套map,外层map的key对应type,value是一个LinkedHashMap,LinkedHashMap的key对应type_value,value对应type_text
实际使用时,可以根据自己的字典表格式变化,比如只建立一张数据字典表的情况下,type不用数字,而是用字符串,那么此时外层map的key就要用String
4 在VUE+springboot项目中使用数据字典表
4.1 缓存完毕的字典表样式
数据字典表存到项目的map后,数据结构如图
4.2 使用方式 直接使用
缓存数据字典表后,可以直接在controller类、serviceimpl类或者transfer类中使用“dic",IDEA会提示需要导入,直接导入即可
4.3 应用场景一 PO转VO时进行转义
在PO转VO时,在transfer类中使用,第一个get要人为指定,第二个get直接将PO的属性值传入,这样VO就能获得对应的字符串
可以把数据库的字典表的注释放在实体类上 方便查看
4.4 应用场景二 令前端下拉列表动态生成
4.4.1 效果图
前端基于VUE+ElementUI,要实现的效果如图
4.4.2 后端
之前考虑的做法是每个字段单独建表,后端分别查询,然后传给前端,一旦字段多了,那也太麻烦了
现在有了数据字典表,直接把数据字典表传给前端即可
封装类如图,返回成功的方法的status和message已经用枚举类预设好
postman请求结果一览
4.4.3 前端
4.4.3.1 请求方法
前端基于VUE+ElementUI
前端向后端的controller发送请求,其中instance是自定义封装好的类
4.4.3.2 methods
methods的代码如下,获取到数据后,存到VUE的属性下
4.4.3.3 前端下拉列表代码
ElementUI实现的下拉列表代码
4.4.4 前端的知识点说明
后端没啥要说的,倒是前端让我吃尽了苦头,下面是我遇到的问题和解决办法
4.4.4.1 console.log显示object
ajax请求后的返回结果显示[object Object]的原因_IT_townlet的博客-CSDN博客
console.log时不要加字符串
4.4.4.2 后端返回的数据是Promise对象
打印后端传进来的数据时返回数据是Promise对象_Olliverzhang的博客-CSDN博客
要给方法加同步锁,即async和await
4.4.4.3 json取值方式 json对象[]
JSON取值(key是中文或者数字)方式详解_Care_about的博客-CSDN博客
json的取值方式有好几种,对于后端传递的这种嵌套map,需要手动指定一下外层map的key
4.4.4.4 ElementUI的下拉列表动态生成的方式
笔记: SpringBoot + VUE实现数据字典展示功能_CJG753951的博客-CSDN博客
下图是文章摘录,其中的取值方式启发了我
动态生成的下拉列表,可以在遍历json对象时,将获得的元素拆开使用
可以对比写死的情况,如图
4.5 应用场景三 前端VUE表格列的动态转义
笔记: SpringBoot + VUE实现数据字典展示功能_CJG753951的博客-CSDN博客
Vue 利用后端的数据字典和Map对象实现表格列字段动态转义的处理方案 - 阿拉伯1999 - 博客园 (cnblogs.com)
这个场景与应用场景一相悖,毕竟如果PO转VO时就把数字转为字符串了,前端就没必要转了,我通常会依据应用场景一去实现转义,所以这个我就不研究了,仅放在这里
5 数据字典表缓存到本地内存还是redis中
5.1 结论 依据公司技术选型而定
在实际使用时,需要在整个项目启动时就执行查询语句,把数据字典表整体保存到一个map中,这个map再存到redis或者本地内存中,然后在项目的各个类中随调随用
而到底是直接用static把map存到内存中还是直接把map存到redis中,就看以后的技术选型,从速度上看前者肯定更快,因为用的就是本地的内存
5.2 本文缓存到本地
本篇文章是将字典表存在本地内存中,所以使用了static修饰方法
5.3 缓存到redis的方式
如果要探究存到redis中,参见SpringBoot+Redis实现数据字典_蹊源的奇思妙想的博客-CSDN博客_springboot缓存数据字典
文章的部分知识点的个人理解如下
5.3.1 springUtils工具类
文章中的RedisDistUtil类用于获取被spring管理的service接口对象,然后调用这个对象的方法实现转义
为了能从spring容器中取出service接口对象,借助了springUtils工具类,这是一个自定义的工具类,用于从spring容器中取出对象
①本篇文章的数据字典表被定义为static 静态的对象,被存在内存中,所以可以直接使用,不需要springUtils这种类
②如果不使用这个工具类,而是在当前类声明要使用的对象并利用@Autowired等注解注入spring容器,那么还得先把最外层这个类(RedisDistUtil)注入spring容器,然而注入spring容器的类不是越多越好
③显然,这个工具类要求springboot项目启动,否则spring容器中是空的,方法getBean取不到东西就报空指针异常
5.3.2 RedisTemplate
文章中提到spring封装了一个对象叫RedisTemplate 基于这个对象提供的方法 我们能操控redis储存的数据
6 数据字典表和常量类对比
我没有使用数据字典表时,直接在项目建了一个常量类,如图
借助这个常量类也能实现转义以及管理,但真的太麻烦了
比如转义,代码量可见一斑,而且每当需要新增一条“订单动作”,我就需要到各个transform中为switch-case增加一条
再就是管理时,每新增一条,我就得准备一个常量名,写起来特麻烦
现在固定值写在一张数据库表中 相比常量类就方便维护了
并且前端下拉菜单不再需要写死 直接去读字典表类的map即可