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,一经查实,立即删除!

相关文章

【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 相关的任务&…

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

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

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

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

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…

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

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

通过安全的云开发环境重新发现 DevOps 的心跳

云开发平台如何“提升” DevOps 首先,我来简单介绍一下什么是云开发环境:它通常运行带有应用程序的 Linux 操作系统,提供预配置的环境,允许进行编码、编译和其他类似于本地环境的操作。从实现的角度来看,这样的环境类…

【算法】前缀和——寻找数组的中心下标

本节博客是用前缀和算法图解“寻找数组的中心下标”,有需要借鉴即可。 目录 1.题目2.题意3.前缀和求解4.示例代码5.细节6.总结 1.题目 题目链接:LINK 2.题意 我们以示例1为例来图解一下题意: 3.前缀和求解 根据已有经验,我…

Idea工具的使用技巧与常见问题解决方案

一、使用技巧 1、启动微服务配置 如上图,在编辑配置选项,将对应的启动入口类加进去, 增加jvm启动参数, 比如: -Denvuat 或者 -Denvuat -Dfile.encodingUTF-8 启动配置可能不是-Denvuat,这个自己看代…

Android 11 Audio音频系统配置文件解析

在AudioPolicyService的启动过程中,会去创建AudioPolicyManager对象,进而去解析配置文件 //frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientIn…

Python机器学习 Tensorflow + keras 实现CNN

一、实验目的 1. 了解SkLearn Tensorlow使用方法 2. 了解SkLearn keras使用方法 二、实验工具: 1. SkLearn 三、实验内容 (贴上源码及结果) 使用Tensorflow对半环形数据集分 #encoding:utf-8import numpy as npfrom sklearn.datasets i…

Dynadot API调整一览

关于Dynadot Dynadot是通过ICANN认证的域名注册商,自2002年成立以来,服务于全球108个国家和地区的客户,为数以万计的客户提供简洁,优惠,安全的域名注册以及管理服务。 Dynadot平台操作教程索引(包括域名邮…

AI Agent教育行业落地案例

【AI赋能教育】揭秘Duolingo背后的AI Agent,让学习更高效、更有趣! ©作者|Blaze 来源|神州问学 引言 随着科技的迅猛发展,人工智能技术已经逐步渗透到我们生活的各个方面。而随着AI技术的广泛应用,教育培训正引领着一场新的…

149.二叉树:二叉树的前序遍历(力扣)

代码解决 这段代码实现了二叉树的前序遍历,前序遍历的顺序是:访问根节点 -> 递归遍历左子树 -> 递归遍历右子树。以下是详细解释,包括各个部分的注释: // 二叉树节点的定义 struct TreeNode {int val; // 节…