建模实体关系
MikroORM 中有 4 种类型的实体关系:
¥There are 4 types of entity relationships in MikroORM:
-
ManyToOne
-
OneToMany
-
OneToOne
-
ManyToMany
关系可以是单向的,也可以是双向的。单向仅在一侧(拥有方)定义。在两侧都定义了双向关系,而一侧是拥有方(存储引用),由指向反侧的 inversedBy
属性标记。在反面,我们用指向所有者的 mappedBy
属性来定义它:
¥Relations can be unidirectional and bidirectional. Unidirectional are defined only on one side (the owning side). Bidirectional are defined on both sides, while one is owning side (where references are store), marked by inversedBy
attribute pointing to the inverse side. On the inversed side we define it with mappedBy
attribute pointing back to the owner:
在对双向关系进行建模时,你也可以省略
inversedBy
属性,在反面定义mappedBy
就足够了,因为它将自动连接。¥When modeling bidirectional relationship, you can also omit the
inversedBy
attribute, definingmappedBy
on the inverse side is enough as it will be auto-wired.
ManyToOne
当前实体的许多实例引用被引用实体的一个实例。
¥Many instances of the current Entity refer to One instance of the referred Entity.
有多种定义关系的方法,以下所有方法都是等效的:
¥There are multiple ways how to define the relationship, all of the following is equivalent:
@Entity()
export class Book {
@ManyToOne() // plain decorator is enough, type will be sniffer via reflection!
author1!: Author;
@ManyToOne(() => Author) // you can specify type manually as a callback
author2!: Author;
@ManyToOne('Author') // or as a string
author3!: Author;
@ManyToOne({ entity: () => Author }) // or use options object
author4!: Author;
}
你还可以指定对给定实体的操作应如何 cascade 到引用的实体。
¥You can also specify how operations on given entity should cascade to the referred entity.
OneToMany
当前实体的一个实例对所引用的实体有许多实例(引用)。
¥One instance of the current Entity has Many instances (references) to the referred Entity.
同样,以下所有内容都是等效的:
¥Again, all of the following is equivalent:
@Entity()
export class Author {
@OneToMany(() => Book, book => book.author)
books1 = new Collection<Book>(this);
@OneToMany('Book', 'author')
books2 = new Collection<Book>(this);
@OneToMany({ mappedBy: book => book.author }) // referenced entity type can be sniffer too
books3 = new Collection<Book>(this);
@OneToMany({ entity: () => Book, mappedBy: 'author', orphanRemoval: true })
books4 = new Collection<Book>(this);
}
如你所见,OneToMany 是 ManyToOne(即拥有方)的反面。有关集合如何工作的更多信息,请参阅 集合页面。
¥As you can see, OneToMany is the inverse side of ManyToOne (which is the owning side). More about how collections work can be found on collections page.
你还可以指定对给定实体的操作应如何 cascade 到引用的实体。还有一种更积极的删除模式称为 孤立删除(books4
示例)。
¥You can also specify how operations on given entity should cascade to the referred entities. There is also more aggressive remove mode called Orphan Removal (books4
example).
OneToOne
当前实体的一个实例引用所引用实体的一个实例。
¥One instance of the current Entity refers to One instance of the referred Entity.
这是 ManyToOne 的变体,其中双方始终只有一个实体。这意味着外键列也是唯一的。
¥This is a variant of ManyToOne, where there is always just one entity on both sides. This means that the foreign key column is also unique.
拥有方
¥Owning Side
@Entity()
export class User {
// when none of `owner/inverseBy/mappedBy` is provided, it will be considered owning side
@OneToOne()
bestFriend1!: User;
// side with `inversedBy` is the owning one, to define inverse side use `mappedBy`
@OneToOne({ inversedBy: 'bestFriend1' })
bestFriend2!: User;
// when defining it like this, you need to specifically mark the owning side with `owner: true`
@OneToOne(() => User, user => user.bestFriend2, { owner: true })
bestFriend3!: User;
}
反向
¥Inverse Side
@Entity()
export class User {
@OneToOne({ mappedBy: 'bestFriend1', orphanRemoval: true })
bestFriend1!: User;
@OneToOne(() => User, user => user.bestFriend2, { orphanRemoval: true })
bestFriend2!: User;
}
如你所见,关系也可以是自引用的(所有关系都可以。OneToOne 也支持 孤立删除)。
¥As you can see, relationships can be also self-referencing (all of them. OneToOne also supports Orphan Removal).
ManyToMany
当前实体的许多实例引用被引用实体的许多实例。
¥Many instances of the current Entity refers to Many instances of the referred Entity.
以下是如何定义 ManyToMany 关系的示例:
¥Here are examples of how you can define ManyToMany relationship:
拥有方
¥Owning Side
@Entity()
export class Book {
// when none of `owner/inverseBy/mappedBy` is provided, it will be considered owning side
@ManyToMany()
tags1 = new Collection<BookTag>(this);
@ManyToMany(() => BookTag, 'books', { owner: true })
tags2 = new Collection<BookTag>(this);
@ManyToMany(() => BookTag, 'books', { owner: true })
tags3 = new Collection<BookTag>(this);
@ManyToMany(() => BookTag, 'books', { owner: true })
tags4 = new Collection<BookTag>(this);
// to define uni-directional many to many, simply provide only
@ManyToMany(() => Author)
friends: Collection<Author> = new Collection<Author>(this);
}
反向
¥Inverse Side
@Entity()
export class BookTag {
// inverse side has to point to the owning side via `mappedBy` attribute/parameter
@ManyToMany(() => Book, book => book.tags)
books = new Collection<Book>(this);
}
同样,有关集合如何工作的更多信息可以在 集合页面 上找到。
¥Again, more information about how collections work can be found on collections page.
ESM 中的关系项目
¥Relations in ESM projects
如果你在 TypeScript 项目中将 ESM 与 reflect-metadata
一起使用,你可能会遇到循环依赖问题,看到如下错误:
¥If you use ESM in your TypeScript project with reflect-metadata
, you might fall into issues with circular dependencies, seeing errors like this:
ReferenceError: Cannot access 'Author' before initialization
要绕过它们,请使用 Rel
映射类型。它是一种身份类型,可禁用来自 reflect-metadata
的有问题的推断,从而导致 ESM 项目失败。
¥To get around them, use the Rel
mapped type. It is an identity type, which disables the problematic inference from reflect-metadata
, that causes ESM projects to fail.
import { Rel } from '@mikro-orm/core';
@Entity()
export class Book {
@ManyToOne(() => Author)
author!: Rel<Author>;
}