跳转到主要内容

常见问题解答

如何更新数据库模式?

TypeORM 的主要职责之一是使您的数据库表与实体保持同步。 有两种方法可以帮助您实现这一点:

  • 在数据源选项中使用 synchronize: true

    import { DataSource } from "typeorm"

    const myDataSource = new DataSource({
    // ...
    synchronize: true,
    })

    每次运行此代码时,此选项会自动将数据库表与给定实体同步。 这个选项在开发过程中非常完美,但在生产环境中,您可能不希望启用此选项。

  • 使用命令行工具并在命令行中手动运行模式同步:

    typeorm schema:sync

    该命令将执行模式同步。

模式同步非常快。 如果您考虑在开发过程中因性能问题而禁用同步选项, 请首先检查它的速度如何。

如何更改数据库中的列名?

默认情况下,列名是根据属性名生成的。 您可以通过指定 name 列选项简单地更改它:

@Column({ name: "is_active" })
isActive: boolean;

如何将某个函数(如 NOW())设置为默认值?

default 列选项支持函数。 如果您传递一个返回字符串的函数, 它将在不转义的情况下将该字符串用作默认值。 例如:

@Column({ default: () => "NOW()"})
date: Date;

如何进行验证?

验证不是 TypeORM 的一部分,因为验证是一个单独的过程,与 TypeORM 所做的事情没有太大关系。如果您想使用验证,可以使用 class-validator - 它与 TypeORM 完美配合。

关系中的 "owner side" 是什么意思?为什么我们需要使用 @JoinColumn@JoinTable

让我们从 one-to-one 关系开始。假设我们有两个实体:UserPhoto

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@OneToOne()
photo: Photo
}
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
url: string

@OneToOne()
user: User
}

此示例中未使用@JoinColumn,这是不正确的。 为什么呢?因为要建立真正的关系,我们需要在数据库中创建一个列。 我们需要在photo中创建一个名为userId的列,或者在user中创建一个名为photoId的列。 但是应该创建哪个列——userId还是photoId? TypeORM无法为您做决定。 要做出决策,您必须在其中一边使用@JoinColumn。 如果将@JoinColumn放在Photo中,则会在photo表中创建一个名为userId的列。 如果将@JoinColumn放在User中,则会在user表中创建一个名为photoId的列。 具有@JoinColumn的一方将被称为“关系的拥有者一方”。 另一方没有@JoinColumn,被称为“关系的反向(非拥有者)一方”。

@ManyToMany关系中也是如此。您使用@JoinTable来显示关系的拥有者一方。

@ManyToOne@OneToMany关系中,由于两个修饰符不同,不需要@JoinColumn,放置@ManyToOne修饰符的表将具有关系列。

@JoinColumn@JoinTable修饰符还可以用于指定附加的联接列/交叉表设置,如联接列名称或交叉表名称。

如何在多对多(交叉)表中添加额外的列?

无法在由多对多关系创建的表中添加额外的列。 您需要创建一个单独的实体,并使用两个多对一关系将其与目标实体绑定(效果与创建多对多表相同),然后在其中添加额外的列。您可以在多对多关系中了解更多信息。

如何处理outDirTypeScript编译器选项?

当您使用outDir编译器选项时,请不要忘记将应用程序使用的资源和资产复制到输出目录。 否则,请确保为这些资产设置正确的路径。

需要了解的一点是,当您删除或移动实体时,旧实体在输出目录中保持不变。 例如,您创建一个Post实体并将其重命名为Blog,项目中不再有Post.ts。然而,Post.js保留在输出目录中。 现在,当TypeORM从输出目录中读取实体时,它看到了两个实体——PostBlog。 这可能是错误的来源。 这就是为什么在启用outDir时删除和移动实体时强烈建议删除输出目录并重新编译项目。

如何在ts-node中使用TypeORM?

您可以使用ts-node避免每次编译文件。如果您使用ts-node,可以在数据源选项中指定ts实体:

