实体生成器
要从现有数据库模式生成实体,你可以使用 EntityGenerator
助手。它位于名为 @mikro-orm/entity-generator
的自己的包中:
¥To generate entities from existing database schema, you can use the EntityGenerator
helper. It lives in its own package called @mikro-orm/entity-generator
:
- npm
- Yarn
- pnpm
npm install @mikro-orm/entity-generator
yarn add @mikro-orm/entity-generator
pnpm add @mikro-orm/entity-generator
版本需要与
@mikro-orm/core
包和数据库驱动程序包保持一致。¥The version needs to be aligned with the
@mikro-orm/core
package and the database driver package.
要使用它,你需要在 ORM 配置中注册 EntityGenerator
扩展:
¥To use it, you need to register the EntityGenerator
extension in your ORM config:
import { defineConfig } from '@mikro-orm/postgresql';
import { EntityGenerator } from '@mikro-orm/entity-generator';
export default defineConfig({
dbName: 'test',
extensions: [EntityGenerator],
});
然后你可以通过 CLI 使用它:
¥Then you can use it either via CLI:
要使用 CLI,请先在本地安装
@mikro-orm/cli
包。版本需要与@mikro-orm/core
包保持一致。¥To work with the CLI, first install
@mikro-orm/cli
package locally. The version needs to be aligned with the@mikro-orm/core
package.
npx mikro-orm generate-entities --dump # Dumps all generated entities
npx mikro-orm generate-entities --save --path=./my-entities # Saves entities into given directory
或者你可以创建一个简单的脚本,像这样初始化 MikroORM:
¥Or you can create simple script where you initialize MikroORM like this:
import { MikroORM } from '@mikro-orm/core';
import { EntityGenerator } from '@mikro-orm/entity-generator';
(async () => {
const orm = await MikroORM.init({
discovery: {
// we need to disable validation for no entities
warnWhenNoEntities: false,
},
extensions: [EntityGenerator],
dbName: 'your-db-name',
// ...
});
const dump = await orm.entityGenerator.generate({
save: true,
path: process.cwd() + '/my-entities',
});
console.log(dump);
await orm.close(true);
})();
然后通过 ts-node
运行此脚本(或将其编译为纯 JS 并使用 node
):
¥Then run this script via ts-node
(or compile it to plain JS and use node
):
$ ts-node generate-entities
高级配置
¥Advanced configuration
实体生成器的行为可以通过 ORM 配置中的 entityGenerator
部分进行调整,也可以使用 GenerateOptions
对象(generate
方法的参数)进行调整,该对象优先于全局配置。
¥The behaviour of the entity generator can be adjusted either via entityGenerator
section in the ORM config, or with the GenerateOptions
object (parameter of the generate
method), which takes precedence over the global configuration.
可用的选项:
¥Available options:
选项 | 描述 |
---|---|
schema: string | 要为其生成实体的目标模式。默认为主配置将使用的名称。 |
takeTables: (string | RegExp)[] | 考虑一些数据库表。接受表名和 RegExp 的数组。如果有对未采用的表的外键引用,则生成的代码将好像该外键不存在一样。 |
skipTables: (string | RegExp)[] | 忽略一些数据库表。接受表名和 RegExp 的数组。如果有对跳过的表的外键引用,则生成的代码将好像该外键不存在一样。 |
skipColumns | 忽略一些数据库表列。接受一个对象,其中键是带有架构前缀(如果可用)的表名,值是列名和 RegExp 的数组。如果跳过的列是外键引用的目标,则生成的代码看起来好像该外键不存在。 |
save: boolean | 是否将生成的实体保存为文件。 |
path: string | 如果启用了保存,则用于保存生成的实体的文件夹。默认为主配置的 baseDir 内名为 generated-entities 的文件夹。 |
fileName: (className: string) => string | 回调覆盖实体文件名(不带扩展名,相对于 path )。默认为实体名称。请注意,每当 onImport 返回 undefined 时,也会调用此回调。 |
onImport: ImportsResolver | |
extraImports: (baseDir: string, fileName: string) => string[] | undefined | 回调定义要在给定文件中导入的其他名称(如果尚未导入)。这旨在与 onInitialMetadata /onProcessedMetadata 结合使用,它们添加自定义函数(例如序列化器、onCreate、onUpdate 等)以及可能需要这些额外导入的源。否则,实体生成器对这些标识符一无所知。 |
bidirectionalRelations: boolean | 默认情况下,EntityGenerator 仅生成关系的拥有方(例如 M:1)。如果设置为 true,还会为它们生成反面 |
identifiedReferences: boolean | 如果设置为 true ,则生成 M:1 和 1:1 关系作为封装引用。还封装任何 lazy 属性。 |
entitySchema: boolean | 默认情况下,使用装饰器生成实体。如果是 true ,则改用 EntitySchema 生成实体。 |
esmImport: boolean | 默认情况下,使用没有扩展的实体的导入语句。如果设置为 true ,则对导入的实体使用 ESM 样式导入,即添加 .js 后缀作为扩展。此外,当不使用封装引用时,会在所有 M:1 和 1:1 关系周围添加 Rel<> ,以避免 Cannot access 'X' before initialization 类错误。 |
scalarTypeInDecorator: boolean | 如果是 true ,请在标量属性装饰器中包含 type 选项。此信息是在运行时发现的,但可以通过在装饰器中包含此选项来跳过发现过程。如果使用 EntitySchema ,则始终包含此类型信息。 |
forceUndefined: boolean | 同名的运行时选项 的实体生成器对应物。它应该配置为与你打算在运行时使用的配置相匹配。设置为 true (此选项的默认值,用于向后兼容;不是运行时选项的默认值)时,可空属性将被视为 null 不是可能值,类型提示会反映这一点,并且 null 默认值将被省略。设置为 false 时,| null 将根据 nullable 选项添加到类型注释中,如果 default 设置为 null ,则会输出它。 |
undefinedDefaults: boolean | 默认情况下,数据库引擎将 null 报告为没有默认值的可空列的默认值,无论你是否明确将 null 设置为默认值。如果 forceUndefined 设置为 false ,则将依次输出此 null 。如果将此选项设置为 true ,则这些 null 值将更改为 undefined ,因此不会输出,并且该属性将变为可选以保留它不是必需的。例如myProp: string | null = null; 将更改为 myProp?: string |null; 。如果 forceUndefined 设置为 true ,则此选项实际上会被忽略。在 onProcessedMetadata 期间,可以根据每个属性覆盖此行为。 |
scalarPropertiesForRelations: 'never' | 'always' | 'smart' | |
onlyPurePivotTables: boolean | 默认情况下,M:N 关系允许使用包含附加列的数据透视表。如果设置为 true ,则不会为此类数据透视表生成 M:N 关系。 |
outputPurePivotTables: boolean | 默认情况下,不包含其他属性的数据透视表不会包含在输出中,除非它们是外键关系的目标。如果你希望与 M:N 关系保持一致,或者计划进行一些生成器无法察觉的扩展,则可以将此选项设置为 true 以输出所有枢轴实体,并使用 pivotEntity 选项引用它们,无论这些枢轴实体是否具有附加列。 |
readOnlyPivotTables: boolean | 默认情况下,仅当集合可写时才会生成 M:N 关系,即任何附加列都需要是可选的并且具有非唯一的默认值。如果设置为 true ,则即使集合是只读的(意味着写入它的唯一方法是直接使用枢轴实体),也会生成 M:N 关系。此类集合将包括 persist: false 选项。如果 onlyPurePivotTables 设置为 true ,则此设置实际上毫无意义。 |
customBaseEntityName: string | 默认情况下,实体类不扩展基类。如果设置了此选项,则将创建一个具有此名称的类(如果尚不存在),并且任何未从其他实体继承的实体都将使用此类。类本身也将设置为包含具有当前运行配置的 [Config] 符号。 |
useCoreBaseEntity: boolean | 默认情况下,实体类不扩展基类。如果将此选项设置为 true ,则未扩展其他实体类的实体类将设置为在 @mikro-orm/core 中使用 BaseEntity 类。当与 customBaseEntityName 选项结合使用时,实际上,只有生成的基类才会使用核心的 BaseEntity 类。 |
coreImportsPrefix: string | 默认情况下,从 MikroORM 核心导入时不使用别名。如果你有共享相同名称的表或自定义类型(例如名为 "entity" 的表),这可能会导致生成的代码出现问题。如果指定了此选项,则所有核心导入都将使用以此前缀开头的别名导入。 |
onInitialMetadata: MetadataProcessor | 可以提供一个函数来处理最初收集的元数据集合。在检查受跳过影响的表和列之后触发,但在执行任何其他操作之前,包括基类生成、多对多关系检测、封装引用和双向关系检测。此钩子是添加虚拟和可嵌入实体、OneToOne 或 ManyToOne 引用的任何拥有方以及封装的惰性引用的适当位置。然后,生成器可以找出反面和/或任何 ManyToMany 关系是否受到影响。该函数可能会返回一个 promise,在这种情况下,将等待该 promise 再继续。 |
onProcessedMetadata: MetadataProcessor | 可以提供一个函数来处理最终的元数据集合。在基类生成、多对多关系检测、封装引用和双向关系检测之后触发,就在将元数据输出到文件之前。这是需要普遍应用的更改的适当位置,包括 ManyToMany 和反向侧关系属性,或选择退出特定的封装引用和基类。该函数可能会返回一个 promise,在这种情况下,将等待该 promise 再继续。 |
示例配置:
¥Example configuration:
const dump = await orm.entityGenerator.generate({
entitySchema: true,
bidirectionalRelations: true,
identifiedReferences: true,
esmImport: true,
save: true,
path: process.cwd() + '/my-entities',
skipTables: ['book', 'author'],
skipColumns: {
'public.user': ['email', 'middle_name'],
},
});
生成的元数据的处理
¥Processing of generated metadata
有几个特性是无法仅从数据库模式中推断出来的。
¥There are several features that are impossible to infer from the database schema alone.
配置设置 onInitialMetadata
和 onProcessedMetadata
可用于将该信息添加到生成的元数据中,然后最终将元数据保存为文件。请注意,这与配置中的 onMetadata
钩子不同。onMetadata
钩子不会影响实体文件,但会影响生成器选项中的实体文件。
¥The configuration settings onInitialMetadata
and onProcessedMetadata
can be used to add that information to the generated metadata, before the metadata is ultimately saved as a file. Note that this is different from the onMetadata
hook in the configuration. The onMetadata
hook does not affect the entity files, but those in the generator options do.
适合这些钩子的一些内容包括
¥Some of the things that are suitable for these hooks include
-
在某些列和关系上添加
hidden
标志。¥Adding the
hidden
flag on certain columns and relations. -
在某些列和关系上添加 序列化
groups
。¥Adding serialization
groups
on certain columns and relations. -
在某些列上添加
lazy
标志。¥Adding the
lazy
flag on certain columns. -
在某些关系上添加
eager
标志。¥Adding the
eager
flag on certain relations. -
在某些关系上添加
mapToPk
标志。¥Adding the
mapToPk
flag on certain relations. -
在某些逆关系上添加
orphanRemoval
标志。¥Adding the
orphanRemoval
flag on certain inverse relations. -
在某些关系上添加
cascade
选项。¥Adding the
cascade
option on certain relations. -
将标量字段的
type
和/或runtimeType
调整为执行额外(应用级别)验证的自定义字段。¥Tweaking the
type
and/orruntimeType
of scalar fields to custom ones that perform extra (application level) validation. -
添加单表继承定义。
¥Adding Single Table Inheritance definitions.
-
添加
@Embedded
实体及其引用。¥Adding
@Embedded
entities and references to them. -
添加
virtual
实体。¥Adding
virtual
entities. -
使用
formula
添加属性。¥Adding properties with
formula
.
示例:添加标志
¥Example: Adding flags
下面是一个例子,它将使任何名为 "password" 的列(无论在哪个表中定义)在其 onInitialMetadata
中变为惰性并隐藏,在 onProcessedMetadata
中将使所有 ManyToMany 关系隐藏。
¥Here's an example that will make any column named "password" (regardless of what table is defined in) be lazy and hidden in its onInitialMetadata
and in onProcessedMetadata
will make all ManyToMany relations be hidden.
import { ReferenceKind, MikroORM } from '@mikro-orm/core';
const orm = await MikroORM.init({
// ORM config
});
await orm.entityGenerator.generate({
onInitialMetadata: (metadata, platform) => {
metadata.forEach(meta => {
meta.props.forEach(prop => {
if (prop.name === 'password') {
prop.hidden = true;
prop.lazy = true;
}
});
});
},
onProcessedMetadata: (metadata, platform) => {
metadata.forEach(meta => {
meta.props.forEach(prop => {
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
prop.hidden = true;
}
});
});
},
});
示例:添加 @Embedded 实体
¥Example: Adding @Embedded entities
通过钩子添加嵌入式(以及虚拟实体)可能有些棘手。元数据对象是内部的,因此对它们的检查较少,这意味着任何错误都可能是隐秘的。
¥Adding embedded (as well as virtual entities) via the hooks can be somewhat tricky. The metadata objects are internal, and thus fewer checks are in place on them, meaning any errors are likely to be cryptic.
下面是一个示例,它定义了一个只有两个属性的简单可嵌入项,并添加了对它的引用,该引用也称为 JSON 列。
¥Here's an example that defines a simple embeddable with just two properties and adds a reference to it that is otherwise known to be a JSON column.
import { ReferenceKind, MikroORM, EntityMetadata } from '@mikro-orm/core';
const orm = await MikroORM.init({
// ORM config
});
await orm.entityGenerator.generate({
onInitialMetadata: (metadata, platform) => {
// Create the Embeddable meta data in the script, rather than in a file.
const embeddableEntityMeta = new EntityMetadata({
className: 'IdentitiesContainer',
collection: platform.getConfig().getNamingStrategy().classToTableName('IdentitiesContainer'),
embeddable: true,
});
embeddableEntityMeta.addProperty({
name: 'github',
type: 'string',
runtimeType: 'string',
nullable: true,
fieldNames: ['github'],
columnTypes: ['varchar(255)'],
});
embeddableEntityMeta.addProperty({
name: 'local',
type: 'integer',
runtimeType: 'number',
nullable: true,
fieldNames: ['local'],
columnTypes: ['int'],
});
metadata.push(embeddableEntityMeta);
// Add a reference to the embeddable.
// This works even if the script does not define the embeddable's meta data.
// This example assumes there exists an "author" table with a JSON column called "identity".
const identitiesPropOnAuthor = metadata.find(meta => meta.className === 'Author')!.properties.identity;
identitiesPropOnAuthor.kind = ReferenceKind.EMBEDDED;
identitiesPropOnAuthor.array = true;
identitiesPropOnAuthor.object = true;
identitiesPropOnAuthor.prefix = false;
identitiesPropOnAuthor.type = 'IdentitiesContainer';
},
});
另一种可能不太容易出错的方法(在实体生成时;如果你的定义有误,你仍然会收到运行时验证错误)是在文件中手动定义嵌入实体,然后将其复制到输出文件夹。生成器的元数据钩子只需要添加对这些嵌入实体的引用。此类实体将像任何其他常规实体一样被导入。
¥An alternative approach that is perhaps less error-prone (at entity generation time; you would still get runtime validation errors if your definition is erroneous) is to define the embedded entities manually in a file that you then copy to the output folder. The generator's metadata hooks only need to add the references to these embedded entities. Such entities will be imported like any other regular entity.
无论哪种方式,你最终都会得到一个类似于的文件
¥Either way, you would end up with a file similar to
import { Embeddable, Property } from '@mikro-orm/core';
@Embeddable()
export class IdentitiesContainer {
@Property()
github!: string;
@Property()
local!: number;
}
然后在引用中,你将得到类似于
¥and then in the reference, you will end up with something similar to
import { Embedded, Entity } from '@mikro-orm/core';
import { IdentitiesContainer } from './IdentitiesContainer';
@Entity()
export class Author {
// ...
@Embedded({ entity: () => IdentitiesContainer, array: true, object: true, prefix: false, nullable: true })
identity?: IdentitiesContainer[];
// ...
}
示例:为标量属性定义自定义类型
¥Example: Defining custom types for scalar properties
实体生成器将尝试通过平台的 getMappedType()
方法首先按字符串查找类型名称(上一个示例中的 "type" 属性的值)。如果找到类型,则输出为该字符串。如果此调用的结果是 MikroORM 的 UnknownType
,则标识符被假定为扩展 MikroORM 的 Type
类的类的名称,并被导入。有关定义此类的更多详细信息,请参阅 自定义类型。请注意,仅使用对标识符的引用(未添加 "new"),这意味着你始终使用 "stateless" 类型实现。
¥The entity generator will try to look for type names (the value of the "type" property in the previous example) by string first, via the platform's getMappedType()
method. If the type is found, the output is that string. If the result of this call is MikroORM's UnknownType
, the identifier is assumed to be the name of a class extending MikroORM's Type
class, and gets imported. See Custom Types for more details on defining such a class. Note that only a reference to the identifier is used (no "new" added), meaning you are always using a "stateless" type implementation.
因此,你可以在配置中注册你的自定义类型(参见 "配置" 中的 "调整默认类型映射"),在这种情况下,你应该在实体生成脚本和运行时配置中定义相同的类型覆盖。或者你可能不这样做,而是使用类名作为类型标识符,在这种情况下,你需要确保类型可由实体文件导入。要实现这一点,你可以覆盖 fileName
,以有条件地调整类型所在的路径(相对于你的输出文件夹)。或者,你可以将类型类放在输出文件夹中。
¥So, you may register your custom types at the config (see "Adjusting default type mapping" in "Configuration"), in which case, you should define the same type override in both your entity generation script and your runtime configuration. Or you may not do that, and instead use the class names as type identifiers, in which case, you need to ensure the types are importable by the entity files. To achieve this, you may override the fileName
, to conditionally adjust the path to where the type is, relative to your output folder. Or alternatively, you may place the type classes inside your output folder.
你也可以更改属性的 runtimeType
选项。如有必要,此类型将被封装在 IType
中(参见 "自定义类型" 中的 "映射到对象和类型安全"),其中运行时类型取自属性的 runtimeType
选项,原始类型取自第一个 customTypes
的 runtimeType
,或者如果未定义,则取自 type
的 runtimeType
,或者,在未注册类型的情况下,取自从 columnTypes
中的第一个值推断出的类型的 runtimeType
。序列化类型将从 customType
选项的 runtimeType
属性中获取,或回退到为原始类型推断的任何内容,如果需要,该类型也将包含在 IType
封装器中。如果所有这些类型都相同,则不会在 runtimeType
周围添加 IType
封装器。
¥You may also change the runtimeType
option of properties. If necessary, this type will be wrapped up in IType
(see "Mapping to objects and type-safety" in "Custom types") where the runtime type is taken from the property's runtimeType
option, and the raw type is taken from the runtimeType
of the first customTypes
, or if that's not defined, the type
's runtimeType
, or, in the case of unregistered types, from the runtimeType
of the type inferred from the first value in columnTypes
. Тhe serialized type will be taken from the runtimeType
property of the customType
option, or fallback to whatever was inferred for the raw type, and if necessary, that type will be included in the IType
wrapper as well. If all of these types are the same, the IType
wrapper will not be added around runtimeType
.
你还可以通过使用将 nullable
设置为所需值的对象填充相应映射类型的 prop
来覆盖 IType
部分的可空性。这意味着在 customTypes
和/或 customType
中使用新的 Type
实例,而不是平台的 getMappedType()
方法或 Type.getType()
方法的结果。
¥You may also override the nullability of the IType
pieces by filling in the prop
of the respective mapped type with an object that has nullable
set to the desired value. This means using new Type
instances in customTypes
and/or customType
, rather than the results from the platform's getMappedType()
method or the Type.getType()
method.
如果你修改主键列的 runtimeType
,并且在引用处引用与 mapToPk
相同的关系,那么在引用处,你可能需要按声明顺序为每个主键列添加带有覆盖的 customTypes
数组。这样,引用列和引用之间的类型将匹配。
¥If you modify the runtimeType
of a primary key column, and also reference that same relation with mapToPk
at the reference, then at the reference, you may need to add the customTypes
array with overrides for each primary key column, in their declaration order. That way, the types between the referenced column and the reference will match.
以下示例显示了一些更典型的用途。
¥The following example shows some of the more typical uses.
import { ReferenceKind, MikroORM, EntityMetadata, Type, JsonType, StringType } from '@mikro-orm/core';
class MetaAsJsonType extends JsonType {
get runtimeType(): string {
return 'MetaType';
}
}
class UrlType extends Type<URL, string | null | undefined> {
#base = 'https://example.com/';
override convertToJSValue(value: string | null | undefined, platform: Platform): URL {
return new URL(value ?? '/', this.#base);
}
override convertToDatabaseValue(value: URL, platform: Platform, context?: TransformContext): string {
return value.toString();
}
}
const orm = await MikroORM.init({
discovery: {
warnWhenNoEntities: false,
getMappedType: (type: string, platform: Platform) => {
switch (type) {
case 'metaAsJson':
return Type.getType(MetaAsJsonType);
case 'url':
return Type.getType(UrlType);
default:
return platform.getDefaultMappedType(type);
}
},
},
// ORM config
});
await orm.entityGenerator.generate({
fileName: (name) => {
switch (name) {
case 'Email':
case 'MetaType':
return `../runtimeTypes/${name}`;
default:
return name;
}
},
onImport: (name, basePath, extension) => {
switch (name) {
// In this example, EmailType is actually an alias to a class called Email.
// But unlike our runtimeType of the same name, it is part of a different larger file.
case 'EmailType':
return { path: `${basePath}/../types`, name: 'Email' };
// In this example, our branded types are the default exports
// in files named after the type, collectively present in one folder above the entities folder.
// This is similar to what fileName would resolve to, except here we can use default imports.
case 'BookVersion':
case 'AuthorVersion':
return { path: `${basePath}/../brandedTypes/${name}${extension}`, name: 'default' };
// URL is a node builtin that is also global. Do not import it.
case 'URL':
return { path: '', name: '' };
}
},
onInitialMetadata: (metadata, platform) => {
// Assume there exists a table "author" with VARCHAR(255) column named "email".
// We want to hydrate the value into an object that can perform further actions on the email.
const emailPropOnAuthor = metadata.find(meta => meta.className === 'Author')!.properties.email;
// The "EmailType" is not registered in the config, so it will be imported.
emailPropOnAuthor.type = 'EmailType';
// The "Email" doesn't match what is inferred from DB, so it will be wrapped as IType<Email, string>.
emailPropOnAuthor.runtimeType = 'Email';
// Assume there exists a table "author" with a nullable VARCHAR(255) column named "website".
// We want to hydrate the value into a URL object.
const websitePropOnAuthor = metadata.find(meta => meta.className === 'Author')!.properties.website;
// We want to guarantee on a TypeScript level that a URL object will be present, always
// while also ensuring our raw values are nullables
const customRawType = new StringType();
// We copy the raw prop to preserve its nullability and any other settings we may take from here.
customRawType.prop = structuredClone(websitePropOnAuthor);
websitePropOnAuthor.customTypes = [customRawType];
// override the original, to affect the runtimeType's nullability.
websitePropOnAuthor.nullable = false;
// The "url" is registered in the config, so it will be outputted as a string.
websitePropOnAuthor.type = 'url';
// The "URL" doesn't match what is inferred from DB, so it will be wrapped, i.e.
// IType<URL, string | null> or IType<URL, sring | undefined> based on the "forceUndefined" option.
websitePropOnAuthor.runtimeType = 'URL';
// Assume there exists a table "book" with JSON column named "meta".
// Assume there is a check constraint that ensures it will be an object with a certain shape.
// We want to enforce it being that same shape on TypeScript level.
const metaPropOnBook = metadata.find(meta => meta.className === 'Book')!.properties.meta;
// The "metaAsJson" type is registered as a name in the config, mapped to MetaAsJsonType.
// Therefore, it will be outputted as the string "metaAsJson" (no import).
metaPropOnBook.type = 'metaAsJson';
// The runtimeType matches what we have defined in MetaAsJsonType's runtimeType.
// The generator is able to look it up due to the registration.
// Because it matches, there will be no IType.
// This means we force this shape during create, selects, and serialization.
// The "MetaType" class itself will be imported.
metaPropOnBook.runtimeType = 'MetaType';
// Assume there's a "book" table with an "INT" column named "version".
// Assume there's also an "author" table with an "INT" column named "version".
// On application level, we want to make sure we don't accidentally mix these versions.
// We shouldn't compare an author version with a book version,
// or assign a book version to an author version.
// The actual underlying type and the things we can do with it should remain the same.
// We don't want to register or import a new type due to the simplicity of the value.
// We also don't need an IType wrapper, since we're not transforming the data from its raw form
// during creation or serialization. We're just concerned with updates and comparrisons.
// Opaque/branded types are the best means to address this.
// Because we don't want to override the "type", we need to override "customTypes" instead.
const versionPropOnBook = metadata.find(meta => meta.className === 'Book')!.properties.version;
versionPropOnBook.runtimeType = 'BookVersion';
versionPropOnBook.customTypes = [
new class extends IntegerType {
get runtimeType(): string {
return 'BookVersion';
}
}
];
const versionPropOnAuthor = metadata.find(meta => meta.className === 'Author')!.properties.version;
versionPropOnAuthor.runtimeType = 'AuthorVersion';
versionPropOnAuthor.customTypes = [
new class extends IntegerType {
get runtimeType(): string {
return 'AuthorVersion';
}
}
];
}
});
当前限制
¥Current limitations
-
在 MySQL 中,
TINYINT(1)
列将被定义为布尔属性。MySQL 中没有真正的BOOLEAN
类型(关键字只是TINYINT(1)
的别名)。¥In MySQL,
TINYINT(1)
columns will be defined as boolean properties. There is no trueBOOLEAN
type in MySQL (the keyword is just an alias forTINYINT(1)
). -
不支持 MongoDB。
¥MongoDB is not supported.