Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
日志服务 SLS,月写入数据量 50GB 1个月
简介: Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询

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)
}
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
32 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
23天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
23天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
102 67
|
1天前
|
监控 算法 安全
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
11 0
|
15天前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
|
24天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
37 7
|
24天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
99 71
|
26天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
94 62
|
24天前
|
存储 Go
go语言中映射
go语言中映射
35 11
|
26天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
33 12