低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计
前言
写最后一篇文章的时候,我本人其实犹豫了半年,在想是否发布出这篇文章,因为可能会动了很多人的利益。所以这篇文章既是整个低代码信创开发的高度总结,也是最为精华的一部分,它点明了低代码中最为核心的技术。虽然你在读这篇文章的时候会有犹抱琵琶半遮面的感觉,但当你领悟之后,会发现原来低代码开发平台的建设是如此的简单。低代码前端设计模型,而设计出来的模型以元数据的方式又能驱动整个系统运行,读完你会发现实在是非常巧妙的设计。一旦拥有了这个设计思想下建立的系统,一个程序员一天做出一套管理系统不再是梦,而且运维和调整将变得极为简单、实时,管理系统将变得极为廉价,所以恕我不能详述具体实现的代码。信息化对于实现中华民族伟大复兴的梦想尤为重要,所以此文章送给有缘人,为祖国的各行各业完成赋能的使命。
前两篇文章
低代码信创开发核心技术(一):基于Vue.js的描述依赖渲染DDR实现模型驱动的组件
低代码信创开发核心技术(二):手撕灵活好用的Vue拖拉拽布局系统
数据库层设计
自顶向下出发来判定设计,我们已经有了界面和组件,接下来就是需要通过组件来访问元数据层,元数据层再调用数据库层完成与数据库的交互。
1、从API角度入手
实现链式增删改查API编程,关键点是在每一次函数执行后返回当前的对象,而不是返回结果。争取让程序员使用的时候只需一个.就能通过IDE提示出来。大大降低二次开发的难度。
链式编程的封装,是指将API设计成一个Builder,直到最后query或update时才会产生结果,这样程序员的记忆负担最少,所有的功能都是一路“点”出来的。
2、SQL的产生
一个平台需要能支持多种数据源,所以需要建立方言机制,简单的做法是写定一个IDialect接口,用工厂模式根据不同传入的JDBC Driver类名判断使用哪个方言。
有了自动化的方言工具,我们就可以实时生成SQL了,其实说来也比较简单,常用SQL的生成无非是Where子句、OrderBy子句、LeftJoin的组合,用于检索、排序、带出关联表名称等等。
尽管可以实时生成SQL,但也会有非常复杂的SQL等待我们去手写,所以API仍要留有query和update相关方法,而且为了安全性也要强制让传递进来的参数进行预编译处理。
增删改其实非常容易实现,但查询是最难的,为了解决复杂度,则需要构建一个查询上下文,因此我们在各个环节传递这个上下文对象并补充它的各种条件,即可无视顺序、无视复杂度、无视前置各种函数的调用关系,在最终进行查询的环节才按顺序逻辑构建SQL,这样最为简单,调整起来也只需修改查询上下文的类即可,所以为了方便,建议把增删改的相关条件也融入到上下文中,还有分页条件等等,需要抽象的东西并不多,但这样做可以做到统一。
3、SQL的执行
SQL被创造出来之后,即可进入执行环节,需要注意的是如果插入的数据较多,则需要用values后面跟多个数据的语法来实现批量插入、update则需要用case when语句来实现批量更新。这样可以大大节约执行的时间。
而且SQL执行层应当直接作用于JDBC之上,因为我们假定项目里并没有Hibernate、JPA、MyBatis,纯手动造一个轮子。这样带来的好处是我们可以灵活的控制分布式事务、设置保存点、增加切面,而无需受制于其他框架的规则。
4、关于数据格式
查询的结果集一定是先转换成List<Map<String,Object>>格式,但是该类型的结果作为返回值无法进行方便的后续运算,所以需要在它基础上封装一层查询结果对象,包括我们可以把它转换成ValueObject的List(实际上低代码或无代码平台基本不需要用户自己定义ValueObject)、直接拿到唯一数据、抽取所有Map里同一个属性形成新的List,或者为了构建缓存、加速运算从而抽取出主键和Map之间对应的关系等算法。
同理,增删改操作也是操作同样的数据结构。
5、插件
我们可以定义一个统一的事件处理接口和事件处理对象,事件处理对象包含事件名称、查询上下文、是否打断执行即可。然后建立一个全局事件处理器,把事件处理的实现类可以动态注册或移除。这样我们就可以做到包括给数据自动创建主键、自动填写审计信息(创建人、创建时间、修改人、修改时间)等功能。
6、查询条件容错机制
一个好的系统一定是会允许容错的,比如我们传入的列名是驼峰式,它可以自动转换成小写字母加下划线。所以写一个过滤器,当查询上下文真正要执行前,对所传入的条件进行自动的修整。
7、数据库元数据
数据库元数据可以通过Connection对象获取,包括了有哪些表、每个表有哪些字段、什么类型的、是否是主键等信息,一旦获取到了,我们可以把它用Caffeine之类的框架放到内存中缓存起来,这样便可以动态获知是否是主键。
8、事务处理
最终事务处理我们要写一个单独的项目和Spring进行结合,比较方便的做法是定义一个Bean,返回PlatformTransactionManager类型,而我们的实现类则继承AbstractPlatformTransactionManager即可。
还需要注意,把LocalDateTime、BigDecimal等格式的前后端转换问题一并解决了,同样也是定义一个Bean,返回Jackson2ObjectMapperBuilderCustomizer对象即可。
9、动态物理表的修改
我们通过第7条已经创建了数据库的元数据,但它的setter一定是protected的,不允许其他包的类对其进行任何的修改,所以我们要创建一个副本类,可以允许修改的那种。然后写一个数据表管理器,当我们对这个副本类的实例进行修改后,把整个定义传送给数据表管理器,应当能实现自动创建表、增加字段、增加索引的功能,同样的这些SQL也应当由方言插件负责完成拼装。
至此我们数据库层就完成了建设。
元数据层
我们常常在应用系统中要让系统能对自己的结构进行认知,并且要把表与表之间的外键关系、主子关系放到一个地方,在需要使用的时候从这个地方拿出关系既可自动完成一些操作,这个地方就叫做元数据层,非常类似于Java的反射机制,如果你了解了Java的反射,那么元数据层的作用就不言而喻。说到底,元数据是一种数据驱动的架构设计,实际使用时可以用一些通用的算法来处理不同的数据结构。当我们有了数据库层,就可以建立元数据层了。
1、结构定义
实体或者聚合实体只需要自身带编码、数据、子实体集合列表即可,那么我们在数据库中建立的元数据表应当也是id、code、name、pid、物理表名的格式,它是一种树状结构。随之还需要建立元数据字段表,id,code,name,物理字段名。
聚合实体我们可以想象成是一个实体下面还挂有若干种实体的样子。比如入职申请表,个人基本信息完全可以放在主实体表中,而学习经历、工作经历因为和主表是1对多关系,所以学习经历、工作经历单独各是一张表,他们与主表“入职申请表”的关系就应当记录在元数据表中,当查询的时候把数据放入子实体集合列表里。
也因此,子实体集合列表实际上只需携带编码、List<数据>、分页信息即可。说是子实体集合列表,但实际上多个主实体也可以打包在一个子实体集合列表对象中。这样我们就可以形成一个无限嵌套的数据结构,做出超级复杂的业务数据结构,保障了主表和子表之间的数据一致性。
我们在实际操作元数据的时候永远操作的都是编码,读取的时候是多表按顺序自动读取并拼装,写入的时候也是视作整体。
2、执行层和事件动作
因为元数据层需要事件驱动,所以我们可以把事件简单的划分为保存、删除、查询。事件驱动实现原理非常简单,本文不做赘述。讲一下执行层究竟要做哪些事情。第一,要把数据库层的API抽离处理,比如做个QueryBuilder之类的,它可以完成对主实体(主表)的各种复杂查询。第二,需要通过数据库层的插件机制来完成主键生成、查询融入(因为数据库层有上下文,所以很容易剥离上下文并融入数据权限功能,比如未来按组织权限划分能看到的数据,实现组织 IN (组织主键清单)这类操作)。
另外在执行更新的时候还需特意注意审计信息中的最后修改时间是否有动过,为了保证多人同时修改时数据一致性,则需要将之前的数据读入,并对比时间戳,确保可以修改并给予新的时间戳,否则就应当提示用户数据已被他人修改。带入旧数据也会带来一些好处,比如我们可以在前端对子实体进行删除时,我们可以准确的得知哪些数据被删除了,从而执行删除校验,避免关键数据被删除。
3、关联定义
如果只有基本的结构定义,没有关联定义,这个系统就只能做简单的增删改查了。关联定义我们可以遵循2NF范式,将表与表之间的一对多关系存储在数据表里,到使用的时候(比如订单明细的商品ID自动翻译成商品名称)就非常有用了。
因此在查询完成后在事件驱动的查询After切面到达时,我们可以遍历主实体的树形结构,把关联到其他表的主键或者编码暂存到一起,然后批量发起查询,以字段名$name、字段名$code为键保存到返回的聚合实体中。
4、实体服务和控制器基类的封装
我们有了以上内容即可完成Service和Controller的封装,善于运用@PathVariable和@RequestBody可以让你事半功倍。需要注意的是我们这里要且仅要一个主入口,后续要根据操作来分发到不同的处理类上进行处理,这样我们可以将平台框架封装起来并用spring.factories机制来实现自动注入,这样在程序员眼里看来项目就一点都不复杂了,依赖你的项目就可以获得全部元数据能力。
5、联动结构
接下来需要有一个联动结构,从前台传递元数据的定义,然后联动把元数据表、元数据字段表的内容填充进去。然后再根据元数据的定义和表和表之间关联的结构,把数据表建立起来,这就需要调用数据库层的物理表修改功能了。
初步实现低代码开发
接下来我们根据前两篇文章讲述前端的技术基础,把项目面板、属性面板搭建出来,然后在属性面板里我们可以做一个弹窗,针对某一个组件创建元数据或选择已有的元数据,弹窗界面里一开始有一个输入框,询问这里需要有哪些字段(只需要给中文名),然后预先写好prompt调用AI,把字段英文名、类型返回回来让用户确认,如果用户觉得不对,可以修改成想要的,点击保存即可自动联动创建表(如果表中没有数据,想删也是可以删的),这种程度的业务逻辑相比于非常抽象的元数据还是非常容易实现的。
后续使用已经有的元数据就没这么麻烦了,连输入框都不需要,只需要把已经有的元数据选中,勾选要展示的字段,点确定即可。
最后运行时环境把设计器代码隐藏掉,只需要渲染即可。至此低代码平台的雏形就有了。
相关文章:
低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计
前言 写最后一篇文章的时候,我本人其实犹豫了半年,在想是否发布出这篇文章,因为可能会动了很多人的利益。所以这篇文章既是整个低代码信创开发的高度总结,也是最为精华的一部分,它点明了低代码中最为核心的技术。虽然…...
HslCommunication模拟西门子读写数据
导入HslCommunication C#端代码(上位机) 这里要注意的是上位机IP用的当前电脑的IP。 using HslCommunication; using HslCommunication.Profinet.Siemens; using System; using System.Collections.Generic; using System.ComponentModel; using Syste…...
多测师肖sir_高级金牌讲师_ui自动化po框架版本02
ui自动化po框架版本02 一、 pages下的BasePage.py模块 此模块是封装所有用例的基类 比如说:所有用例要用到的元素定位,以及输入框输入,点击,下拉等等公共方法import unittest #导入unittest 框架 from time import *# 调试代码…...
线性判别分析(Linear Discriminant Analysis,LDA)
Linear Discriminant Analysis(LDA) 输入: 原始数据$D((x_1,y_1),(x_2,y_2),...,(x_m,y_m)$ 、 类别标签$Y[y_1,y_2,...,y_n]$、 降维到的维度d输出: 投影矩阵W、投影后的样本$Z$、算法步骤: 1.计算类内散度…...
git的分支及标签使用及情景演示
目录 一. 环境讲述 二.分支 1.1 命令 1.2情景演练 三、标签 3.1 命令 3.2 情景演示 编辑 一. 环境讲述 当软件从开发到正式环境部署的过程中,不同环境的作用如下: 开发环境:用于开发人员进行软件开发、测试和调试。在这个环境中…...
深度解析找不到msvcp120.dll相关问题以及解决方法
在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp120.dll丢失”。这个错误通常会导致某些应用程序无法正常运行,给用户带来很大的困扰。那么,如何解决msvcp120.dll丢失的问题呢?本文将为大家介绍…...
SQL Server 2022 安装步骤——SQL Server设置身份验证教程
目录 前言: 安装详细步骤: 第一步: 第二步: 第三步: 第四步: SQL Server 连接的方式: Window验证: SQL Server验证: 两者之间区别: 总结: SQL Server身份验证登录配置教程: 第一步: 第二步: 第三步: 番外篇: 前言: 本文讲解,如何安装SQL Server安…...
Maven各方面配置好了却无法显示版本
今天配置了maven环境,各方面都配置好了命令行却一直没办法显示maven的版本,原因 竟是两个JDK导致maven无法选择,因为maven依赖于JDK,导致在选择JDK的时候差生了二义 性,在环境变量里面删除不常用的JDK,只…...
Jdk 1.8 for mac 详细安装教程(含版本切换)
Jdk 1.8 for mac 详细安装教程(含版本切换) 官网下载链接 https://www.oracle.com/cn/java/technologies/downloads/#java8-mac 一、选择我们需要安装的jdk版本,这里以jdk8为例,下载 macOS 版本,M芯片下载ARM64版本…...
02MyBatisPlus条件构造器,自定义SQL,Service接口
一、条件构造器 1.MyBatis支持各种复杂的where条件,满足开发的需求 Wrapper是条件构造器,构建复杂的where查询 AbstractWrapper有构造where条件的所有方法,QueryWrapper继承后并有自己的select指定查询字段。UpdateWrapper有指定更新的字段的…...
c语言练习11周(6~10)
输入任意字串,将串中除了首尾字符的其他字符升序排列显示,串中字符个数最多20个。 题干 输入任意字串,将串中除了首尾字符的其他字符升序排列显示,串中字符个数最多20个。输入样例gfedcba输出样例gbcdefa 选择排序 #include<s…...
钉钉API与集简云无代码开发连接:电商平台与营销系统的自动化集成
连接科技与能源:钉钉API与集简云的一次集成尝试 在数字化时代,许多公司面临着如何将传统的工作方式转变为更智能、高效的挑战。某能源科技有限公司也不例外,他们是一家专注于能源科技领域的公司,产品包括节能灯具、光伏逆变器、电…...
C++算法:包含三个字符串的最短字符串
涉及知识点 有序集合 字符串 题目 给你三个字符串 a ,b 和 c , 你的任务是找到长度 最短 的字符串,且这三个字符串都是它的 子字符串 。 如果有多个这样的字符串,请你返回 字典序最小 的一个。 请你返回满足题目要求的字符串。…...
华为开源carbondata中的使用问题处理
carbondata中的使用问题处理 Q:什么是不良记录? A:由于数据类型不兼容而无法加载到CarbonData中的记录或为空或具有不兼容格式的记录被归类为不良记录。 Q:CarbonData中的不良记录存储在哪里? A:不良记录…...
AI:76-基于机器学习的智能城市交通管理
🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…...
区块链游戏,游戏开发
区块链游戏是一种基于区块链技术的新兴游戏类型,它具有去中心化、安全性高、透明度高、可追溯等特点。与传统的游戏开发相比,区块链游戏开发需要更多的技术和知识储备,同时也需要更加注重游戏本身的玩法和用户体验。 在区块链游戏中ÿ…...
单片机程序无法下载?
原因一:电源问题 电源可能是导致STM32微控制器无法下载程序的一个常见原因。确保电源稳定对于正常运行和下载程序至关重要。以下是一些电源问题: 1. 电源电压不足:如果STM32微控制器没有足够的电压供应,它可能无法正常工作或下载程…...
【数据库】【sql】如何用SQL实现跨行计算
【背景】 这里的跨行计算不是指整体聚合类的函数比如SUM等的功能,而是指递归算法。 比如我接到有需求,有一个结果字段需要是目前所有行该字段的和,这是属于递归类的算法,SQL中如何实现呢? 【方法】 可以使用窗口函数…...
Oracle(概念含安装)
Oracle是一种关系数据库管理系统(RDBMS),是由美国甲骨文公司(Oracle Corporation)开发的。它是一个客户端/服务器系统,可以在各种操作系统上运行,包括Windows、Linux和Unix等。Oracle的设计重点…...
P6入门:项目初始化4-项目详情之预算日志及汇总Budget
前言 使用项目详细信息查看和编辑有关所选项目的详细信息,在项目创建完成后,初始化项目是一项非常重要的工作,涉及需要设置的内容包括项目名,ID,责任人,日历,预算,资金,分类码等等&…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
