Gorm 数据库表迁移与表模型定义

打印 上一主题 下一主题

主题 923|帖子 923|积分 2769

目录

一、Docker快速创建MySQL实例

1.1 创建

因为这里我们是测试学习使用,单独安装MySQL 比较费时费力,所以这里使用Docker方便快速掌握Gorm 相关知识。
如果你没有docker环境,可以参考:【一文搞定】Linux、Mac、Windows安装Docker与配置教程!
下载镜像:
  1. docker pull mysql
复制代码
运行MySQL容器:
  1. docker run -p 3306:3306 --name mysql -v $PWD/conf/my.cnf:/etc/mysql/my.cnf -v $PWD/logs:/logs -v $PWD/data:/mysql_data -e MYSQL_ROOT_PASSWORD=123456 -d mysql
复制代码
下面是对命令中参数的解释:

  • -p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口
  • -v $PWD/conf/my.cnf:/etc/mysql/my.cnf:将主机当前目录下的 conf/my.cnf 挂载到容器的 /etc/mysql/my.cnf
  • -v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的 /logs
  • -v $PWD/data:/mysql_data:将主机当前目录下的 data 目录挂载到容器的 /mysql_data
  • -e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码
查看运行中的容器:
  1. docker ps  
复制代码
1.3 创建数据库

首先,使用Datagrip 链接数据,接着在使用GORM前手动创建数据库db1,执行如下SQL:
  1. CREATE DATABASE db1;
复制代码
二、AutoMigrate介绍与使用

2.1 AutoMigrate介绍

AutoMigrate 是 Gorm 提供的一个功能强大的数据库迁移工具,它可以自动创建或更新数据库表结构,使数据库的结构与 Golang 模型一致。使用 AutoMigrate 可以方便地进行数据库表的初始化和更新,而无需手动执行 SQL 语句。
2.2 AutoMigrate 基本使用

在 Gorm 中,你可以通过调用 db.AutoMigrate 方法来进行数据库表的自动迁移。以下是一个基本的使用示例:
  1. package main
  2. import (
  3.         "gorm.io/driver/mysql"
  4.         "gorm.io/gorm"
  5.         "gorm.io/gorm/logger"
  6.         "log"
  7.         "os"
  8.         "time"
  9. )
  10. type Product struct {
  11.         gorm.Model
  12.         Code  string
  13.         Price uint
  14. }
  15. func main() {
  16.         // 日志配置
  17.         newLogger := logger.New(
  18.                 log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
  19.                 logger.Config{
  20.                         SlowThreshold:             time.Second, // 慢 SQL 阈值
  21.                         LogLevel:                  logger.Info, // 日志级别为info
  22.                         IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
  23.                         Colorful:                  true,        // 彩色打印
  24.                 },
  25.         )
  26.         dsn := "root:123456@tcp(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"
  27.         db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  28.                 Logger: newLogger,
  29.         })
  30.         if err != nil {
  31.                 panic(err) // 如果数据库不存在会报错
  32.         }
  33.         db.AutoMigrate(&Product{}) // 可以加多个
  34.         log.Println("Auto Migration Completed")
  35. }
复制代码
三、模型定义

3.1 模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 ScannerValuer 接口的自定义类型及其指针或别名组成
例如:
  1. type User struct {
  2.   ID           uint
  3.   Name         string
  4.   Email        *string
  5.   Age          uint8
  6.   Birthday     *time.Time
  7.   MemberNumber sql.NullString
  8.   ActivatedAt  sql.NullTime
  9.   CreatedAt    time.Time
  10.   UpdatedAt    time.Time
  11. }
