跳转到主要内容

migrations

迁移

迁移工作原理

一旦您进入生产环境,您就需要将模型更改同步到数据库中。通常,当您在数据库中有数据时,在生产环境中使用 synchronize: true 进行模式同步是不安全的。这就是迁移发挥作用的地方。

迁移只是一个包含 SQL 查询的单个文件,用于更新数据库架构并将新更改应用于现有数据库。

假设您已经有一个数据库和一个帖子实体:

import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"

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

@Column()
title: string

@Column()
text: string
}

并且您的实体在生产环境中运行了数月而没有任何更改。您的数据库中有成千上万个帖子。

现在您需要进行一个新的发布,并将 title 更名为 name。您将怎么做?

您需要创建一个新的迁移,其中包含以下 SQL 查询(postgres 方言):

ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name";

一旦运行此 SQL 查询,您的数据库架构就准备好与新的代码库一起使用了。TypeORM 提供了一个地方,您可以编写此类 SQL 查询并在需要时运行它们。这个地方被称为 "迁移"。

创建新迁移

前提条件安装 CLI

在创建新迁移之前,您需要正确设置数据源选项:

{
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: [/*...*/],
migrations: [/*...*/],
migrationsTableName: "custom_migration_table",
}

在这里,我们设置了两个选项:

  • "migrationsTableName": "migrations" - 仅在迁移表名与 "migrations" 不同时才指定此选项。
  • "migrations": [/*...*/] - 要由 TypeORM 加载的迁移列表。

一旦您设置了连接选项,您可以使用 CLI 创建一个新的迁移:

typeorm migration:create ./path-to-migrations-dir/PostRefactoring

在这里,PostRefactoring 是迁移的名称 - 您可以指定任何您想要的名称。运行命令后,您可以在 "migration" 目录中看到一个新生成的文件,名称为 {TIMESTAMP}-PostRefactoring.ts,其中 {TIMESTAMP} 是生成迁移时的当前时间戳。现在,您可以打开文件并在其中添加迁移的 SQL 查询。

您应该在迁移中看到以下内容:

import { MigrationInterface, QueryRunner } from "typeorm"

export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {}

async down(queryRunner: QueryRunner): Promise<void> {}
}

在这里,有两个方法您必须使用您的迁移代码填充:updownup 方法必须包含执行迁移所需的代码。down 方法必须撤销 up 所做的更改。down 方法用于还原最后一个迁移。

updown 方法内部,您有一个 QueryRunner 对象。所有数据库操作都是使用此对象执行的。了解更多关于 query runner

让我们看看带有我们 Post 更改的迁移是什么样子:

import { MigrationInterface, QueryRunner } from "typeorm"

export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "post" RENAME COLUMN "title" TO "name"`,
)
}

async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "post" RENAME COLUMN "name" TO "title"`,
) // 撤销 "up" 方法所做的更改
}
}

Running and reverting migrations

一旦您有要在生产环境中运行的迁移,您可以使用以下 CLI 命令来运行它们:

typeorm migration:run -- -d path-to-datasource-config

typeorm migration:createtypeorm migration:generate 会创建 .ts 文件,除非您使用 o 标志(更多信息请参见生成迁移)。migration:runmigration:revert 命令仅适用于 .js 文件。因此,在运行命令之前,需要对 TypeScript 文件进行编译。 或者,您可以在运行 .ts 迁移文件时使用 ts-node

使用 ts-node 的示例:

npx typeorm-ts-node-commonjs migration:run -- -d path-to-datasource-config

ESM 项目中使用 ts-node 的示例:

npx typeorm-ts-node-esm migration:run -- -d path-to-datasource-config

此命令将执行所有待处理的迁移,并按照它们的时间戳顺序依次运行它们。 这意味着您创建的迁移的 up 方法中编写的所有 SQL 查询都将被执行。 这就是全部!现在您的数据库模式已经是最新的。

如果出于某种原因,您想要撤销更改,可以运行:

typeorm migration:revert -- -d path-to-datasource-config

