简介

AnyLine的核心是一个面向运行时的 元数据动态关系映射 主要用于

  • 动态注册切换数据源
  • 读写元数据
  • 对比数据库结构差异
  • 生成动态SQL,组合动态查询条件
  • 复杂的结果集操作

适配各种关系型与非关系型数据库(及各种国产小众数据库)
常用于动态结构场景的底层支持,作为SQL解析引擎或适配器出现
如:数据中台、可视化、低代码、SAAS、自定义表单、异构数据库迁移同步、 物联网车联网数据处理、 条件/数据结构、 爬虫数据解析等。 参考【适用场景

数据源注册及切换

注意这里的数据源并不是主从关系,而是多个完全无关的数据源。

DataSource ds_sso = new DruidDataSource();
ds_sso.setUrl("jdbc:mysql://localhost:3306/sso");
ds_sso.setDriverClassName("com.mysql.cj.jdbc.Driver");
...
DataSourceHolder.reg("ds_sso", ds_sso);
或
DataSourceHolder.reg("ds_sso", pool, driver, url, user, password);
DataSourceHolder.reg("ds_sso", Map<String, Object> params); //对应连接池的属性k-v

//查询ds_sso数据源的SSO_USER表
DataSet set = ServiceProxy.service("ds_sso").querys("SSO_USER");

来自静态配置文件数据源(如果是spring环境可以按spring格式)

#默认数据源
anyline.datasource.type=com.zaxxer.hikari.HikariDataSource
anyline.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
anyline.datasource.url=jdbc:mysql://localhost:33306/simple
anyline.datasource.user-name=root
...更多参数
#其他数据源
anyline.datasource-list=crm,erp,sso,mg

anyline.datasource.crm.driver-class-name=com.mysql.cj.jdbc.Driver
anyline.datasource.crm.url=jdbc:mysql://localhost:33306/simple_crm
anyline.datasource.crm.username=root

anyline.datasource.erp.driver-class-name=com.mysql.cj.jdbc.Driver
anyline.datasource.erp.url=jdbc:mysql://localhost:33306/simple_erp
anyline.datasource.erp.username=root

DML

如果是web环境可以
service.querys("SSO_USER", 
   condition(true, "NAME:%name%", "TYPE:[type]", "[CODES]:code"));
//true表示需要分页,没有传参籹值的条件默认忽略
//生成SQL:
SELECT * 
FROM SSO_USER 
WHERE 1=1 
AND NAME LIKE '%?%' 
AND TYPE IN(?,?,?)
AND FIND_IN_SET(?, CODES)	
LIMIT 5,10 //根据具体数据库类型

//用户自定义查询条件,低代码等场景一般需要更复杂的查询条件
ConfigStore confis;
service.query("SSO_USER", configs);
ConfigStore提供了所有的SQL操作  
//多表、批量提交、自定义SQL以及解析XML定义的SQL参数示例代码和说明

读写元数据

@Autowired("anyline.service")
AnylineService service;

//查询默认数据源的SSO_USER表结构
Table table = serivce.metadata().table("SSO_USER");
LinkedHashMap<String, Column> columns = table.getColumns();                 //表中的列
LinkedHashMap<String, Constraint> constraints = table.getConstraints();     //表中上约束
List<String> ddls = table.getDdls();                                        //表的创建SQL

//删除表 重新创建
service.ddl().drop(table);
table = new Table("SSO_USER");

//这里的数据类型随便写,不用管是int8还是bigint,执行时会转换成正确的类型
table.addColumn("ID", "BIGINT").autoIncrement(true).setPrimary(true).setComment("主键");
table.addColumn("CODE", "VARCHAR(20)").setComment("编号");
table.addColumn("NAME", "VARCHAR(20)").setComment("姓名");
table.addColumn("AGE", "INT").setComment("年龄");
service.ddl().create(table);

或者service.ddl().save(table); 执行时会区分出来哪些是列需要add哪些列需要alter

事务

//因为方法可以有随时切换多次数据源,所以注解已经捕捉不到当前数据源了
//更多事务参数通过TransactionDefine参数
TransactionState state = TransactionProxy.start("ds_sso"); 
//操作数据
TransactionProxy.commit(state);
TransactionProxy.rollback(state);

已经有ORM了 为什么还要有AnyLine, 与ORM有什么区别

  • 面向场景不同
    anyline主要面向动态场景,就是运行时随时可变的场景。
    如我们常用的动态数据源,不是在部署时可以固定在配置文件中,
    而是可能在不确定的时间,由不确定的用户提供的不确定数据源。
    表结构等元数据也可能随着着用户或数据源的不同而随时变化。
  • 针对产品不同
    anyline一般不会直接用来开发一个面向终端用户的产品(如ERP、CRM等),
    而是要开发一个中间产品(如低代码平台),让用户通过中间产品来生成一个最终产品。
    再比如用anyline开发一个自定义查询分析工具,让用户通过这个工具根据业务需求生成动态报表。
    anyline不是要提供一个可二次开发的半成品船,而是可以用来造船的动态船坞。
  • 操作对象不同 anyline主要操作元数据,因为在项目开发之初,可能就没有一个如ERP/CRM之类的明确的产品,
    当然也就没有订单、客户之类的具体对象及属性,所以也没什么具体数据可操作。
  • 面向用户(开发设计人员)不同
    anyline要面向的不是开船的人,而是造船的人,而不是使用工具的人,而是设计工具的人。 anyline的大部分代码与灵感也是来自这部分用户的日常实践。
  • 所以对用户(开发设计人员)要求不同
    一个ORM用户用了许多年的连接池,他可以只知道配置哪几个默认参数,也能正常开展工作。
    但anyline的用户不行,他要求这个团队中至少有一个人要明白其所以然。