{
entities: ["src/entity/*.ts"],
subscribers: ["src/subscriber/*.ts"]
}

此外,如果您将js文件编译到与TypeScript文件相同的文件夹中,请确保使用outDir编译器选项以防止此问题

如果您想使用ts-node CLI,可以通过以下方式执行TypeORM:

npx typeorm-ts-node-commonjs schema:sync

对于ESM项目,请使用以下命令:

npx typeorm-ts-node-esm schema:sync

如何为后端使用Webpack?

由于Webpack认为所有TypeORM支持的驱动程序都缺少require语句,因此会产生警告。要消除对未使用驱动程序的警告,您需要编辑Webpack配置文件。

const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');

module.exports = {
...
plugins: [
// 忽略您不想要的驱动程序。这是所有驱动程序的完整列表--删除您想要使用的驱动程序的抑制。
new FilterWarningsPlugin({
exclude: [/mongodb/, /mssql/, /mysql/, /mysql2/, /oracledb/, /pg/, /pg-native/, /pg-query-stream/, /react-native-sqlite-storage/, /redis/, /sqlite3/, /sql.js/, /typeorm-aurora-data-api-driver/]
})
]
};

打包迁移文件

默认情况下,Webpack尝试将所有内容打包成一个文件。当您的项目具有迁移文件时,这可能会导致问题,这些文件应在打包代码部署到生产环境后执行。为确保TypeORM能识别并执行所有迁移文件,您可能需要为迁移文件仅使用entry配置的“对象语法”。

const glob = require("glob")
const path = require("path")

module.exports = {
// ... 您的Webpack配置在这里...
// 使用`entry`选项为`{ [name]: sourceFileName }`映射动态生成
// 将`src/db/migrations`更改为您的迁移文件夹的相对路径
entry: glob
.sync(path.resolve("src/db/migrations/*.ts"))
.reduce((entries, filename) => {
const migrationName = path.basename(filename, ".ts")
return Object.assign({}, entries, {
[migrationName]: filename,
})
}, {}),
resolve: {
// 假设所有迁移文件都是用TypeScript编写的
extensions: [".ts"],
},
output: {
// 更改`path`以将转换后的迁移文件放在哪里。
path: __dirname + "/dist/db/migrations",
// 这很重要-我们希望迁移文件的UMD(通用模块定义)。
libraryTarget: "umd",
filename: "[name].js",
},
}

此外,自Webpack 4起,当使用mode: 'production'时,默认优化文件,包括为了最小化文件大小而混淆代码。这会破坏迁移,因为TypeORM依赖它们的名称来确定已经执行了哪些操作。您可以通过添加以下内容来完全禁用最小化:

module.exports = {
// ... 其他Webpack配置在这里
optimization: {
minimize: false,
},
}

或者,如果您正在使用UglifyJsPlugin,可以通过以下方式告诉它不更改类或函数名称:

const UglifyJsPlugin = require("uglifyjs-webpack-plugin")

module.exports = {
// ... 其他Webpack配置在这里
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
keep_classnames: true,
keep_fnames: true,
},
}),
],
},
}

最后,请确保在您的数据源选项中包含已转换的迁移文件:

// TypeORM配置
module.exports = {
// ...
migrations: [
// 这是生产模式下转换的迁移文件的相对路径
"db/migrations/**/*.js",
// 您的源迁移文件,在开发模式下使用
"src/db/migrations/**/*.ts",
],
}

如何在ESM项目中使用TypeORM?

确保在项目的package.json中添加"type": "module",这样TypeORM才能知道在文件上使用import( ... )

为了避免循环依赖导入问题,请在实体中使用Relation包装器类型进行关系类型定义:

@Entity()
export class User {
@OneToOne(() => Profile, (profile) => profile.user)
profile: Relation<Profile>
}

这样做可以防止在元数据中保存属性的类型,并防止循环依赖问题。

由于列的类型已经使用@OneToOne装饰器定义,因此没有必要保存由TypeScript提供的额外类型元数据。

重要提示:请勿在非关系列类型上使用Relation