此命令将执行最近执行的迁移的 down 方法。 如果您需要撤销多个迁移,则必须多次调用此命令。

Faking Migrations and Rollbacks

您还可以使用 --fake 标志(缩写为 -f)来模拟运行迁移。这将将迁移添加到迁移表中,而不运行它。这对于在数据库已经进行了手动更改或在外部运行了迁移(例如由其他工具或应用程序运行)之后仍然希望保持一致的迁移历史记录非常有用。

typeorm migration:run --fake

回滚也可以进行虚拟运行。

typeorm migration:revert --fake

Transaction modes

默认情况下,TypeORM 将在一个包装事务内运行所有迁移。 这对应于 --transaction all 标志。 如果您需要更精细的事务控制,可以使用 --transaction each 标志将每个迁移单独包装在事务中,或使用 --transaction none 标志完全取消将迁移包装在事务中的操作。

除了这些标志之外,还可以通过在 MigrationInterface 上设置 transaction 属性为 truefalse 来覆盖每个迁移的事务行为。这仅在 eachnone 事务模式下起作用。

import { MigrationInterface, QueryRunner } from "typeorm"

export class AddIndexTIMESTAMP implements MigrationInterface {
transaction = false

async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE INDEX CONCURRENTLY post_names_idx ON post(name)`
)
}

async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DROP INDEX CONCURRENTLY post_names_idx`,
)
}
}

生成迁移

TypeORM能够根据你所做的模式更改自动生成迁移文件。

假设你有一个带有title列的Post实体,并且你已经将title更改为name。 你可以运行以下命令:

typeorm migration:generate PostRefactoring -d 数据源配置路径

如果遇到任何错误,它会要求你提供迁移名称和数据源的路径。你可以尝试这个选项:

typeorm migration:generate -d <path/to/datasource> path/to/migrations/<migration-name>

它将生成一个名为{TIMESTAMP}-PostRefactoring.ts的新迁移文件,内容如下:

import { MigrationInterface, QueryRunner } from "typeorm"

export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`,
)
}

async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "post" ALTER COLUMN "name" RENAME TO "title"`,
)
}
}

或者你也可以使用o--outputJs的别名)标志将迁移输出为Javascript文件。这对于仅使用Javascript的项目很有用,其中没有安装TypeScript附加包。这个命令将生成一个新的迁移文件{TIMESTAMP}-PostRefactoring.js,内容如下:

const { MigrationInterface, QueryRunner } = require("typeorm")

module.exports = class PostRefactoringTIMESTAMP {
async up(queryRunner) {
await queryRunner.query(
`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`,
)
}

async down(queryRunner) {
await queryRunner.query(
`ALTER TABLE "post" ALTER COLUMN "name" RENAME TO "title"`,
)
}
}

你不需要手动编写查询语句。 生成迁移的经验法则是,在对模型进行任何更改后都要生成迁移。如果要对生成的迁移查询应用多行格式,可以使用p--pretty的别名)标志。

DataSource选项

如果你需要运行/回滚/生成/显示迁移,请使用-d--dataSource的别名)选项,并将你定义DataSource实例的文件路径作为参数传递

typeorm -d <你的数据源路径> migration:{run|revert}

Timestamp选项

如果你需要为迁移名称指定一个时间戳,请使用-t--timestamp的别名)选项,并传递一个时间戳(应为非负数)

typeorm -t <具体时间戳> migration:{create|generate}

你可以使用以下代码获得时间戳:

Date.now()
/* 或者 */ new Date().getTime()

使用迁移API编写迁移

为了使用API更改数据库模式,你可以使用QueryRunner

示例:

import {
MigrationInterface, QueryRunner, Table, TableIndex, TableColumn, TableForeignKey
} from "typeorm"

export class QuestionRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: "question",
columns: [
{
name: "id",
type: "int",
isPrimary: true,
},
{
name: "name",
type: "varchar",
},
],
}),
true,
)

await queryRunner.createIndex(
"question",
new TableIndex({
name: "IDX_QUESTION_NAME",
columnNames: ["name"],
}),
)