实际操作中与ORM最明显的区别是

  • 摒弃了各种繁琐呆板的实体类以及相关的配置文件
    让数据库操作更简单,不要一动就是一整套的service/dao/mapping/VOPODTO有用没用的各种O,生成个简单的SQL也各种判断遍历。
  • 强强化了结果集的对象概念
    面向对象的对象不是只有get/set/注解这么弱
    需要把数据及对数据的操作封装在一起,作为一个相互依存的整体,并实现高度的抽象
    要关注元数据,不要关注姓名、年龄等具体属性
    强化针对结果集的数据二次处理能力
    如结果集的聚合、过滤、行列转换、格式化及各种数学计算尽量作到一键…一键…
    而不要像ORM提供的entity, map, list除了提供个get/set/foreach,稍微有点用的又要麻烦程序员各种判断各种遍历
    参考【疑问与优劣对比

与如何实现

数据操作的两个阶段,1.针对数据库中数据 2.针对数据库查询的结果集(内存中的数据)

  • 提供一个通用的AnylineService实现对数据库的一切操作
  • 提供一对DataSet/DataRow实现对内存数据的一切数学计算
    DataSet/DataRow不是对List/Map的简单封装 他将是提高我们开发速度的重要工具,各种想到想不到的数学计算,只要不是与业务相关的都应该能实现

AnyLine解决或提供了什么

动态、运行时

即运行时才能最终确定 动态的数据源、数据结构、展现形式
如我们需要开发一个数据中台或者一个数据清洗插件,编码阶段我们还不知道数据来源、什么类型的数据库甚至不是数据库、会有什么数据结构对应什么样的实体类,
如果需要前端展示的话,更不会知道不同的终端需要什么各种五花八门的数据组合
那只能定义一个高度抽象的实体了,想来想去也只有Collection可以胜任了。

简单快速的操作数据库

最常见的操作:根据条件分页查询一个表的几列
这一动就要倾巢出动一整套的service/dao/vo dto 各种O/mapper,生成个查询条件各种封装、为了拼接个SQL又是各种if else forearch
如果查询条件是由前端的最终用户动态提供的,那Java里if完了还不算完,xml中if也少不了
一旦分了页,又要搞出另一套数据结构,另一组接口,另一组参数(当然这种拙劣的设计只是极个别,不能代表ORM)

简单快速的操作结果集

数据库负责的是存储,其结构肯定是与业务需要不一样的。所以结果集需要处理。当我们需要用Map处理数据或数学计算时,
如最常见的数据格式化、筛选、分组、平均值、合计、方差等聚合计算
再如空值的处理包括, “”, null, “null”,"\n","\r","\t"," "全角、半角等各种乱七八糟的情况
这时就会发现Map太抽象了,除了get/set/forearch好像也没别的可施展了。
要处理的细节太多了,if都不够用了。

动态数据源

再比如多数据源的情况下,要切换个数据源又是IOC又是AOP一堆设计模式全出场。经常是在方法配置个拦截器。
在同一个方法里还能切换个数据源了?
数据中台里有可能有几百几千个数据源,还得配上几千个方法?
数据源是用户动态提交的呢怎么拦截呢?
这不是DB Util的本职工作么,还要借助其他?
哪个项目少了AOP依赖还切换不了数据源了?

重复工作

如果只是写个helloworld,以上都不是问题,没什么解决不了的。但实际工作中是需要考虑工作量和开发速度的。
比如一个订单可能有几十上百列的数据,每个分析师需要根据不同的列查询。有那么几十列上同时需要<>=!=IN FIND_IN_SET多种查询方式算正常吧
不能让开发人员挨个写一遍吧,写一遍是没问题,但修改起来可就不是一遍两遍的事了
所以需要提供一个字典让用户自己去配置,低代码开发平台、自定义报表、动态查询条件应该经常有这个需求。
当用户提交上来一个列名、一个运算算、一组值,怎么执行SQL呢,不能在代码中各种判断吧,如果=怎么合成SQL,如果IN怎么合成SQL

多方言

DML方面hibernate还可以处理,DDL呢?国产库呢?

当然这种问题很难有定论,只能在实际应用过程中根据情况取舍。
可以参考【适用场景】和【实战对比】中的示例
造型之前,当然要搞明白优势劣势,参考【优势劣势

误解