复制代码
3.2 快速增删改查
  1. package main
  2. import (
  3.         "fmt"
  4.         "gorm.io/driver/mysql"
  5.         "gorm.io/gorm"
  6.         "gorm.io/gorm/logger"
  7.         "log"
  8.         "os"
  9.         "time"
  10. )
  11. // UserInfo 用户信息
  12. type UserInfo struct {
  13.         ID     uint
  14.         Name   string
  15.         Gender string
  16.         Hobby  string
  17. }
  18. func main() {
  19.         // 日志配置
  20.         newLogger := logger.New(
  21.                 log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
  22.                 logger.Config{
  23.                         SlowThreshold:             time.Second, // 慢 SQL 阈值
  24.                         LogLevel:                  logger.Info, // 日志级别为info
  25.                         IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
  26.                         Colorful:                  true,        // 彩色打印
  27.                 },
  28.         )
  29.         dsn := "root:123456@tcp(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local"
  30.         db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  31.                 Logger: newLogger,
  32.         })
  33.         if err != nil {
  34.                 panic(err) // 如果数据库不存在会报错
  35.         }
  36.         // 自动迁移
  37.         db.AutoMigrate(&UserInfo{})
  38.         u1 := UserInfo{1, "贾维斯", "男", "篮球"}
  39.         u2 := UserInfo{2, "荆轲", "女", "足球"}
  40.         // 创建记录
  41.         db.Create(&u1)
  42.         db.Create(&u2)
  43.         // 查询
  44.         var u = new(UserInfo)
  45.         db.First(u)
  46.         fmt.Printf("%#v\n", u)
  47.         var uu UserInfo
  48.         db.Find(&uu, "hobby=?", "足球")
  49.         fmt.Printf("%#v\n", uu)
  50.         // 更新
  51.         db.Model(&u).Update("hobby", "双色球")
  52.         // 删除
  53.         db.Delete(&u)
  54. }
复制代码
3.3 约定

GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间
如果您遵循 GORM 的约定,您就可以少写的配置、代码。 如果约定不符合您的实际要求,GORM 允许你配置它们
3.4 gorm.Model

GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt
  1. // gorm.Model 的定义
  2. type Model struct {
  3.   ID        uint           `gorm:"primaryKey"`
  4.   CreatedAt time.Time
  5.   UpdatedAt time.Time
  6.   DeletedAt gorm.DeletedAt `gorm:"index"`
  7. }
复制代码
您可以将它嵌入到您的结构体中,以包含这几个字段,详情请参考 嵌入结构体
四、表模型主键、表名、列名的约定

4.1 主键(Primary Key)

4.1.1 使用 ID 作为主键

默认情况下,GORM 会使用 ID 作为表的主键。
  1. type User struct {
  2.   ID   string // 默认情况下,名为 `ID` 的字段会作为表的主键
  3.   Name string
  4. }
复制代码
你可以通过标签 primaryKey 将其它字段设为主键
  1. // 将 `UUID` 设为主键
  2. type Animal struct {
  3.   ID     int64
  4.   UUID   string `gorm:"primaryKey"`
  5.   Name   string
  6.   Age    int64
  7. }
复制代码
4.1.2 复合主键

通过将多个字段设为主键,以创建复合主键,例如:
  1. type Product struct {
  2.   ID           string `gorm:"primaryKey"`
  3.   LanguageCode string `gorm:"primaryKey"`
  4.   Code         string
  5.   Name         string
  6. }
复制代码
注意:默认情况下,整型 PrioritizedPrimaryField 启用了 AutoIncrement,要禁用它,您需要为整型字段关闭 autoIncrement:
  1. type Product struct {
  2.   CategoryID uint64 `gorm:"primaryKey;autoIncrement:false"`
  3.   TypeID     uint64 `gorm:"primaryKey;autoIncrement:false"`
  4. }
复制代码
4.2 表名(Table Name)

GORM 使用结构体名的 蛇形命名 作为表名。对于结构体 User,根据约定,其表名为 users
4.2.1 TableName

您可以实现 Tabler 接口来更改默认表名,例如:
  1. type Tabler interface {
  2.     TableName() string
  3. }
  4. // TableName 会将 User 的表名重写为 `profiles`
  5. func (User) TableName() string {
  6.   return "profiles"
  7. }
复制代码
注意: TableName 不支持动态变化,它会被缓存下来以便后续使用。想要使用动态表名,你可以使用 Scopes,例如:
  1. func UserTable(user User) func (tx *gorm.DB) *gorm.DB {
  2.   return func (tx *gorm.DB) *gorm.DB {
  3.     if user.Admin {
  4.       return tx.Table("admin_users")
  5.     }
  6.     return tx.Table("users")
  7.   }
  8. }
  9. db.Scopes(UserTable(user)).Create(&user)
