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

Hook(钩子)

和我们在gin框架中讲解的Hook函数一样,我们也可以在定义Hook结构体,完成一些操作,相关接口声明如下:

go 复制代码
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函数,示例:

go 复制代码
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为例:
go 复制代码
// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务

具体可以参考官方文档:
Hook

  • 在 GORM 中保存、删除操作会默认运行在事务上, 因此在事务完成之前该事务中所作的更改是不可见的,如果Hook返回了任何错误,则修改将被回滚。

高级查询

初始化相关表

go 复制代码
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("[email protected]"), Sex: true},
		{ID: 2, Name: "张武", Age: 18, Email: PtrString("[email protected]"), Sex: true},
		{ID: 3, Name: "枫枫", Age: 23, Email: PtrString("[email protected]"), Sex: true},
		{ID: 4, Name: "刘大", Age: 54, Email: PtrString("[email protected]"), Sex: true},
		{ID: 5, Name: "李武", Age: 23, Email: PtrString("[email protected]"), Sex: true},
		{ID: 6, Name: "李琦", Age: 14, Email: PtrString("[email protected]"), Sex: false},
		{ID: 7, Name: "晓梅", Age: 25, Email: PtrString("[email protected]"), Sex: false},
		{ID: 8, Name: "如燕", Age: 26, Email: PtrString("[email protected]"), Sex: false},
		{ID: 9, Name: "魔灵", Age: 21, Email: PtrString("[email protected]"), Sex: true},
	}
	myDB.Create(&employeeList)
}

func PtrString(email string) *string {
	return &email
}

func main() {

}

Where查询

  • 简单示例:
go 复制代码
	var employee Employee

	//Where
	myDB.Where("name like ?", "李%").Find(&employee) //查询姓李的
	fmt.Println(employee)
  • Not条件
go 复制代码
	myDB.Not("name like ?", "李%").Find(&employee) //查询第一条不是姓李的
	fmt.Println(employee)
  • Or条件
go 复制代码
	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条件
go 复制代码
	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选择字段

  • 简单示例
go 复制代码
	employeeList := []Employee{}
	myDB.Select("name", "age").Find(&employeeList)
	for _, value := range employeeList {
		data, _ := json.Marshal(value)
		fmt.Println(string(data))
	}
  • Scan函数
    我们可以用Scan函数将搜索结果导入带新的结构体中
go 复制代码
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))
	}

输出为:

排序

go 复制代码
	//排序
	employeeList := []Employee{}
	myDB.Order("age desc").Find(&employeeList)
	for _, value := range employeeList {
		data, _ := json.Marshal(value)
		fmt.Println(string(data))
	}

分页查询

go 复制代码
	//分页
	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))
	}

去重

go 复制代码
	//去重
	var agelist []int
	myDB.Table("employees").Select("distinct age").Find(&agelist)
	for _, value := range agelist {
		fmt.Println(value)
	}

分组查询

go 复制代码
	//分组查询
	var ageList []int
	// 查询男生的个数和女生的个数
	myDB.Table("employees").Select("count(id)").Group("Sex").Scan(&ageList)
	fmt.Println(ageList)

执行原生sql

go 复制代码
	//执行原生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))
	}
}

子查询

go 复制代码
	//子查询
	//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层写一些通用的查询方法,让外界直接来调用:

go 复制代码
func Age23(db *gorm.DB) *gorm.DB {
	return db.Where("age>?", 23)
}

	myDB.Scopes(Age23).Find(&employee)
	fmt.Println(employee)

完整代码

go 复制代码
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("[email protected]"), Sex: true},
		{ID: 2, Name: "张武", Age: 18, Email: PtrString("[email protected]"), Sex: true},
		{ID: 3, Name: "枫枫", Age: 23, Email: PtrString("[email protected]"), Sex: true},
		{ID: 4, Name: "刘大", Age: 54, Email: PtrString("[email protected]"), Sex: true},
		{ID: 5, Name: "李武", Age: 23, Email: PtrString("[email protected]"), Sex: true},
		{ID: 6, Name: "李琦", Age: 14, Email: PtrString("[email protected]"), Sex: false},
		{ID: 7, Name: "晓梅", Age: 25, Email: PtrString("[email protected]"), Sex: false},
		{ID: 8, Name: "如燕", Age: 26, Email: PtrString("[email protected]"), Sex: false},
		{ID: 9, Name: "魔灵", Age: 21, Email: PtrString("[email protected]"), 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)
}
相关推荐
进击的雷神几秒前
Perl测试起步:从零到精通的完整指南
开发语言·后端·scala
点云SLAM3 分钟前
Python中in和is关键字详解和使用
开发语言·人工智能·python·python学习·in和is关键字·python中for循环
郭尘帅6661 小时前
Vue3中实现轮播图
开发语言·前端·javascript
豌豆花下猫1 小时前
Python 潮流周刊#102:微软裁员 Faster CPython 团队(摘要)
后端·python·ai
秋野酱1 小时前
基于javaweb的SpringBoot驾校预约学习系统设计与实现(源码+文档+部署讲解)
spring boot·后端·学习
Thomas_YXQ1 小时前
Unity3D Overdraw性能优化详解
开发语言·人工智能·性能优化·unity3d
北辰浮光1 小时前
[springboot]SSM日期数据转换易见问题
java·spring boot·后端
lanbing1 小时前
PHP 与 面向对象编程(OOP)
开发语言·php·面向对象
yzx9910132 小时前
Gensim 是一个专为 Python 设计的开源库
开发语言·python·开源
木梓辛铭2 小时前
Spring Cache的详细使用
java·后端·spring