GORM 的自动迁移功能可以自动创建、更新数据库表结构,使数据库 schema 与 Go 结构体保持同步。
AutoMigrate 基本用法
基本迁移
go// 自动迁移 User 模型 db.AutoMigrate(&User{}) // 迁移多个模型 db.AutoMigrate(&User{}, &Profile{}, &Order{}) // 迁移所有模型 db.AutoMigrate( &User{}, &Profile{}, &Order{}, &Product{}, )
模型定义和标签
基本模型
gotype User struct { ID uint `gorm:"primaryKey"` Name string `gorm:"size:100;not null"` Email string `gorm:"size:100;uniqueIndex"` Age int `gorm:"default:0"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"` DeletedAt gorm.DeletedAt `gorm:"index"` }
使用 gorm.Model
gotype User struct { gorm.Model Name string `gorm:"size:100;not null"` Email string `gorm:"size:100;uniqueIndex"` }
常用标签
主键
goID uint `gorm:"primaryKey"`
字段类型
goName string `gorm:"type:varchar(100)"` Age int `gorm:"type:tinyint"` Price float64 `gorm:"type:decimal(10,2)"`
字段大小
goName string `gorm:"size:100"`
默认值
goAge int `gorm:"default:0"` Status string `gorm:"default:'active'"`
非空约束
goName string `gorm:"not null"`
唯一索引
goEmail string `gorm:"unique"` Email string `gorm:"uniqueIndex"`
普通索引
goName string `gorm:"index"` Name string `gorm:"index:idx_name"`
复合索引
gotype User struct { Name string `gorm:"index:idx_name_age"` Age int `gorm:"index:idx_name_age"` }
自定义表名
gotype User struct { gorm.Model Name string } func (User) TableName() string { return "sys_users" }
忽略字段
gotype User struct { Name string Password string `gorm:"-"` TempField string `gorm:"-"` }
高级迁移功能
添加外键
gotype Profile struct { gorm.Model UserID uint `gorm:"not null;index"` User User `gorm:"foreignKey:UserID;references:ID"` Bio string }
多对多关系
gotype User struct { gorm.Model Roles []Role `gorm:"many2many:user_roles;"` } type Role struct { gorm.Model Name string Users []User `gorm:"many2many:user_roles;"` }
嵌入结构体
gotype BaseModel struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"` } type User struct { BaseModel Name string }
迁移限制
AutoMigrate 不会做的事情
- 不会删除列
- 不会重命名列
- 不会删除表
- 不会修改列类型(可能导致数据丢失)
手动处理复杂变更
go// 添加列 db.Migrator().AddColumn(&User{}, "NewField") // 删除列 db.Migrator().DropColumn(&User{}, "OldField") // 重命名列 db.Migrator().RenameColumn(&User{}, "OldName", "NewName") // 添加索引 db.Migrator().CreateIndex(&User{}, "Email") // 删除索引 db.Migrator().DropIndex(&User{}, "Email") // 重命名索引 db.Migrator().RenameIndex(&User{}, "OldIndex", "NewIndex") // 检查表是否存在 hasTable := db.Migrator().HasTable(&User{}) // 删除表 db.Migrator().DropTable(&User{}) // 重命名表 db.Migrator().RenameTable("old_users", "new_users")
迁移最佳实践
1. 版本控制
gotype Migration struct { ID uint `gorm:"primaryKey"` Name string `gorm:"unique"` AppliedAt time.Time `gorm:"autoCreateTime"` } func RunMigrations(db *gorm.DB) error { // 迁移表结构 if err := db.AutoMigrate(&User{}, &Profile{}); err != nil { return err } // 迁移数据 migrations := []string{ "add_email_index", "update_user_status", } for _, migration := range migrations { var count int64 db.Model(&Migration{}).Where("name = ?", migration).Count(&count) if count > 0 { continue } if err := applyMigration(db, migration); err != nil { return err } db.Create(&Migration{Name: migration}) } return nil }
2. 数据迁移
gofunc migrateUserData(db *gorm.DB) error { return db.Transaction(func(tx *gorm.DB) error { var users []User if err := tx.Find(&users).Error; err != nil { return err } for _, user := range users { if user.Status == "" { user.Status = "active" if err := tx.Save(&user).Error; err != nil { return err } } } return nil }) }
3. 回滚机制
gotype Migration struct { Name string Up func(*gorm.DB) error Down func(*gorm.DB) error } var migrations = []Migration{ { Name: "add_email_field", Up: func(db *gorm.DB) error { return db.Migrator().AddColumn(&User{}, "Email") }, Down: func(db *gorm.DB) error { return db.Migrator().DropColumn(&User{}, "Email") }, }, }
注意事项
- 生产环境谨慎:在生产环境中使用 AutoMigrate 要非常谨慎,建议先在测试环境验证
- 数据备份:执行迁移前务必备份数据
- 版本控制:将迁移脚本纳入版本控制
- 测试覆盖:为迁移逻辑编写充分的测试
- 性能考虑:大型表的迁移可能需要较长时间,考虑分批处理
- 兼容性:注意不同数据库的兼容性问题
常见问题
Q: AutoMigrate 会删除数据吗?
A: 不会,AutoMigrate 只会添加或修改表结构,不会删除数据。
Q: 如何处理列重命名?
A: AutoMigrate 不支持列重命名,需要使用 Migrator API 手动处理。
Q: 如何回滚迁移?
A: 需要自己实现回滚逻辑,GORM 不提供自动回滚功能。
Q: 如何处理大型表的迁移?
A: 考虑使用在线 DDL 工具或分批处理数据。