1. 迁移机制概述
1.1 迁移的概念和作用
数据库迁移(Migration)是将数据模型的更改同步到数据库结构的过程,这在实现数据持久化时非常重要。随着应用程序版本的迭代,数据模型经常会发生变动,如添加或删除字段、更改索引等。迁移允许开发者以版本化和系统化的方式来管理这些变动,确保数据库结构与数据模型的一致性。
在现代web开发中,迁移机制提供以下作用:
- 版本控制:迁移文件可以跟踪数据库结构的变化历史,方便回滚和理解每个版本的变动。
- 自动化部署:通过迁移机制可以自动化数据库的部署和更新,减少手动干预的可能性和出错的风险。
- 团队协作:迁移文件确保团队成员在不同的开发环境中使用同步的数据库结构,便于团队成员之间的协作开发。
1.2 ent框架的迁移特性
ent
框架与迁移机制的集成提供了以下特点:
- 声明式编程:开发者只需要关心实体的Go表示,
ent
框架会处理实体到数据库表的转换。 - 自动迁移:
ent
能自动创建和更新数据库表结构,无需手动编写DDL语句。 - 灵活控制:
ent
提供了多种配置选项,以支持不同的迁移需求,如有或无外键约束、生成全局唯一ID等。
2. 自动迁移介绍
2.1 自动迁移的基本原理
ent
框架的自动迁移功能基于schema定义文件(通常在ent/schema
目录下)生成数据库结构。开发者定义实体和关系后,ent
会检查数据库中的现有结构,并根据定义生成相应的操作来创建表、添加或修改列、创建索引等。
此外,ent
的自动迁移原理按照“追加模式”工作:它默认只会添加新表、新索引,或向表增加列,并不会删除已有的表或列。这种设计有利于防止数据意外丢失,并且易于正向地扩展数据库结构。
2.2 使用自动迁移
使用ent
自动迁移的基本步骤如下:
package main
import (
"context"
"log"
"ent"
)
func main() {
client, err := ent.Open("mysql", "root:pass@tcp(localhost:3306)/test")
if err != nil {
log.Fatalf("无法连接到MySQL: %v", err)
}
defer client.Close()
ctx := context.Background()
// 执行自动迁移,创建或更新数据库模式
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("创建数据库模式失败: %v", err)
}
}
以上代码中ent.Open
负责建立与数据库的连接,并返回一个客户机实例,而client.Schema.Create
则执行实际的自动迁移操作。
3. 自动迁移的高级应用
3.1 删除列和索引
在一些情况下,我们可能需要从数据库模式中移除不再需要的列或索引,此时可以使用WithDropColumn
和WithDropIndex
选项。例如:
// Run migration with options to drop columns and indexes.
err = client.Schema.Create(
ctx,
migrate.WithDropIndex(true),
migrate.WithDropColumn(true),
)
此代码片段会在自动迁移中启用删除列和索引的配置,ent
在执行迁移时将会删除任何在schema定义中不存在的列和索引。
3.2 全局唯一ID
默认情况下,SQL数据库中的主键从每个表的1开始,不同的实体类型可能共享相同的ID。在某些应用场景下,如使用GraphQL时,可能需要为不同实体类型的对象ID提供全局唯一性。在ent
中,可以通过WithGlobalUniqueID
选项进行配置:
// Run migration with universal unique IDs for each entity.
if err := client.Schema.Create(ctx, migrate.WithGlobalUniqueID(true)); err != nil {
log.Fatalf("创建数据库模式失败: %v", err)
}
启用WithGlobalUniqueID
选项后,ent
将会在名为ent_types
的表中分配一个2^32范围的ID给每个实体。
3.3 离线模式
离线模式允许将schema变更写入io.Writer
,而不是直接在数据库上执行。它适用于在变动生效之前验证SQL命令,或者生成一个SQL脚本供手动执行。例如:
// Dump migration changes to a file
f, err := os.Create("migrate.sql")
if err != nil {
log.Fatalf("创建迁移文件失败: %v", err)
}
defer f.Close()
if err := client.Schema.WriteTo(ctx, f); err != nil {
log.Fatalf("打印数据库模式变更失败: %v", err)
}
这段代码会将迁移变动写入到名为migrate.sql
的文件中。在实践中,开发者可以选择直接打印到标准输出或写入到文件,便于审查或记录。
4. 外键支持与自定义钩子
4.1 启用或禁用外键
在Ent中,外键是通过定义实体间的关系(边)来实现的,这些外键关系会在数据库层面上自动创建,以强制实现数据的完整性和一致性。然而,在某些情况下,例如性能优化或是数据库不支持外键时,你可能会选择禁用它们。
为了在迁移中启用或禁用外键约束,你可以通过WithForeignKeys
配置项来控制:
// 启用外键
err = client.Schema.Create(
ctx,
migrate.WithForeignKeys(true),
)
if err != nil {
log.Fatalf("failed creating schema resources with foreign keys: %v", err)
}
// 禁用外键
err = client.Schema.Create(
ctx,
migrate.WithForeignKeys(false),
)
if err != nil {
log.Fatalf("failed creating schema resources without foreign keys: %v", err)
}
此配置项需要在调用Schema.Create
时传入,根据指定的值来决定是否在生成的DDL中包含外键约束。
4.2 迁移钩子(Hooks)的应用
迁移钩子(Hooks)是自定义逻辑,可以在迁移执行的不同阶段被插入和执行。它们非常有用于在执行迁移之前/之后操作数据库的特定逻辑,例如验证迁移结果、预填充数据等。
下面是一个如何实现自定义迁移钩子的例子:
func customHook(next schema.Creator) schema.Creator {
return schema.CreateFunc(func(ctx context.Context, tables ...*schema.Table) error {
// 在迁移执行之前执行的自定义代码
// 例如记录日志、检查某些先决条件等
log.Println("迁移前的自定义逻辑")
// 调用下一个Hook或者迁移的默认逻辑
err := next.Create(ctx, tables...)
if err != nil {
return err
}
// 在迁移执行之后执行的自定义代码
// 例如清理、数据迁移、安全检测等
log.Println("迁移后的自定义逻辑")
return nil
})
}
// 在迁移中使用自定义钩子
err := client.Schema.Create(
ctx,
schema.WithHooks(customHook),
)
if err != nil {
log.Fatalf("在应用自定义迁移钩子时出现错误: %v", err)
}
钩子的功能强大,在进行复杂迁移时它们是不可或缺的工具,能让你在所需的时候拥有直接控制数据库迁移行为的能力。
5. 版本化迁移(Versioned Migrations)
5.1 版本化迁移介绍
版本化迁移是数据库迁移管理的一种模式,它允许开发者将数据库结构的更改分步分为多个版本,每个版本包含了一组特定的数据库修改命令。这与自动迁移(Auto Migration)相比,版本化迁移提供了更细粒度的控制,保证了数据库结构变化的可追踪性和可回溯性。
版本化迁移的主要优点在于它支持向前迁移和向后迁移(即升级或降级),使得开发者可以根据需要应用、回滚或跳过特定的变更。当我们在一个团队内协同开发时,版本化迁移能够确保每个成员都在相同的数据库结构上工作,减少因不同步而引发的问题。
自动迁移往往是不可逆的,它根据实体模型的最新状态自动生成和执行SQL语句来匹配数据库的结构,主要用于开发阶段或小型项目。
5.2 使用版本化迁移
1. 安装atlas工具
在使用版本化迁移之前,你需要在系统中安装Atlas
工具。Atlas
是一个支持多数据库系统的迁移工具,它提供了一系列强大的功能来管理数据库架构变更。
macOS + Linux
curl -sSf https://atlasgo.sh | sh
Homebrew
brew install ariga/tap/atlas
Docker
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
Windows
https://release.ariga.io/atlas/atlas-windows-amd64-latest.exe
2. 根据当前的实体定义生成迁移文件
atlas migrate diff migration_name \
--dir "file://ent/migrate/migrations" \
--to "ent://ent/schema" \
--dev-url "docker://mysql/8/ent"
3. 应用迁移文件
迁移文件生成后,它们可以应用到开发、测试或生产环境中。通常,你会在一个开发或测试数据库上首先应用这些迁移文件,以确保它们能够如预期的执行。然后再在生产环境上执行相同的迁移步骤。
# 应用迁移到特定的数据库环境
atlas migrate apply \
--dir "file://ent/migrate/migrations" \
--url "mysql://root:pass@localhost:3306/example"
使用atlas migrate apply
命令,指定迁移文件目录 (--dir
) 和目标数据库的URL (--url
) 来应用迁移文件。