golang数据库模式迁移助手插件库gormigrate的使用

Golang数据库模式迁移助手插件库Gormigrate的使用

Gormigrate简介

Gormigrate是一个用于Gorm的极简迁移助手。Gorm已经有有用的迁移功能,但缺少适当的模式版本控制和迁移回滚支持。

注意:如果你需要使用Gorm v1版本(使用github.com/jinzhu/gorm作为导入路径),请使用gopkg.in/gormigrate.v1导入路径。

当前Gorm版本(v2)通过使用github.com/go-gormigrate/gormigrate/v2导入路径来支持。

支持的数据库

Gormigrate支持Gorm支持的任何数据库:

  • MySQL
  • MariaDB
  • PostgreSQL
  • SQLite
  • Microsoft SQL Server
  • TiDB
  • Clickhouse

使用示例

下面是一个完整的使用示例:

package main

import (
	"log"

	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/google/uuid"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

func main() {
	db, err := gorm.Open(sqlite.Open("./data.db"), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatal(err)
	}

	m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{{
		// 创建`users`表
		ID: "201608301400",
		Migrate: func(tx *gorm.DB) error {
			// 最好在函数内部复制结构体,以防止原始结构体随时间变化而产生副作用
			type user struct {
				ID   uuid.UUID `gorm:"type:uuid;primaryKey;uniqueIndex"`
				Name string
			}
			return tx.Migrator().CreateTable(&user{})
		},
		Rollback: func(tx *gorm.DB) error {
			return tx.Migrator().DropTable("users")
		},
	}, {
		// 向`users`表添加`age`列
		ID: "201608301415",
		Migrate: func(tx *gorm.DB) error {
			// 当表已存在时,只定义将要更改的列
			type user struct {
				Age int
			}
			return tx.Migrator().AddColumn(&user{}, "Age")
		},
		Rollback: func(tx *gorm.DB) error {
			type user struct {
				Age int
			}
			return db.Migrator().DropColumn(&user{}, "Age")
		},
	}, {
		// 创建`organizations`表,用户属于该表
		ID: "201608301430",
		Migrate: func(tx *gorm.DB) error {
			type organization struct {
				ID      uuid.UUID `gorm:"type:uuid;primaryKey;uniqueIndex"`
				Name    string
				Address string
			}
			if err := tx.Migrator().CreateTable(&organization{}); err != nil {
				return err
			}
			type user struct {
				OrganizationID uuid.UUID `gorm:"type:uuid"`
			}
			return tx.Migrator().AddColumn(&user{}, "OrganizationID")
		},
		Rollback: func(tx *gorm.DB) error {
			type user struct {
				OrganizationID uuid.UUID `gorm:"type:uuid"`
			}
			if err := db.Migrator().DropColumn(&user{}, "OrganizationID"); err != nil {
				return err
			}
			return tx.Migrator().DropTable("organizations")
		},
	}})

	if err := m.Migrate(); err != nil {
		log.Fatalf("Migration failed: %v", err)
	}
	log.Println("Migration did run successfully")
}

初始化模式

如果你有很多迁移,在干净的数据库中部署新应用实例时运行所有迁移会很痛苦。为了防止这种情况,你可以设置一个函数,如果之前没有运行过任何迁移(在新的干净数据库中),该函数将运行。

type Organization struct {
	gorm.Model
	Name    string
	Address string
}

type User struct {
	gorm.Model
	Name string
	Age int
	OrganizationID uint
}

m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
    // 你的迁移在这里
})

m.InitSchema(func(tx *gorm.DB) error {
	err := tx.AutoMigrate(
		&Organization{},
		&User{},
		// 你应用的所有其他表
	)
	if err != nil {
		return err
	}

	if err := tx.Exec("ALTER TABLE users ADD CONSTRAINT fk_users_organizations FOREIGN KEY (organization_id) REFERENCES organizations (id)").Error; err != nil {
		return err
	}
	// 所有其他约束、索引等...
	return nil
})

选项

这是选项结构体,如果你不想使用默认值:

type Options struct {
	// TableName是迁移表
	TableName string
	// IDColumnName是存储迁移id的列名
	IDColumnName string
	// IDColumnSize是迁移id列的长度
	IDColumnSize int
	// UseTransaction使Gormigrate在单个事务中执行迁移
	// 请注意并非所有数据库都支持事务中的DDL命令
	UseTransaction bool
	// ValidateUnknownMigrations如果数据库中有未知的迁移ID,会导致迁移失败
	ValidateUnknownMigrations bool
}

Gormigrate适合谁?

Gormigrate是为使用Gorm的小型项目设计的简单和极简的迁移工具。如果你计划扩展,你可能想看看更高级的解决方案,如golang-migrate/migrate。

请注意,Gormigrate没有内置的锁机制,所以如果你自动运行它并且有分布式设置(即多个可执行文件同时运行),你可能想使用分布式锁/互斥机制来防止运行迁移时的竞争条件。


更多关于golang数据库模式迁移助手插件库gormigrate的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang数据库模式迁移助手插件库gormigrate的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Gormigrate - Golang数据库模式迁移助手

Gormigrate是一个用于Golang的数据库迁移库,专门为GORM设计。它可以帮助你管理数据库模式的变更,类似于Ruby on Rails的迁移或Django的migrations。

