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

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: 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操作。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
6月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
393 86
|
5月前
|
JavaScript 前端开发 Java
【GoWails】Go做桌面应用开发?本篇文章带你上手Wails框架!一步步带你玩明白前后端双端的数据绑定!
wails是一个可以让你使用Go和Web技术编写桌面应用的项目 可以将它看作Go的快并且轻量级的Electron替代品。可以使用Go的功能,并结合现代化UI完成桌面应用程序的开发
1043 5
|
5月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
302 2
|
5月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
495 1
|
7月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
494 0
|
7月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
334 0
|
7月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
373 0
|
7月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
419 0
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。