Skip to content

Files

Latest commit

1556378 · Jan 26, 2025

History

History
387 lines (281 loc) · 10.2 KB

migrations.md

File metadata and controls

387 lines (281 loc) · 10.2 KB

数据库迁移

[[toc]]

简介

当多人协作开发应用程序时,如果同步数据库结构没有一个统一的规范,以保证所有人的本地数据都是一致的,那将是灾难。数据库迁移就是为了解决这个问题,将数据库的结构进行版本控制,以保证所有开发人员的数据库结构的一致性。

配置

数据库迁移文件存放在 database/migrations 目录下,你可以在 config/database.go 文件中配置数据库连接信息。当前支持 Go 语言迁移和 SQL 迁移两个驱动,但 SQL 迁移将在后续版本中移除。

// Available Drivers: "default", "sql"
"migrations": map[string]any{
  "driver": "default",
  // You can cumstomize the table name of migrations
  "table":  "migrations",
},

生成迁移

使用 make:migration 命令来创建迁移:

go run . artisan make:migration create_users_table

该命令会在 database/migrations 目录下生成迁移文件,所有迁移文件都以一个时间戳为开头,Goravel 将以此作为迁移文件的执行顺序。

快捷生成

使用 create_users_table 将会自动生成包含 users 基础结构的表,实现原理是根据正则进行匹配:

^create_(\w+)_table$
^create_(\w+)$

使用 add_avatar_to_users_table 将会自动生成向 users 表增加字段的结构,实现原理是根据正则进行匹配:

_(to|from|in)_(\w+)_table$
_(to|from|in)_(\w+)$

未匹配到上述情况时,框架会生成一个空的迁移文件。

迁移结构

Go 语言迁移

迁移类包含两个方法:UpDownUp 方法用于向数据库中添加新表、列或索引,而 Down 方法用于撤销 Up 方法执行的操作。在这两种方法中,可以使用 facades.Schema() 来创建和操作数据库表,起可用方法详见文档。以下迁移会创建一个 users 表:

package migrations

import (
	"github.com/goravel/framework/contracts/database/schema"
	"github.com/goravel/framework/facades"
)

type M20241207095921CreateUsersTable struct {
}

// Signature The unique signature for the migration.
func (r *M20241207095921CreateUsersTable) Signature() string {
	return "20241207095921_create_users_table"
}

// Up Run the migrations.
func (r *M20241207095921CreateUsersTable) Up() error {
	if !facades.Schema().HasTable("users") {
		return facades.Schema().Create("users", func(table schema.Blueprint) {
			table.ID()
			table.String("name").Nullable()
			table.String("email").Nullable()
			table.Timestamps()
		})
	}

	return nil
}

// Down Reverse the migrations.
func (r *M20241207095921CreateUsersTable) Down() error {
	return facades.Schema().DropIfExists("users")
}

设置迁移连接

如果迁移将与应用程序默认数据库连接以外的数据库连接进行交互,你可以使用迁移的 Connection 方法:

func (r *M20241207095921CreateUsersTable) Connection() string {
  return "connection-name"
}

SQL 迁移

迁移命令会同时生成两个迁移文件:***.up.sql***.down.sql,分别对应执行、回滚。你可以在这两个文件中直接编写 SQL 语句。

-- ***.up.sql
CREATE TABLE `users` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ***.down.sql
DROP TABLE `users`;

注册迁移

使用 Go 语言迁移时,迁移文件生成后需要再在 database/kernel.go 文件中注册迁移文件:

// database/kernel.go
func (kernel Kernel) Migrations() []schema.Migration {
	return []schema.Migration{
		&migrations.M20241207095921CreateUsersTable{},
	}
}

SQL 迁移不需要注册,框架会自动扫描 database/migrations 目录下的 SQL 文件。

执行迁移

执行 Artisan 命令 migrate,来运行所有未执行过的迁移:

go run . artisan migrate

如果你想查看目前的迁移状态,可以使用 migrate:status Artisan 命令:

go run . artisan migrate:status

回滚迁移

如果要回滚最后一次迁移操作,可以使用 Artisan 命令 rollback。该命令会回滚最后「一批」的迁移,这可能包含多个迁移文件:

go run . artisan migrate:rollback

通过向 rollback 命令加上 step 参数,可以回滚指定数量的迁移。例如,以下命令将回滚最后五个迁移:

go run . artisan migrate:rollback --step=5

命令 migrate:reset 会回滚应用已运行过的所有迁移:

go run . artisan migrate:reset

使用单个命令同时进行回滚和迁移操作

命令 migrate:refresh 首先会回滚已运行过的所有迁移,随后会执行 migrate。这一命令可以高效地重建你的整个数据库:

go run . artisan migrate:refresh

