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

news2024/12/23 10:33:45

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)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1704491.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小识MFC,一套设计优雅与不优雅并存的类库----小话MFC(2)

Q1: CPoint继承于POINT,这样有什么好处? A: 继承的一个最基本的好处当然就是减少代码量。CPoint和POINT内部数据一样,只是一个提供了更多的方法来操作对象。 typedef struct tagPOINT {LONG x;LONG y; } POINT, *P…

ARM IHI0069F GIC architecture specification (7)

3.1 GIC逻辑组件 GICv3体系结构由一组逻辑组件组成: •Distributor。 •每个受支持的PE都有一个Redistributor。 •支持的每个PE都有一个CPU interface。 •中断翻译服务组件(ITS),支持将事件翻译为LPI。 Distri…

APM2.8飞控

ArduPilotMega 主控可应用于 固定翼、直升机、多旋翼、地面车辆 APM2.8飞控供电有两种 1.电流计供电, 2.带BEC(稳压功能)的电调供电 ArduPilotMega 内部的硬件结构图: 调试时,不要使用向导,由于向导功能不…

windows内存管理

一 windows系统的内存管理涉及哪些 1.1 虚拟内存管理机制 windows操作系统使用虚拟内存技术,将磁盘文件,通过映射对象(存储在物理内存)关联,映射到虚拟内存作为文件试图。即用户操作"虚拟内存中File View Objec…

卷出新高度,直呼太强!时隔三月,YOLO再度进化升级:《YOLOv10—实时端到端目标检测》重磅来袭

真的是不止一次感叹,学习的速度都跟不上发论文出新品的速度。。。。。 继前文YOLOv9发布以来也就不到三个月的时间,YOLOv10就来了! 《太卷了,目标检测新成员——YOLOv9: Learning What You Want to LearnUsing Programmable Gra…

openflow协议抓包分析

1、准备实验拓扑: 在Mininet环境中创建一个简单的SDN拓扑,包括控制器、交换机、主机等。 确保拓扑能够正常运行,SDN交换机与控制器建立连接。 采用主机Ubuntu22.04主机,IP地址是192.168.87.130,安装opendaylight控制…

DreamerV3阅读笔记

DreamerV3 文章希望解决的一个挑战是用固定的hyperparameter来同时处理不同domain的任务。文章发现,通过结合KL balancing 和free bits可以使得world model learn without tuning(是指上面这件事,即不需要对不同任务改变hyperparameter&#…

力扣239. 滑动窗口最大值

Problem: 239. 滑动窗口最大值 文章目录 题目描述思路复杂度Code 题目描述 思路 1.编写实现优先队列类: 1.1.实现push(int n):将元素n添加到队列尾,同时将n前面大于n的元素删除 1.2.实现int max():将队列头元素取出(由于实现了push所以此时队…

【Python性能优化】取最值的差异

取最值的差异 测试Windows 测试结果Linux 测试结果 测试 测试内容:从一组 x, y, z 坐标值中获得每个维度(x、y、z)的值域范围。此处不考虑将数据临时存放到内存,再整组获取值域的操作(因为对单文件这么做问题不大&…

多线程基本常识

多线程的状态 在Java中,一个线程的生命周期有以下几种状态: 新建(New):当线程对象被创建时,线程处于新建状态。此时线程对象存在,但还没有调用start()方法启动线程。 运行(Runnable…

Prometheus Operator创建告警规则并接入钉钉报警

prometheus之钉钉报警 前言1. 添加prometheus报警规则1.2 添加自定义报警规则文件 2. 配置钉钉报警2.2 部署dingding插件 3. 编写alertmanager配置文件 前言 在kubenetes上安装了kube-promethues(包含Prometheus Operator),程序正常跑起来了&#xff0c…

【找出缺失的观测数据】python

思路: 主要在于分配剩余的部分分配问题 代码: class Solution:def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]:m len(rolls)total_sum (n m) * meantoset total_sum - sum(rolls)# 检查 toset 是否在可能的范围内i…

影响所有股票、债券和ETF交易!一文看懂美国“T+1”结算新规

T1对投资者有何好处?有哪些风险?T1已经到来,T0还远吗? 美股将在本周迎来历史性时刻。 从当地时间5月28日开始,美股交易结算周期将由T2缩短至T1,即投资者当天卖出的股票,在交易后一个工作日就能…

C# Chart图表应用

1,Chart简介 Chart控件是微软自带的一种图形可视化组件,使用简单灵活。在.NET4.0之后(即VS2010之后)已集成在了VS里面,直接拖拽控件到窗体即可使用。 需要使用命名空间:using System.Windows.Forms.DataVis…

NDIS驱动程序堆栈

NDIS 6.0 引入了暂停和重启驱动程序堆栈的功能。 若要支持 NDIS 6.0 提供的堆栈管理功能,必须重写旧版驱动程序。 NDIS 6.0 还引入了 NDIS Filter驱动程序。 Filter驱动程序可以监视和修改协议驱动程序与微型端口驱动程序之间的交互。 与 NDIS 5 相比,F…

AI智能体研发之路-模型篇(四):一文入门pytorch开发

博客导读: 《AI—工程篇》 AI智能体研发之路-工程篇(一):Docker助力AI智能体开发提效 AI智能体研发之路-工程篇(二):Dify智能体开发平台一键部署 AI智能体研发之路-工程篇(三&am…

Vue中,点击提交按钮,路由多了个问号

问题 当点击提交按钮是路由多了问号: http://localhost:8100/#/ 变为 http://localhost:8100/?#/原因 路由中出现问号通常是由于某些路径或参数处理不当造成的。在该情况下,是因为表单的默认行为导致的。提交表单时,如果没有阻止表单的默…

完全背包洛谷题单

[USACO08NOV] Buying Hay S 题解:这题看到每个都可以卖出无限多个干草包,就应该想到完全背包,但又不同于普通的完全背包,普通的完全背包是让你通过对应的背包求出最大的价值,但是在这题理解上却是知道能够达到背包容量…

EPSON爱普生RTC RA8900CE/RA8000CE+松下Panasonic电池组合

RTC是一种实时时钟,用于记录和跟踪时间,具有独立供电和时钟功能。在某些应用场景中,为了保证RTC在断电或者其他异常情况下依然能够正常工作,需要备份电池方案来提供稳定的供电。本文将介绍EPSON爱普生RTC RA8900CE/RA8000CE松下Pa…

【Linux】升级GCC(版本9.3),补充:binutils

GCC:GNU Compiler Collection 。编译器,几乎Linux中所有程序(包括内核)都是gcc编译的,包括libc。 gcc不仅仅是编译器,gcc也有很多库,依赖libc。gcc和libc互相依赖。 GCC官网:GCC, …