Hook(钩子)
和我们在gin
框架中讲解的Hook函数一样,我们也可以在定义Hook
结构体,完成一些操作,相关接口声明如下:
type CreateUser interface { //创建对象时使用的Hook
BeforeCreate() error
BeforeSave() error
AfterCreate() error
AfterSave() error
}
type UpdateUser interface {
BeforeUpdate() error
BeforeSave() error
AfterUpdate() error
AfterSave() error
}
type DeleteUser interface {
BeforeDelete() error
AfterDelete() error
}
type FindUser interface {
AfterFind() error
}
我们可以根据自己的需求来订制我们所需要的Hook函数,示例:
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()
if !u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
if u.ID == 1 {
tx.Model(u).Update("role", "admin")
}
return
}
注意:
- Hook函数在执行过程的执行时间有规定的时间,以创建对象的
Hook
为例:
// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务
具体可以参考官方文档:
Hook
- 在 GORM 中保存、删除操作会默认运行在事务上, 因此在事务完成之前该事务中所作的更改是不可见的,如果
Hook
返回了任何错误,则修改将被回滚。
高级查询
初始化相关表
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Employee struct {
ID uint `gorm:"size:3"`
Name string `gorm:"size:8"`
Age int `gorm:"size:3"`
Sex bool `gorm:"size:3"`
Email *string `gorm:"size:32"`
}
var myDB *gorm.DB
func init() {
//连接数据库
user := "root"
password := "ba161754"
dbname := "gorm"
ip := "127.0.0.1"
port := "3306"
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("数据库连接失败,err:", err)
return
}
fmt.Println("数据库连接成功")
myDB = db
//初始化日志
var mysqlLogger logger.Interface
mysqlLogger = logger.Default.LogMode(logger.Info) //设置日志打印级别
mysqlLogger = logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // (日志输出的目标,前缀和日志包含的内容)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用彩色打印
},
)
myDB.Logger = mysqlLogger
//创建所要使用的单表
err = myDB.AutoMigrate(&Employee{})
if err != nil {
fmt.Println("创建表失败,err:", err)
return
}
//插入测试数据
employeeList := []Employee{
{ID: 1, Name: "李元芳", Age: 32, Email: PtrString("lyf@yf.com"), Sex: true},
{ID: 2, Name: "张武", Age: 18, Email: PtrString("zhangwu@lly.cn"), Sex: true},
{ID: 3, Name: "枫枫", Age: 23, Email: PtrString("ff@yahoo.com"), Sex: true},
{ID: 4, Name: "刘大", Age: 54, Email: PtrString("liuda@qq.com"), Sex: true},
{ID: 5, Name: "李武", Age: 23, Email: PtrString("liwu@lly.cn"), Sex: true},
{ID: 6, Name: "李琦", Age: 14, Email: PtrString("liqi@lly.cn"), Sex: false},
{ID: 7, Name: "晓梅", Age: 25, Email: PtrString("xiaomeo@sl.com"), Sex: false},
{ID: 8, Name: "如燕", Age: 26, Email: PtrString("ruyan@yf.com"), Sex: false},
{ID: 9, Name: "魔灵", Age: 21, Email: PtrString("moling@sl.com"), Sex: true},
}
myDB.Create(&employeeList)
}
func PtrString(email string) *string {
return &email
}
func main() {
}
Where查询
- 简单示例:
var employee Employee
//Where
myDB.Where("name like ?", "李%").Find(&employee) //查询姓李的
fmt.Println(employee)
Not
条件
myDB.Not("name like ?", "李%").Find(&employee) //查询第一条不是姓李的
fmt.Println(employee)
Or
条件
var employeeList []Employee
myDB.Not("name like ?", "李%").Or("age>20").Find(&employeeList) //用Where表示and
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
And
条件
employeeList=[]Employee{}
myDB.Not("name like ?", "李%").Where("age>20").Find(&employeeList) //用Where表示and
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
select选择字段
- 简单示例
employeeList := []Employee{}
myDB.Select("name", "age").Find(&employeeList)
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
Scan
函数
我们可以用Scan
函数将搜索结果导入带新的结构体中
type Employee1 struct {
Name string
Age int
}
//select
employeeList := []Employee{}
employeeList1 := []Employee1{}
myDB.Select("name", "age").Find(&employeeList).Scan(&employeeList1)
for _, value := range employeeList1 {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
输出为:
排序
//排序
employeeList := []Employee{}
myDB.Order("age desc").Find(&employeeList)
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
分页查询
//分页
employeeList := []Employee{}
myDB.Limit(4).Offset(0).Order("age desc").Find(&employeeList) //Limit:每页限定记录数,offset:偏移量
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
去重
//去重
var agelist []int
myDB.Table("employees").Select("distinct age").Find(&agelist)
for _, value := range agelist {
fmt.Println(value)
}
分组查询
//分组查询
var ageList []int
// 查询男生的个数和女生的个数
myDB.Table("employees").Select("count(id)").Group("Sex").Scan(&ageList)
fmt.Println(ageList)
执行原生sql
//执行原生sql
type SexGroup struct {
Count int `gorm:"column:count(id)"`
Sex bool
Name string `gorm:"column:group_concat(name)"`
}
var sexlist []SexGroup
myDB.Raw("select count(id) ,sex,group_concat(name) from employees group by sex").Scan(&sexlist)
for _, value := range sexlist {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
}
子查询
//子查询
//select * from students where age > (select avg(age) from students); 原生sql
myDB.Where("age > (?)", myDB.Model(&Employee{}).Select("avg(age)")).Find(&employee)
fmt.Println(employee)
查询调用
我们可以在model
层写一些通用的查询方法,让外界直接来调用:
func Age23(db *gorm.DB) *gorm.DB {
return db.Where("age>?", 23)
}
myDB.Scopes(Age23).Find(&employee)
fmt.Println(employee)
完整代码
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Employee struct {
ID uint `gorm:"size:3"`
Name string `gorm:"size:8"`
Age int `gorm:"size:3"`
Sex bool `gorm:"size:3"`
Email *string `gorm:"size:32"`
}
type Employee1 struct {
Name string
Age int
}
var myDB *gorm.DB
func init() {
//连接数据库
user := "root"
password := "ba161754"
dbname := "gorm"
ip := "127.0.0.1"
port := "3306"
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, dbname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("数据库连接失败,err:", err)
return
}
fmt.Println("数据库连接成功")
myDB = db
//初始化日志
var mysqlLogger logger.Interface
mysqlLogger = logger.Default.LogMode(logger.Info) //设置日志打印级别
mysqlLogger = logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // (日志输出的目标,前缀和日志包含的内容)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 使用彩色打印
},
)
myDB.Logger = mysqlLogger
//创建所要使用的单表
err = myDB.AutoMigrate(&Employee{})
if err != nil {
fmt.Println("创建表失败,err:", err)
return
}
//插入测试数据
employeeList := []Employee{
{ID: 1, Name: "李元芳", Age: 32, Email: PtrString("lyf@yf.com"), Sex: true},
{ID: 2, Name: "张武", Age: 18, Email: PtrString("zhangwu@lly.cn"), Sex: true},
{ID: 3, Name: "枫枫", Age: 23, Email: PtrString("ff@yahoo.com"), Sex: true},
{ID: 4, Name: "刘大", Age: 54, Email: PtrString("liuda@qq.com"), Sex: true},
{ID: 5, Name: "李武", Age: 23, Email: PtrString("liwu@lly.cn"), Sex: true},
{ID: 6, Name: "李琦", Age: 14, Email: PtrString("liqi@lly.cn"), Sex: false},
{ID: 7, Name: "晓梅", Age: 25, Email: PtrString("xiaomeo@sl.com"), Sex: false},
{ID: 8, Name: "如燕", Age: 26, Email: PtrString("ruyan@yf.com"), Sex: false},
{ID: 9, Name: "魔灵", Age: 21, Email: PtrString("moling@sl.com"), Sex: true},
}
myDB.Create(&employeeList)
}
func PtrString(email string) *string {
return &email
}
func Age23(db *gorm.DB) *gorm.DB {
return db.Where("age>?", 23)
}
func main() {
employee := Employee{}
employeeList:=[]Employee{}
//Where
myDB.Where("name like ?", "李%").Find(&employee) //查询姓李的
fmt.Println(employee)
myDB.Not("name like ?", "李%").Find(&employee) //查询第一条不是姓李的
fmt.Println(employee)
myDB.Not("name like ?", "李%").Or("age>20").Find(&employeeList) //用Where表示and
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
employeeList = []Employee{}
myDB.Not("name like ?", "李%").Where("age>20").Find(&employeeList) //用Where表示and
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
//select
employeeList = []Employee{}
employeeList1 := []Employee1{}
myDB.Select("name", "age").Find(&employeeList).Scan(&employeeList1)
for _, value := range employeeList1 {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
//排序
employeeList = []Employee{}
myDB.Order("age desc").Find(&employeeList)
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
//分页
employeeList = []Employee{}
myDB.Limit(4).Offset(0).Order("age desc").Find(&employeeList) //Limit:每页限定记录数,offset:偏移量
for _, value := range employeeList {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
//去重
var agelist []int
myDB.Table("employees").Select("distinct age").Find(&agelist)
for _, value := range agelist {
fmt.Println(value)
}
//分组查询
var ageList []int
// 查询男生的个数和女生的个数
myDB.Table("employees").Select("count(id)").Group("Sex").Scan(&ageList)
fmt.Println(ageList)
//执行原生sql
type SexGroup struct {
Count int `gorm:"column:count(id)"`
Sex bool
Name string `gorm:"column:group_concat(name)"`
}
var sexlist []SexGroup
myDB.Raw("select count(id) ,sex,group_concat(name) from employees group by sex").Scan(&sexlist)
for _, value := range sexlist {
data, _ := json.Marshal(value)
fmt.Println(string(data))
}
//子查询
//select * from students where age > (select avg(age) from students); 原生sql
myDB.Where("age > (?)", myDB.Model(&Employee{}).Select("avg(age)")).Find(&employee)
fmt.Println(employee)
//查询引用Scope
myDB.Scopes(Age23).Find(&employee)
fmt.Println(employee)
}