主要特性

  • 基于GORM构建
  • 支持事务性迁移
  • 可以回滚迁移
  • 迁移历史记录
  • 支持初始化函数
  • 简单的API设计

安装

go get -u github.com/go-gormigrate/gormigrate/v2

基本用法

1. 初始化Gormigrate

package main

import (
	"log"
	
	"github.com/go-gormigrate/gormigrate/v2"
	"gorm.io/gorm"
	"gorm.io/driver/sqlite"
)

func main() {
	db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	
	m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
		// 在这里添加你的迁移
	})
	
	if err = m.Migrate(); err != nil {
		log.Fatalf("Could not migrate: %v", err)
	}
	log.Printf("Migration did run successfully")
}

2. 创建迁移

每个迁移需要定义一个唯一的ID、Migrate函数和Rollback函数:

type User struct {
	gorm.Model
	Name string
	Email string
}

// 创建users表的迁移
createUsersTable := &gormigrate.Migration{
	ID: "202108010000", // 使用时间戳确保唯一性
	Migrate: func(tx *gorm.DB) error {
		return tx.AutoMigrate(&User{})
	},
	Rollback: func(tx *gorm.DB) error {
		return tx.Migrator().DropTable("users")
	},
}

// 添加age字段到users表
addAgeToUsers := &gormigrate.Migration{
	ID: "202108010001",
	Migrate: func(tx *gorm.DB) error {
		// 检查字段是否已存在
		if tx.Migrator().HasColumn(&User{}, "age") {
			return nil
		}
		return tx.Migrator().AddColumn(&User{}, "Age")
	},
	Rollback: func(tx *gorm.DB) error {
		return tx.Migrator().DropColumn(&User{}, "Age")
	},
}

3. 完整示例

package main

import (
	"log"
	
	"github.com/go-gormigrate/gormigrate/v2"
	"gorm.io/gorm"
	"gorm.io/driver/sqlite"
)

type User struct {
	gorm.Model
	Name  string
	Email string `gorm:"unique"`
	Age   int
}

func main() {
	db, err := gorm.Open(sqlite.Open("mydb.db"), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	
	// 设置迁移选项
	options := &gormigrate.Options{
		TableName:      "migrations",
		IDColumnName:   "id",
		IDColumnSize:   255,
		UseTransaction: true,
	}
	
	// 初始化迁移
	m := gormigrate.New(db, options, []*gormigrate.Migration{
		// 初始迁移 - 创建users表
		{
			ID: "202108010000",
			Migrate: func(tx *gorm.DB) error {
				return tx.AutoMigrate(&User{})
			},
			Rollback: func(tx *gorm.DB) error {
				return tx.Migrator().DropTable("users")
			},
		},
		// 第二次迁移 - 添加age字段
		{
			ID: "202108010001",
			Migrate: func(tx *gorm.DB) error {
				if tx.Migrator().HasColumn(&User{}, "age") {
					return nil
				}
				return tx.Migrator().AddColumn(&User{}, "Age")
			},
			Rollback: func(tx *gorm.DB) error {
				return tx.Migrator().DropColumn(&User{}, "Age")
			},
		},
	})
	
	// 执行迁移
	if err = m.Migrate(); err != nil {
		log.Fatalf("Could not migrate: %v", err)
	}
	
	log.Println("Migration did run successfully")
}

高级用法

1. 初始化函数

你可以在第一次迁移前执行初始化代码:

m := gormigrate.New(db, options, []*gormigrate.Migration{
    // 你的迁移
})

m.InitSchema(func(tx *gorm.DB) error {
    // 初始化数据库模式
    err := tx.AutoMigrate(&User{}, &Product{})
    if err != nil {
        return err
    }
    
    // 添加外键
    if err := tx.Table("products").AddForeignKey("user_id", "users(id)", "RESTRICT", "RESTRICT").Error; err != nil {
        return err
    }
    
    return nil
})

2. 回滚迁移

可以回滚到特定迁移:

// 回滚到特定迁移ID
err := m.RollbackTo("202108010000")
if err != nil {
    log.Fatal(err)
}

// 回滚最后一步迁移
err := m.RollbackLast()
if err != nil {
    log.Fatal(err)
}

3. 使用事务

默认情况下,Gormigrate会在事务中执行每个迁移。你可以通过选项控制:

options := &gormigrate.Options{
    UseTransaction: true, // 默认为true
}

4. 自定义迁移表

可以自定义存储迁移历史的表名和列名:

options := &gormigrate.Options{
    TableName:      "custom_migrations",
    IDColumnName:   "migration_id",
    IDColumnSize:   100,
}

最佳实践

  1. 迁移ID命名:使用时间戳格式(YYYYMMDDhhmmss)确保唯一性和顺序
  2. 幂等性:确保迁移可以安全地多次运行
  3. 小步迁移:每个迁移应该只做一件事
  4. 测试迁移:在生产环境运行前测试迁移和回滚
  5. 版本控制:将迁移文件纳入版本控制系统

Gormigrate是一个简单但功能强大的迁移工具,特别适合与GORM一起使用。它提供了数据库模式变更所需的基本功能,同时保持了API的简洁性。

回到顶部