级联持久化、合并和删除
从 v4.2 开始,级联合并不再可配置(并且对所有关系保持启用状态)。
¥From v4.2, cascade merging is no longer configurable (and is kept enabled for all relations).
本节介绍应用级级联。为了实现这一点,我们需要填充关系。
¥This section is about application level cascading. For that to work, we need to have relations populated.
在持久化或删除实体时,默认情况下,所有引用都是级联持久化的。这意味着通过持久化任何实体,ORM 将自动持久化其所有关联。
¥When persisting or removing entity, all your references are by default cascade persisted. This means that by persisting any entity, ORM will automatically persist all of its associations.
你可以通过 @ManyToOne
、@ManyToMany
、@OneToMany
和 @OneToOne
字段的 cascade
属性来控制此行为。
¥You can control this behaviour via cascade
attribute of @ManyToOne
, @ManyToMany
, @OneToMany
and @OneToOne
fields.
没有主键的新实体将始终保留,无论
cascade
值如何。¥New entities without a primary key will always be persisted, regardless of
cascade
value.
// cascade persist is default value
@OneToMany({ entity: () => Book, mappedBy: 'author' })
books = new Collection<Book>(this);
// same as previous definition
@OneToMany({ entity: () => Book, mappedBy: 'author', cascade: [Cascade.PERSIST] })
books = new Collection<Book>(this);
// only cascade remove
@OneToMany({ entity: () => Book, mappedBy: 'author', cascade: [Cascade.REMOVE] })
books = new Collection<Book>(this);
// no cascade
@OneToMany({ entity: () => Book, mappedBy: 'author', cascade: [] })
books = new Collection<Book>(this);
// cascade all (persist and remove)
@OneToMany({ entity: () => Book, mappedBy: 'author', cascade: [Cascade.ALL] })
books = new Collection<Book>(this);
// same as previous definition
@OneToMany({ entity: () => Book, mappedBy: 'author', cascade: [Cascade.PERSIST, Cascade.REMOVE] })
books = new Collection<Book>(this);
级联持久化
¥Cascade persist
以下是级联持久化如何工作的示例:
¥Here is example of how cascade persist works:
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.persist(book).flush(); // all book tags and author will be persisted too
当级联持久化集合时,请记住只有完全初始化的集合才会级联持久化。
¥When cascade persisting collections, keep in mind only fully initialized collections will be cascade persisted.
级联删除
¥Cascade remove
级联删除的工作方式与级联持久相同,只是用于删除实体。以下示例假定 Book.publisher
设置为 Cascade.REMOVE
:
¥Cascade remove works same way as cascade persist, just for removing entities. Following example assumes that Book.publisher
is set to Cascade.REMOVE
:
请注意,集合的级联删除效率低下,因为它将为集合中的每个实体触发 1 个查询。
¥Note that cascade remove for collections can be inefficient as it will fire 1 query for each entity in collection.
await orm.em.remove(book).flush(); // this will also remove book.publisher
请记住,在 @ManyToOne
字段上使用级联删除可能会很危险,因为级联删除的实体可以在未被删除的另一个实体中保持引用。
¥Keep in mind that cascade remove can be dangerous when used on @ManyToOne
fields, as cascade removed entity can stay referenced in another entities that were not removed.
const publisher = new Publisher(...);
// all books with same publisher
book1.publisher = book2.publisher = book3.publisher = publisher;
await orm.em.remove(book1).flush(); // this will remove book1 and its publisher
// but we still have reference to removed publisher here
console.log(book2.publisher, book3.publisher);
孤立删除
¥Orphan removal
除了 Cascade.REMOVE
之外,还有额外的、更积极的移除级联模式,可以使用 @OneToOne
和 @OneToMany
属性的 orphanRemoval
标志来指定:
¥In addition to Cascade.REMOVE
, there is also additional and more aggressive remove cascading mode which can be specified using the orphanRemoval
flag of the @OneToOne
and @OneToMany
properties:
@Entity()
export class Author {
@OneToMany({ entity: () => Book, mappedBy: 'author', orphanRemoval: true })
books = new Collection<Book>(this);
}
orphanRemoval
标志的行为与删除操作的Cascade.REMOVE
相同,因此同时指定两者是多余的。¥
orphanRemoval
flag behaves just likeCascade.REMOVE
for remove operation, so specifying both is redundant.
使用简单的 Cascade.REMOVE
,你需要删除 Author
实体以将操作级联到所有已加载的 Book
。通过在集合上启用孤立删除,当 Book
与集合断开连接时(通过 remove()
或通过 set()
替换集合项),它们也将被删除:
¥With simple Cascade.REMOVE
, you would need to remove the Author
entity to cascade the operation down to all loaded Book
s. By enabling orphan removal on the collection, Book
s will be also removed when they get disconnected from the collection (either via remove()
, or by replacing collection items via set()
):
await author.books.set([book1, book2]); // replace whole collection
await author.books.remove(book1); // remove book from collection
await orm.em.persist(author).flush(); // book1 will be removed, as well as all original items (before we called `set()`)
在此示例中,由于没有执行删除操作,因此不会使用简单的 Cascade.REMOVE
删除任何 Book
。
¥In this example, no Book
would be removed with simple Cascade.REMOVE
as no remove operation was executed.
声明式引用完整性
¥Declarative Referential Integrity
这仅在 SQL 驱动程序中受支持。
¥This is only supported in SQL drivers.
与 cascade
选项控制的应用级级联相反,我们还可以定义数据库级引用完整性操作:on update
和 on delete
。
¥As opposed to the application level cascading controlled by the cascade
option, we can also define database level referential integrity actions: on update
and on delete
.
它们的值是从 cascade
选项值自动推断出来的。你还可以通过 updateRule
和 deleteRule
选项手动控制值。
¥Their values are automatically inferred from the cascade
option value. You can also control the value manually via updateRule
and deleteRule
options.
@Entity()
export class Book {
@ManyToOne({ updateRule: 'set null', deleteRule: 'cascade' })
author?: Author;
}