概述
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,如 JDBCDataSourceLoader、MilvusDataSourceLoader。
DataSourceHolder
真正创建数据源以及 DataRuntime 的角色。所有 DataSourceHolder 统一注册进注册表并附带自己支持的数据源特征。
DataSourceHolder.reg() 方法注册数据源时,先根据数据源特征定位到特定的 *DataSourceHolder 并实际调用其方法注册数据源,如 JDBCDataSourceHolder、MilvusDataSourceHolder。
Adapter
用来适配不同数据库语法,生成相应数据库的命令(如 SQL)。
创建 Adapter 时要先复制模板再修改,以保持方法的统一和顺序方便阅读:
-
关系型 / JDBC 数据库 先复制
org.anyline.data.jdbc.adapter.init.TemplateJDBCAdapter,并把TemplateAdapter的内容复制进去,参考其中的"复制过程"说明 -
其他数据库 先复制
org.anyline.data.adapter.init.TemplateAdapter再修改
Actuator
用来根据 Adapter 生成的命令调用数据库官方驱动方法,如 JDBCActuator、MilvusActuator。
创建 Actuator 时应该先复制 org.anyline.data.adapter.init.TemplateActuator 再修改。
DataRuntime
与数据源一一对应,用来封装对应数据源的驱动类 / Adapter 等属性。由 RuntimeHolder 创建。
Run
用来封装数据库命令。对于非关系型数据库一般有自己的 Run 用来区分不同的命令,Actuator 会根据 Run 类型不同调用不同的驱动方法。
MilvusRun InfluxRun InfluxVndRun InfluxJsonRun
二、Select 实现过程
调用流程
AnylineService.selects()参数:数据源、ConfigStore、其他查询条件
AnylineServiceDao.selects(RunPrepare, ConfigStore, ...)dao 定位到对应的 DataRuntime 和 DriverAdapter
DriverAdapter.selects()通过
buildSelectRun() 把 RunPrepare 封装成对应数据库的 Run
selects(runtime, random, system, action, table, configs, run, sql, values)
actuator.selects(adapter, runtime, random, system, action, table, configs, run, sql, values, columns)调用官方驱动接口并封装数据
详细步骤
1. 数据源统一封装为 RunPrepare
数据源有可能是表名、text 以及 XML 中定义的 SQL 的 ID。其中 text 代表了 SQL、AQL 等各种数据库的命令。数据源统一封装成 RunPrepare。
2. Adapter 中 buildSelectRun 封装 Run
-
关系数据库 通常创建
TextRun、TableRun、XMLRun等 -
非关系型数据库 可能创建
InfluxRun、InfluxVndRun、InfluxJsonRun等特定 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 类似,遵循相同的调用链路:
Service → Dao → Adapter → Actuator
四、查询元数据(以 Table 为例)
调用流程
AnylineService.metadata().tables()参数:catalog, schema, name, type, struct
buildSelectTablesRun() 构造查询元数据的命令(返回 List)
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)
封装过程
调用两个方法:
- init() 初始化 name 等基础属性
- detail() 赋值其他更多属性
两者都需要根据 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 接口方法。此时直接调用驱动方法返回元数据信息:
-
databases()— 返回数据库列表 -
collections()— 返回集合列表(文档数据库的 collection 对应关系数据库的 table)
查询出元数据列表后,再根据 struct 参数决定是否查询其他属性(如 table 的 column)。
五、创建 / 修改 / 删除元数据(以 Table 为例)
调用流程
AnylineService.ddl().tables()参数:已封装好的 Table 对象
create(DataRuntime runtime, Table meta)调用
buildCreateRun() 生成命令(返回 List)
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 为例)
AnylineService.authorize().users()参数:User 对象
buildSelectUsersRun() 生成命令(返回 List)
封装过程与元数据封装类似。
6.2 创建 / 修改 / 删除权限(以 User 为例)
AnylineService.authorize().create(User)参数:User 对象(用户名、密码等)
buildCreateUserRun() 生成命令(返回 List)
Anyline MDM — Architecture Documentation