概述

Anyline 通过 DataSourceHolder Adapter Actuator DataRuntime 等核心组件,实现了 100+ 数据库的统一访问接口。整体框架采用模板方法模式,通过继承和重写实现数据库兼容性。

组件 职责 典型实现
DataSourceLoader 加载 DataSourceHolder JDBCDataSourceLoader, MilvusDataSourceLoader
DataSourceHolder 创建数据源与 DataRuntime JDBCDataSourceHolder, MilvusDataSourceHolder
Adapter 适配数据库语法,生成命令 JDBCAdapter, MilvusAdapter
Actuator 调用官方驱动执行命令 JDBCActuator, MilvusActuator
DataRuntime 封装数据源运行时属性 与数据源一一对应
RuntimeHolder 创建 DataRuntime RuntimeHolder
Run 封装数据库命令 MilvusRun, InfluxRun, InfluxVndRun, InfluxJsonRun

一、关键类说明

DataSourceLoader

只负责加载相应的 DataSourceHolder,如 JDBCDataSourceLoaderMilvusDataSourceLoader

DataSourceHolder

真正创建数据源以及 DataRuntime 的角色。所有 DataSourceHolder 统一注册进注册表并附带自己支持的数据源特征。

DataSourceHolder.reg() 方法注册数据源时,先根据数据源特征定位到特定的 *DataSourceHolder 并实际调用其方法注册数据源,如 JDBCDataSourceHolder、MilvusDataSourceHolder。

Adapter

用来适配不同数据库语法,生成相应数据库的命令(如 SQL)。

创建 Adapter 规范

创建 Adapter 时要先复制模板再修改,以保持方法的统一和顺序方便阅读:

Actuator

用来根据 Adapter 生成的命令调用数据库官方驱动方法,如 JDBCActuatorMilvusActuator

创建 Actuator 时应该先复制 org.anyline.data.adapter.init.TemplateActuator 再修改。

DataRuntime

与数据源一一对应,用来封装对应数据源的驱动类 / Adapter 等属性。由 RuntimeHolder 创建。

Run

用来封装数据库命令。对于非关系型数据库一般有自己的 Run 用来区分不同的命令,Actuator 会根据 Run 类型不同调用不同的驱动方法。

MilvusRun InfluxRun InfluxVndRun InfluxJsonRun


二、Select 实现过程

调用流程

ENTRY 调用 AnylineService.selects()
参数:数据源、ConfigStore、其他查询条件
ENTRY 调用 AnylineServiceDao.selects(RunPrepare, ConfigStore, ...)
dao 定位到对应的 DataRuntime 和 DriverAdapter
ADAPTER 调用 DriverAdapter.selects()
通过 buildSelectRun() 把 RunPrepare 封装成对应数据库的 Run
ADAPTER 合成最终命令 — selects(runtime, random, system, action, table, configs, run, sql, values)
ACTUATOR actuator.selects(adapter, runtime, random, system, action, table, configs, run, sql, values, columns)
调用官方驱动接口并封装数据
RESULT 返回 DataSet<DataRow> 结果集

详细步骤

1. 数据源统一封装为 RunPrepare

数据源有可能是表名、text 以及 XML 中定义的 SQL 的 ID。其中 text 代表了 SQL、AQL 等各种数据库的命令。数据源统一封装成 RunPrepare

2. Adapter 中 buildSelectRun 封装 Run

这个过程中会处理 join、union、order、group、查询条件等参数以及占位符和值。

这一步是实现数据库兼容的关键。

3. 分页处理

封装完 Run 后先处理分页。

4. 合成最终命令

selects(DataRuntime runtime, String random, boolean system, ACTION.DML action, Table table, ConfigStore configs, Run run, String sql, List values)

5. Actuator 执行

actuator.selects(DriverAdapter adapter, DataRuntime runtime, String random, boolean system, ACTION.DML action, Table table, ConfigStore configs, Run run, String sql, List values, LinkedHashMap columns)

这个方法负责调用官方驱动的接口并封装数据。


三、Delete / Update 过程

Delete、Update 过程与 Select 类似,遵循相同的调用链路:

ServiceDaoAdapterActuator


四、查询元数据(以 Table 为例)

调用流程

ENTRY AnylineService.metadata().tables()
参数:catalog, schema, name, type, struct
DAO 传给 Dao → 传给相应 Adapter
ADAPTER buildSelectTablesRun() 构造查询元数据的命令(返回 List)
ACTUATOR 执行命令返回查询结果集
RESULT 结果集封装成 Table 对象(init + detail)

