Skip to main content
Version: 6.4

通过 EntitySchema 定义实体

使用 EntitySchema 助手,我们以编程方式定义模式。

¥With EntitySchema helper we define the schema programmatically.

./entities/Book.ts
export interface Book extends CustomBaseEntity {
title: string;
author: Author;
publisher: Publisher;
tags: Collection<BookTag>;
}

// The second type argument is optional, and should be used only with custom
// base entities, not with the `BaseEntity` class exported from `@mikro-orm/core`.
export const schema = new EntitySchema<Book, CustomBaseEntity>({
// name should be used only with interfaces
name: 'Book',
// if we use actual class, we need this instead:
// class: Book,
extends: CustomBaseEntitySchema, // only if we extend custom base entity
properties: {
title: { type: 'string' },
author: { kind: 'm:1', entity: 'Author', inversedBy: 'books' },
publisher: { kind: 'm:1', entity: 'Publisher', inversedBy: 'books' },
tags: { kind: 'm:n', entity: 'BookTag', inversedBy: 'books', fixedOrder: true },
},
});

创建新实体实例时,你需要使用 em.create() 方法来创建内部创建的类的实例。

¥When creating new entity instances, you will need to use em.create() method that will create instance of internally created class.

// instance of internal Author class
const author = em.create<Author>('Author', { name: 'name', email: 'email' });
await em.flush();

使用此方法,元数据缓存会自动禁用,因为它是不需要的。

¥Using this approach, metadata caching is automatically disabled as it is not needed.

使用自定义实体类

¥Using custom entity classes

你可以选择对实体实例使用自定义类。

¥You can optionally use custom class for entity instances.

./entities/Author.ts
export class Author extends CustomBaseEntity {
name: string;
email: string;
age?: number;
termsAccepted?: boolean;
identities?: string[];
born?: string;
books = new Collection<Book>(this);
favouriteBook?: Book;
version?: number;

constructor(name: string, email: string) {
super();
this.name = name;
this.email = email;
}
}

export const schema = new EntitySchema<Author, CustomBaseEntity>({
class: Author,
extends: CustomBaseEntitySchema,
properties: {
name: { type: 'string' },
email: { type: 'string', unique: true },
age: { type: 'number', nullable: true },
termsAccepted: { type: 'boolean', default: 0, onCreate: () => false },
identities: { type: 'string[]', nullable: true },
born: { type: DateType, nullable: true, length: 3 },
books: { kind: '1:m', entity: () => 'Book', mappedBy: book => book.author },
favouriteBook: { kind: 'm:1', type: 'Book' },
version: { type: 'number', persist: false },
},
});

然后你可以像往常一样使用实体类:

¥Then you can use the entity class as usual:

const author = new Author('name', 'email');
await em.persist(author).flush();

使用自定义基础实体

¥Using custom base entity

不要忘记,基本实体需要像普通实体一样被发现。

¥Do not forget that base entities needs to be discovered just like normal entities.

./entities/BaseEntity.ts
export interface CustomBaseEntity {
id: number;
createdAt: Date;
updatedAt: Date;
}

export const schema = new EntitySchema<CustomBaseEntity>({
name: 'CustomBaseEntity',
abstract: true,
properties: {
id: { type: 'number', primary: true },
createdAt: { type: 'Date', onCreate: () => new Date(), nullable: true },
updatedAt: { type: 'Date', onCreate: () => new Date(), onUpdate: () => new Date(), nullable: true },
},
});

配置参考

¥Configuration Reference

EntitySchema 的参数需要提供 nameclass 参数。使用 class 时,将自动推断 extends。你可以选择传递这些附加参数:

¥The parameter of EntitySchema requires to provide either name or class parameters. When using class, extends will be automatically inferred. You can optionally pass these additional parameters:

name: string;
class: Constructor<T>;
extends: string;
tableName: string; // alias for `collection: string`
properties: { [K in keyof T & string]: EntityProperty<T[K]> };
indexes: { properties: string | string[]; name?: string; type?: string }[];
uniques: { properties: string | string[]; name?: string }[];
repository: () => Constructor<EntityRepository<T>>;
hooks: Partial<Record<keyof typeof EventType, ((string & keyof T) | NonNullable<EventSubscriber[keyof EventSubscriber]>)[]>>;
abstract: boolean;

然后每个属性都需要包含类型规范 - typeentity 选项之一。以下是各种属性类型的一些示例:

¥Every property then needs to contain a type specification - one of type or entity options. Here are some examples of various property types:

export enum MyEnum {
LOCAL = 'local',
GLOBAL = 'global',
}

export const schema = new EntitySchema<FooBar>({
name: 'FooBar',
tableName: 'tbl_foo_bar',
indexes: [{ name: 'idx1', properties: 'name' }],
uniques: [{ name: 'unq1', properties: ['name', 'email'] }],
repository: () => FooBarRepository,
properties: {
id: { type: 'number', primary: true },
name: { type: 'string' },
baz: { kind: '1:1', entity: 'FooBaz', orphanRemoval: true, nullable: true },
fooBar: { kind: '1:1', entity: 'FooBar', nullable: true },
publisher: { kind: 'm:1', entity: 'Publisher', inversedBy: 'books' },
books: { kind: '1:m', entity: () => 'Book', mappedBy: book => book.author },
tags: { kind: 'm:n', entity: 'BookTag', inversedBy: 'books', fixedOrder: true },
version: { type: 'Date', version: true, length: 0 },
type: { enum: true, items: () => MyEnum, default: MyEnum.LOCAL },
},
});

作为 type 的值,你还可以使用 String/Number/Boolean/Date 之一。

¥As a value for type you can also use one of String/Number/Boolean/Date.

MongoDB 示例

¥MongoDB example

export class BookTag {
_id!: ObjectId;
id!: string;
name: string;
books = new Collection<Book>(this);

constructor(name: string) {
this.name = name;
}
}

export const schema = new EntitySchema<BookTag>({
class: BookTag,
properties: {
_id: { type: 'ObjectId', primary: true },
id: { type: 'string', serializedPrimaryKey: true },
name: { type: 'string' },
books: { kind: 'm:n', entity: () => Book, mappedBy: book => book.tags },
},
});

钩子示例

¥Hooks example

实体钩子可以定义为属性名称,也可以定义为函数。定义为函数时,this 参数将是实体实例。如果需要,可以使用箭头函数,并且实体将在 args.entity 中可用。有关更多示例,请参阅 事件和生命周期钩子

¥Entity hooks can be defined either as a property name, or as a function. When defined as a function, the this argument will be the entity instance. Arrow functions can be used if desired, and the entity will be available at args.entity. See Events and Lifecycle Hooks section for more details on EventArgs.

export class BookTag {
_id!: ObjectId;
id!: string;
name: string;
books = new Collection<Book>(this);

constructor(name: string) {
this.name = name;
}

// Defined on an entity class.
beforeCreate() {
this.version = 1;
}

beforeUpdate() {
this.version++;
}
}

// Defined outside of entity class - this bound to entity instance
function beforeUpdate() {
this.version++;
}

// Defined outside, this available via args.
const beforeUpdate2 = (args: EventArgs) => args.entity.version++;

export const schema = new EntitySchema({
class: BookTag,
hooks: {
beforeCreate: ['beforeCreate'], // Instance method
beforeUpdate: ['beforeUpdate', beforeUpdate, beforeUpdate2] // Instance method, normal function, arrow function
},
properties: {
_id: { type: 'ObjectId', primary: true },
id: { type: 'string', serializedPrimaryKey: true },
name: { type: 'string' },
books: { kind: 'm:n', entity: () => Book, mappedBy: book => book.tags },
},
});