await queryRunner.createTable(
new Table({
name: "answer",
columns: [
{
name: "id",
type: "int",
isPrimary: true,
},
{
name: "name",
type: "varchar",
},
{
name: "created_at",
type: "timestamp",
default: "now()",
},
],
}),
true,
)

await queryRunner.addColumn(
"answer",
new TableColumn({
name: "questionId",
type: "int",
}),
)

await queryRunner.createForeignKey(
"answer",
new TableForeignKey({
columnNames: ["questionId"],
referencedColumnNames: ["id"],
referencedTableName: "question",
onDelete: "CASCADE",
}),
)
}

async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("answer")
const foreignKey = table.foreignKeys.find(
(fk) => fk.columnNames.indexOf("questionId") !== -1,
)
await queryRunner.dropForeignKey("answer", foreignKey)
await queryRunner.dropColumn("answer", "questionId")
await queryRunner.dropTable("answer")
await queryRunner.dropIndex("question", "IDX_QUESTION_NAME")
await queryRunner.dropTable("question")
}
}

getDatabases(): Promise<string[]>

获取所有可用的数据库名称,包括系统数据库。


getSchemas(database?: string): Promise<string[]>
  • database - 如果指定了数据库参数,则返回该数据库的模式

获取所有可用的模式名称,包括系统模式。仅适用于 SQL Server 和 PostgreSQL。


getTable(tableName: string): Promise<Table|undefined>
  • tableName - 要加载的表名

从数据库中加载指定名称的表。


getTables(tableNames: string[]): Promise<Table[]>
  • tableNames - 要加载的表名

从数据库中加载指定名称的表。


hasDatabase(database: string): Promise<boolean>
  • database - 要检查的数据库名称

检查是否存在指定名称的数据库。


hasSchema(schema: string): Promise<boolean>
  • schema - 要检查的模式名称

检查是否存在指定名称的模式。仅适用于 SQL Server 和 PostgreSQL。


hasTable(table: Table|string): Promise<boolean>
  • table - 表对象或名称

检查是否存在指定的表。


hasColumn(table: Table|string, columnName: string): Promise<boolean>
  • table - 表对象或名称
  • columnName - 要检查的列名

检查表中是否存在指定的列。


createDatabase(database: string, ifNotExist?: boolean): Promise<void>
  • database - 数据库名称
  • ifNotExist - 如果为 true,则在数据库已存在时跳过创建;否则,如果数据库已存在则抛出错误

创建新的数据库。


dropDatabase(database: string, ifExist?: boolean): Promise<void>
  • database - 数据库名称
  • ifExist - 如果为 true,则在数据库未找到时跳过删除;否则,如果数据库未找到则抛出错误

删除数据库。


createSchema(schemaPath: string, ifNotExist?: boolean): Promise<void>
  • schemaPath - 模式名称。对于 SQL Server,可以接受模式路径(例如 'dbName.schemaName')作为参数。 如果传递了模式路径,则在指定的数据库中创建模式。
  • ifNotExist - 如果为 true,则在模式已存在时跳过创建;否则,如果模式已存在则抛出错误

创建新的表模式。


dropSchema(schemaPath: string, ifExist?: boolean, isCascade?: boolean): Promise<void>
  • schemaPath - 模式名称。对于 SQL Server,可以接受模式路径(例如 'dbName.schemaName')作为参数。 如果传递了模式路径,则在指定的数据库中删除模式。
  • ifExist - 如果为 true,则在模式未找到时跳过删除;否则,如果模式未找到则抛出错误。
  • isCascade - 如果为 true,则自动删除包含在模式中的对象(表、函数等)。 仅适用于 PostgreSQL。

删除表模式。


createTable(table: Table, ifNotExist?: boolean, createForeignKeys?: boolean, createIndices?: boolean): Promise<void>
  • table - 表对象。
  • ifNotExist - 如果为 true,则在表已存在时跳过创建;否则,如果表已存在则抛出错误。默认为 false
  • createForeignKeys - 指示在创建表时是否创建外键。默认为 true
  • createIndices - 指示在创建表时是否创建索引。默认为 true

