一架梯子,一头程序猿,仰望星空!
Go Ent ORM框架教程 > 内容正文

迁移机制


1. 迁移机制概述

1.1 迁移的概念和作用

数据库迁移(Migration)是将数据模型的更改同步到数据库结构的过程,这在实现数据持久化时非常重要。随着应用程序版本的迭代,数据模型经常会发生变动,如添加或删除字段、更改索引等。迁移允许开发者以版本化和系统化的方式来管理这些变动,确保数据库结构与数据模型的一致性。

在现代web开发中,迁移机制提供以下作用:

  1. 版本控制:迁移文件可以跟踪数据库结构的变化历史,方便回滚和理解每个版本的变动。
  2. 自动化部署:通过迁移机制可以自动化数据库的部署和更新,减少手动干预的可能性和出错的风险。
  3. 团队协作:迁移文件确保团队成员在不同的开发环境中使用同步的数据库结构,便于团队成员之间的协作开发。

1.2 ent框架的迁移特性

ent框架与迁移机制的集成提供了以下特点:

  1. 声明式编程:开发者只需要关心实体的Go表示,ent框架会处理实体到数据库表的转换。
  2. 自动迁移ent能自动创建和更新数据库表结构,无需手动编写DDL语句。
  3. 灵活控制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 删除列和索引

在一些情况下,我们可能需要从数据库模式中移除不再需要的列或索引,此时可以使用WithDropColumnWithDropIndex选项。例如:

// 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) 来应用迁移文件。