结合项目开发进行 GORM 介绍 | 青训营

91 阅读5分钟

GORM 是一个使用 Go 语言编写的 ORM 框架。本文结合极简抖音项目开发对 GORM 进行简介。

下载安装

// 安装MySQL依赖
go get -u gorm.io/driver/mysql
// 安装Gorm依赖
go get -u gorm.io/gorm

连接 MySQL

GORM 不止可以连接 MySQL 这一类数据库,不过以本项目为例,讲解其用法。

func main() {
    // 连接数据库
    dsn := "root:123456@tcp(127.0.0.1:3306)/douyin?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        return err
    }
    
    // Do something.
}

其中的 dsn 为数据库地址, 其格式为 user:password@tcp(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local, 其中 user 为数据库账号,本机下多为 root, password 即数据库密码,localhost 是数据库的地址,多为 127.0.0.1:3360. 后面的 dbname 则为数据库的名称。

自动迁移

为了能在数据库中存入对应结构,需要建立对应的数据表。

// 如果要指定数据库列名,可以采用 tag `gorm:"column:name"` 的形式给定列名
type User struct {
    UserId          int64  `gorm:"column:user_id;primary_key;NOT NULL"`
    UserName        string `gorm:"column:user_name;type:varchar(100)"`
    Password        string `gorm:"column:password;type:varchar(100)"`
}
​
func main() {
    ...
    // 自动迁移,在数据库中建立 users 的表
    db.AutoMigrate(&User{})
    ...
}

通过对结构体的 tag 设置我们可以设置结构体写入数据库时的列名,大小限制,非空标识等等,而自动迁移则保证了对应数据表能在正常服务启动前已经被初始化建立。

特别的,需要指定主键,一般为 ID 所在的列。

模型创建

使用NewRecord()查询主键是否存在,主键为空使用Create()创建记录:

user := User{"111", "Harry", "123456"}
​
db.NewRecord(user) // 主键为空则返回 `true`
db.Create(&user)   // 创建 user,调用地址

注意:字段的零值,如 0, "", false 等都不会存入数据库内,如果想要存储需要使用指针或 Scanner/Valuer 接口方式。指针使用案例:

type User struct {
    UserId          int64  `gorm:"column:user_id;primary_key;NOT NULL"`
    UserName        *string `gorm:"column:user_name;type:varchar(100)"`
    Password        string `gorm:"column:password;type:varchar(100)"`
}
user := User{"111", "", "123456"}
db.Creat(&user) // 此时数据库中对应的 user_name 即为空字符串

查询

查询时最重要的操作之一,方式有

// 根据主键查询第一条记录
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;// 随机获取一条记录
db.Take(&user)
//// SELECT * FROM users LIMIT 1;// 根据主键查询最后一条记录
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;// 查询所有的记录
db.Find(&users)
//// SELECT * FROM users;// 查询指定的某条记录(仅当主键为整型时可用)
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;

特别注意,如果采用 First() 方式找不到的话会返回 error,如果不想要这样的返回应该采用 Find() 进行查询。

Where 条件

Where 条件可以给查找的键增加条件,如

db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

其可以把对应的判断语句转为相应的 MySOL 查询方式,也可以同时满足多个条件或者数据库中的逻辑判断,可以理解为其就是填充了 sql 语句中 WHERE 后的一段

Not 条件

db.Not([]int64{1,2,3}).First(&user)
//// SELECT * FROM users WHERE id NOT IN (1,2,3);

Or 条件

db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

此时相当于把前后两句用 OR 连接以后再给 WHERE 做判断。

Select 条件

选择想要检索的字段,默认选择所有字段。

db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;

对应只会找 name 和 age 两列出来。

Order 排序

指定数据库中检索出记录的顺序,其中 desc 为从大到小排列,不写后者 esc 为从小到大排列。

db.Order("age desc, name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

// 多字段排序
db.Order("age desc").Order("name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

从上面也可以看出来,其实这个搜索方法的顺序会影响结果的,比如一个本次项目的踩坑处:

db.Model(&Video{}).Select("videoid").Order("created_time desc").Find(&videos)

db.Model(&Video{}).Order("created_time desc").Select("videoid").Find(&videos)

原本我是想根据 created_time 排序,再抽取出其中的 videoid 这一列,上面两句中应该第二句才对。第一句会先执行 Select 方法,抽取出了 videoid 这一列,再去排序的话,由于 created_time 已经没有了,对应的排序结果也就不正确了。

Limit 数量

指定从数据库检索出的最大记录数。

db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;

Count 计数

获取该 model 能获取记录的总数

db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
//// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)

注意 Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT,但如果里面使用了 count 时不会覆盖。

更新

更新所有字段 -- Save()

使用 Save() 会更新该对象所有字段。

type User {
    Name string
    Age  int
}
db.First(&user)

user.Name = "哈利"
user.Age = 99
db.Save(&user)

更新修改字段 -- Update(单个属性) / Updates(多个属性)

// 更新单个属性,如果它有变化
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

// 根据给定的条件更新单个属性
db.Model(&user).Where("active = ?", true).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

删除

使用 Delete 方法,删除时请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。

db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
//// DELETE from emails where email LIKE "%jinzhu%";

db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
//// DELETE from emails where email LIKE "%jinzhu%";

· 软删除

当一个 model 中调用了 gorm model, 即有 DeletedAt 字段,自动获得软删除功能,即此时直接调用 Delete 方法不会物理删除,只会记录一个删除时间,再次搜索时只要删除时间非空就认为程序中已经被删除。