创建新表。


dropTable(table: Table|string, ifExist?: boolean, dropForeignKeys?: boolean, dropIndices?: boolean): Promise<void>
  • table - 表对象或要删除的表名称。
  • ifExist - 如果为 true,则在表不存在时跳过删除;否则,如果表不存在则抛出错误。
  • dropForeignKeys - 指示在删除表时是否删除外键。默认为 true
  • dropIndices - 指示在删除表时是否删除索引。默认为 true

删除表。


renameTable(oldTableOrName: Table|string, newTableName: string): Promise<void>
  • oldTableOrName - 要重命名的旧表对象或名称。
  • newTableName - 新表名称。

重命名表。


addColumn(table: Table|string, column: TableColumn): Promise<void>
  • table - 表对象或名称。
  • column - 新列。

添加新列。


addColumns(table: Table|string, columns: TableColumn[]): Promise<void>
  • table - 表对象或名称。
  • columns - 新列数组。

添加新列。


renameColumn(table: Table|string, oldColumnOrName: TableColumn|string, newColumnOrName: TableColumn|string): Promise<void>
  • table - 表对象或名称。
  • oldColumnOrName - 旧列。可接受 TableColumn 对象或列名称。
  • newColumnOrName - 新列。可接受 TableColumn 对象或列名称。

重命名列。


changeColumn(table: Table|string, oldColumn: TableColumn|string, newColumn: TableColumn): Promise<void>
  • table - 表对象或名称。
  • oldColumn - 旧列。可接受 TableColumn 对象或列名称。
  • newColumn - 新列。可接受 TableColumn 对象。

修改表中的列。


changeColumns(table: Table|string, changedColumns: { oldColumn: TableColumn, newColumn: TableColumn }[]): Promise<void>
  • table - 表对象或名称。
  • changedColumns - 更改的列数组。
    • oldColumn - 旧的 TableColumn 对象。
    • newColumn - 新的 TableColumn 对象。

修改表中的列。


dropColumn(table: Table|string, column: TableColumn|string): Promise<void>
  • table - 表对象或名称。
  • column - 要删除的 TableColumn 对象或列名称。

删除表中的列。


dropColumns(table: Table|string, columns: TableColumn[]|string[]): Promise<void>
  • table - 表对象或名称。
  • columns - 要删除的 TableColumn 对象数组或列名称数组。

删除表中的列。


createPrimaryKey(table: Table|string, columnNames: string[]): Promise<void>
  • table - 表对象或名称。
  • columnNames - 主键的列名称数组。

创建新的主键。


updatePrimaryKeys(table: Table|string, columns: TableColumn[]): Promise<void>
  • table - 表对象或名称。
  • columns - 要更新的 TableColumn 对象数组。

更新复合主键。


dropPrimaryKey(table: Table|string): Promise<void>
  • table - 表对象或名称。

删除主键。


createUniqueConstraint(table: Table|string, uniqueConstraint: TableUnique): Promise<void>
  • table - 表对象或名称。
  • uniqueConstraint - 要创建的 TableUnique 对象。

创建新的唯一约束。

注意:对于 MySQL 不起作用,因为 MySQL 将唯一约束存储为唯一索引。请改用 createIndex() 方法。


createUniqueConstraints(table: Table|string, uniqueConstraints: TableUnique[]): Promise<void>
  • table - 表对象或名称。
  • uniqueConstraints - 要创建的 TableUnique 对象数组。

创建新的唯一约束。

注意:对于 MySQL 不起作用,因为 MySQL 将唯一约束存储为唯一索引。请改用 createIndices() 方法。


dropUniqueConstraint(table: Table|string, uniqueOrName: TableUnique|string): Promise<void>
  • table - 表对象或名称。
  • uniqueOrName - 要删除的 TableUnique 对象或唯一约束名称。

删除唯一约束。

