数据中台、低代码、可视化的基石与引擎 D-ORM

对我们开发人员来说,所谓的低代码就是把原来的硬编码换成根据参数生成的动态代码。
动态代码大体分两部分

1.指令部分

也就是参数收集部分

这是最困难的一部分,困难不在于技术上,而是在于用户体验上,一个没有编码基础的用户体验。

2.执行部分

也就是解析参数、执行命令、格式化数据,展示数据这部分重在点于
2.1. SQL操作包括DDL和DML
2.2.内存计算
2.3.前端可视化

2.1. SQL操作​

对于后台来说,最核心的部分就是从数据库中查询或更新数据。操作数据库我们最常用的就是ORM了,但是还不幸,ORM在这里并不适用。

按以往的习惯以及MVC设计思想,我们会先创建一整套的service/dao/mapper/vo po dto各种O,到了这一步你可能已经发现了,此路很难走。

在传统的ORM框架中,需要生成大量呆板低效的模板文件,特别是在sql生成过程中,需要各种便利判断,占据了编码环节大量的时间成本。而在数据计算过程中,VO DTO等实体类丝毫没有发挥其应有的价值。

这还只是低效的问题,通过时间,人海毕竟还是可以解决的。

但是遇到动态场景时,ORM已经完全不能胜任。

就如结构化数据库保存非结构化数据一样绕路。如mysql代替neo4j+mongodb+redis一样困难。

场景已经变了,路已经不是原来的路

原来有明确固定的实体,确定了实体后在这个基础上生成一系列的模板,

如订单类,产品类,不同的系统中这几个类还是大同小异的,无非也就是属性可能不同,最多也就是属性间可能组合出笛卡尔积,无论怎么千变万化,设计和编码阶段还是看得见的。

而在动态场景中,实体虽然还在,但在设计和编码阶段是看不到的,只有在运行时才能看到,并且也不固定而是随时可能变化,如动态表单场景中,不同的业务会定义不同的表单,随着业务的发展表单也会随之变化。

这时ORM已经完全找不着路了。为了适应动态场景,可以:

  • 通过动态生成的DML创建/更改数据库对象

  • 创建具有大量稀疏物理列的表,并且仅使用"重叠"逻辑架构所需的表

  • 创建一个"长而窄"表,将动态列值存储为行,然后需要对其进行透视处理以创建一个"短而宽"行集,其中包含特定实体的所有值

  • 也可以利用MongoDB等非关系型数据库

但是无论采用什么方式,都需要一个能够与之相配合的ORM,只不过ORM所要面对的不再是静态的数据结构,而是动态数据结构,不再是数据而是元数据。

  • 对象已经不是原来静态的对象,而是需要一个高度抽象的动态对象,需要能够囊括原来所有的对象。

对象与表,属性与列不再是一一对应,属性也没有固定的数据类型,而是需要更加详细复杂的定义。

这里还要还原一下“面向对象”中对象的概念,对象不是只有get/set/注解这么弱

需要把数据及对数据的操作封装在一起,作为一个相互依存的整体,并实现高度的抽象

要关注元数据,不再关注姓名、姓别等具体属性

应该能提供统一的数学计算能力,如结果集的聚合、过滤、行列转换、格式、方差、分组等

  • sql也不能写在配置文件里了,需要在运行时动态生成。 既然表与列已经不固定,那SQL自然也无法提前定义了。

  • 数据源也不能在配置文件中枚举了 因为在数据中台场景中可能在运行时随时出现各种各样的数据源。

如果把这些主要的问题都解决了,那就已经蜕变成前文所说的D-ORM了。

D-ORM是一种更高抽象的动态ORM,可以在运行时动态生成对象关系映射。与传统的ORM相比,D-ORM能够根据数据库表结构的变化动态地生成和更新对象模型,以适应不断变化的业务需求。D-ORM的实现方式通常是通过在运行时解析数据库表结构信息,然后根据这些信息动态地生成与数据库表对应的对象模型。可以解决静态ORM在面对动态数据库时的不适用性问题。

映射关系已经解决了,还有一个不可忽视的问题是SQL条件的生成,在传统的ORM中,开发人员需要编写大量的SQL,特别是在生成查询条件时需要在XML中各种遍历各种判断,很明显这种机械式的重复根本不应该占用开发人员的时间。比如生成一个IN条件,大家可以看看现在的ORM项目中是怎么生成的。有没有这样生成的

<if test="types != null and types.size > 0">
    and TYPE_CODE in 
    <foreach collection="types" item="type" open="(" separator="," close=")">
        #{type}
    </foreach>
</if>

在一个传统的项目中还可以接受,但在一个本身就是低代码平台中,还要用FOR,IF,ELSE来生成一个常见的简单的IN条件,是不是很LOW,低代码要作的是什么,不正是根据用户提供的参数来生成运行时代码么。所以为了解决类似的问题需要有一个SQL解析器,我们只需要提供参数,其他的由解析器生成。以上反例中应该只需要提供列名TYPE_CODE,还一个值,如果要求再多,就是这个平台不合格了。

