数据库迁移

未匹配的标注

模式迁移是针对数据库的版本控制,是用 TypeScript 编写的脚本,用来随着时间的推移改变你的数据库模式。

简而言之,迁移的工作原理如下。

你为每个数据库模式更改(即创建或更改表)创建一个新的迁移文件。

  • 在迁移文件中,你将编写语句以执行所需的操作。
  • 使用 AdonisJS 命令行工具运行迁移。
  • AdonisJS 将跟踪执行的迁移,可确保每次迁移仅运行一次。
  • 在开发过程中,你还可以回滚迁移以对其进行编辑。

创建你的第一个迁移

你可以通过运行以下 Ace 命令来创建新迁移。迁移文件存储在 database/migrations 目录中。

注意:你还可以通过运行 node ace make:model -m 标志一起创建 Lucid 模型和迁移。

node ace make:migration users

# CREATE: database/migrations/1630981615472_users.ts

你会注意到,迁移文件名以某个数值为前缀。我们将当前时间戳添加到文件名,以便迁移文件按创建的顺序排序。

迁移类结构

迁移类始终扩展框架提供的- BaseSchema 类,并且必须实现 updown 方法。

  • up 方法用于进一步扩展数据库模式。通常,你将在此方法中创建新表/索引或更改现有表。
  • down 方法用于回滚 up 方法执行的操作。例如,如果 up 方法创建了一个表,down 方法应该删除同一个表。

这两种方法都可以访问 AdonisJS schema builder,你可以使用它来构造 SQL DDL 查询。

import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'users'

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}

运行和回滚迁移

创建所需的迁移文件后,你可以运行以下 Ace 命令来处理迁移。例如,migration:run 命令对所有迁移文件执行 up 方法。

node ace migration:run

每个迁移文件的 SQL 语句都包装在事务中。因此,如果一个语句失败,同一文件中的所有其他语句都将回滚。

此外,如果失败,后续迁移将被中止。但是,失败迁移之前的迁移保持已完成状态。

跟踪完成的迁移

AdonisJS 在 adonis_schema 数据库表中跟踪已执行迁移的文件路径。这样做是为了避免重新运行相同的迁移文件。

以下是 adonis_schema 表中的列。

+----+----------------------------------------------+-------+----------------------------------+
| id |                     name                     | batch |          migration_time          |
+----+----------------------------------------------+-------+----------------------------------+
|  1 | database/migrations/1587988332388_users      |     1 | 2021-08-26 10:41:31.176333+05:30 |
|  2 | database/migrations/1592489784670_api_tokens |     1 | 2021-08-26 10:41:31.2074+05:30   |
+----+----------------------------------------------+-------+----------------------------------+
  • name: 迁移文件的路径,相对于项目根目录。
  • batch: 执行迁移的批次。每次运行 migration:run 命令时,批次号都会递增。
  • migration_time: 迁移执行时间戳。

回滚迁移

你可以通过运行 migration:rollback 命令回滚迁移。回滚操作是对最近一批的迁移执行的。但是,你也可以指定要回滚到的自定义批号。

# 回滚最新的批次
node ace migration:rollback

# 回滚到迁移开始
node ace migration:rollback --batch=0

# 回滚到第 1 批
node ace migration:rollback --batch=1

migration:reset 命令基本上是 migration:rollback --batch=0 的别名。这将回滚你应用程序的所有迁移:

node ace migration:reset

rollback 命令执行迁移类的 down 方法。与 up 方法一样,down 方法的 SQL 语句也包装在数据库事务中。

使用单个命令回滚和迁移

migration:refresh 命令将回滚所有迁移,然后执行 migration:run 命令。此命令有效地重新创建整个数据库:

node ace migration:refresh

# 刷新数据库并运行所有指令
node ace migration:refresh --seed

删除表并迁移

migration:refresh 命令不同,migration:reset 命令不会运行迁移文件的 down 方法。相反,它将使用 db:wipe 命令删除所有表,然后运行 migration:run 命令。

