ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MikroORM 级联操作完全指南:persist、remove、孤儿移除与数据库引用完整性

MikroORM 级联操作完全指南:persist、remove、孤儿移除与数据库引用完整性 后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载本文基于 MikroORM 仓库 v5.9 版 Cascading 文档 编写并结合 packages/core/src/enums.ts、packages/core/src/unit-of-work/UnitOfWork.ts、packages/core/src/metadata/MetadataDiscovery.ts 等源码进行纵深解读。MikroORM 的级联Cascading机制决定了当你持久化、合并或删除一个实体时其关联的实体ManyToOne、ManyToMany、OneToMany、OneToOne会以何种方式被自动跟随处理。本文将完整讲解cascade属性的四种取值、orphanRemoval孤儿移除模式以及仅 SQL 驱动支持的数据库层引用完整性on update/on delete并深入源码揭示 UnitOfWork 的实际级联调度逻辑与默认规则来源。读完本文你将能够精确控制实体关系图的传播行为避免级联删除引发的悬空引用与性能陷阱。应用层级联默认行为与cascade属性MikroORM 在**应用层Application level**默认开启级联持久化当你持久化任意实体时ORM 会自动持久化它的所有关联引用。换句话说只要被持久化的实体在内存中持有已加载的关联这些关联就会随之一同写入数据库。注意本文讨论的是应用层级联它的前提是关联已经被 populate 加载populate策略否则 ORM 无法对未初始化的集合执行级联操作。你可以通过ManyToOne、ManyToMany、OneToMany、OneToOne装饰器上的cascade属性来控制这一行为。cascade接受Cascade枚举值的数组该枚举在 packages/core/src/enums.ts#L202-L216 中定义枚举值含义Cascade.PERSIST级联持久化新增的关联实体被自动持久化Cascade.MERGE级联合并游离detached实体被合并进身份映射Identity MapCascade.REMOVE级联删除移除父实体时连带移除关联实体Cascade.ALL启用全部级联操作persist merge removeCascade.SCHEDULE_ORPHAN_REMOVAL/Cascade.CANCEL_ORPHAN_REMOVAL内部使用用于调度/取消孤儿移除用户不应直接使用自 v4.2 起级联合并merge不再可配置对任何关系都保持启用。这一点在 UnitOfWork.ts 的shouldCascade方法中有明确注释ignore user settings for merge, it is kept only for back compatibility, this should have never been configurable忽略用户对 merge 的设置仅保留向后兼容这本就不应可配置。以下代码展示了cascade的典型配置方式// cascade persist 是默认值 OneToMany({ entity: () Book, mappedBy: author }) books new CollectionBook(this); // 与上一写法完全等价显式声明 Cascade.PERSIST OneToMany({ entity: () Book, mappedBy: author, cascade: [Cascade.PERSIST] }) books new CollectionBook(this); // 仅级联删除 OneToMany({ entity: () Book, mappedBy: author, cascade: [Cascade.REMOVE] }) books new CollectionBook(this); // 关闭所有级联 OneToMany({ entity: () Book, mappedBy: author, cascade: [] }) books new CollectionBook(this); // 级联全部persist remove OneToMany({ entity: () Book, mappedBy: author, cascade: [Cascade.ALL] }) books new CollectionBook(this); // 与上一写法等价 OneToMany({ entity: () Book, mappedBy: author, cascade: [Cascade.PERSIST, Cascade.REMOVE] }) books new CollectionBook(this);一个重要的边界规则是没有主键的新实体总是会被持久化无论cascade配置如何。这意味着即使你将cascade: []设为空数组只要关联集合中加入了尚未拥有主键的新实体persistAndFlush时它依然会被写入数据库。Cascade persist一次持久化连带写入整张关系网级联持久化是 MikroORM 最常用的行为。假设Book与Author、BookTag之间存在关联在加载并修改关联对象后只需持久化根实体即可const book await orm.em.findOne(Book, id, { populate: [author, tags] }); book.author.name Foo Bar; book.tags[0].name new name 1; book.tags[1].name new name 2; await orm.em.persistAndFlush(book); // 所有 book tags 和 author 也会一并持久化在上述示例中book、book.author、book.tags的改动会在一次 flush 中全部提交。其底层机制是 UnitOfWork 的cascade()调度当 flush 主实体时UnitOfWork 会遍历实体的所有关系属性helper(entity).__meta.relations对每个关联递归调用cascade()见 UnitOfWork.ts#L1211-L1214。对于 to-many 关系则遍历集合中的每个元素collection.getItems(false)逐一级联见 UnitOfWork.ts#L1237-L1241。当级联持久化集合时请记住只有完全初始化fully initialized的集合才会被级联持久化。未加载的懒加载集合不会被遍历也就不会触发级联。Cascade remove级联删除及其危险性级联删除与级联持久化对称只不过作用对象是删除操作。以下示例假设Book.publisher被配置为Cascade.REMOVEManyToOne({ entity: () Publisher, cascade: [Cascade.REMOVE] }) publisher?: Publisher;删除book时其publisher也会被一并删除await orm.em.remove(book).flush(); // 这也会删除 book.publisher性能提醒集合上的级联删除可能效率较低因为它会对集合中的每个实体各触发一次删除查询而不是一条批量 SQL。级联删除在ManyToOne字段上可能很危险被级联删除的实体可能仍然被其他未被删除的实体引用从而产生悬空引用。考虑下面的场景const publisher new Publisher(/* ... */); // 三本书共享同一个 publisher book1.publisher book2.publisher book3.publisher publisher; await orm.em.remove(book1).flush(); // 这会删除 book1 及其 publisher // 但这里 book2、book3 仍然持有对已删除 publisher 的引用 console.log(book2.publisher, book3.publisher);删除book1时由于book1.publisher配置了Cascade.REMOVEpublisher被连带删除然而内存中book2.publisher、book3.publisher仍指向那个已经被删除的实体对象后续如果 flush 它们或访问其属性就可能导致不一致或外键错误。因此除非能确认被引用实体不会被其他实体共享否则不要轻易在 to-one 关系上使用Cascade.REMOVE。Orphan removal孤儿移除断开即删除除了Cascade.REMOVEMikroORM 还提供了一种更激进的删除级联模式OneToOne和OneToMany属性上的orphanRemoval标志。Entity() export class Author { OneToMany({ entity: () Book, mappedBy: author, orphanRemoval: true }) books new CollectionBook(this); }orphanRemoval在删除操作上的行为与Cascade.REMOVE完全一致因此同时指定两者是冗余的。源码中 UnitOfWork.ts#L1249-L1255 明确只要prop.orphanRemoval为真REMOVE、SCHEDULE_ORPHAN_REMOVAL、CANCEL_ORPHAN_REMOVAL、ALL这几种级联类型都会返回true从而触发级联。两者的区别在于触发时机普通Cascade.REMOVE要求你显式删除Author实体级联才会向下传播到已加载的Book而启用orphanRemoval后只要Book从集合中被断开——无论是通过remove()移除还是通过set()整体替换——它就会被自动删除await author.books.set([book1, book2]); // 整体替换集合 await author.books.remove(book1); // 从集合中移除 book1 await orm.em.persistAndFlush(author); // book1 会被删除同时 set() 替换掉的原集合项也会被删除这个示例中如果只使用简单的Cascade.REMOVE由于没有执行任何删除操作任何Book都不会被删除而orphanRemoval让脱离集合本身成为删除条件——这正是孤儿orphan一词的含义一旦失去父级引用子实体就不应继续存在于数据库中。在 packages/core/src/entity/Collection.ts 中还能看到相关的一致性保护例如第 978 行附近当从集合中remove()一个实体时如果反向属性不可为空!prop2.nullable且deleteRule ! cascadeCollection 会抛出错误提醒你配置级联删除规则避免出现父级删了、子级孤零零挂着不可空外键的数据状态。Declarative Referential Integrity数据库层级的引用完整性以上所有级联都发生在应用层ORM 层即由 MikroORM 的 UnitOfWork 在应用进程内调度 SQL 完成。与之相对MikroORM 还支持声明数据库层级的引用完整性动作on update和on delete。该能力仅在 SQL 驱动中支持PostgreSQL、MySQL、MariaDB、SQL Server、SQLite/libSQL、Oracle 等MongoDB 等非关系型驱动没有外键概念无法使用。在 v5.9 中这些动作的值默认会从cascade选项的值自动推断你也可以通过onUpdateIntegrity和onDelete两个选项手动控制Entity() export class Book { ManyToOne({ onUpdateIntegrity: set null, onDelete: cascade }) author?: Author; }这里onDelete: cascade告诉数据库当Author被删除时数据库自动删除引用它的Book生成ON DELETE CASCADE外键约束onUpdateIntegrity: set null则告诉数据库当Author的主键被更新时Book.author外键被置为NULL生成ON UPDATE SET NULL约束。新版命名updateRule与deleteRule需要说明的是当前仓库主分支v7的文档与源码已将该选项更名为updateRule/deleteRule见新版 docs/docs/cascading.md。在 packages/core/src/metadata/types.ts#L581-L584 与 packages/core/src/typings.ts#L1557-L1558 中这两个字段的类型定义为cascade | no action | set null | set default | AnyString。新版用法如下Entity() export class Book { ManyToOne({ updateRule: set null, deleteRule: cascade }) author?: Author; }同时新版还支持在全局配置中设定默认规则MikroORM.init({ schemaGenerator: { defaultDeleteRule: cascade, defaultUpdateRule: cascade, }, });ORM 语义默认值Semantic Defaults在回退到数据库原生默认值之前ORM 会对特定关系模式应用合理的默认规则。这些默认规则在 packages/core/src/metadata/MetadataDiscovery.ts#L748-L791 中有对应实现场景deleteRuleupdateRule原因FK 兼作 PK实体的主键同时也是外键cascadecascade子行离开父行无法独立存在中间表M:N 连接表cascadecascade缺少任一侧时连接行无意义关联到复合主键目标—cascade复合主键可能发生变化可空关系set null—保留行本身仅解除引用MSSQL 上自引用外键no actionno action避免多重级联路径错误例如源码中MetadataDiscovery.ts第 760-761 行if (prop.nullable) { prop.deleteRule ?? set null; }——可空外键在父实体删除时默认置空而非报错第 788-789 行当实体的主键全部由外键组成典型如中间表实体则强制deleteRule与updateRule均为cascade。优先级顺序外键规则按以下顺序解析属性级显式deleteRule/updateRulev5.9 中为onDelete/onUpdateIntegrity——优先级最高ORM 语义默认值上表全局配置schemaGenerator.defaultDeleteRule/defaultUpdateRule数据库原生默认值PostgreSQL、SQLite、MSSQL、Oracle 为NO ACTIONMySQL/MariaDB 为RESTRICT。Oracle 特别提醒Oracle 不支持ON UPDATE CASCADE。如果你需要在 Oracle 上实现级联更新必须在应用层自行处理或借助触发器实现。源码视角UnitOfWork 如何调度级联操作理解cascade的底层实现有助于你预判复杂关系图中的行为。核心逻辑集中在 packages/core/src/unit-of-work/UnitOfWork.ts#L1185-L1263cascade(entity, type, visited)每个实体只处理一次通过visited集合去重防止环状关系无限递归根据type分发到persist/merge/remove/scheduleOrphanRemoval/cancelOrphanRemoval第 1193-1209 行cascadeReference(entity, prop, type)遍历实体的全部关系属性先调用shouldCascade判断该关系是否应传播当前类型的级联再根据关系类型处理to-one 关系直接级联目标实体to-many 关系遍历集合元素逐一级联第 1216-1242 行shouldCascade(prop, type)核心决策方法——orphanRemoval关系对所有删除类级联返回trueMERGE恒为true不可配置其余情况检查prop.cascade数组中是否包含目标类型或Cascade.ALL第 1249-1263 行。值得注意的是Cascade.ALL的实现方式就是prop.cascade.includes(Cascade.ALL)时对所有类型放行因此它等价于显式列出PERSIST、MERGE、REMOVE三者的组合。最佳实践小结默认即持久化OneToMany等关系的级联持久化默认开启多数场景无需显式配置显式写cascade: [Cascade.PERSIST]只是为了可读性。慎用Cascade.REMOVE于 to-one共享引用的实体如多个Book共用同一个Publisher被级联删除后会留下悬空引用建议改用数据库层onDelete: set null或显式解除引用。集合级联删除的开销每个集合元素会单独发一条删除语句大批量场景应评估性能必要时改为 SQL 层ON DELETE CASCADE。orphanRemoval与Cascade.REMOVE二选一前者覆盖后者同时配置属于冗余。明确数据库约束的最终归属ORM 级联由应用调度、数据库级联由 DDL 外键保证二者互相独立生产环境应根据数据一致性要求应用层可控性 vs 数据库层原子性选择合适的一层或合理组合使用。留意驱动差异on update/on delete仅 SQL 驱动可用且 Oracle 不支持ON UPDATE CASCADE跨数据库迁移前务必核对平台支持矩阵。通过cascade、orphanRemoval与数据库引用完整性三者的配合你可以把实体关系图的写入与清理工作交给 MikroORM同时保持对边界情况悬空引用、批量删除性能、平台差异的完全掌控。赞分享后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载相关推荐MikroORM 级联操作全解析Cascade Persist、Remove、Orphan Removal 与数据库参照完整性规则MikroORM 级联操作全解析Cascade Persist、Remove、Orphan Removal 与数据库参照完整性规则 本文以 MikroORM后端MikroORM 级联操作详解Cascade.PERSIST / REMOVE、orphanRemoval 与数据库级参照完整性规则MikroORM 级联操作详解Cascade.PERSIST / REMOVE、orphanRemoval 与数据库级参照完整性规则 在 MikroORM 中后端MikroORM 级联操作全解析Cascade Persist、Remove 与 Orphan Removal 实战指南MikroORM 级联操作全解析Cascade Persist、Remove 与 Orphan Removal 实战指南 本文基于 MikroORM v6.6后端上一篇如何用Slime-Simulation创建震撼视觉效果Unity Compute Shader实现高性能黏液模拟下一篇12306ForMac部署与发布指南从开发到上架App Store的完整流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表