需要注意,引入包: gorm.io/gorm 而不是 github.com/jinzhu/gorm

连接数据库

自动生成表

var models []any

// 方式一: 会自动同步添加、更新字段,不会删除
db.AutoMigrate(...models)

// 方式二
for _, value := range models {
	hasTable := db.HasTable(value)
	if !hasTable {
		db.CreateTable(value)
	}

	refType := reflect.TypeOf(value)
	if refType.Kind() == reflect.Pointer { // 引用类型
		refType = refType.Elem() // 取引用实体
	}
	println("reflact.Type", refType.Name(), refType.PkgPath(), refType.Kind())
}

设置日志

db.LogMode(true)

模型定义

重置表名

func (Model) TableName() string {
    return "new_table_name"
}

数据插入前置操作

func (Model) BeforeCreate(db *gorm.DB) error {
	// 插入数据前调用
    return nil
}

查询数据

  • Take
  • Find
  • Scan
  • Where
  • Distinct
  • Not
  • Order
  • Group
  • Limit
  • Offset

插入数据

更新数据

  • Update
  • Set

高级查询

一对多

表模型结构

type User struct {
	ID       uint      `gorm:"size:4"`
	Name     string    `gorm:"size:8"`
	Articles []Article // 用户拥有的文章
}

type Article struct {
	ID     uint   `gorm:"size:4"`
	Title  string `gorm:"size:16"`
	UserID uint   `gorm:"size:4"` // 属于, 注意这里的类型要和User.ID的类型保持一致
	User   User
}

默认外键名就是关联表明+ID名

后置添加关联

给用户绑定文章

var user User
DB.Take(&user, 1)
var article Article
DB.Take(&article,  1)
DB.Model(&user).Association("Articles").Append(&article)

给文章绑定用户

var user User
DB.Take(&user, 1)
var article Article
DB.Take(&article,  1)
DB.Model(&article).Association("User").Append(&user)

预加载

嵌套预加载

var user User
DB.Debug().Preload("Articles.User").Take(&user)

带条件的预加载

var user User
DB.Debug().Preload("Articles", "id = ?", 1).Take(&user)

自定义预加载

var user User
DB.Debug().Preload("Articles", func(db *gorm.DB) *gorm.DB {
    return db.Where("id in ?", []int{1, 2})
}).Take(&user)

删除

级联删除

删除用户,与用户关联的文章也会删除

var user User
DB.Take(&user, 1)
DB.Select("Articles").Delete(&user)

清除外键关系

删除用户,与将用户关联的文章外键置为null

var user User
DB.Preload("Articles").Take(&user, 1)
DB.Model(&user).Association("Articles").Delete(&user.Articles)

一对一

一对一很多地方跟一对多操作是相同或类似的

表模型结构

type User struct {
	ID       uint      `gorm:"size:4"`
	Name     string    `gorm:"size:8"`
	UserInfo UserInfo  // 通过UserInfo可以拿到用户详情信息
    UserPwd  UserPwd   // 用户密码表
}

type UserInfo struct {
    ID       uint
	UserID   uint      `gorm:"size:4"`
    User     *User
	CardNo   string
}

多对多

表模型结构

type Tag struct {
	ID       uint
	Name     string
	Articles []Article `gorm:"many2many:article_tags;"`
}

type Article struct {
	ID    uint
	Title string
	Tags  []Tag `gorm:"many2many:article_tags;"`
}

添加

DB.Create(&Article{
    Title: "Gorm基础",
    Tags: []Tag{
        {Name: "gorm"},
        {Name: "golang"},
    },
})

更新

移除文章标签

var article Article
DB.Preload("Tags").Take(&article, 1)
DB.Model(&article).Association("Tags").Delete(&article.Tags)

更新文章标签

var tags []Tag
DB.Find(&tags, []int{1,2,3})	// 实际可能是传入tag ids

var article Article
DB.Preload("Tags").Take(&article, 1)
DB.Model(&article).Association("Tags").Replace(&tags)

删除

var article Article
DB.Preload("Tags").Take(&article, 1)
DB.Model(&article).Association("Tags").Delete(article.Tags)