注意:对于 MySQL 不起作用,因为 MySQL 将唯一约束存储为唯一索引。请改用 dropIndex() 方法。


dropUniqueConstraints(table: Table|string, uniqueConstraints: TableUnique[]): Promise<void>
  • table - 表对象或名称。
  • uniqueConstraints - 要删除的 TableUnique 对象数组。

删除唯一约束。

注意:对于 MySQL 不起作用,因为 MySQL 将唯一约束存储为唯一索引。请改用 dropIndices() 方法。


createCheckConstraint(table: Table|string, checkConstraint: TableCheck): Promise<void>
  • table - 表对象或名称。
  • checkConstraint - TableCheck 对象。

创建新的检查约束。

注意:MySQL 不支持检查约束。


createCheckConstraints(table: Table|string, checkConstraints: TableCheck[]): Promise<void>
  • table - 表对象或名称。
  • checkConstraints - TableCheck 对象数组。

创建新的检查约束。

注意:MySQL 不支持检查约束。


dropCheckConstraint(table: Table|string, checkOrName: TableCheck|string): Promise<void>
  • table - 表对象或名称。
  • checkOrName - 要删除的 TableCheck 对象或检查约束名称。

删除检查约束。

注意:MySQL 不支持检查约束。


dropCheckConstraints(table: Table|string, checkConstraints: TableCheck[]): Promise<void>
  • table - 表对象或名称。
  • checkConstraints - 要删除的 TableCheck 对象数组。

删除检查约束。

注意:MySQL 不支持检查约束。


createForeignKey(table: Table|string, foreignKey: TableForeignKey): Promise<void>
  • table - 表对象或名称。
  • foreignKey - TableForeignKey 对象。

创建新的外键。


createForeignKeys(table: Table|string, foreignKeys: TableForeignKey[]): Promise<void>
  • table - 表对象或名称。
  • foreignKeys - 要创建的 TableForeignKey 对象数组。

创建新的外键。


dropForeignKey(table: Table|string, foreignKeyOrName: TableForeignKey|string): Promise<void>
  • table - 表对象或名称。
  • foreignKeyOrName - 要删除的 TableForeignKey 对象或外键名称。

删除外键。


dropForeignKeys(table: Table|string, foreignKeys: TableForeignKey[]): Promise<void>
  • table - 表对象或名称。
  • foreignKeys - 要删除的 TableForeignKey 对象数组。

删除外键。


createIndex(table: Table|string, index: TableIndex): Promise<void>
  • table - 表对象或名称。
  • index - TableIndex 对象。

创建新的索引。


createIndices(table: Table|string, indices: TableIndex[]): Promise<void>
  • table - 表对象或名称。
  • indices - TableIndex 对象数组。

创建新的索引。


dropIndex(table: Table|string, index: TableIndex|string): Promise<void>
  • table - 表对象或名称。
  • index - TableIndex 对象或索引名称。

删除索引。


dropIndices(table: Table|string, indices: TableIndex[]): Promise<void>
  • table - 表对象或名称。
  • indices - TableIndex 对象数组。

删除索引。


clearTable(tableName: string): Promise<void>
  • tableName - 表名称。

清空表中的所有内容。

注意:此操作使用 SQL 的 TRUNCATE 查询,无法在事务中回滚。


enableSqlMemory(): void

启用特殊的查询运行器模式,其中 SQL 查询不会被执行,而是会被存储在查询运行器内的一个特殊变量中。 可以使用 getMemorySql() 方法获取存储的 SQL。


disableSqlMemory(): void

禁用特殊的查询运行器模式,之前存储的 SQL 将被清空。


clearSqlMemory(): void

清空所有存储的 SQL 语句。


getMemorySql(): SqlInMemory
  • 返回一个包含 upQueriesdownQueries SQL 语句数组的 SqlInMemory 对象。

获取存储在内存中的 SQL。其中的参数已被替换。


executeMemoryUpSql(): Promise<void>

执行存储的升级 SQL 语句。


executeMemoryDownSql(): Promise<void>

执行存储的降级 SQL 语句。