当然我们并不是要抛弃Entity或ORM,相反的 AnyLine源码中也使用了多达几十个Entity
在一些 可预知的 固定的 场景下,Entity的优势还是不可替代的
程序员应该有分辨场景的能力
AnyLine希望程序员手中多一个数据库操作的利器,而不是被各种模式各种hello world限制

如何使用

数据操作***不要***再从生成xml/dao/service以及各种配置各种O开始
默认的service已经提供了大部分的数据库操作功能。
操作过程大致如下:

DataSet set = service.querys("HR_USER(ID, NM)", 
    condition(true, "anyline根据约定自动生成的=, in, like等查询条件"));

这里的查询条件不再需要各种配置, 各种if else foreach标签
Anyline会自动生成, 生成规则可以【参考】这里的【约定规则】 分页也不需要另外的插件,更不需要繁琐的计算和配置,指定true或false即可

如何集成

只需要一个依赖、一个注解即可实现与springboot, netty等框架项目完美整合,参考【入门系列】 大概的方式就是在需要操作数据库的地方注入AnylineService 接下来service就可以完成大部分的数据库操作了。常用示例可以参考【示例代码

兼容

如果实现放不下那些已存在的各种XOOO
DataSet与Entity之间可以相互转换
或者这样:

EntitySet<User> = service.querys(User.class, 
    condition(true, "anyline根据约定自动生成的查询条件")); 
//true:表示需要分页
//为什么不用返回的是一个EntitySet而不是List?
//因为分页情况下, EntitySet中包含了分页数据, 而List不行。
//无论是否分页都返回相同的数据结构,而不需要根据是否分页实现两个接口返回不同的数据结构

//也可以这样(如果真要这样就不要用anyline了, 还是用MyBatis, Hibernate之类吧)
public class UserService extends AnylinseService<User> 
userService.querys(condition(true, "anyline根据约定自动生成的查询条件"));

适用场景

  • 低代码后台
    主要用来处理动态属性、动态数据源、运行时自定义查询条件、元数据管理等。
    比较容易落地的几个场景如财务、库存等ERP模块用户经常需要输出不同格式的报表,根据不同维度查询统计数据
    前端可以用百度amis前端低代码框架,后端由anyline解析SQL及查询条件,管理元数据。
    【示例】
  • 数据中台
    动态处理各种异构数据源、强大的结果集批量处理能力,不再需要对呆板的实体类各种遍历各种转换。
    通常需要在运行时频繁的注册、切换、注销数据源
    【示例】
  • 可视化数据源
    主要用来处理动态属性,以及适配前端的多维度多结构的数据转换
    可视化中最主要的工作就是生成图表了,如果是通过map可实体类那又免不了各种判断、遍历、计算如果是有限的几个固定的图表还可以处理, 如果是大量的动态图表呢(比如由前端用户提供统计方式与维度)
    【参考】
  • 物联网车联网数据处理
    如车载终端、交通信号灯、数字化工厂传感器、环境检测设备数据等
    这种场景通常会涉及到时序数据库
    时序库虽然快,但是结构简单,数据需要经过各种组合后给业务系统
    时序库通常需要在运行时操作大量的DDL
    【示例】
  • 数据清洗、数据批量处理
    各种结构的数据、更多的是不符合标准甚至是错误的结构
    这种场景下需要一个灵活的数据结构来统一处理各种结构的数据
    再想像一下临时有几个数据需要处理一下(如补齐或替换几个字符)
    这个时候先去创建个Entity, XML, Service, Dao吗
    【示例】
  • 报表输出,特别是用户自定义报表
    类似于可视化环境, 样式相对简单一点,但精度要求极高,需要控制到像素、字体等
    如检验检测报告、资质证书等,当然这需要配合 anyline-office
    【office示例】
  • 工作流(运行时自定义表单/查询条件/数据结构)
    各个阶段都要自定义,比低代码要求更高的是:操作用户不懂编程 【示例】
  • 网络爬虫数据解析
    不固定的结构、html解析(当然不是用正则或dom那太费脑子了)
    【参考】
  • 异构数据库迁移同步
    动态的数据结构可以灵活的适配多种不同的表, 需不需要反复的get/set
    兼容多种数据库的DDL也可以方便的在不同类型的数据库中执行
    【核心代码示例(Mysql到Apache Ignite)】【基础应用项目】 【完整应用代替datax】
  • 还有一种很实现的场景是 许多项目到了交付的那一天 实体也没有设计完成
    别说设计了,需求都有可能还没结束就催交付了, Entity哪里找
    【示例】

关于数据库的适配 【更多查看】


links:

anyline: 运行时动态注册切换数据源,自动生成SQL(DDL/DML/DQL),读写元数据,对比数据库结构差异。适配100+关系/非关系数据库。 常用于动态场景的底层支持,如:数据中台、可视化、低代码后台、工作流、自定义表单、异构数据库迁移同步、物联网车联网数据处理、数据清洗、运行时自定义报表/查询条件/数据结构、爬虫数据解析等 (gitee.com)