node ace migration:fresh

# 删除所有表,迁移并运行所有指令
node ace migration:fresh --seed

注意:
migration:freshdb:wipe 命令将删除所有数据库表。在与其他应用程序共享的数据库上进行开发时,应谨慎使用这些命令。

避免生产环境的回滚

在开发过程中执行回滚非常好,因为不用担心数据丢失。但是,在大多数情况下,在生产环境中执行回滚不是一种选择。考虑以下示例:

  • 你创建并运行迁移以设置 users 表。
  • 随着时间的推移,自从应用程序在生产中运行以来,此表已包含数据。
  • 你的产品已经发展,现在你想在 users 表中添加一个新列。

你不能简单地回滚、编辑现有迁移并重新运行,因为回滚将删除 users 表。

相反,你应该创建一个新的迁移文件,通过添加所需的列来更改现有的 users 表。换句话说,迁移应该始终向前推进。

创建一个表

你可以使用 schema.createTable 方法来创建一个新的数据库表。该方法接受表名作为第一个参数和一个回调函数来定义表列。

import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class extends BaseSchema {
  protected tableName = 'users'

  public async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down() {
    this.schema.dropTable(this.tableName)
  }
}

[架构构建器参考](https://docs.adonisjs.com/reference/database/schema-builder)

修改表

你可以使用 schema.alterTable 方法更改现有数据库表。该方法接受表名作为第一个参数和一个回调函数来更改/添加表列。

export default class extends BaseSchema {
  public up() {
    this.schema.alterTable('user', (table) => {
      table.dropColumn('name')
      table.string('first_name')
      table.string('last_name')
    })
  }
}

重命名/删除表

你可以使用 schema.renameTable 重命名表。该方法接受现有表名作为第一个参数,新名称作为第二个参数。

export default class extends BaseSchema {
  public up() {
    this.schema.renameTable('user', 'app_users')
  }
}

你可以使用 schema.dropTable 删除表。该方法只接受表名作为唯一参数。

export default class extends BaseSchema {
  public down() {
    this.schema.dropTable('users')
  }
}

试运行

迁移的试运行模式允许你在控制台中查看 SQL 查询,而不是执行它们。只需将 --dry-run 标志传递给迁移命令即可打开试运行模式。

# 允许
node ace migration:run --dry-run

# 回滚
node ace migration:rollback --dry-run

执行其他数据库操作

很多时候,除了创建/更改表之外,你还需要运行 SQL 查询。例如:在删除旧表之前将数据迁移到新创建的表中。

你应该使用 this.defer 方法定义这些操作,如下所示。

注意:以下示例中,我们将邮件从 users 表迁移到 user_emails 表。

export default class extends BaseSchema {
  public up() {
    this.schema.createTable('user_emails', (table) => {
      // 表列
    })

    this.defer(async (db) => {
      const users = await db.from('users').select('*')
      await Promise.all(users.map((user) => {
        return db
          .table('user_emails')
          .insert({ user_id: user.id, email: user.email })
      }))
    })

    this.schema.alterTable('users', (table) => {
      table.dropColumn('email')
    })
  }
}

将数据库查询包装在 this.defer 方法中可确保在 dry run 模式下运行迁移时不会执行。

更改迁移数据库连接

你可以通过几种不同的方式管理用于迁移的数据库连接。

单独的迁移源

第一个选项是为每个数据库连接保持迁移分开。当每个数据库连接查询不同的表时,这通常很有用。例如,对用户数据使用不同的数据库,对产品数据使用不同的数据库。

在数据库连接配置旁边定义迁移路径。

{
  users: {
    client: 'mysql',
    migrations: {
      paths: ['./database/users/migrations']
    }
  },
  products: {
    client: 'mysql',
    migrations: {
      paths: ['./database/products/migrations']
    }
  }
}

创建新迁移时,定义 --connection 标志,该命令将在正确的目录中创建文件。

node ace make:migration --connection=products

运行迁移时,--connection 标志将仅从选定的连接目录运行迁移。

node ace migration:run --connection=products

共享迁移

如果要在不同的数据库连接下运行相同的迁移,可以使用 --connection 标志。迁移将使用所选连接中的配置来运行迁移。

此选项对于多租户应用程序很有帮助,你希望每次运行迁移时都切换连接。

node ace migration:run --connection=tenantA

以编程方式运行迁移

使用 Migrator 模块,你可以以编程方式运行迁移。从 Web 界面而不是命令行运行迁移时,这通常很有用。

以下是从路由运行迁移并在响应中返回迁移文件列表的示例。

import Route from '@ioc:Adonis/Core/Route'
import Database from '@ioc:Adonis/Lucid/Database'
import Application from '@ioc:Adonis/Core/Application'
import Migrator from '@ioc:Adonis/Lucid/Migrator'

Route.get('/', async () => {
  const migrator = new Migrator(Database, Application, {
    direction: 'up',
    dryRun: false,
    // connectionName: 'pg',
  })

  await migrator.run()
  return migrator.migratedFiles
})
  • direction = up 表示在迁移文件中运行 up 方法,可以设置 direction = down 来回滚迁移。
  • 启用 dryRun 不会执行查询,而是将它们收集到 queries 数组中。
  • 还可以选择定义 connectionName 属性以针对特定数据库连接执行迁移。

已迁移文件

migrator.migratedFiles 是一个对象,键是唯一名(源自文件路径),值是迁移文件属性的另一个对象。

{
  "database/migrations/1623289360244_users": {
    "status": "completed",
    "queries": [],
    "file": {
      "filename": "1623289360244_users.ts",
      "absPath": "/path/to/project/database/migrations/1623289360244_users.ts",
      "name": "database/migrations/1623289360244_users"
    },
    "batch": 1
  }
}
  • status 将是 "pending""completed""error" 之一。
  • queries 数组包含一个已执行查询的数组。仅当 dryRun 启用时生效。
  • file 属性保存迁移文件的信息。
  • batch 属性告诉执行迁移的批次。

getList

migrator.getList 方法返回所有迁移的列表,包括已完成和待处理的迁移,这与你在运行 node ace migration:status 命令时看到的列表相同。

await migrator.getList()
[
  {
    "name": "database/migrations/1623289360244_users",
    "status": "pending"
  }
]

status

返回迁移器的当前 status,它将始终是以下之一。

  • pending 状态表示尚未调用 migrator.run 方法。
  • completed 状态表示迁移已成功执行。
  • error 状态表示迁移过程中出现错误。如果出现错误状态,你可以从 migrator.error 属性中读取错误。
  • skipped 状态表示没有要运行或回滚的迁移。

迁移配置

迁移的配置存储在连接配置对象下的 config/database.ts 文件中。

{
  mysql: {
    client: 'mysql',
    migrations: {
      naturalSort: true,
      disableTransactions: false,
      paths: ['./database/migrations'],
      tableName: 'adonis_schema',
      disableRollbacksInProduction: true,
    }
  }
}

naturalSort

使用 naturalSort 对迁移文件进行排序。大多数编辑器使用自然排序,因此迁移将按照你在编辑器中看到的顺序运行。


path

查找迁移的一系列路径,你还可以定义已安装包的路径。例如:

paths: [
  './database/migrations',
  '@somepackage/migrations-dir',
]

tableName

用于存储迁移状态的表的名称。默认为 adonis_schema


disableRollbacksInProduction

在生产中禁用迁移回滚,建议永远不要在生产环境中回滚迁移。


disableTransactions

将该值设置为 true 以不将迁移语句包装在事务中。默认情况下,Lucid 将在其事务中运行每个迁移文件。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/adonisjs/5.x/da...

译文地址:https://learnku.com/docs/adonisjs/5.x/da...

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~