基于AOP的数据字典实现:实现前端下拉框的可配置更新

作者:后端小肥肠

创作不易,未经允许严禁转载。

目录

1. 前言

2. 数据字典

2.1. 数据字典简介

2.2. 数据字典如何管理各模块的下拉框

3. 数据字典核心内容解读

3.1. 表结构

3.2. 核心代码 

3.2.1. 根据实体类名称获取下属数据字典

3.2.2. 数据字典AOP切面

3.2.2.1. 场景模拟

3.2.2.2. 数据字典交互流程

3.2.2.3. AOP代码

4. 数据字典使用

4.1. 新增Student类对应数据字典值

4.2. 新增学生数据

4.3. 根据id查询学生数据详细信息

5. 结语

6. 参考链接


1. 前言

在现代软件开发中,数据字典作为管理系统常量和配置项的重要工具,其灵活性和可维护性对系统的健壮性起着至关重要的作用。然而,传统的数据字典与业务模块的整合方式往往存在着严重的耦合问题。通常情况下,为了在业务模块中使用数据字典的标签(label),我们不得不在VO类中添加字段,并通过查询数据字典来获取对应的标签值,这种做法不仅增加了代码的复杂性,还使得业务模块与数据字典的耦合度过高,不利于系统的模块化和扩展。

本文将探讨如何利用面向切面编程(AOP)的思想,通过注解的方式实现数据字典与其他业务模块的无侵入性整合。我们将重点关注如何通过AOP技术,使数据字典的值(value)在业务模块中自动转换为其对应的标签(label),从而实现业务逻辑与数据字典的松耦合,为系统的可维护性和拓展性提供新的解决方案。

2. 数据字典

2.1. 数据字典简介

数据字典是软件系统中用于管理常量、配置项或者枚举值的集合。它通常包括标签(label)和值(value)两部分,标签用于展示给用户或者其他系统模块,而值则是实际的业务逻辑中使用的数据标识。我举个例子吧,比如前端下拉框的渲染:

我们来看一下前端代码:

<template><el-select v-model="value" placeholder="请选择"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"></el-option></el-select>
</template><script>export default {data() {return {options: [{value: '选项1',label: '黄金糕'}, {value: '选项2',label: '双皮奶'}, {value: '选项3',label: '蚵仔煎'}, {value: '选项4',label: '龙须面'}, {value: '选项5',label: '北京烤鸭'}],value: ''}}}
</script>

从前端代码可看出 下拉框的渲染主要依靠valuelabel,常规的做法有枚举,或者后端建表后从表中获取,这两种方法都有许多弊端,枚举的话需要开发人员写死在代码中,再来看建表,如果每个下拉框都建表,那就会浪费大量后端资源,采用数据字典,统一管理各个功能模块的下拉框是较优的选择。

2.2. 数据字典如何管理各模块的下拉框

数据字典中是如何把各模块的下拉框管理起来的,在数据字典中一共管理三块内容,分别是实体类(表),属性字段,属性字段值(数据字典value和label);以前端的视角来看就是表单,下拉框,下拉框的值(数据字典label和value)。

3. 数据字典核心内容解读

3.1. 表结构

数据字典一共涵盖两张表,分别为dictionary_type和dictionary_value,下面将分别对这两张表进行解释。

dictionary_type

CREATE TABLE "public"."dictionary_type" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"type_name" varchar(50) COLLATE "pg_catalog"."default","type_description" varchar(100) COLLATE "pg_catalog"."default","parent_id" varchar(32) COLLATE "pg_catalog"."default","create_time" timestamp(6),"update_time" timestamp(6),"version" int4 DEFAULT 1,"type_label" varchar(50) COLLATE "pg_catalog"."default","is_deleted" int2 DEFAULT 0,CONSTRAINT "dictionary_type_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."dictionary_type" OWNER TO "postgres";COMMENT ON COLUMN "public"."dictionary_type"."id" IS '主键ID';COMMENT ON COLUMN "public"."dictionary_type"."type_name" IS '字典类型名称';COMMENT ON COLUMN "public"."dictionary_type"."type_description" IS '字典类型描述';COMMENT ON COLUMN "public"."dictionary_type"."parent_id" IS '父节点id';COMMENT ON COLUMN "public"."dictionary_type"."create_time" IS '创建时间';COMMENT ON COLUMN "public"."dictionary_type"."update_time" IS '更新时间';COMMENT ON COLUMN "public"."dictionary_type"."version" IS '乐观锁';COMMENT ON COLUMN "public"."dictionary_type"."type_label" IS '字典类型标签';COMMENT ON TABLE "public"."dictionary_type" IS '字典类型表';

 dictionary_type表管理实体类和属性字段,当parent_id为null时则该数据为实体类,否则为归属某实体类下的属性字段。

