数据库迁移
模式迁移是针对数据库的版本控制,是用 TypeScript 编写的脚本,用来随着时间的推移改变你的数据库模式。
简而言之,迁移的工作原理如下。
你为每个数据库模式更改(即创建或更改表)创建一个新的迁移文件。
- 在迁移文件中,你将编写语句以执行所需的操作。
- 使用 AdonisJS 命令行工具运行迁移。
- AdonisJS 将跟踪执行的迁移,可确保每次迁移仅运行一次。
- 在开发过程中,你还可以回滚迁移以对其进行编辑。
创建你的第一个迁移
你可以通过运行以下 Ace 命令来创建新迁移。迁移文件存储在 database/migrations
目录中。
注意:你还可以通过运行
node ace make:model -m
标志一起创建 Lucid 模型和迁移。
node ace make:migration users
# CREATE: database/migrations/1630981615472_users.ts
你会注意到,迁移文件名以某个数值为前缀。我们将当前时间戳添加到文件名,以便迁移文件按创建的顺序排序。
迁移类结构
迁移类始终扩展框架提供的- BaseSchema
类,并且必须实现 up
和 down
方法。
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:fresh
和db: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 将在其事务中运行每个迁移文件。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。