关系型数据库数据库前端【免费下载链接】lovefieldLovefield is a relational database for web apps. Written in JavaScript, works cross-browser. Provides SQL-like APIs that are fast, safe, and easy to use.项目地址https://gitcode.com/gh_mirrors/lov/lovefield点击查看免费下载导读本文聚焦 Lovefield 的 SPACSchema Parser And Code-generatorSchema 解析与代码生成器静态建库方案通过一份 YAML 文件描述完整数据库 Schema由 SPAC 校验并生成带 Closure 类型注解的 JavaScript 代码。读者将掌握 YAML Schema 的完整语法表、列、约束、索引、pragma、SPAC 命令行用法、生成代码的调用方式connect / insert / select / update / delete并理解静态 Schema 与动态 Schema API 的差异及体积优化原理。文中所有语法与行为均以仓库 spac 源码与 docs/spec/07_spac.md 规范为据。一、什么是 SPAC静态 Schema 与动态 SchemaLovefield 提供两种数据库 Schema 定义方式静态方式用 YAML 文件描述 SchemaSPAC 读取文件、校验并生成对应 JavaScript 代码。这是本文讲解的核心。动态方式不借助 YAML直接通过编程 APIlf.schema.create/createTable等在代码中构建 Schema其 API 详见 Schema 规范。SPAC 存在的根本原因是为 Closure 编译器用户提供更好的编译覆盖。Lovefield 从第一天起就使用 Closure 编译器高级优化advanced optimization完成全量编译以保证类型安全。静态 Schema 是唯一能通过 Closure 编译器把代码体积充分优化的路径前提是 Lovefield 与用户代码一起编译。其收益为minified 后体积从约 200KB 降至约 70KB且类型检查更严格docs/spec/07_spac.md。从源码实现看SPAC 由三部分构成spac/spac.js入口加载器spac.js另有可直接执行的脚本 spac/lovefield-spac负责解析命令行参数、读取模板、调用解析与代码生成解析器spac/parser.js基于js-yaml的safeLoad读取 YAML再逐项转换为lf.schema.Database实例代码生成器spac/codegen.js依据 spac/template/schema.jstemplate 与 spac/template/database.jstemplate 两套模板输出最终代码。二、SPAC 的命令行用法SPAC 设计为通过 Node.js 运行。入口 spac/lovefield-spac#!/usr/bin/env node与 spac/spac.js 逻辑一致命令参数如下源码中knownOpts定义于 spac/spac.js参数类型是否必需说明--schemaString必需YAML Schema 文件路径--namespaceString必需生成代码的命名空间--outputdirString必需输出目录不存在时会自动创建--templatedirString可选模板目录默认使用__dirname /template--nocombineBoolean可选不合并生成文件默认false合并典型调用node spac/lovefield-spac \ --schema path/to/schema.yaml \ --namespace my.app.db \ --outputdir generated/执行流程对应 spac/spac.js解析参数 → 创建输出目录mkdirp.sync→ 读取template/templates.json中的模板清单spac/template/templates.json 声明了schema.jstemplate与database.jstemplate两个模板→ 用parser.convert解析并校验 YAML → 对每个模板调用codegen.generate。输出文件命名规则baseName namespace.replace(/\./g, _)。默认合并模式下所有模板生成内容合并写入outputdir/baseName_gen.js第一个模板结果覆盖写入后续模板追加若指定--nocombine则每个模板分别输出outputdir/baseName_template名.js例如schema.js、database.js。namespace 与 DB name 的区别详见 docs/spec/07_spac.mdDB nameYAML 中的name唯一标识持久化数据库只用于打开数据库namespace用于封装指定 DB Schema 的生成代码每个 namespace 只能承载一个 DB若想在不改 Schema 的情况下换 namespace可复用同一 YAML 文件、传入不同 namespace生成代码仍会打开同一个数据库文件。此外解析器 spac/parser.js 定义了INVALID_DB_NAMES [Db, Transaction]数据库名不得为这两个保留名。三、Schema YAML 顶层结构静态 Schema YAML 文件整体结构如下docs/spec/07_spac.md%YAML 1.2 --- name: string # 必需 version: integer # 必需必须为 1 的正整数 table: # 必需至少一个表定义 table1_definition table2_definition ...各字段说明name数据库唯一标识会被锻造进生成代码name与 origin来源共同唯一确定一个数据库具体见 Lovefield 初始化。version必须大于 0 的整数。Lovefield 用它判断数据库是否需要升级开发者应通过 database upgrade 机制做数据迁移。table至少包含一个表定义。解析器 spac/parser.js 的convert()会校验version为数字、name不落入保留名然后对每个表调用createTableDefinition构建lf.schema.Database。真实样例见仓库 spac/testdata/codegen.yaml含Album、Photo、Details、Curator四表覆盖 datetime、arraybuffer、object 列以及复合主键、外键、unique、复合索引等特性以及 SPAC 代码生成测试 spac/codegen_test.js 与解析测试 spac/parser_test.js。四、表定义Table Definition表定义是 YAML 对象语法如下table_name: column: # 必需每表至少一列 column1_name: type column2_name: type ... constraint: # 可选 constraint_definition index: # 可选 index1_definition index2_definition ... pragma: # 可选 persistentIndex: value命名规则docs/spec/07_spac.md表中所有名字只能由字母数字或下划线组成且首字符不能是数字即必须满足/^[A-Za-z_][A-Za-z0-9_]*$/名字区分大小写与 SQL 的不区分大小写相反建议使用 Camel 风格以便生成代码可读性最佳虽然名字区分大小写但若无法为它们生成唯一名字SPAC 仍会报错。例如同时存在表hd与HdSPAC 无法自动生成代码将拒绝该 Schema。从源码看每个表最终会生成#namespace.schema.TablePascal类继承lf.schema.Table并把各列构造为lf.schema.BaseColumn注册进表见 spac/template/schema.jstemplate。五、列定义Column Definition与数据类型列定义是 YAML 字段column_name: type。列名在表内必须唯一每列必须有数据类型。支持的类型及默认值如下docs/spec/07_spac.md类型说明默认值arraybufferJavaScript ArrayBuffernullboolean布尔falsedatetime伪类型内部转为时间戳整数可空—integer32 位整数0number数字0object可空的通用 JS 对象原样存储nullstring字符串可空空字符串关键行为boolean、integer、number不可空Lovefield 不允许任何字段值为undefined可索引键仅限 string 与 numberarraybuffer 与 object 既不能作为索引/约束的一部分也不能出现在WHERE子句中不可搜索用作索引/主键或唯一约束时会执行隐式转换boolean→ string、datetime→ number、integer→ numberarraybuffer 存入数据存储时可能被转换为十六进制字符串部分浏览器实现禁止直接存储 blob需注意 blob 转换的性能开销Lovefield 默认所有列均为 NOT NULL这与 SQL 行为不同需特别注意。源码中类型字符串到枚举的映射实现在 spac/parser.js 的columnTypeToEnumType支持arraybuffer/boolean/datetime/integer/number/string/object七种其他取值直接抛Invalid column type。六、约束定义Constraint Definition约束为可选Lovefield 支持四种主键、外键、可空、唯一。约束定义为 YAML 对象docs/spec/07_spac.mdconstraint: primaryKey: # 可选 primaryKey_definition unique: # 可选 uniqueness1_definition uniqueness2_definition ... nullable: [ nullable_columns ] # 可选 foreignKey: # 可选 foreignKey1_definition foreignKey2_definition ...约束对象的所有属性均可选。主键隐含 not null 与 unique若与其他定义冲突SPAC 将拒绝该 Schema。解析器对约束的处理顺序见 spac/parser.js主键 → unique → 外键 → 索引 → nullable → pragma。6.1 主键定义Primary Key两种主键定义类型类型一简单主键默认升序primaryKey: [columns]类型二有两个变体。变体 A 定义自增主键primaryKey: - column: column_name autoIncrement: trueLovefield 仅支持单列整数自增主键若声明非整数或多列自增主键将抛出lf.Exception.SYNTAX。变体 B 指定排序primaryKey: - column: column_name order: desc | asc - column: column_name order: desc | asc ...源码中processPrimaryKeyConstraintspac/parser.js会判断空主键抛Empty primaryKey主键首元素为对象时逐项提取column/order/autoIncrement后调用addPrimaryKey(indexedColumns)否则直接把列数组传给addPrimaryKey。复合主键样例见 spac/testdata/codegen.yaml 中Details表的primaryKey: [id1, id2]与InfoCard表的primaryKey: [ id, lang ]。6.2 唯一性定义Uniquenessunique可定义在单列或跨列每个都会隐含一个索引。定义为 YAML 对象uniqueness_name: column: [ unique_columns ] # 必需跨列 unique 表示这些列的取值组合必须唯一。由于 unique 自带隐式索引无需再为这些列手动建索引。实现见 spac/parser.js 的processUniqueConstraints。6.3 可空定义Nullablenullable是可空列的数组nullable: [ nullable_columns ]代码生成样例见 spac/testdata/codegen.yaml 的Photo表nullable: [ proto, tacotownJspb, accessTimestamp, createdByAction ]。6.4 外键定义Foreign Key外键定义为 YAML 对象foreignKey_name: local: local_column # 必需 ref: remote_table.remote_column # 必需 action: restrict | cascade # 可选 timing: immediate | deferrable # 可选主键与外键约束违规会导致事务被拒绝与 SQL 行为一致外键定义的详细信息见 Referential Integrity。源码中processForeignKeyConstraintsspac/parser.js提取local/ref并把action/timing字符串映射到枚举actionrestrict→lf.ConstraintAction.RESTRICTcascade→lf.ConstraintAction.CASCADEspac/parser.jstimingdeferrable→lf.ConstraintTiming.DEFERRABLEimmediate→lf.ConstraintTiming.IMMEDIATEspac/parser.js。order的映射同理asc→lf.Order.ASCdesc→lf.Order.DESC其他取值抛错spac/parser.js。仓库还提供大量外键合法性测试数据如 spac/testdata/foreign_key_invalid_action.yaml、spac/testdata/foreign_key_chain.yaml 等。七、索引定义Index Definition索引定义为 YAML 对象支持两种语法docs/spec/07_spac.md。语法一所有列同序时使用index_name: column: [ column_names ] # 必需 order: desc | asc # 可选默认 asc unique: boolean # 可选语法二各列排序不同时使用index_name: column: - name: column_name order: desc | asc # 可选默认 asc - name: column_name order: desc | asc ... unique: boolean # 可选要点索引可以是单列或跨列与多数 SQL 引擎不同Lovefield 要求索引列的值不能为 nullorder与unique均可选未指定时索引一律升序创建所有 unique 约束已构建隐式索引无需再为它们手动建索引。源码中processIndicesspac/parser.js通过判断indexDefinition.column[0]是否为字符串来区分两种语法字符串数组走addIndex(name, columns, unique, order)对象数组则逐项提取name/order后走addIndex(name, indexedColumns, unique)。为保持与动态 Schema API 默认值一致未指定的字段如order刻意不赋默认值见代码注释 spac/parser.js。自定义索引Custom Index自定义索引指基于变换创建的索引例如对 email 字段做文本反转后建索引。SPAC不支持自定义索引因为 Chrome Apps v2 的限制无法持久化 JavaScript 函数与 eval。用户应在自己的 JavaScript 中完成变换并存储变换后的数据。样例YAML Schema 与其 SQL 等价物规范 docs/spec/07_spac.md 给出crdb示例。YAML 部分%YAML 1.2 --- name: crdb version: 1 table: ImageCache: column: remote: string local: string constraint: primaryKey: [ remote ] Asset: column: id: string asset: string timestamp: integer constraint: primaryKey: [ id ] Pin: column: id: string state: integer sessionId: string constraint: foreignKey: fkId: local: id ref: Asset.id action: cascade InfoCard: column: id: string lang: string itag: integer country: string fileName: string constraint: primaryKey: [ id, lang ] unique: uniqFN: column: [ fileName ] index: idxPinItag: column: [ itag ]等价 SQLCREATE DATABASE crdb; CREATE TABLE ImageCache ( remote AS TEXT PRIMARY KEY, local AS TEXT NOT NULL ); CREATE TABLE Asset ( id AS TEXT PRIMARY KEY, asset AS TEXT, timestamp AS INTEGER ); CREATE TABLE Pin ( id, state AS INTEGER, sessionId AS TEXT, FOREIGN KEY id REFERENCES Asset(id) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE InfoCard ( id AS TEXT, lang AS TEXT, itag AS INTEGER, country AS TEXT, fileName AS TEXT UNIQUE PRIMARY KEY (id, lang) ); CREATE INDEX idxItag ON InfoCard.itag;注意默认列即 NOT NULL如local AS TEXT NOT NULLSQL 对照中显式标出而Pin.id虽无类型标注仍是主键引用关系中的一部分。八、Pragma当前仅提供persistentIndex一个 pragma 选项要求布尔值docs/spec/07_spac.mdpragma: persistentIndex: value设为true时该表的所有索引将存储在持久化存储上。解析器中通过tableBuilder.persistentIndex(tableSchema.pragma.persistentIndex)应用spac/parser.js。另外仓库测试数据还展示了数据库级enableBundledModepragma见 spac/testdata/bundled_mode.yaml 与 spac/testdata/bundled_mode_disabled.yaml其对应生成代码中的pragma_.enableBundledMode字段spac/template/schema.jstemplate代码生成测试通过这两个文件断言 bundle 模式开关是否正确写入spac/codegen_test.js。九、代码生成Code Generation与生成产物YAML Schema 需由 SPAC 解析并校验随后生成提供以下能力的 JavaScript 代码docs/spec/07_spac.md用于创建数据库实例的静态函数namespace.connect()可用于构造查询条件的数据库 Schema 类namespace.schema.Database及各表 Schema可用于创建查询的数据库实例类namespace.Database。9.1 自动生成的类SPAC 自动生成如下内容docs/spec/07_spac.mdClasses/Functions说明namespace.connect()数据库初始化namespace.Database数据库类namespace.row.table每张表的行类型namespace.schema.Database数据库 Schemanamespace.schema.table每张表的 Schema每个类都带有完整的 Closure 类型定义注解。以 namespacelovefield.db为例真实生成产物见 spac/testdata/database.js 与 spac/testdata/foo_schema.js对应foo.db命名空间。从模板 spac/template/database.jstemplate 可以看到connect()的实现要点通过lf.service.ServiceId(ns_#dbname)注册一个命名空间隔离的lf.Global重复connect()已连接或连接中会抛lf.Exception(113)connect()内部先取 Schema再实例化lf.proc.Database并调用init(opt_options)。Schema 模板 spac/template/schema.jstemplate 则为每张表生成row.TablePascal含Type/DbType两个类型别名与schema.TablePascal类并提供createRow(opt_value)内部调用lf.Row.getNextId()分配行号、deserializeRow(dbRecord)、getConstraint()等实现。9.2 生成的 API 用法示例以下为使用 SPAC 生成代码namespace 假设为my.namespace.db执行最基础 SCUD 操作的完整示例docs/spec/07_spac.mdvar db; var infoCard; function createRows() { // Method 1: use lovefield-generated functions var row infoCard.createRow(); row.setId(something); row.setLang(en); row.setItag(140); row.setCountry(US); row.setFileName(140-en-US); // Method 2: directly pass in JSON object var row2 infoCard.createRow({ id: something, lang: fr, itag: 145, country: FR, fileName: 145-fr-FR }); return [row, row2]; } // namespace.instanceName.connect() to connect to DB instance my.namespace.db.connect().then(function(dbInstance) { db dbInstance; infoCard db.getSchema().getInfoCard(); // INSERT INTO InfoCard VALUES (...); var rows createRows(); return db.insert().into(infoCard).values(rows).exec(); }).then(function() { // SELECT id, lang, fileName FROM InfoCard // WHERE id something AND lang en; db.select([infoCard.id, infoCard.lang, infoCard.fileName]). from(infoCard). where(lf.op.and( infoCard.id.eq(something), infoCard.lang.eq(en))). exec().then(function(rows) { console.log(rows[0].id, rows[0].lang, rows[0].fileName); }); // UPDATE InfoCard SET lang fr WHERE id LIKE whatever% return db.update(infoCard). set(infoCard.lang, fr). where(infoCard.id.like(/^whatever/)). exec(); }).then(function() { // DELETE FROM InfoCard WHERE lang es; var q db.delete().from(infoCard).where(infoCard.lang.eq(es)); return q.exec() });要点两种建行方式既可以用生成的行对象 setterrow.setId(...)也可以直接传 JSON 对象给createRow({...})建表时生成的行类提供了每个字段的 setter/getter字段名对应 YAML 中列名转换的 camelCasedb.getSchema().getInfoCard()直接取表 Schema 对象其属性如infoCard.id即lf.schema.BaseColumn可参与eq/like/lf.op.and等谓词组合查询构建器 APIselect/insert/update/delete的完整语义见 Query 与 Transaction。9.3 生成代码的编译与体积收益静态 Schema 的价值核心是配合 Closure 编译器当 Lovefield 与用户代码一起以 advanced optimization 模式编译时所有类型信息可被静态推导从而把 minified 体积从约 200KB 压缩到约 70KB并获得更严格的类型检查。这要求使用 SPAC 的工程构建流程中集成 Closure 编译器Lovefield 自身构建与依赖脚本见 tools/node_bootstrap.js 与 tools/scan_deps.js。十、校验、测试与实战注意事项SPAC 的可靠性由两层测试保障解析层解析与校验行为由 spac/parser_test.js 覆盖仓库还提供大量正/反例 YAML 数据spac/testdata如invalid.yaml、invalid_primary_key.yaml、index_on_nonexist_column.yaml、foreign_key_non_unique.yaml、nan.yaml等可用于学习 SPAC 会拒绝哪些非法 Schema代码生成层生成结果与 golden 文件逐字节比对见 spac/codegen_test.js 及 spac/testdata/database.js、spac/testdata/foo_schema.js。实战注意事项汇总命名全部使用/^[A-Za-z_][A-Za-z0-9_]*$/合法名且避免仅大小写不同的重名如hd与Hd否则 SPAC 拒绝类型arraybuffer/object不可建索引、不可进WHEREboolean/integer/number不可空默认全列 NOT NULL索引索引列值不得为 nullunique 约束自带索引勿重复建主键自增主键仅支持单列 integer且会隐式建索引版本version为正整数升级时通过 database upgrade 迁移数据生成物默认合并输出为namespace去掉点_gen.js可配合--nocombine拆分输出便于调试。延伸阅读Schema 规范动态 API数据库生命周期与初始化/升级Query 查询规范Transaction 事务规范Referential Integrity 参照完整性SPAC 测试运行方式见 running_tests 与 dev_setup赞分享关系型数据库数据库前端【免费下载链接】lovefieldLovefield is a relational database for web apps. Written in JavaScript, works cross-browser. Provides SQL-like APIs that are fast, safe, and easy to use.项目地址https://gitcode.com/gh_mirrors/lov/lovefield点击查看免费下载相关推荐如何用FGO-py实现全自动FGO游戏体验解放双手的终极指南如何用FGO py实现全自动FGO游戏体验解放双手的终极指南 还在为无尽的FGO刷本而烦恼吗每天重复点击屏幕、选择助战、释放技能不仅耗时耗力还让你错过了GUI 自动化桌面应用计算机视觉RPA任务调度Lovefield 数据库 Schema 设计深度解析动态 Builder 与静态 SPAC 双路径全指南Lovefield 数据库 Schema 设计深度解析动态 Builder 与静态 SPAC 双路径全指南 Lovefield 是运行在浏览器中的关系型数据库关系型数据库数据库前端LunaTV 配置订阅首次同步5 步把播放源配好LunaTV 配置订阅首次同步5 步把播放源配好 这篇文章写给刚部署完 LunaTV、还不知道播放源怎么来的站长。它只讲一件事用配置订阅功能把别人整理好的前端后端音视频上一篇如何免费解锁全网音乐MusicFree插件终极指南2024下一篇解放双手阴阳师自动化脚本让你的游戏生活更轻松创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?