dictionary_value

CREATE TABLE "public"."dictionary_value" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"value_name" varchar(50) COLLATE "pg_catalog"."default","type_id" varchar(32) COLLATE "pg_catalog"."default","create_time" timestamp(6),"update_time" timestamp(6),"version" int4 DEFAULT 1,"value_label" varchar(50) COLLATE "pg_catalog"."default","value_sort" int4,"is_deleted" int2,CONSTRAINT "dictionary_value_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."dictionary_value" OWNER TO "postgres";COMMENT ON COLUMN "public"."dictionary_value"."id" IS '主键ID';COMMENT ON COLUMN "public"."dictionary_value"."value_name" IS '字典值名称';COMMENT ON COLUMN "public"."dictionary_value"."type_id" IS '字典类型id';COMMENT ON COLUMN "public"."dictionary_value"."create_time" IS '创建时间';COMMENT ON COLUMN "public"."dictionary_value"."update_time" IS '更新时间';COMMENT ON COLUMN "public"."dictionary_value"."version" IS '乐观锁';COMMENT ON COLUMN "public"."dictionary_value"."value_label" IS '字典值标签';COMMENT ON COLUMN "public"."dictionary_value"."value_sort" IS '字典值排序';COMMENT ON TABLE "public"."dictionary_value" IS '字典值表';

dictionary_value 中管理某实体类下属性字段多对应的数据字典(label和value)。dictionary_value dictionary_type为多对一的关系(一个属性字段下对应多个数据字典值)。

3.2. 核心代码 

3.2.1. 根据实体类名称获取下属数据字典

controller层

    /*** 获取模块数据字典* @param typeName* @return*/@ApiOperation("获取某个模块下的数据字典")@GetMapping("/parameter/{typeName}")Map<String, Object> getCompleteParameter(@PathVariable("typeName") String typeName){return iDictionaryValueService.getParameters(typeName);}

在上述代码中typeName为实体类名称。 

service层

    public Map<String, Object> getParameters(String typeName) {List<Map<String, Object>> dictParameters=baseMapper.getDictParameters(typeName);Set<Object> typeSet= new HashSet<>();Map<String,Object>resParam=new HashMap<>();for (Map<String, Object> dictParameter : dictParameters) {typeSet.add(dictParameter.get("type_name").toString());}for (Object o : typeSet) {List<ParameterVO> parameterVoList = new ArrayList<>();for (Map<String, Object> dictParameter : dictParameters) {if(dictParameter.get("type_name").toString().equals(o.toString())){ParameterVO parameterVO=new ParameterVO(dictParameter.get("value_name").toString(),dictParameter.get("value_label").toString());parameterVoList.add(parameterVO);}}resParam.put(o.toString(),parameterVoList);}return resParam;}

mapper层

@Select("select a.value_name,a.value_label,a.type_name from  dictionary_type d JOIN  (select v.value_name,v.value_label,t.type_name,t.parent_id from dictionary_value v,dictionary_type t where v.type_id=t.id and  v.is_deleted = 0 and t.is_deleted = 0)a on a.parent_id=d.id where d.type_name =#{typeName} AND d.is_deleted = 0")List<Map<String, Object>> getDictParameters(@Param("typeName") String typeName);
3.2.2. 数据字典AOP切面
3.2.2.1. 场景模拟

先预设一个场景,假设有一张学生表需要整合数据字典,表结构如下:

CREATE TABLE "public"."student" ("id" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,"name" varchar(50) COLLATE "pg_catalog"."default","blood_type" varchar(10) COLLATE "pg_catalog"."default","constellation_type" varchar(10) COLLATE "pg_catalog"."default","create_time" timestamp(6),"update_time" timestamp(6),"version" int4 DEFAULT 1,"is_deleted" int2 DEFAULT 0,CONSTRAINT "student_pkey" PRIMARY KEY ("id")
)
;ALTER TABLE "public"."student" OWNER TO "postgres";COMMENT ON COLUMN "public"."student"."blood_type" IS '血型';COMMENT ON COLUMN "public"."student"."constellation_type" IS '星座类型';

在上表中星座和血型为需要和数据字典集成的字段。 

3.2.2.2. 数据字典交互流程

AOP切面主要使用在分页查询和查询详情时。与数据字典有交集的实体类(Student)在分页或查询详情时技术流程图如下:

在上图中可看出与数据字典有交集的模块要进行分页或查询详情时,需要远程调用数据字典模块的相关接口,通过数据表中的value查询数据字典对应的label,最后封装为vo类返回给前端,如果把这个逻辑以硬编码的形式内嵌到查询详情代码中的话,有个比较致命的缺点就是代码的耦合性太高了,不利于模块的迁移复用。

 上述代码为查看详情的部分代码,在封装VO类时进行了硬编码,可以看出,在耦合性极高的同时,代码的可读性也较差,故引入AOP切面,将远程调用label和将label值更新至VO类写入AOP切面。 

3.2.2.3. AOP代码

数据字典AOP注解,它的作用是用于标记类的字段,指示字段的字典类型,并且在序列化过程中使用自定义的序列化器进行处理。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializer.class)
public @interface Dict {/** 字典类型 */String type();
}

通过 @JsonSerialize(using = DictSerializer.class),我们告诉 Jackson 在对带有 @Dict 注解的字段进行序列化时,使用 DictSerializer 类来处理序列化过程。

数据字典序列化类: 

