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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 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
相关文章
|
4天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
51 20
|
2天前
|
存储 监控 算法
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
10天前
|
Go C语言
Go语言入门:分支结构
本文介绍了Go语言中的条件语句,包括`if...else`、`if...else if`和`switch`结构,并通过多个练习详细解释了它们的用法。`if...else`用于简单的条件判断;`if...else if`处理多条件分支;`switch`则适用于基于不同值的选择逻辑。特别地,文章还介绍了`fallthrough`关键字,用于优化重复代码。通过实例如判断年龄、奇偶数、公交乘车及成绩等级等,帮助读者更好地理解和应用这些结构。
34 14
|
24天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
28 5
|
9天前
|
监控 关系型数据库 MySQL
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
19 0
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
95 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
2月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
48 7
|
2月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
2月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
124 71
|
2月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
121 67

热门文章

最新文章