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

Hook(钩子)

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

type CreateUser interface {    //创建对象时使用的HookBeforeCreate() errorBeforeSave() errorAfterCreate() errorAfterSave() error
}type UpdateUser interface {BeforeUpdate() errorBeforeSave() errorAfterUpdate() errorAfterSave() error
}type DeleteUser interface {BeforeDelete() errorAfterDelete() 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 mainimport ("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.DBfunc 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.InterfacemysqlLogger = 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//WheremyDB.Where("name like ?", "李%").Find(&employee) //查询姓李的fmt.Println(employee)
  • Not条件
	myDB.Not("name like ?", "李%").Find(&employee) //查询第一条不是姓李的fmt.Println(employee)
  • Or条件
	var employeeList []EmployeemyDB.Not("name like ?", "李%").Or("age>20").Find(&employeeList)  //用Where表示andfor _, value := range employeeList {data, _ := json.Marshal(value)fmt.Println(string(data))}
  • And条件
	employeeList=[]Employee{}myDB.Not("name like ?", "李%").Where("age>20").Find(&employeeList)  //用Where表示andfor _, 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 stringAge  int
}//selectemployeeList := []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 []intmyDB.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

	//执行原生sqltype SexGroup struct {Count int `gorm:"column:count(id)"`Sex   boolName  string `gorm:"column:group_concat(name)"`}var sexlist []SexGroupmyDB.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); 原生sqlmyDB.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 mainimport ("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 stringAge  int
}var myDB *gorm.DBfunc 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.InterfacemysqlLogger = 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{}//WheremyDB.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表示andfor _, value := range employeeList {data, _ := json.Marshal(value)fmt.Println(string(data))}employeeList = []Employee{}myDB.Not("name like ?", "李%").Where("age>20").Find(&employeeList) //用Where表示andfor _, value := range employeeList {data, _ := json.Marshal(value)fmt.Println(string(data))}//selectemployeeList = []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 []intmyDB.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)//执行原生sqltype SexGroup struct {Count int `gorm:"column:count(id)"`Sex   boolName  string `gorm:"column:group_concat(name)"`}var sexlist []SexGroupmyDB.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); 原生sqlmyDB.Where("age > (?)", myDB.Model(&Employee{}).Select("avg(age)")).Find(&employee)fmt.Println(employee)//查询引用ScopemyDB.Scopes(Age23).Find(&employee)fmt.Println(employee)
}

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

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

相关文章

Java中的锁机制详解:synchronized与ReentrantLock

在Java并发编程中,锁机制是确保多线程安全访问共享资源的关键手段。Java提供了多种锁机制,其中最为常用的两种是synchronized关键字和ReentrantLock。下面我将从技术难点、面试官关注点、回答吸引力以及代码举例等方面对这两种锁机制进行详细描述。 一、…

【C#】委托

文章目录 委托自定义委托模板方法(工厂模式回调(callback)函数(观察者模式多播(multicast)委托委托的高级使用使用接口 重构 模板方法代码注意参考 委托 委托(delegate)是一种类型,定义了一种方…

实践部署 浦语·灵笔2 模型,写作图文并茂的文章

1 初步介绍 XComposer2 相关知识 浦语灵笔2 是基于 书生浦语2 大语言模型研发的突破性的图文多模态大模型,具有非凡的图文写作和图像理解能力,在多种应用场景表现出色,总结起来其具有: 自由指令输入的图文写作能力: 浦…

Android ANR Trace日志阅读分析技巧

什么是Trace日志 Trace日志是指ANR目录下的一份txt文件 adb pull /data/anr/traces.txt Trace日志有什么用 分析应用ANR无响应的问题, Trace怎么用 Cmd line: com.xx ABI: arm Build type: optimized Zygote loaded classes3682 post zygote classes3750 Intern…

uniapp - 填充页面

在上一篇文章中,创建了一个空白的文章模块页面。在这一篇文章,让我们来向页面中填充内容。 目录 页面效果涉及uniapp组件1.view2.swiper3.scroll-view4.属性解读1) class"style1 style2 .."2) circular单属性无赋值3) :autoplay"autoplay…

如何关闭MySQL凌晨12点自动弹窗?

要关闭 MySQL 在凌晨 12 点自动弹窗的行为,首先需要确定弹窗的具体原因。 打开“任务计划程序”: 按 Win R,输入 taskschd.msc,然后按 Enter。 在左侧导航栏中,选择“任务计划程序库”。 查找与 MySQL 相关的任务&…