复制代码
4.2.2 临时指定表名

您可以使用 Table 方法临时指定表名,例如:
  1. // 根据 User 的字段创建 `deleted_users` 表
  2. db.Table("deleted_users").AutoMigrate(&User{})
  3. // 从另一张表查询数据
  4. var deletedUsers []User
  5. db.Table("deleted_users").Find(&deletedUsers)
  6. // SELECT * FROM deleted_users;
  7. db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
  8. // DELETE FROM deleted_users WHERE name = 'jinzhu';
复制代码
查看 from 子查询 了解如何在 FROM 子句中使用子查询
4.2.3 命名策略

GORM 允许用户通过覆盖默认的命名策略更改默认的命名约定,命名策略被用于构建: TableName、ColumnName、JoinTableName、RelationshipFKName、CheckerName、IndexName。查看 GORM 配置 获取详情
4.3 列名(Column Name)

根据约定,数据表的列名使用的是 struct 字段名的 蛇形命名
  1. type User struct {
  2.   ID        uint      // 列名是 `id`
  3.   Name      string    // 列名是 `name`
  4.   Birthday  time.Time // 列名是 `birthday`
  5.   CreatedAt time.Time // 列名是 `created_at`
  6. }
复制代码
您可以使用 column 标签或 命名策略 来覆盖列名
  1. type Animal struct {
  2.   AnimalID int64     `gorm:"column:beast_id"`         // 将列名设为 `beast_id`
  3.   Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设为 `day_of_the_beast`
  4.   Age      int64     `gorm:"column:age_of_the_beast"` // 将列名设为 `age_of_the_beast`
  5. }
复制代码
4.4 时间戳跟踪

4.4.1 CreatedAt

对于有 CreatedAt 字段的模型,创建记录时,如果该字段值为零值,则将该字段的值设为当前时间
  1. db.Create(&user) // 将 `CreatedAt` 设为当前时间
  2. user2 := User{Name: "jinzhu", CreatedAt: time.Now()}
  3. db.Create(&user2) // user2 的 `CreatedAt` 不会被修改
  4. // 想要修改该值,您可以使用 `Update`
  5. db.Model(&user).Update("CreatedAt", time.Now())
复制代码
你可以通过将 autoCreateTime 标签置为 false 来禁用时间戳追踪,例如:
  1. type User struct {
  2.   CreatedAt time.Time `gorm:"autoCreateTime:false"`
  3. }
复制代码
4.4.2 UpdatedAt

对于有 UpdatedAt 字段的模型,更新记录时,将该字段的值设为当前时间。创建记录时,如果该字段值为零值,则将该字段的值设为当前时间
  1. db.Save(&user) // 将 `UpdatedAt` 设为当前时间
  2. db.Model(&user).Update("name", "jinzhu") // 会将 `UpdatedAt` 设为当前时间
  3. db.Model(&user).UpdateColumn("name", "jinzhu") // `UpdatedAt` 不会被修改
  4. user2 := User{Name: "jinzhu", UpdatedAt: time.Now()}
  5. db.Create(&user2) // 创建记录时,user2 的 `UpdatedAt` 不会被修改
  6. user3 := User{Name: "jinzhu", UpdatedAt: time.Now()}
  7. db.Save(&user3) // 更新时,user3 的 `UpdatedAt` 会修改为当前时间
复制代码
你可以通过将 autoUpdateTime 标签置为 false 来禁用时间戳追踪,例如:
  1. type User struct {
  2.   UpdatedAt time.Time `gorm:"autoUpdateTime:false"`
  3. }
复制代码
4.4.3 DeletedAt
  1. // 只要使用了gorm.Model结构体继承,DeletedAt DeletedAt `gorm:"index"`  字段
  2. // 执行删除是其实是update语句,并没有真正的删除
复制代码
五、模型定义高级选项与标签

5.1 字段级权限控制

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略
注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段
[code]type User struct {  Name string `gorm:"
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表