Gorm实践 | 青训营笔记

68 阅读5分钟

前置准备

为了玩一下gorm,我们首先需要安装gorm、mysql,这里不展开说明安装步骤。

在安装完mysql后,需要创建一个允许我们操作的数据库hello

数据库模型设计

使用gorm的第一步是构造数据库中的表所对应的结构体。我们首先简单的设计一个User对象。

type User struct {
    gorm.Model
    Name     string
    Email    string
    Age      uint8
    Birthday *time.Time
}

其中Model是gorm提供的结构体,其具体设计如下。通过将Model作为匿名结构体嵌入User,可以让User获取到Model中的四个字段,这样我们的User看起来会更加简洁一点。

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

事实上我们可以看到gorm允许在字段后面添加注释,gorm会自动解析注释并为字段添加对应的配置。例如 gorm:"primaryKey"注释会将对应字段配置为主键。

连接数据库

ok,为了后面的自动建表与增删改查等操作,我们先需要连接数据库,连接的代码如下:

核心操作包括:1、构建dsn 2、使用gorm.Open函数连接数据库获取连接。

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log/slog"
	"time"
)

func main() {
	dsn := "root:123456@tcp(127.0.0.1:3306)/hello?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn))
	if err != nil {
		slog.Error("db connect error", err)
	}
	slog.Info("db connect success")
	fmt.Println(db.Name())
}

运行后可以正常连接。

自动建表

在完成了结构体的设计后,我们有两个方法去创建结构体对应的数据库表,一个是自己手动在mysql用sql创表,另外一个就是使用gorm的迁移功能自动生成表。

现在看看如何使用gorm创建User表。

首先确认数据库hello中还没有表存在。

ok, 我们运行下面的语句就能通过AutoMigrate自动建表了。

	e := db.AutoMigrate(User{})
	if e != nil {
		fmt.Println(e)
	}

检查成果

增删改查

Create

gorm提供了很简单的数据插入功能,插入User对象对应数据的代码如下。

	user := User{
		Name:  "test",
		Email: "test@123.com",
		Age:   18,
	}
	db.Create(&user)

Read

我们常用的sql语句有 “Select Name from Users where age = 18;”

对应到gorm中的操作就是:

user := &User{}
db.Model(&User{}).Select("name").Where("age = ?", 18).Find(&user)
fmt.Println(user.Name)

Upate

更新操作一般使用update函数实现,为了实现对数据有条件的修改,可以通过设置User的主键值以及Where语句限定修改范围

首先通过User主键限定搜索范围

user := &User{}
// user主键为1
user.ID = 1	
// 检索时搜索id为1的数据
db.Model(&user).Update("name", "test2") 
fmt.Println(user.Name)

更新效果如下

然后通过Where限定搜索范围

db.Model(&User{}).Where("name = ?", "test2").Update("email", "test2@123.com")

更新效果如下

Delete

在gorm中,删除时必须带有数据行的主键,否则会报错。删除使用Delete函数,使用方法与Update类似。

user := &User{}
user.ID = 2
db.Delete(&user)

关联

关联指的是表与表之间的关系,上面的操作都是针对单个表的。所以我们需要学习两件事情:1、在gorm中标语表的关联有多少种?2、在gorm中标语表的关联如何创建?3、如何实现联合查询?

Gorm中的关联类型

一对一

一对一关系是指,一个表的记录对应到另一个表的唯一一个记录。

拥有(has one)

关联has one与另一个模型建立一对一连接,但语义(和后果)略有不同。此关联表示一个模型的每个实例都包含或拥有另一个模型的一个实例。

下面的例子中,User拥有一个CreditCard,creditcard 有且只能被一个 user 拥有。

// User has one CreditCard, UserID is the foreign key
type User struct {
  gorm.Model
    // 这里只储存了一个结构体,所以是只能拥有一个
  CreditCard CreditCard
}

type CreditCard struct {
  gorm.Model
  Number string
    // 通过id指向唯一一个User
  UserID uint
}

一对多

属于(belongs to)

关联<font style="color:rgb(77, 77, 76);background-color:rgb(238, 238, 238);">belongs to</font>与另一个模型建立一对一的连接,使得声明模型的每个实例都“属于”另一个模型的一个实例。

下面的User属于公司Company,一个公司可以有多个User,所以是一对多的关系。

具体的实现如下:

// `User` belongs to `Company`, `CompanyID` is the foreign key
type User struct {
  gorm.Model
  Name      string
    // 外键,也即是Company的主键
    // 外键默认使用 外键所属结构体的类型+其主键字段 命名
    // 所以下面是 Company + ID
    // 当然也可以使用   `gorm:"foreignKey:CompanyRefer"` 注释指定外键
  CompanyID int
    // 有趣的是为什么这里嵌入了一个Company结构体?
  Company   Company
}

type Company struct {
  ID   int
  Name string
}
有多个(has many)

关联has many与另一个模型建立一对多连接,与 不同has one,所有者可以拥有零个或多个模型实例。例如,如果您的应用程序包括用户和信用卡,并且每个用户可以拥有多张信用卡。

// User has many CreditCards, UserID is the foreign key
type User struct {
  gorm.Model
    // 用切片储存,所以可对应多个
  CreditCards []CreditCard
}

type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}

多对多

多对多在两个模型之间添加连接表。例如,如果您的应用程序包括用户和语言,并且一个用户可以说多种语言,并且许多用户可以说一种指定的语言。

// User has and belongs to many languages, `user_languages` is the join table
type User struct {
  gorm.Model
    // 通过注释正生成了连接表
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}
我们也可以在Language种添加结构体列表,以允许从language的方向进行联合查询
// User has and belongs to many languages, use `user_languages` as join table
type User struct {
  gorm.Model
  Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
  Users []*User `gorm:"many2many:user_languages;"`
}

所以preload到底属于哪种?join又是属于哪种?

官方文档明说了join属于左连接。