钩子函数
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。
如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。
钩子方法的函数签名应该是 func(*gorm.DB) error
创建 HOOK
在插入一条记录的时候,我希望做一点事情。
比如在插入数据前,我希望将 student 的默认赋值成 test@qq.com ,我们可以定义一个该结构体的方法,它就会在插入数据前生效。
func (user *Student) BeforeCreate(tx *gorm.DB) (err error) {
email := "text@qq.com"
user.Email = &email
return nil
}
HOOK 接口
HOOK 有不少的接口,以满足在各种情况下的情况。所有的 HOOK 的用法和上面的例子基本类似。
- 在创建时可用的 HOOK
// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务
一共有四个,分别是创建之前的可使用的 BeforeSave、BeforeCreate ;创建之后可使用的 AfterCreate、AfterSave
- 在更新时可用的 HOOK
// 开始事务
BeforeSave
BeforeUpdate
// 关联前的 save
// 更新 db
// 关联后的 save
AfterUpdate
AfterSave
// 提交或回滚事务
同样有有四个,分别是更新之前可使用的 BeforeSave、BeforeUpdate ;更新之后可使用的 AfterUpdate、AfterSave
- 在删除时可用的 HOOK
// 开始事务
BeforeDelete
// 删除 db 中的数据
AfterDelete
// 提交或回滚事务
只有两个,分别是删除之前可使用的 BeforeDelete ;删除之后可使用的 AfterDelete
- 在查询时可用的 HOOK
// 从 db 中加载数据
// Preloading (eager loading)
AfterFind
只有一个,是查询之后可使用的 AfterFind
具体的实例在 GORM 文档的 HOOK 中有详细介绍。
高级查询操作
使用 Where 查询
使用 Where 查询相当于使用 SQL 中的 Where 来进行查询。我们将查询到的数据放入 studentList 的切片中。
查询用户名为李元芳的数据
DB.Where("name = ?", "李元芳").Find(&studentList) //第一种写法
DB.Find(&studentList, "name = ?", "李元芳") //第二种写法
上述的代码等效的 SQL 语句为:SELECT * FROM ` student1` WHERE name = '李元芳'
查询用户名不是李元芳的数据
DB.Where("name <> ?", "李元芳").Find(&studentList) //第一种写法
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE name <> '李元芳'
DB.Where("not name = ?", "李元芳").Find(&studentList) //第二种写法
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE not name = '李元芳'
DB.Not("name = ?", "李元芳").Find(&studentList) //第三种写法
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE not name = '李元芳'
这些查询方式十分的灵活,有许多种写法...
查询用户名为李元芳、如燕的数据
DB.Where("name in (?)", []string{"李元芳", "如燕"}).Find(&studentList) //第一种写法
DB.Find(&studentList, "name in (?)", []string{"李元芳", "如燕"}) //第二种写法
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE name in ('李元芳','如燕')
查询姓李的
模糊查询
DB.Where("name like ?", "李%").Find(&studentList)
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE name like '李%'
如果是查询姓李的两个字的名字,则 李% 应该修改为 李_ ,如果是三个字的名字就是两个下划线 李__
查询年龄大于 23 且是使用 qq 邮箱的
DB.Where("age > ? and email like ?", "23", "%@qq.com").Find(&studentList)
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE age > '23' and email like '%@qq. com'
如果是多个与条件,使用 and 连接。
上述代码还可以写成两个 Where 连接使用,同样是表示与逻辑:
DB.Where("age > ?", "23").Where("email like ?", "%@qq.com").Find(&studentList)
查询是 qq 邮箱的,或者是女
DB.Where("email like ? or gender like ?", "%@qq.com", false).Find(&studentList)
上述的代码等效的 SQL 语句为:SELECT * FROM `student1` WHERE email like '%@qq. com' or gender like false
如果是多个或条件,使用 or 连接。
同样的,上述代码还可以在后面加上 Or,同样是表示或·逻辑:
DB.Where("email like ?", "%@qq.com").Or("gender like ?", false).Find(&studentList)
使用结构体查询
使用结构体查询,在结构体中赋值上需要查询的条件即可。
结构体中赋值的成员就相当于是查询的条件,这些条件是与逻辑。
结构体会过滤零值,例如查找名字为李元芳并且年龄为 0 的数据:
DB.Where(&Student1{Name: "李元芳", Age: 0}).Find(&studentList)
此时的 SQL 语句并没有带上年龄:
当把年龄修改成 32 的时候,就会带上年龄:
使用 map 查询
使用 map 查询和结构体类似,map 查询不会过滤零值。
同样的,查询名字为李元芳并且年龄为 0 的数据:
DB.Where(map[string]any{"Name": "李元芳", "Age": 0}).Find(&studentList)
此时没有过滤零值:
Not 条件
Not 函数是筛选掉你选择的条件。
例如我想筛选掉名字为李元芳的数据,也就是查询所有名字不为李元芳的数据:
DB.Not("name = ?", "李元芳").Find(&studentList)
我们在查询用户名不是李元芳的数据的时候就已经展示过了。
Or 条件
就是或逻辑。与 Where 中的 Or 其实是等价的:
DB.Or("email like ?", "%@qq.com").Or("gender like ?", false).Find(&studentList)
Select 选择字段
当我们只需要查询某个字段的所有的数据的时候,可以选择使用 Select 函数。
因为 Find 函数是查询所有字段的所有数据,加上 Select 会更加高效。
例如我只需要表中的所有名字,而不需要其他的数据。
- 当使用 Find 函数
DB.Find(&studentList)
它会查询所有数据,这样比较耗时
- 加上 Select 函数之后
DB.Select("name").Find(&studentList)
这样就只会返回名字数据,其它为零值,更加高效和具有目的性
如果需要选择多个字段,使用逗号隔开继续添加字段名或者使用字符串列表:
DB.Select("name", "age").Find(&studentList) //第一种写法
DB.Select([]string{"name", "age"}).Find(&studentList) //第二种写法
因为有时我们只需要其中的一个字段或几个字段的数据,使用整个模型去存储比较浪费空间,我们可以使用 Scan 函数,将获取到的字段数据存入另一个比较小的结构体。
type User struct {
Name string
Age int
}
var userList []User
DB.Model(Student1{}).Select("name", "age").Scan(&userList)
我们定义了一个只存储名字和年龄的结构体,然后需要只用 Model 函数指定要操作的数据库模型或表。使用 Select 选择要查询的字段,最后使用 Scan 将其扫描进 userList 中。
完整代码地址:hdheid/GormStudy