
关系型数据库数据库前端【免费下载链接】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 是一个面向 Web 应用的关系型数据库用 JavaScript 编写、跨浏览器运行对外提供 SQL 风格的查询 API。本文基于官方规范文档 docs/spec/08_referential_integrity.md系统讲解 Lovefield 的外键Foreign Key约束语义、RESTRICT与CASCADE两种动作模式以及IMMEDIATE与DEFERRABLE两种约束检查时序并结合仓库源码lib/schema、lib/cache与测试用例tests/proc说明这些规则在引擎内部的真实落地方式。读完本文你将能够在建表时正确声明外键约束、预判各操作在不同模式下的行为并理解 Lovefield 如何在客户端数据库中以可预测的方式保证引用完整性。一、外键约束的语义约束仅在建表期定义Lovefield 的规范明确外键约束只能在 schema 创建期间定义且所有外键约束必须满足以下条件违反任何一条都会抛出lf.Exception.SYNTAX_ERROR一个外键约束恰好建立在两个已存在的列之间父列parent column也称 referenced / 被引用列和子列child column也称 referencing / 引用列。父列和子列可以属于同一张表也可以属于不同表即允许自引用外键。父列和子列必须具有相同且可索引的lf.Type。父列必须是唯一的要么是主键要么被标记为 unique。一个约束的子列不能被声明为另一个外键约束的父列即不允许外键约束链。禁止循环引用cyclic references。对应到代码层面上述约束在 schema 构建阶段由 lib/schema/builder.js 中的三个私有方法在finalize_()时统一校验checkForeignKeyValidity_()lib/schema/builder.js校验父表存在错误码 536、父列存在错误码 537、父子列类型一致错误码 538、父列唯一错误码 539checkForeignKeyChain_()lib/schema/builder.js检测外键链即某个外键的父列恰好是另一个外键的子列错误码 534checkFkCycle_()基于 DFS 检测有向图中是否存在环错误码 533其算法注释引用了《算法导论》第 22 章关于“有向图无环当且仅当 DFS 不产生回边”的结论。此外lib/schema/table_builder.js 中的checkPrimaryKeyNotForeignKey_()还禁止主键列同时作为外键子列错误码 543这是建表期另一条与引用完整性相关的硬性校验。外键的声明语法外键通过TableBuilder.addForeignKey(name, rawSpec)声明其中rawSpec的类型定义在 lib/schema/foreign_key_spec.js/** * typedef {{ * local: string, // 子列child column列名 * ref: string, // 父列格式为 父表名.父列名 * action: (lf.ConstraintAction|undefined), // RESTRICT 或 CASCADE * timing: (lf.ConstraintTiming|undefined) // IMMEDIATE 或 DEFERRABLE * }} */ lf.schema.RawForeignKeySpec;ref必须采用表名.列名的两段式语法若格式非法如缺少.lf.schema.ForeignKeySpec构造函数会抛出错误码 540lib/schema/foreign_key_spec.js。一个典型用法如下var db lf.schema.create(demo, 1); db.createTable(Department) .addColumn(id, lf.Type.INTEGER) .addPrimaryKey([id]) .addColumn(name, lf.Type.STRING); db.createTable(Employee) .addColumn(id, lf.Type.INTEGER) .addPrimaryKey([id]) .addColumn(departmentId, lf.Type.INTEGER) .addForeignKey(fk_dept, { local: departmentId, // 子列 ref: Department.id, // 父列父表名.父列名 action: lf.ConstraintAction.RESTRICT, timing: lf.ConstraintTiming.IMMEDIATE });声明外键时 Lovefield 还会自动为子列建立一个索引lib/schema/table_builder.js该索引的规范化名称即约束名childTable.name后续运行时约束检查正是依赖这个索引完成的详见下文第四节。在 Codelab 与测试的 schema 中也能看到这种声明方式的实际使用例如 tests/proc/end_to_end_foreign_key_cascade_test.js。二、约束动作模式RESTRICT 与 CASCADE每个外键约束都有两种动作模式由lf.ConstraintAction枚举表示定义于 lib/enums.jslf.ConstraintAction.RESTRICT 0; lf.ConstraintAction.CASCADE 1;若在 schema 定义中未显式指定动作模式默认值为RESTRICT由 lib/schema/table_builder.js 中的addForeignKey()补齐默认值。下面分别说明两种模式的行为。2.1 RESTRICT 模式在该模式下任何违反约束的操作都会被直接取消。规范以父表 被引用表、子表 引用表为基准给出了三种操作的具体规则INSERT若父表中不存在对应行则不能向子表插入新行。UPDATE/INSERT_OR_REPLACE若被修改的行在子表中有相关行则不能更新父表被引用列若子表引用列在父表中没有对应行则不能更新子表引用列。DELETE若父表的某行在子表中有相关行则不能删除该行。在运行时实现中RESTRICT 检查由 lib/cache/constraint_checker.js 完成主要分两个方向检查被引用的键必须存在checkReferredKeys_()lib/cache/constraint_checker.js用于 INSERT 和 UPDATE 场景验证子列引用的父键确实存在于父索引中否则抛出错误码 203Foreign key constraint violation。注意其中的一个细节子列为 null 时会被忽略不视为违例。检查没有引用者checkReferringKeys_()lib/cache/constraint_checker.js用于 UPDATE 和 DELETE 场景通过 schema 的getReferencingForeignKeys()找出引用当前被修改表的外键若子索引中仍包含被删除/被修改的父键则抛出错误码 203。对应到事务处理流程Journal会在插入、更新、删除时分别调用checkForeignKeysForInsert()、checkForeignKeysForUpdate()、checkForeignKeysForDelete()lib/cache/constraint_checker.js其中更新和删除场景只针对RESTRICT动作执行阻断式检查。2.2 CASCADE 模式在该模式下约束违例不会取消操作而是自动修改相关表以维持数据完整性INSERT/INSERT_OR_REPLACE与 RESTRICT 模式行为相同父行必须存在才能插入子行。需要特别注意的是insertOrReplace()查询不触发级联若希望级联效果应改用update()查询。DELETE删除父表某行时子表中所有相关行会被自动删除。UPDATE更新父表被引用列时所有引用列会被自动更新为相同的值。需要强调的一点是Lovefield 不提供为DELETE和UPDATE分别指定约束模式的粒度——一旦处于CASCADE模式UPDATE和DELETE操作都会级联。级联的运行时实现同样位于 lib/cache/constraint_checker.jsdetectCascadeUpdates()lib/cache/constraint_checker.js找出所有因父列值变更而需要同步更新的子行返回行 ID → {外键约束、原始更新行}的映射detectCascadeDeletion()lib/cache/constraint_checker.js迭代式地找出所有需要级联删除的行并记录表处理顺序tableOrder以保证删除过程中不破坏任何约束——这也是为什么级联删除能穿透多层引用而无需用户关心删除次序findReferringRowIds_()lib/cache/constraint_checker.js专门查找CASCADE动作下引用被修改行的子表行 ID。事务层则通过 lib/cache/journal.js 的updateByCascade_()第 303 行和removeByCascade_()第 335 行把检测结果真正应用到缓存中完成级联更新与级联删除。三、约束检查时序IMMEDIATE 与 DEFERRABLE在RESTRICT模式下约束还可以标记为DEFERRABLE或IMMEDIATE以控制检查发生的时机。在CASCADE模式下DEFERRABLE的概念不适用因此统一使用IMMEDIATE。时序参数通过 schema 创建 API 的可选枚举参数暴露枚举名为lf.ConstraintTiming定义于 lib/enums.jslf.ConstraintTiming.IMMEDIATE 0; lf.ConstraintTiming.DEFERRABLE 1;若未提供该参数默认值为IMMEDIATE同样由 lib/schema/table_builder.js 补齐。此外lib/schema/table_builder.js 中还有一条硬性规则不允许CASCADE动作与DEFERRABLE时序组合若同时指定会抛出错误码 506Lovefield allows only immediate evaluation of cascading constraints。3.1 DEFERRABLE可延迟可延迟约束在事务提交之前才被强制检查。在事务生命周期内各条语句可以违反该约束而不会抛出任何错误——约束检查被推迟到事务即将提交的那一刻统一执行。这为需要先写子行、后补父行的批处理场景提供了灵活性。从源码实现看checkReferredKeys_与checkReferringKeys_都接收一个constraintTiming参数只检查timing constraintTiming的约束lib/cache/constraint_checker.js即运行时按时序筛选出本次应检查的约束集合。3.2 IMMEDIATE立即立即约束在每条语句执行期间就被强制检查。这是默认行为也是最严格的模式——任何一条 INSERT/UPDATE/DELETE 语句若违反约束会立刻抛出lf.Exception错误码 203并使该操作失败。关于 DEFERRABLE 约束检查被推迟到提交前这一行为可以参考事务相关的测试与文档如 tests/proc/end_to_end_foreign_key_test.js 中对外键违例场景的验证以及 docs/spec/05_transaction.md 对事务生命周期的描述。四、约束的运行时检查机制索引驱动Lovefield 的引用完整性检查完全基于索引完成而非扫描整张表这也是其性能保证的关键。在addForeignKey()时引擎会自动为子列建立索引lib/schema/table_builder.jsthis.addIndex(name, [spec.childColumn], this.uniqueColumns_.has(spec.childColumn));即约束名直接成为子列索引名。运行时ConstraintChecker从全局服务获取IndexStore、Schema和Cachelib/cache/constraint_checker.js通过getParentIndex_()lib/cache/constraint_checker.js找到父列的索引父列必为主键或 unique因此必然存在专属索引并用缓存 Map 避免重复查找对每条修改记录先判断被引用列的值是否真的发生了变化didColumnValueChange_lib/cache/constraint_checker.js仅在值变化时才执行containsKey()/get()等索引查询子列值为 null 时跳过检查视为未引用。整个流程避免了全表扫描约束违例检测的时间复杂度取决于索引查询的开销这也是 Lovefield 能在客户端数据库场景下高频执行 DML 的底气之一。五、通过测试与 YAML 配置验证行为仓库为引用完整性提供了充分的测试与数据样本tests/proc/end_to_end_foreign_key_cascade_test.js在同一 schema 中混合声明CASCADE与RESTRICT外键如第 114-137 行所示分别验证级联删除、级联更新以及 RESTRICT 阻断行为tests/proc/end_to_end_foreign_key_test.js覆盖外键违例、INSERT/UPDATE/DELETE 各方向上的约束检查tests/proc/end_to_end_foreign_key_cascade_test.js 之外spac/testdata目录下还有一批专门用于验证非法 schema 定义的 YAML 样本例如foreign_key_invalid_action.yamlforeign_key_invalid_combination.yamlforeign_key_invalid_local_column.yamlforeign_key_invalid_reference.yamlforeign_key_invalid_remote_column.yamlforeign_key_non_unique.yamlforeign_key_chain.yaml 与 foreign_key_loop.yaml这些样本分别对应前文提到的无效引用语法540、类型不匹配538、非唯一父列539、外键链534、循环引用533等违例场景由 spac/run_test.js 驱动的 parser/codegen 测试执行校验可作为理解建表期规则边界的直接参考。六、实践要点速查外键只能在建表期声明且ref必须写成父表名.父列名不指定action时默认为RESTRICT阻断违例操作不指定timing时默认为IMMEDIATE逐语句检查RESTRICTDEFERRABLE可将检查推迟到事务提交前适合先写子后写父的批处理CASCADE会同时级联 UPDATE 和 DELETE但不接受DEFERRABLE且insertOrReplace()不级联——需要级联更新时请改用update()约束检查基于索引完成子列会自动建索引子列取 null 值时不参与引用检查若在建表或运行期收到SYNTAX_ERROR或错误码 203/533/534/536/537/538/539/540/543/506可对照上文定位到对应的 schema 声明问题或数据违例。延伸阅读完整规范索引docs/spec_index.md本主题对应的规范原文见 docs/spec/08_referential_integrity.mdSchema 定义细节docs/spec/01_schema.md 与 docs/dd/01_schema.md事务与生命周期docs/spec/05_transaction.md、docs/spec/03_life_of_db.md相关源码外键规格解析 lib/schema/foreign_key_spec.js、建表期校验 lib/schema/table_builder.js 与 lib/schema/builder.js、运行期检查 lib/cache/constraint_checker.js 与 lib/cache/journal.js赞分享关系型数据库数据库前端【免费下载链接】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点击查看免费下载相关推荐深入Austin TUI源码从控制器到视图的终端UI设计思想深入Austin TUI源码从控制器到视图的终端UI设计思想 Austin TUI是一款基于Austin性能分析工具的终端用户界面采用了经典的MVC模型技术深度BatteryML如何构建企业级电池寿命预测平台技术深度BatteryML如何构建企业级电池寿命预测平台 在电动汽车和储能系统快速发展的今天电池健康状态预测已成为制约行业发展的关键技术瓶颈。传统电池管理系机器学习科研特征工程数据分析GORM约束管理外键约束与数据完整性终极指南GORM约束管理外键约束与数据完整性终极指南 GORM作为Golang生态中最受欢迎的ORM库提供了强大的约束管理功能帮助开发者轻松维护数据库表之间的关系后端数据库ORM上一篇Serilog审计日志终极指南AuditSink实现不可篡改的关键操作记录下一篇Pose-Search终极指南如何用人体姿势直接搜索图片的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考