buildSelectTablesRun 方法签名

/**
 * [命令合成] 查询表,不是查表中的数据
 * @param runtime 运行环境(驱动适配器、数据源或客户端)
 * @param greedy  贪婪模式
 *                true : 查询权限范围内尽可能多的数据
 *                false: 只查当前 catalog/schema/database 范围内数据
 * @param query   查询条件 根据 metadata 属性
 * @param types   查询类型 参考 Table.TYPE 多个类型相加算出总和
 */ List<Run> buildSelectTablesRun(DataRuntime runtime, boolean greedy, Table query, int types, ConfigStore configs)

封装过程

调用两个方法:

两者都需要根据 MetadataFieldRefer 来确定 Table 属性与结果集中列的对应关系。

MetadataFieldRefer

在 Adapter 中定义,描述元数据属性与结果集列名的映射关系:

public MetadataFieldRefer initTableFieldRefer() { MetadataFieldRefer refer = new MetadataFieldRefer(Table.class);
    refer.map(Table.FIELD_NAME, "TABLE_NAME,NAME,TABNAME");
    refer.map(Table.FIELD_CATALOG, "TABLE_CATALOG");
    refer.map(Table.FIELD_SCHEMA, "TABLE_SCHEMA,TABSCHEMA,SCHEMA_NAME");
    refer.map(Table.FIELD_COMMENT, "TABLE_COMMENT,COMMENTS,COMMENT"); return refer;
}

获取引用:MetadataFieldRefer refer = refer(runtime, Table.class);

驱动内置接口

有些数据库驱动不提供查询元数据的命令,但提供 Java 接口方法。此时直接调用驱动方法返回元数据信息:

查询出元数据列表后,再根据 struct 参数决定是否查询其他属性(如 table 的 column)。


五、创建 / 修改 / 删除元数据(以 Table 为例)

调用流程

ENTRY AnylineService.ddl().tables()
参数:已封装好的 Table 对象
DAO 传给 Dao → 传给相应 Adapter
ADAPTER create(DataRuntime runtime, Table meta)
调用 buildCreateRun() 生成命令(返回 List)
ACTUATOR 逐个执行命令

buildCreateRun 实现

通常分步合成最终命令:

List runs = new ArrayList<>(); Run run = new SimpleRun(runtime);
runs.add(run); StringBuilder builder = run.getBuilder();

builder.append("CREATE ").append(keyword(meta)).append(" "); // 基础结构 checkTableExists(runtime, builder, false); name(runtime, builder, meta); // 列与索引 body(runtime, builder, meta); // 列 indexes(runtime, builder, meta); // 索引 // 继承表 inherit(runtime, builder, meta); // 引擎与编码 engine(runtime, builder, meta); // 引擎 charset(runtime, builder, meta); // 编码方式 // 主键 keys(runtime, builder, meta); // type keys // 注释 comment(runtime, builder, meta); // 分表分桶 partitionBy(runtime, builder, meta); partitionFor(runtime, builder, meta); distribution(runtime, builder, meta); // 分桶方式 // 物化视图 materialize(runtime, builder, meta); // 扩展属性 property(runtime, builder, meta); // 附加命令 runs.addAll(buildAppendCommentRun(runtime, meta));
runs.addAll(buildAppendColumnCommentRun(runtime, meta));
runs.addAll(buildAppendPrimaryRun(runtime, meta));
runs.addAll(buildAppendIndexRun(runtime, meta));
根据数据库不同,分别实现以上方法。如果遇到序列不一致的情况,Adapter 单独实现 buildCreateRun 用来改变子方法调用顺序,但子方法只需要覆盖不同部分。
⚠️ 执行注意事项

关系型数据库一般可以生成文本命令,但有些数据库驱动只提供 Java 接口方法。Actuator 需要根据 Run 的类型及属性分别调用不同的驱动接口。


六、权限相关操作

6.1 查询权限(以 Users 为例)

ENTRY AnylineService.authorize().users()
参数:User 对象
DAO 传给 Dao → 传给 Adapter
ADAPTER buildSelectUsersRun() 生成命令(返回 List)
RESULT Actuator 执行 → 封装成 User 对象

封装过程与元数据封装类似。

6.2 创建 / 修改 / 删除权限(以 User 为例)

ENTRY AnylineService.authorize().create(User)
参数:User 对象(用户名、密码等)
DAO 传给 Dao → 传给 Adapter
ADAPTER buildCreateUserRun() 生成命令(返回 List)
ACTUATOR 执行命令

Anyline MDM — Architecture Documentation