semantic-fk.md
3.43 KB
无物理外键、语义外键的现实
xlyweberp_saas_ai schema 在任何 xly 自研表(gds*、ele*、mft*、quo*、sal*、acc* 等表族)上都有 0 个触发器和 0 个外键约束。确实存在的 41 个 FK 约束都在捆绑的第三方 schema 上:Activiti 表(act_*)36 个、Quartz 调度表(qrtz_*)5 个;框架自身运行时不会通过这些表做核心连接。在 901 张基础表中,框架依赖的元数据和业务数据连接全部只是约定。
这是一个有意的设计选择,继续阅读前必须理解。
为什么 xly 禁用 FK
架构上给出的两个原因都很务实:
- 批量写入性能。 大量插入(工单计算、月结、批量导入)一次会写入几十万行。启用 FK 后,MySQL 会在插入时验证每一行引用;以 xly 的数据量,这会成为限制因素。
-
schema 迁移敏捷性。 xly 演进很快:新模块、新字段、新表。启用 FK 时,每次 schema 变更都必须考虑约束图;没有 FK 时,
CREATE TABLE或ALTER TABLE是局部操作。代价由运行时应用代码承担。
什么是“语义 FK”
语义 FK 指如果启用 FK 本该成为外键、但实际上没有约束的列。关系编码在:
-
列命名。
sCustomerId被理解为引用eleCustomer.sId。从表上的sParentId被理解为引用主表的sId。 -
存储过程和 MyBatis mapper 中的共同出现。 运行时在很多地方连接
A.sParentId = B.sId;阅读过程体能发现哪些表配对。 -
列注释。 许多列填了
COLUMN_COMMENT,并常常指出引用概念,例如加工商ID。 -
元数据声明。 框架的
gdsconfigformslave.sActiveId/sActiveKey会明确写出下拉控件指向的表单和字段,这是一种元数据编码的 FK。
这些都不会被数据库强制。你可以从 eleCustomer 删除一行,数据库不会阻止;孤儿 sCustomerId 引用会继续存在。
恢复关系
当自动目录或 Wiki 需要知道“列 X 引用什么”时,查找流程是:
- 读列注释,通常这是最有帮助的一行。
- 在 schema 中搜索匹配的 ID 列(在
recon/columns.tsv中grepXSomethingId)。 - 在代码库中搜索在该列上 JOIN 的 SQL 片段。
- 检查
gdsconfigformslave.sActiveId,框架会在这里记录下拉和 lookup。 - 最后再按主从命名约定推断。
自动目录的目标就是简化这些工作:每个生成的表页列出字段,并在未来增强中列出引用它们的过程,从而缩小搜索范围。
失效模式
禁用 FK 的成本在运行时体现。三种失效模式反复出现:
-
孤儿行。 比如客户被删除后仍存在的
sCustomerId。框架查询通过 left join 穿过这些行,可能静默丢弃,也可能显示空白。 -
跨租户 ID 不匹配。 没有 FK 时,没有东西阻止一行引用另一个
sBrandsId下的 ID。多租户过滤(切片 2)是阻止它演变成跨租户泄漏的唯一边界。 -
需要手动验证引用完整性的存储过程。 许多 xly 存储过程在插入前显式检查
EXISTS (...)。本该由 FK 自动完成的完整性工作散布在过程体中。漏掉一次,bug 可能几周后才暴露。
本 Wiki 的任务是在数据库不强制的情况下暴露这些关系。大部分暴露发生在自动目录和切片交叉引用中;本页是“为什么没有 FK、应该信什么”的规范参考。