自定义连接表

需要添加多余字段时会用到

type ArticleTags struct {
	ArticleId uint      `gorm:"primaryKey"`
	TagId     uint      `gorm:"primaryKey"`
	CreatedAt time.Time `json:"created_at"`
}

// init 
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTags{})
DB.SetupJoinTable(&Tag{}, "Articles", &ArticleTags{})

添加文章,关联已有Tag

var tags []Tag
DB.Find(tags, uint[]{1, 2})

DB.Create(&Article{
    Title: "gorm基础",
    Tags: tags,
}) // 这里只有在使用SetupJoinTable后才会更新,否则为新增

自定义连接表主键

type ArticleModel struct {
    ID		uint
    Title	string
    Tags	[]TagModel `gorm:"many2many:article_tags;joinForeignKey:ArticleID;JoinReferences:TagID"`
}

type TagModel struct {
    ID			uint
    Name		string
    Articles	[]ArticleModel	`gorm:"many2many:article_tags;joinForeignKey:TagID;JoinReferences:ArticleID"`
}

type ArticleTagModel struct {
	ArticleId uint      `gorm:"primaryKey"`	// 默认应该是 表名 + 字段名 = ArticleModelId
	TagId     uint      `gorm:"primaryKey"`	// 默认应该是 TagModelId
	CreatedAt time.Time `json:"created_at"`
}


func init() {
    DB.SetupJoinTable(&ArticleModel{}, "Tags", &ArticleTagModel{})
    DB.SetupJoinTable(&TagModel{}, "Articles", &ArticleTagModel{})
}

查询多对多连接表

type UserMode struct {
    ID			uint
    Name		string
    Collects	[]ArticleModel `gorm:"many2many:user_collect_model;joinForeignKey:UserID;JoinReferences:ArticleID"`
}

type ArticleModel struct {
    ID		uint
    Title	string
    // 这里也可以反向引用,根据文章查找哪些用户收藏了
}

type UserCollectModel struct {
	UserId    		uint      		`gorm:"primaryKey"`	// 默认应该是 UserModelId
    UserModel		UserModel		`gorm:"foreignKey:UserId"`
	ArticleId 		uint      		`gorm:"primaryKey"`	// 默认应该是 表名 + 字段名 = ArticleModelId
    ArticleModel	ArticleModel	`gorm:"foreignKey:ArticleId"`
	CreatedAt 		time.Time 		`json:"created_at"`
}

func init() {
    DB.SetupJoinTable(&UserModel{}, "Collects", &UserCollectModel{})
    err := DB.AutoMigrate(&UserModel{}, &ArticleModel{}, &UserCollectModel{})
    fmt.Println(err)
}

根据用户查文章

var userCoolects []UserCollect
DB.Preload("ArticleModel").Preload("UserModel").Find(&userCoolects, 1)

自定义数据类型

JSON

type Info struct {
    Status	string	`json:"status"`
    Addr	string	`json::"addr"`
    Age		int		`json:"age"`
}

func (i *Info) Scan(value any) error {
    bytes, ok := value.([]byte)
    if !ok {
        return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
    }
    err := json.Unmarshal(byte, i)
    return err
}

func (i Info) Value() (driver.Value, error) {
    return json.Marshal(i)
}

使用

type AutoModel struct {
    ID		uint
    Name	string
    Info	Info	`gorm:"type:string"`
}

枚举

type Status int

func (s Status) MarshalJSON() ([]byte, error) {
    var result string
    switch s {
        case Status_Running:
        	result = "Running"
        case Status_Offline:
        	result = "Offline"
        case Status_Except:
        	result = "Except"
    }
    return json.Marshal(result)
}

const (
    Status_Running Status = 0
    Status_Offline Status = 1
    Status_Except Status = 2
)

使用

type Host {
    ID		uint
    Status	Status	`gorm:"size:8" json:"status"`
    IP		string	`json:"ip"`
}

var host Host
DB.Take(&host, 1)

str, _ := json.Marshal(&host)
fmt.Println(string(str))