安装

$ go get -u gorm.io/gorm
$ go get -u gorm.io/driver/mysql

连接

package main

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func main() {
    // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
  dsn := "root:root@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
    _, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(any(err))
    }
}

全局日志

package main

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

func main() {
    // 定义全局日志
    newLogger := logger.New(
        log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
        logger.Config{
            SlowThreshold:             time.Second, // 慢 SQL 阈值
            LogLevel:                  logger.Info, // 日志级别(全部日志
            IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound(记录未找到)错误
            Colorful:                  false,       // 禁用彩色打印
        },
    )
}

模型

模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 ScannerValuer 接口的自定义类型及其指针或别名组成

type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivatedAt  sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}

默认情况使用ID作为主键,使用结构体名的蛇形复数作为表明,字段名的蛇形作为列名,CreatedAtUpdatedAtDeletedAt作为时间

// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

模型嵌套

包含匿名结构体字段

type User struct {
  gorm.Model
  Name string
}
// 等效于
type User struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
  Name string
}

对于正常的结构体字段,可以通过标签 embedded 将其嵌入,使用标签 embeddedPrefix 来为 db 中的字段名添加前缀

type Blog struct {
  ID      int
  Author  Author `gorm:"embedded;embeddedPrefix:author_"`
  Upvotes int32
}
// 等效于
type Blog struct {
  ID          int64
  AuthorName  string
  AuthorEmail string
  Upvotes     int32
}

跟踪时间

使用 CreatedAtUpdatedAt 追踪创建/更新时间定义后模型会自动填充当前时间,使用不同名称的字段,可以配置 autoCreateTimeautoUpdateTime 标签

如果想要保存 UNIX(毫/纳)秒时间戳,而不是 time,只需将 time.Time 修改为 int

type User struct {
  CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
  UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
  Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间
  Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
  Created   int64 `gorm:"autoCreateTime"`      // 使用时间戳秒数填充创建时间
}

字段标签

标签名说明
column指定 db 列名
type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime
size定义列数据类型的大小或长度,例如 size: 256
primaryKey将列定义为主键
unique将列定义为唯一键
default定义列的默认值
precision指定列的精度
scale指定列大小
not null指定列为 NOT NULL
autoIncrement指定列为自动增长
autoIncrementIncrement自动步长,控制连续记录之间的间隔
embedded嵌套字段
embeddedPrefix嵌入字段的列名前缀
autoCreateTime创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndexindex 相同,但创建的是唯一索引
check创建检查约束,例如 check:age > 13,查看 约束 获取详情
<-设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
->设置字段读的权限,->:false 无读权限
-忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限
comment迁移时为字段添加注释

索引

通过index,uniqueIndex创建索引, 通过classtypewherecommentexpressionsortcollateoption设置索引参数

type User struct {
    Name  string `gorm:"index"`
    Name2 string `gorm:"index:idx_name,unique"`
    Name3 string `gorm:"index:,sort:desc,collate:utf8,type:btree,length:10"`
    Name4 string `gorm:"uniqueIndex"`
    Age   int64  `gorm:"index:,class:FULLTEXT,comment:hello"`
    Age2  int64  `gorm:"index:,expression:ABS(age)"`
}

唯一索引

通过uniqueIndex指定唯一索引或者使用index:,unique

type User struct {
    Name1 string `gorm:"uniqueIndex"`
    Name2 string `gorm:"uniqueIndex:idx_name,sort:desc"`
}

复合索引

两个字段使用同一个索引名将创建复合索引

使用priority 指定顺序,默认优先级值是 10,如果优先级值相同,则顺序取决于模型结构体字段的顺序

type User struct {
    Name   string `gorm:"index:idx_member,priority:2"`
    Number string `gorm:"index:idx_member,priority:1"`
}

常见问题

零值

只有 Updates 方法是不会更新空值的字段, 而 update 方法可以更新,要想解决需要在结构体设置 sql.NullStringint 类型字段使用 sql.NullInt64, 也可以使用指针类型

指针

package main

import (
    "database/sql"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "time"
)

// User 定义表结构
type User struct {
    ID           uint
    Name         string
    Email        *string
    Age          uint8
    Birthday     *time.Time
    MemberNumber sql.NullString
    ActivatedAt  sql.NullTime
    CreatedAt    time.Time
    UpdatedAt    time.Time
}

func main() {
    // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
    dsn := "root@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(any(err))
    }

    // 自动迁移
    db.AutoMigrate(&User{})

    // 创建
    db.Create(&User{Name: "Gyi"})

    // 更新
    empty := ""
    db.Model(&User{ID: 1}).Updates(&User{
        Name:  "",     // 不会更新
        Email: &empty, // 会更新, 因为字段类型为指针
    })
}

SQL.NULL

package main

import (
    "database/sql"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

// Product 定义表结构
type Product struct {
    gorm.Model
    Code  sql.NullString
    Price uint
}

func main() {
    // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
    dsn := "root@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        panic(any(err))
    }

    // 读取
    var product Product
    db.First(&product, 1) // 根据整型主键查找

    // Update - 更新多个字段
    db.Model(&product).Updates(Product{Price: 200, Code: sql.NullString{ // 更新D43
        String: "D43",
        Valid:  true,
    }})

    db.Model(&product).Updates(Product{Price: 200, Code: sql.NullString{ // 更新成0值
        Valid: true,
    }})
}

取消外键

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true, //不带表名
        },
        DisableForeignKeyConstraintWhenMigrating: true, // 禁用外键关联
})

自定义名称

表名

GORM 使用结构体名的 蛇形命名 作为表名。对于结构体 User,根据约定,其表名为 users,通过实现Tabler 接口来更改默认表名

type Tabler interface {
    TableName() string
}

// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
  return "profiles"
}

列名

字段名默认使用结构体中字段名的蛇形进行命名,可使用 column 标签来对列名进行定义

type Animal struct {
  AnimalID int64     `gorm:"column:beast_id"`         // 将列名设为 `beast_id`
  Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设为 `day_of_the_beast`
  Age      int64     `gorm:"column:age_of_the_beast"` // 将列名设为 `age_of_the_beast`
}
Last modification:July 23rd, 2024 at 11:14 am