@Component
public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {private IDictionaryValueService dictionaryValueService;private String type;@Autowiredpublic DictSerializer(IDictionaryValueService dictionaryValueService) {super(Object.class);this.dictionaryValueService = dictionaryValueService;}public DictSerializer(String type, IDictionaryValueService dictionaryValueService) {super(Object.class);this.type = type;this.dictionaryValueService = dictionaryValueService;}@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {if (Objects.isNull(value)) {gen.writeObject(value);return;}String label = null;if (dictionaryValueService != null && type != null) {try {String response = dictionaryValueService.getLabelByValue(value.toString());label = response; // 设置为空时返回 "null"} catch (RuntimeException e) {label = null;}}gen.writeObject(value);gen.writeFieldName(gen.getOutputContext().getCurrentName() + "Label");gen.writeObject(label);}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {if (property != null) {Dict dict = property.getAnnotation(Dict.class);if (dict != null) {return new DictSerializer(dict.type(), dictionaryValueService);}}return this;}}

DictSerializer 是一个用于处理带有 @Dict 注解字段的自定义 Jackson 序列化器。它利用注入的 IDictionaryValueService 接口,根据字段值获取对应的标签,并将原始值与标签作为新字段输出,实现了动态字典值的序列化处理。

我写的示例代码把AOP相关代码写到了数据字典模块,但是实际项目中应当放到common模块,方便所有和数据字典有交集的业务模块调用。

4. 数据字典使用

基于第3章预设的场景,我们这章直接实操来看一下如何使用数据字典(ps,我将Student类相关代码写到了数据字典中,实际应该是在别的模块,这里为了方便我就写到了一个模块)。

4.1. 新增Student类对应数据字典值

新增dictionary_type表数据:

新增dictionary_value 表数据:

根据实体类名获取该实体类对应的数据字典,返回至前端进行下拉框动态渲染:

4.2. 新增学生数据

这里新增和平时操作无异:

    @PostMapping("")public boolean saveStudent(@RequestBody Student student){return  studentService.save(student);}

在传数据字典值时只需要传入value值即可:

 

4.3. 根据id查询学生数据详细信息

编写VO类:

@Data
public class StudentVO {private String id;private String name;@Dict(type = "bloodType")private String bloodType;@Dict(type = "constellationType")private String constellationType;@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")private Date createTime;
}

查看详情方法:

   public StudentVO getStudentInfoById(String id) {Student student = baseMapper.selectById(id);StudentVO studentVO= BeanCopyUtils.copyBean(student,StudentVO.class);return studentVO;}

运行结果:

5. 结语

本文探讨了如何通过面向切面编程(AOP)实现数据字典与业务模块的无侵入整合。通过自定义注解和序列化器,我们有效地降低了系统中业务模块与数据字典的耦合度,提升了系统的灵活性和可维护性。希望本文能为读者在实际项目中应用这些技术提供启发,进一步提升软件开发的效率和质量。若本文对你有帮助,别忘记三连哦~

6. 参考链接

基于Springboot,一个注解搞定数据字典问题 - 掘金 (juejin.cn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回顾 DTC 2024 大会——聚焦数据技术创新:揭秘下一代纯实时搜索引擎 INFINI Pizza

2024 年 4 月 12 日至 13 日&#xff0c;备受瞩目的第十三届“数据技术嘉年华”&#xff08;DTC2024&#xff09;在北京新云南皇冠假日酒店盛大开幕。本次大会由中国 DBA 联盟&#xff08;ACDU&#xff09;与墨天轮社区联合主办&#xff0c;以“智能云原生一体化——DB 与 AI 协…

在先企业字号被申请注册成商标!

今天一网友联系普推商标知产老杨&#xff0c;说自己注册的商标被某公司无效宣告了&#xff0c;去年联系老杨时&#xff0c;当时就给说这个商标名称存在风险&#xff0c;与别人的字号权存在高度近似&#xff0c;而且是同行业同地区在后面注册的。 十几年前某公司先成功注册成字号…

Ubuntu 安装CGAL

一、什么是CGAL CGAL&#xff08;Computational Geometry Algorithms Library&#xff09;是一个广泛使用的开源库&#xff0c;主要用于计算几何算法的实现。该库提供了一系列高效、可靠和易于使用的几何算法和数据结构&#xff0c;适用于各种应用领域。以下是 CGAL 的主要功能…

JD-GUI下载和使用

JD-GUI是专门查看jar包的&#xff0c;包括source.jar和doc.jar。JD-GUI可以把.class文件反编译为可编辑的.java文件&#xff0c;有图形化界面。 github下载地址&#xff1a;https://github.com/java-decompiler/jd-gui/releases 下载windows版本的zip包&#xff0c;如下图&…

智能化代码审查系统设计

设计一个智能化代码审查系统&#xff0c;特别是针对Java开发&#xff0c;需要综合考虑多个维度来提升代码质量、提高审查效率&#xff0c;并促进团队间的协作。以下是该系统设计的关键要素和功能特性&#xff1a; 系统架构 客户端-服务器架构&#xff1a;前端提供友好的Web界面…

破局 AI 2.0 时代:利用 AI 提升自我核心竞争力

文章目录 破局 AI 2.0 时代&#xff1a;利用 AI 提升自我核心竞争力1. AI 2.0 时代1.1 特点1.2 发展1.3 影响 2. AI 2.0 时代的机遇 & 挑战2.1 AI 对行业市场的冲击2.2 挑战变为机遇2.3 不同场景下的 AI 效能提升2.3.1 自动化办公任务2.3.2 提升学习效率2.3.3 创意生成与内…

LabVIEW光谱测试系统

在现代光通信系统中&#xff0c;光谱分析是不可或缺的工具。开发了一种基于LabVIEW的高分辨率光谱测试系统&#xff0c;通过对可调谐激光器、可编程光滤波器和数据采集系统的控制&#xff0c;实现了高效、高精度的光谱测量。 项目背景 随着光通信技术的迅速发展&#xff0c;对…

仿哔哩哔哩视频app小程序模板源码

仿哔哩哔哩视频app小程序模板源码 粉色的哔哩哔哩手机视频网页&#xff0c;多媒体视频类微信小程序ui前端模板下载。包含&#xff1a;视频主页和播放详情页。 仿哔哩哔哩视频app小程序模板源码

thinkphp6/8 验证码

html和后台验证代码按官方来操作 ThinkPHP官方手册 注意&#xff1a; 如果验证一直失败&#xff0c;看看Session是否开启&#xff0c; 打印dump(session_status());结果2为正确的&#xff0c; PHP_SESSION_DISABLED: Session功能被禁用&#xff08;返回值为0&#xff09;。…

科普文:一文搞懂jvm实战(四)深入理解逃逸分析Escape Analysis

概叙 Java 中的对象是否都分配在堆内存中&#xff1f; 好了太抽象了&#xff0c;那具体一点&#xff0c;看看下面这个对象是在哪里分配内存&#xff1f; public void test() { Object object new Object(); }这个方法中的object对象&#xff0c;是在堆中分配内存么&#xff1…

【C语言】C语言编译链接和Win32API简单介绍

目录 翻译环境和运行环境翻译环境编译器预处理&#xff08;预编译&#xff09;编译链接 执行环境 Win32API是什么控制台程序控制台获取坐标COORDGetStdHandle函数GetConsoleCursorinfo函数CONSOLE_CURSOR_INFOSetConsoleCursorInfo函数SetConsoleCursorPostion函数GetAsyncKeyS…

Qt(MSVC)下报“语法错误缺少“}““语法错误缺少“常数“ 的解决办法

1.现象 目前我在工程中试图使用QHttpServer时&#xff0c;一编译&#xff0c;就报了一堆奇奇怪怪的错误&#xff1a; D:\Qt\httpServer\Qt5.15.2\include\QtHttpServer\qhttpserverrequest.h:75: error: C2143: 语法错误: 缺少“}”(在“(”的前面) D:\Qt\httpServer\Qt5.15.…

栈和队列---循环队列

1.循环队列的出现 &#xff08;1&#xff09;上面的这个就是一个普通的数据的入队和出队的过程我们正常情况下去实现这个入队和出队的过程&#xff0c;就是这个数据从这个队尾进入&#xff0c;从队头离开&#xff0c;但是这个加入的时候肯定是没有其他的问题的&#xff0c;直接…

【分布式技术】——监控平台zabbix 介绍与部署

一、监控系统的相关知识 1.监控系统运用的原因 当我们需要实时关注与其相关的各项指标是否正常&#xff0c;往往存在着很多的服务器、网络设备等硬件资源&#xff0c;如果我们想要能够更加方便的、集中的监控他们&#xff0c;zabix可以实现集中监控管理的应用程序 监控的初衷…

ARM汇编与机器码、汇编指令

文章目录 1. CISC与RISC指令集 2. ARM汇编指令 3. 汇编与机器码 4. 汇编指令格式 5. MOV指令 6. BL指令 7. B指令 8. ADD/SUB指令 9. LDR/STR指令 1. CISC与RISC指令集 根据指令的复杂度&#xff0c;所有CPU可以分为两类&#xff1a; CISC&#xff08;Complex Instr…

C#——多态详情

多态 多态: 是同一个行为&#xff0c;具有多个不同表现形式或形态的能力 多态分为两种 : 静态性多态: 函数重载&#xff0c;符号重载动态性多态: 虚方法&#xff0c;抽象类&#xff0c;接口 静态多态 在编译时&#xff0c;函数和对象的连接机制被称为早期绑定&#xff0c;…

知识图谱的符号表示

1、基于图的表示建模 2、有效标记图 3、属性图表示方法的优点和去点 4、RDF是什么&#xff1f;表达是有限的 5、OWL&#xff0c;本体语言 6、OWL有很多家族 7、属性图、RDF\三元组、关系图 ---------------------------PPT---------------------

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法,亲测有效!!!

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 一、问题分析 二、报错原因 三、解决思路 四、解决方法 五、总结 博主v&#xff1a;XiaoMing_Java 博主v&#x…

【微服务网关——服务发现】

1.服务发现 1.1 介绍 服务发现是指用注册中心来记录服务信息&#xff0c;以便其他服务快速查找已注册服务服务发现分类: 客户端服务发现服务端服务发现 1.2 客户端服务发现 客户端服务发现&#xff08;Client-side Service Discovery&#xff09;是一种微服务架构中的模式…

Vine: 一种全新定义 Vue 函数式组件的解决方案

7月6日的 vue confg 大会上 ShenQingchuan 大佬介绍了他的 Vue Vine 项目&#xff0c; 一种全新定义 Vue 函数式组件的解决方案。 和 React 的函数式组件有异曲同工之妙&#xff0c;写起来直接起飞了。 让我们来快速体验一下 vine&#xff0c; 看看到底给我们带来了哪些惊喜吧…