2.2.内存计算

在传统的ORM项目中,我们查询出数据后经常需要作一些简单的计算或格式化,而Entity是完成不了这些操作的,如查询出所有学生成绩后,计算一个平均分,再计算每一行与平均分的差。是不是又来了浪费时间的FOR,IF,作为一个低代码平台,不要让前端用户在取出结果集后再写一段动态执行的代码。动不动就编码实现这么简单常用的功能还配叫作低代码么?这种常用的功能交给前面动态生成的对象处理。这个对象能不能一键计算平均值,能不能一键把计算集合中每一行与平均值的差。

2.3前端可视化。

这虽然是前端的问题,但是数据不能交给前端来格式化,比如生成一个折线图,作为一个低代码后台,要能够提供前端需要的所有格式,就是刚才生成的动态对象的任务,看他能不能实现。

最后再说最困难的指令部分,就是怎么让用户把参数提供给后台。从最简单的开始,如生成一个学生列表页,一般是让用户自己写表头,然后每一列绑定表中的一列。再复杂一点的有些下拉列表或复选框需要后台提供数据源,或者有联动的情况,这些在目前上低代码平台上基本都能很好的解决。传统的ORM也基本都可以支持。

其中有几个比较困的问题,如以下动态表头,水平垂直方向上都是动态数据,这时用户在设计报表时就很难操作。当然这是前端设计的问题,不是本文的重点。

检测项COCO2SO2PM2.5PM10........
一检
二检

在前端以无论什么形式设计完成后,把参数提交给后台,开始了对2.1+2.2+2.3三部分集中检测,需要D-ORM有足够的灵活度能在不编码的情况下处理各种形式的参数。查询出目标数据,并按前端要标的格式提供数据。具体怎么实现,我们在下期逐个举例说明。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HibernateD 是 D 语言的 ORM 框架,类似 Java 的 Hibernate,示例代码:import hibernated.core; // Annotations of entity classes class User {     long id;     string name;     Customer customer;     @ManyToMany // cannot be inferred, requires annotation     LazyCollection!Role roles; } class Customer {     int id;     string name;     // Embedded is inferred from type of Address     Address address;     Lazy!AccountType accountType; // ManyToOne inferred     User[] users; // OneToMany inferred     this() {         address = new Address();     } } @Embeddable class Address {     string zip;     string city;     string streetAddress; } class AccountType {     int id;     string name; } class Role {     int id;     string name;     @ManyToMany // w/o this annotation will be OneToMany by convention     LazyCollection!User users; } // create metadata from annotations EntityMetaData schema = new SchemaInfoImpl!(User, Customer, AccountType,                                   T1, TypeTest, Address, Role, GeneratorTest); // setup DB connection factory MySQLDriver driver = new MySQLDriver(); string url = MySQLDriver.generateUrl("localhost", 3306, "test_db"); string[string] params = MySQLDriver.setUserAndPassword("testuser", "testpasswd"); DataSource ds = ConnectionPoolDataSourceImpl(driver, url, params); // create session factory Dialect dialect = new MySQLDialect(); SessionFactory factory = new SessionFactoryImpl(schema, dialect, ds); scope(exit) factory.close(); // Create schema if necessary { // get connection Connection conn = ds.getConnection(); scope(exit) conn.close(); // create tables if not exist factory.getDBMetaData().updateDBSchema(conn, false, true); } // Now you can use HibernateD // create session Session sess = factory.openSession(); scope(exit) sess.close(); // use session to access DB // read all users using query Query q = sess.createQuery("FROM User ORDER BY name"); User[] list = q.list!User(); // create sample data Role r10 = new Role(); r10.name = "role10"; Role r11 = new Role(); r11.name = "role11"; Customer c10 = new Customer(); c10.name = "Customer 10"; User u10 = new User(); u10.name = "Alex"; u10.customer = c10; u10.roles = [r10, r11]; sess.save(r10); sess.save(r11); sess.save(c10); sess.save(u10); // load and check data User u11 = sess.createQuery("FROM User WHERE name=:Name").                            setParameter("Name", "Alex").uniqueResult!User(); assert(u11.roles.length == 2); assert(u11.roles[0].name == "role10" || u11.roles.get()[0].name == "role11"); assert(u11.roles[1].name == "role10" || u11.roles.get()[1].name == "role11"); assert(u11.customer.name == "Customer 10"); assert(u11.customer.users.length == 1); assert(u11.customer.users[0] == u10); assert(u11.roles[0].users.length == 1); assert(u11.roles[0].users[0] == u10); // remove reference u11.roles.get().remove(0); sess.update(u11); // remove entity sess.remove(u11); 标签:HibernateD
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值