vite构建build选项配置(2024-05-29)

build.target​ 类型: string | string[]默认: modules相关内容: 浏览器兼容性 设置最终构建的浏览器兼容目标。默认值是一个 Vite 特有的值:modules,这是指 支持原生 ES 模块、原生 ESM 动态导入 和 import.meta 的…

软件构造复习的一些经验笔记

软件构造复习的一些经验笔记 术语解释 LSP原则(里氏替换原则) 什么是LSP原则,就是A类继承B类,A类应该比B类的spec(规约)更强 换句话说:你爹会做鱼香肉丝,你爹的手艺遗传给了你&a…

基于springboot实现医疗挂号管理系统项目【项目源码+论文说明】

基于springboot实现医疗挂号管理系统演示 摘要 在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以&…

JAVA -- 逻辑控制详解

JAVA逻辑控制详解 1.顺序结构 按照代码书写的顺序一行一行执行 System.out.println("123");//123 System.out.println("456");//456 System.out.println("789");//7892.分支结构 if 语句 switch 语句 2.1 if 语句 语法格式1(单分支) //if(布…

安全阀检测周期:确定因素与操作流程详解

在工业生产中,安全阀扮演着至关重要的角色,其性能的稳定性和准确性直接关系到设备和系统的安全。为确保安全阀的正常运行和事故防范,对其进行定期检测显得尤为关键。 接下来,佰德将深入探讨安全阀检测周期相关的内容,…

HTML静态网页成品作业(HTML+CSS)——家乡芷江侗族自治县介绍网页(1个页面)

🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…

【ROS机器人学习】--------1ROS工作空间和功能包创建

虚拟机工具和镜像链接: https://pan.baidu.com/s/1HDmpbMESiUA2nj3qFVyFcw?pwd8686 提取码: 8686 ROS工作空间是一个用于组织和管理ROS(机器人操作系统)包的目录结构,它通常包含多个子目录,用于存放源码、构建文件和安装文件。工…

香橙派OrangePI AiPro测评

实物 为AI而生 打开盒子 截图电源开机进入 作为一个AI产品,必须有一个人机交互的界面才行。大家都在跑算法,于是我就开始进行整理着手整理搭建Qt的环境。 1、下载源码 wget https://download.qt.io/archive/qt/5.12/5.12.12/single/qt-everywhere-src-5.12.12.tar.xz待…

RDP方式连接服务器上传文件方法

随笔 目录 1. RDP 连接服务器 2. 为避免rdp 访问界面文字不清晰 3. 本地上传文件到服务器 1. RDP 连接服务器 # mstsc 连接服务器step1: 输入mstscstep2: 输入 IP, username, passwd 2. 为避免rdp 访问界面文字不清晰 解决方法: 3. 本地上传文件到服务器 step…

关于C++的特殊类定制

特殊类定制 在C中,一些特殊性质的类如何设计 类禁止拷贝的对象 C11 使用delete关键字赋值给拷贝构造和赋值C98将拷贝构造和赋值声明在私有里 类只能在堆上创建的对象 将构造函数私有化, 提供一个获取对象堆上创建对象的公有函数将析构函数私有化, 提供一个释放…

JavaScript面向对象编程入门:从0到1的奇幻之旅【含代码示例】

JavaScript面向对象编程入门:从零到英雄的奇幻之旅【含代码示例】 一、OOP:编程界的哈利波特基本概念类与实例 二、挥舞魔杖:创建类与实例基本语法 三、继承与封装:家族的力量继承封装 四、实战与技巧:打造坚固的魔法城…

IT行业的现状与未来发展趋势:从云计算到量子计算的技术变革

随着技术的不断进步,IT行业已经成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链,这些技术正在重塑我们的生活和工作方式。本文将深入探讨当前IT行业的现状,并展望未来发展趋势,旨在为…

vscode当前分支有未提交的修改,但是暂时不想提交,想要切换到另一个分支该怎么办

当前分支有未提交的修改,但是暂时不想提交,想要切换到另一个分支该怎么办? 首先,可以将当前修改暂存起来,以便之后恢复 git stash 然后切换到目标分支,例如需求A所在分支 git checkout feat-a-jie 修改完A需求后,需要先切换回之前的分支,例如需求B所在分支 git checkout feat…

免费插件集-illustrator插件-Ai插件-文本对象分行

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件,加强illustrator使用人员工作效率,进行文本对象分行。首先从下载网址下载这款插件 https://download.csdn.net/download/m0_67316550/87890501&…