gin gorm学习笔记

代码仓库
https://gitee.com/zhupeng911/go-advanced.git
https://gitee.com/zhupeng911/go-project.git

1. gin介绍

Gin 是使用纯 Golang 语言实现的 HTTP Web框架,Gin接口设计简洁,提供类似Martini的API,性能极高,现在被广泛使用。

主要特性

  • 快速 - 基于 Radix 树的路由,小内存占用,没有反射,可预测的 API 性能。

  • 支持中间件 - 传入的 HTTP 请求可以由一系列**中间件**和最终操作来处理。 例如:Logger,Authorization,最终操作 DB。

  • 路由组 - 更好地组织路由。例如将需要授权和不需要授权的API分组,不同版本的api分组。分组可嵌套,且性能不受影响。

  • Crash 处理 - Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

  • **JSON 验证 **- Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。

  • 内置渲染 - Gin 原生为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

官方文档

// 下载gin框架
go get -u github.com/gin-gonic/gin

2. gin关键技术点

2.1 数据解析和参数绑定

为了能够更方便的获取请求相关参数,提高开发效率,基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON等参数到结构体中,后端接收并解析该结构体。

type Student struct {UserName     string `form:"user_name" json:"user_name" binding:"required"`UserPassword string `form:"user_password" json:"user_password" binding:"required"`
}func TestGin006() {router := gin.Default()// 1.绑定JSON的示例 ({"user_name": "小王子", "user_password": "123456"})router.POST("/loginjson", func(c *gin.Context) {var stu Student// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&stu); err == nil {fmt.Printf("stu: %v \n", stu)c.JSON(http.StatusOK, gin.H{"user_name":     stu.UserName,"user_password": stu.UserPassword,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 2.绑定form表单示例 (user=小王子i&password=123456)router.POST("/loginForm", func(c *gin.Context) {var stu Studentif err := c.ShouldBind(&stu); err == nil {c.JSON(http.StatusOK, gin.H{"user":     stu.UserName,"password": stu.UserPassword,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 3.绑定QueryString示例 (/loginQuery?user_name=q1mi&user_password=123456)router.GET("/loginQuery", func(c *gin.Context) {var stu Studentif err := c.ShouldBind(&stu); err == nil {c.JSON(http.StatusOK, gin.H{"user":     stu.UserName,"password": stu.UserPassword,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})router.Run(":8080")
}

2.2 gin中间件

中间件是Gin的精髓,一个个中间件组成一条中间件链,对HTTP Request请求进行拦截处理,实现了代码的解耦和分离,每个中间件只需要处理自己需要处理的业务。

简单来说,Gin中间件的作用有两个:

(1)Web请求到到达我们定义的HTTP请求处理方法之前,拦截请求并进行相应处理(比如:权限验证,数据过滤等),这个可以类比为前置拦截器前置过滤器;

(2)在我们处理完成请求并响应客户端时,拦截响应并进行相应的处理(比如:添加统一响应部头或数据格式等),这可以类型为后置拦截器后置过滤器

2.2.1 定义中间件

在Gin框架中,中间件的类型定义如下所示,可以看出,中间件实际上就是一个以gin.Context为返回值的函数而已,与我们定义处理HTTP请求的Handler本质上是一样的,并没有什么神秘可言。