通过在命令 refresh 中使用 step 参数,你可以回滚并重新执行指定数量的迁移操作。例如,下列命令会回滚并重新执行最后五个迁移操作:

go run . artisan migrate:refresh --step=5

删除所有表然后执行迁移

命令 migrate:fresh 会删去数据库中的所有表,随后执行命令 migrate

go run . artisan migrate:fresh

Tables

创建表

facades.Schema().Create("users", func(table schema.Blueprint) {
  table.ID()
  table.String("name").Nullable()
  table.String("email").Nullable()
  table.Timestamps()
})

检查表 / 列是否存在

if facades.Schema().HasTable("users") {}
if facades.Schema().HasColumn("users", "email") {}
if facades.Schema().HasColumns("users", []string{"name", "email"}) {}
if facades.Schema().HasIndex("users", "email_unique") {}

数据库连接

facades.Schema().Connection("sqlite").Create("users", func(table schema.Blueprint) {
  table.ID()
})

更新表

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.String("name").Nullable()
})

重命名 / 删除表

facades.Schema().Rename("users", "new_users")
facades.Schema().Drop("users")
facades.Schema().DropIfExists("users")

字段

可用的字段类型

BigIncrements BigInteger Boolean Char
Date DateTime DateTimeTz Decimal
Double Enum Float ID
Increments Integer IntegerIncrements Json
Increments LongText MediumIncrements MediumInteger
MediumText SmallIncrements SmallInteger SoftDeletes
SoftDeletesTz String Text Time
TimeTz Timestamp Timestamps TimestampsTz
TimestampTz UnsignedBigInteger TinyIncrements TinyInteger
TinyText UnsignedInteger UnsignedMediumInteger UnsignedSmallInteger
UnsignedTinyInteger

Enum

创建一个 Enum 字段,该字段在 Mysql 中可以根据 []any 中的类型进行储存,但在 Postgres, SqliteSqlserver 数据库中都是 String 类型。

table.Enum("difficulty", []any{"easy", "hard"})
table.Enum("num", []any{1, 2})

ID

ID 方法是 BigIncrements 方法的别名。默认情况下,该方法将创建一个 id 列;但是,如果你想要为该列分配一个不同的名称,可以传递一个列名称:

table.ID()
table.ID("user_id")

SoftDeletes

SoftDeletes 方法添加了一个可以为空的 deleted_at TIMESTAMP 列。此列旨在存储 Orm 「软删除」功能所需的 deleted_at 时间戳:

table.SoftDeletes()

自定义字段

如果你正在使用框架尚不支持的字段类型,可以通过 Column 方法自定义字段类型:

table.Column("geometry", "geometry")

列修饰符

除了上面列出的列类型之外,当给数据库表添加一个列时,你还可以给列添加「修饰符」。例如,要使列可以「为空」,你可以使用 Nullable 方法:

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.String("name").Nullable()
})

以下表格包含所有可用的列修饰符:

修饰符 描述
.AutoIncrement() 设置整数列为自动增长的(主键)
.Comment("my comment") 向列添加注释(MySQL / PostgreSQL)
.Default(value) 为列指定「默认」值
.Nullable() 允许插入 NULL 值到列中
.Unsigned() 设置整数列为 UNSIGNED(仅限 MySQL)
.UseCurrent() 设置时间戳列使用 CURRENT_TIMESTAMP 作为默认值
.UseCurrentOnUpdate() 设置记录更新时,时间戳列使用 CURRENT_TIMESTAMP(仅限 MySQL)

删除列

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.DropColumn("name")
  table.DropColumn("name", "age")
})

索引

创建索引

facades.Schema().Table("users", func(table schema.Blueprint) {
  // 添加主键
  table.Primary("id")
  // 添加复合主键
  table.Primary("id", "name")

  // 添加唯一索引
  table.Unique("name")
  table.Unique("name", "age")

  // 添加普通索引
  table.Index("name")
  table.Index("name", "age")

  // 添加全文索引
  table.FullText("name")
  table.FullText("name", "age")
})

重命名索引

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.RenameIndex("users_name_index", "users_name")
})

删除索引

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.DropPrimary("id")
  table.DropUnique("name")
  table.DropUniqueByName("name_unique")
  table.DropIndex("name")
  table.DropIndexByName("name_index")
  table.DropFullText("name")
  table.DropFullTextByName("name_fulltext")
})

外键约束

facades.Schema().Table("posts", func(table schema.Blueprint) {
  table.UnsignedBigInteger("user_id")
  table.Foreign("user_id").References("id").On("users")
})

删除外键

facades.Schema().Table("users", func(table schema.Blueprint) {
  table.DropForeign("user_id")
  table.DropForeignByName("user_id_foreign")
})