数据库迁移
简介
当多人协作开发应用程序时,如果同步数据库结构没有一个统一的规范,以保证所有人的本地数据都是一致的,那将是灾难。数据库迁移就是为了解决这个问题,将数据库的结构进行版本控制,以保证所有开发人员的数据库结构的一致性。
配置
数据库迁移文件存放在 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 语言迁移
迁移类包含两个方法:Up
和 Down
。Up
方法用于向数据库中添加新表、列或索引,而 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 | Char | Date |
DateTime | DateTimeTz | Decimal | Double |
Enum | Float | ID | Increments |
Integer | IntegerIncrements | Json | Jsonb |
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
, Sqlite
和 Sqlserver
数据库中都是 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()
列修饰符
除了上面列出的列类型之外,当给数据库表添加一个列时,你还可以给列添加「修饰符」。例如,要使列可以「为空」,你可以使用 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")
})