// ProgramTimeCost a、定义中间件:统计接口耗时的中间件
func ProgramTimeCost() gin.HandlerFunc {return func(c *gin.Context) {fmt.Printf("中间件---ProgramTimeCost in... \n")start := time.Now()c.Next() // 调用该请求的剩余处理程序// c.Abort() // 不调用该请求的剩余处理程序cost := time.Since(start)fmt.Printf("该接口耗时: %v \n", cost)fmt.Printf("中间件---ProgramTimeCost out... \n")}
}

PS:内置中间件
在这里插入图片描述

2.2.2 全局使用中间件

使用gin.Engine结构体的Use()方法便可以在所有请求应用中间件。

// TestGin010 
func TestGin010() {r := gin.Default()// 注册一个全局中间件,可以一次性注册多个中间件r.Use(ProgramTimeCost())r.GET("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"msg": "index"}) })r.GET("/login", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"msg": "login"}) })r.Run()
}

2.2.3 分组使用中间件

更多的时候,我们会根据业务不同划分不同路由分组(RouterGroup ),不同的路由分组再应用不同的中间件,在这种下就是在路由分组中局部使用中间件。

// TestGin010 
func TestGin010() {router := gin.New()user := router.Group("user", ProgramTimeCost(){user.GET("info", func(context *gin.Context) {...})user.GET("article", func(context *gin.Context) {...})}
}

2.2.4 单个路由使用中间件

除了路由分组,在单个请求路由中也可以应用中间件

// TestGin010 
func TestGin010() {r := gin.Default()// 给路由单独注册中间件(一个接口可注册多个中间件)r.GET("/logout", ProgramTimeCost(), func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"msg":  "logout","name": name,})})r.Run()
}

2.3 gin路由组

gin 框架中采用的路由库是基于httprouter实现的,并支持Restful风格的API。为了管理具有相同前缀的URL, 将拥有URL共同前缀的路由划分为一组。

// TestGin009 路由组
func TestGin009() {r := gin.Default()// 1.普通路由r.GET("/index", func(c *gin.Context) {})r.GET("/login", func(c *gin.Context) {})r.POST("/login", func(c *gin.Context) {})// 2.没有匹配到路由的请求返回统一404页面r.LoadHTMLFiles("./static/404.html")r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "404.html", nil)})//3.路由组 将拥有共同URL前缀的路由划分为一个路由组//为路由组注册中间件方法一userGroup := r.Group("/user", ProgramTimeCost()){userGroup.GET("/queryName", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"path": "/user/queryName",})})userGroup.GET("/queryAge", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"path": "/user/queryAge",})})// 路由组也是支持嵌套的//为路由组注册中间件方法二shopGroup := userGroup.Group("/shop")shopGroup.Use(ProgramTimeCost()){shopGroup.GET("/queryShopName", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"path": "/user/shop/queryShopName",})})shopGroup.GET("/queryShopAddr", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"path": "/user/shop/queryShopAddr",})})}}r.Run()
}

3. gorm介绍

The fantastic ORM library for Golang aims to be developer friendly.

gorm是GoLang实现的,在GitHub上活跃度很高的对象关系映射框架(ORM)。它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。

## 必须安装gorm
go get -u gorm.io/gorm     
## 安装相应的数据库驱动。GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
go get -u gorm.io/driver/mysql  

4. gorm常用示例

4.1 连接数据库

连接不同的数据库都需要导入对应数据的驱动程序,GORM已经贴心的为我们包装了一些驱动程序,只需要按如下方式导入需要的数据库驱动即可:

GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server

连接MySQL

import ("github.com/jinzhu/gorm"_ "github.com/jinzhu/gorm/dialects/mysql"
)func main() {db, err := gorm.Open("mysql", "username:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local")defer db.Close()
}// MySQl 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:
func main() {db, err := gorm.Open(mysql.New(mysql.Config{DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source nameDefaultStringSize: 256, // string 类型字段的默认长度DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置}), &gorm.Config{})defer db.Close()
}

连接PostgreSQL…等,详见

GORM

4.2 快速入门

// 模型定义
type DFUser struct {gorm.Model              // gorm.Model是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt四个字段的Golang结构体UserId           uint64 `gorm:"column:user_id;type:int;primary_key;unique;not null"`UserName         string `gorm:"column:user_name;type:varchar(255)"`UserAge          int    `gorm:"column:user_age;default:0"`UserMemberNumber string `gorm:"unique_index;not null"` // 设置会员号(member number)唯一并且不为空UserAddress      string `gorm:"index:addr"`            // 给UserAddress字段创建名为addr的索引UserBirthday     time.TimeIgnoreMe         int `gorm:"-"` // 忽略本字段
}// 设置表名001
func (DFUser) TableName() string {return "df_user"
}func TestGorm001() {// 连接数据库mysqldb, err := gorm.Open("mysql", "root:zhupeng123@(127.0.0.1:3306)/go_db_1?charset=utf8&parseTime=True&loc=Local")if err != nil {panic(err)}defer db.Close()// 操作数据库-建表db.AutoMigrate(&DFUser{})					 // 自动迁移【结构体与表对应,类似于JPA】//db.Table("df_user").CreateTable(&DFUser{}) // 手动设置表名002//db.SingularTable(true)					 // 禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user`// 表中添加数据dfUser01 := DFUser{UserId: 1000, UserName: "小王子", UserAge: 23, UserMemberNumber: "VIP1000", UserAddress: "江苏南京", UserBirthday: time.Now()}dfUser02 := DFUser{UserId: 1001, UserName: "小王子2", UserAge: 24, UserMemberNumber: "VIP1001", UserAddress: "江苏南京", UserBirthday: time.Now()}db.Create(&dfUser01)db.Create(&dfUser02)// 查询var user DFUserdb.First(&user)    // 根据主键查询第一条记录 SELECT * FROM df_user ORDER BY id LIMIT 1;db.Last(&user)     // 根据主键查询最后一条记录 SELECT * FROM df_user ORDER BY id DESC LIMIT 1;db.Find(&user)     // 查询所有的记录 SELECT * FROM df_user;db.Where(&DFUser{UserName: "小王子", UserAge: 23}).First(&user)// SELECT * FROM df_user WHERE user_name = "朱鹏" AND user_age = 23 LIMIT 1;// 更新某个字段db.Model(&user).Update("user_name", "zhupeng123")// UPDATE users SET name='zhupeng123' WHERE id=111;// 更新多个字段db.Model(&user).Updates(DFUser{UserName: "zhupeng_update", UserAge: 0, UserBirthday: time.Now()})// UPDATE users SET user_name='zhupeng_update', user_age=18, updated_at='2013-11-17 21:34:10' WHERE id=111;// 批量更新db.Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"user_name": "hello", "user_age": 18})// UPDATE users SET user_name='hello', user_age=18 WHERE id IN (10, 11);// 删除记录db.Delete(&user)// DELETE from df_user where id=10;
}

推荐:
普通场景:简单查询用Find+Where的函数结合实现,结合Limit+Offset+Order实现分页等高频功能;
追求性能:可以引入Select避免查询所有字段,但会导致返回结果部分字段不存在的奇怪现象,需要权衡;
复杂查询:例如Join+子查询等,推荐使用下面的原生SQL,用GORM拼接的体验并不好。

4.3 SQL是怎样生成的

两个核心文件

在GORM库中,有两个核心的文件,也是我们调用频率最高的函数所在:chainable_api.go和 finisher_api.go。顾名思义,前者是整个链式调用的中间部分,后者则是最终获取结果的函数。以查询为例:

db.Where(&User{Name: "小王子"}, "name", "Age").Find(&users)

其中Where是chainable,也就是还在拼接SQL条件,Find则是触发真正查询的finisher,从finisher入手,看看一个SQL的到底是怎么在GORM中拼接并执行的。

核心-构建SQL的实现

func BuildQuerySQL(db *gorm.DB) {// SQL为空,表示需要自己构建if db.Statement.SQL.String() == "" {db.Statement.SQL.Grow(100) // 分配初始空间if len(db.Statement.Selects) > 0 {// 表示只select某几个字段,而不是select *} else if db.Statement.Schema != nil && len(db.Statement.Omits) > 0 {// Omit表示忽略特定字段} else if db.Statement.Schema != nil && db.Statement.ReflectValue.IsValid() {// 查询到指定结构体}// 对join的处理,涉及到多表关联,暂时忽略if len(db.Statement.Joins) != 0 {} else {db.Statement.AddClauseIfNotExists(clause.From{})}// 用一个map去重,符合名字中的 IfNotExists 含义db.Statement.AddClauseIfNotExists(clauseSelect)// 最后拼接出完整 SQL 的地方db.Statement.Build(db.Statement.BuildClauses...)}
}

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

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

相关文章

《C语言缺陷和陷阱》-笔记

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 前言 在这一节中,我们将探索对记号的意义的普遍的误解以及记号和组成它们的字…

指针习题二

使用函数指针实现转移表 #include <stdio.h> int add(int a, int b) {return a b; } int sub(int a, int b) {return a - b; } int mul(int a, int b) {return a * b; } int div(int a, int b) {return a / b; } int main() {int x, y;int input 1;int ret 0;int(*p[…

学习python时一些笔记

1、winr 命令提示符的快捷键 输入cmd进入终端 2、在终端运行桌面上的python文件 cd desktop(桌面) cd是进入该文件夹的意思。 cd .. 回到上一级 运行python时一定要找到文件的所在地 输入python进入&#xff0c;exit()退出%s字符串占位符%d数字占位符%f浮点数占位符input输…

Linux速览(1)——基础指令篇

在上一章对Linux有了一些基础了解之后&#xff0c;本章我们来学习一下Linux系统下一些基本操作的常用的基础指令。 目录 1. ls 指令 2. pwd&&whoami命令 3. cd 指令 4. touch指令 5.mkdir指令&#xff08;重要&#xff09;&#xff1a; 6.rmdir指令 && …

vue面试题:如何保存页面的当前的状态?

如何保存页面的当前的状态&#xff1f; 既然是要保持页面的状态&#xff08;其实也就是组件的状态&#xff09;&#xff0c;那么会出现以下两种情况&#xff1a;组件会被卸载&#xff1a;&#xff08;1&#xff09;将状态存储在LocalStorage / SessionStorage优点&#xff1a;缺…

带大家做一个,易上手的水煮牛肉

今天带大家做川菜系中的 水煮牛肉 这个菜是比较费辣椒的 制作成本相对一般菜来说 会高一些 一块牛肉 泡水划冰 从超时买的干腐竹 切成小片 温水浸泡五分钟 泡软它 然后捞出来 去干水分 牛肉切片 尽量切薄一点 三瓣左右蒜 一块生姜 去皮切末 牛肉中下入 一个鸡蛋 小半勺…

python实现视频或音频转文本

python实现视频或音频转文本 当然可以,以下是您的Python语音视频转文本代码的描述: 内容概要: 这段Python代码利用强大的语音识别库,能够自动将本地存储的语音视频文件转换成文本。它通过分析音频轨道中的语音数据,识别并转录为可编辑和可搜索的文本格式。 适用人群: …

装修必看干货|入户玄关设计进门就是客厅应该怎么设计?福州中宅装饰,福州装修

入户玄关设计在进门就是客厅的情况下&#xff0c;想要拥有单独的玄关空间&#xff0c;以下是五点设计建议&#xff1a; ①隔断屏风 使用隔断屏风是传统而常见的一种空间分割方法。可以选用木制、金属或玻璃等材质的屏风&#xff0c;根据需要进行灵活搭配和定制。 屏风的款式和…

Python爬虫——Urllib库-1

这几天都在为了蓝桥杯做准备&#xff0c;一直在刷算法题&#xff0c;确实刷算法题的过程是及其的枯燥且枯燥的。于是我还是决定给自己找点成就感出来&#xff0c;那么Python的爬虫就这样开始学习了。 注&#xff1a;文章源于观看尚硅谷爬虫视频后笔记 目录 Urllib库 基本使…

【C++】字符串 1478 - 出现次数最多的小写字母 1475 - 字符串对比 1098 - 判断是否构成回文 1102 - 字符串中的空格移位

文章目录 问题一&#xff1a;1478 - 出现次数最多的小写字母问题二&#xff1a;1475 - 字符串对比问题三&#xff1a;1098 - 判断是否构成回文问题四&#xff1a;1102 - 字符串中的空格移位五、感谢 问题一&#xff1a;1478 - 出现次数最多的小写字母 类型&#xff1a;字符串 …

oracle基础体系

一、 Oracle数据库服务器 数据库在各个行业都会有使用到&#xff1b;其实&#xff0c;我们平时无论是在与客户沟通或者交流中&#xff0c;所说的Oracle数据库是指Oracle数据库服务器&#xff08;Oracle Server&#xff09;&#xff0c;它由Oracle实例&#xff08;Oracle Instan…

什么是杠杆?WeTrade众汇这样举例,大家都明白

杠杆是投资交易者一定要知道的一个金融术语。那么什么是杠杆呢?下面WeTrade众汇就用苹果进行举例&#xff0c;大家就都会明白&#xff0c;原来如此简单。 发挥我们投资者的想象&#xff0c;我们现在要进行一场苹果的买卖&#xff0c;能够赚钱的本质就是高买低卖&#xff0c;所…

二十篇esp3454

jfjjfj from machine import I2C,Pin from ssd1306 import SSD1306_I2C i2c I2C(sdaPin(“Y8”), sclPin(“Y6”)) oled SSD1306_I2C(128, 64, i2c, addr0x3c) oled.text(“Hello World!”, 0, 0) oled.text(“MicroPython”, 0, 20) oled.text(“By 01Studio”, 0, 50) oled…

【在巴厘岛学点印尼语】日常篇

BINTANG BIR 槟棠啤酒 今天不写代码&#xff0c;在巴厘岛休养&#xff0c;顺便聊点印尼语。 印尼语&#xff0c;Bahasa Indonesia&#xff0c;是印度尼西亚的官方语言&#xff0c;也即印尼化的马来语廖内方言&#xff0c;其变种包括 爪哇语&#xff08;岛民方言&#xff09; 等…

如何选择适合电商的WordPress主题模板?

选择适合电商的WordPress主题模板时&#xff0c;首先应考虑主题模板是否与WooCommerce兼容。WooCommerce是WordPress中一个强大的电商插件&#xff0c;能够帮助用户实现在线电子商务销售或产品展示。因此&#xff0c;选择一个与WooCommerce高度兼容的主题模板至关重要。 其次&…

5.测试教程 - 进阶篇

文章目录 1.按测试对像划分1.1**界面测试**1.2**可靠性测试**1.3**容错性测试**1.4**文档测试**1.5**兼容性测试**1.6**易用性测试**1.7**安装卸载测试**1.8**安全测试**1.9**性能测试**1.10**内存泄漏测试** 2.按是否查看代码划分2.1黑盒测试(Black-box Testing)2.2白盒测试(W…

部署kubernetes-dashboard改成http免密登录

原始链接地址 https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml 修改Service端口 增加80端口&#xff0c;改成http访问 修改前: spec:ports:- port: 443targetPort: 8443selector:k8s-app: kubernetes-dashboard修改后&#xff…

snakemake: 基本语法知识点

Snakemake 使用一种基于 Python 的语法来定义工作流&#xff0c;允许用户编写规则&#xff08;rules&#xff09;来指定数据分析流程中的各个步骤。以下是一些基础语法知识点&#xff0c;帮助你理解和使用 Snakemake。 1. 规则&#xff08;Rules&#xff09; 规则是 Snakemak…

【ROS源码阅读】

项目需要研读ROS源码&#xff0c;这其中碰到的一些问题记录一下&#xff1a; 源码编译过程 (1) 在ubuntu 18.04 上安装ROS melodic的版本。 (2) 下载源码&#xff0c; ROS源码链接&#xff1a; https://github.com/ros/ros_comm/tree/melodic-devel/ros_comm(3) 编译 例如想…

图像增强预处理对于深度学习训练的提高有帮助吗?

答案&#xff1a;图像增强预处理对于深度学习训练非常有帮助&#xff0c;它可以显著提高模型的性能和泛化能力。 图像增强包括许多技术&#xff0c;可以通过各种方法改进图像数据&#xff0c;使其更适合训练深度学习模型。 可以增加数据集的多样性&#xff0c;减少模型对特定图…