这是我参与「第三届青训营 -后端场」笔记创作活动的第8篇笔记
1. gin框架的基本使用
1.1 gin框架安装
首先需要配置环境变量,就类似配置镜像一样的。终端输入:
go env -w GOPROXY=https://goproxy.io,direct
然后开始安装gin框架:
go get -u github.com/gin-gonic/gin
1.2 第一个gin程序
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func sayWorld(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello!",
})
}
func main() {
r := gin.Default() //返回路由引擎
r.GET("/hello", sayWorld) //绑定处理函数
err := r.Run(":9090")
if err != nil {
fmt.Printf("some wrong happend!%v", err)
}
}
1.3 RESTful API
REST与技术无关,代表的是一种软件架构风格,翻译为表征状态转移和表现层状态化,简单来说,REST的含义就是客户端与WEB服务器之间进行交互的时候,使用HTTP的4个请求方法代表不同的动作
- GET用来获取资源
- POST用来新建资源
- PUT用来更新资源
- DELETE用来删除资源
1.4 GO语言模板标准库
html/template包含了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。
template包含相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。
模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记,机制说明:
- 模板文件通常定义为.tmpl和.tpl的后缀(也可以使用其它的后缀),
必须使用UTF-8编码 - 模板文件使用
{{和}}包裹和标识需要传入的数据 - 传给模板这样的数据可以使用
.来进行访问,如果是复杂类型的数据,可以通过{.FiledName}来进行访问它的字段 - 除了使用{{}}包裹的内的数据,其它都不做改动,原样输出
1.5 模板引擎的使用
模板引擎的使用可以分为三部分
- 定义模板文件
- 解析模板文件
- 模板渲染
以下可以写出一个基本的代码框架
HTML模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello</title>
</head>
<body>
<p>Hello </p>
<p>姓名:{{.user.Name}}</p>
<p>年龄:{{.user.Age}}</p>
<p>性别:{{.user.Gender}}</p>
<p>学校:{{.stu.School}}</p>
<p>班级:{{.stu.Class}}</p>
<p>学号:{{.stu.Code}}</p>
</body>
</html>
后端渲染逻辑
package main
import (
"fmt"
"html/template"
"net/http"
)
type UserInfo struct {
Name string
Gender string
Age int
}
type stuInfo struct {
School string
Class int
Code string
}
func sayWorld(w http.ResponseWriter, r *http.Request) {
//解析模板文件
//resources/basciHTMLDoc.tmpl
tmpl, err := template.ParseFiles("../resources/basciHTMLDoc.tmpl") //解析模板文件
if err != nil {
fmt.Printf("there are something wrong happended,%v", err)
return
} //该接口可以传入任意类型的数据
user := UserInfo{
Name: "Tom",
Gender: "男",
Age: 15,
}
stu := stuInfo{
School: "华南农业大学",
Class: 2,
Code: "2022335454",
}
//然后将这两个数据整合到一起
tmpl.Execute(w, map[string]interface{}{
"user": user,
"stu": stu,
})
}
func main() {
http.HandleFunc("/hello", sayWorld)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Printf("there are something wrong happended,%v", err)
return
}
}
2. 数据库的基本使用
2.1 基本用法
package main
import (
"database/sql"
)
type user struct {
id string
name string
}
//database quick start
func main() {
//定义数据库连接驱动
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
if err != nil {
return
}
//定义sql语句
rows, err := db.Query("select id,name from users where id=?", 1)
if err != nil {
return
}
//记得关闭
defer func() {
err := rows.Close()
if err != nil {
return
}
}()
var users []user
for rows.Next() {
var userNow user
err := rows.Scan(&userNow.id, &userNow.name)
if err != nil {
return
}
users = append(users, userNow)
}
if rows.Err() != nil {
return
}
}
2.2 设计原理
- 设计原则:
极简原则 - 设计原理图
- 为上层应用程序提供了操作接口,database/sql为数据库提供了
统一的连接接口和操作接口,为数据库创建连接池,在数据库连接层面,只需要实现不同的接口就可以实现不同数据库的连接方式。 池化技术(pooling):当存在一些特别昂贵的开销、一些特别耗时的操作才能获取的数据时,这时候可以将这些数据装入池中,供程序取用。- 连接池配置
//连接池配置
db.SetConnMaxLifetime()
db.SetConnMaxIdleTime()
db.SetMaxIdleConns()
db.SetMaxOpenConns()
//获取连接池状态
db.Stats()
- 伪实现
//定义数据库连接驱动
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
if err != nil {
return
}
//driver的伪实现
maxBadConn := 2
for i := 0; i < maxBadConn; i++ { //最大重试两次
dc, err := db.Conn(ctx, strategy)
//策略1:如果有空闲连接->复用该空闲连接->直到该连接达到了它的生命周期
//策略2:如果一直无法复用该连接,在连接的最后一次将会直接新建连接,直接新建连接->直到连接池中达到了最大连接数量
//操作完成后将链接放回链接池
defer dc.db.putConn(dc, err, true)
if err == nil {
err = dc.ci.Query("query", arg1)
}
isBadConn := errors.Is(err, driver.ErrBadConn)
if !isBadConn {
break
}
}
- DB连接的三种类型
- 直接连接/Conn:直接传入sql并且执行
- 预编译/stmt:有prepare这个过程,先预编译并且生成一个引用id,之后无需再传入sql指令,而是通过引用id来执行sql,降低网络传输时间
- 事务/Tx
- 返回数据的三种方式
- Exec/ExecContext->result:
关注原生查询结果 - Query/QueryContext->row(cols):
将查询结果以行与列的方式显示,关注行与列 - QueryRow/QueryRowContext->Row(Rows简化)
- Exec/ExecContext->result:
3. GORM的基础用法
3.1 ORM简介
Object(对象) Relational(关系) Mapping(映射) 程序中的对象实例(例如Go中的结构体实例) 关系数据库(例如Mysql)
3.2 GORM基本使用-CURD
//1.通过gorm建立关系,ORM:对象->(关系)->模型
dsn := "root:root123@tcp(127.0.0.1:3306)/test_gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return
}
//2.自动迁移操作,自动迁移仅仅会创建表,添加缺少列和索引,并且不会改变现有列的类型或删除未使用的列以保护数据
db.AutoMigrate(&User{})
//3.1 增加数据
db.Create(&User{
id: "1",
name: "Lumxi",
})
//3.2 批量增加数据
var users_ = []User{{id: "2", name: "A"}, {id: "3", name: "A"}, {id: "2", name: "A"}}
db.Create(&users_)
db.CreateInBatches(users_, 100)
//4.查找数据
var user User
db.First(&user, 1) //查找id为1的
db.First(&user, "code = ?", "L1212") //查找code为L1212的数据并且填入user
//支持批量查询
result := db.Find(&users_, []int{1, 2, 3})
fmt.Println(result.RowsAffected)
errors.Is(result.Error, gorm.ErrRecordNotFound)
//5.修改
//5.1 更新某个字段
db.Model(&user).UpdateColumn("id", "new-id")
db.Model(&user).Update("id", "new-id")
//5.2 批量更新字段
db.Model(&user).Updates(User{id: "new-id", name: "new-name"})
db.Model(&user).Updates(map[string]interface{}{"id": "new-id", "name": "new-name"})
db.Model(&User{}).Where("id == ?", "new-id").Updates(map[string]interface{}{"id": "new-id"})
//6.删除
db.Delete(&user)
3.3 约定优于配置
- 表名为struct name的snake_cases
复数格式 - 字段名为field name的snake_case
单数格式 - ID/id字段为主键,如果为数字,则为自增主键
- CreatedAt字段,创建时,保存
当前时间 - UpdatedAt字段,创建、更新时,保存
当前时间 - gorm.DeletedAt字段,默认开启
soft delete模式 - 软删除是通过
逻辑的方式进行删除数据,不会真正从物理上进行删除;即数据还存在数据库,通过字段方式控制查询不显示。通常在重要数据中会使用软删除。在实际项目使用上,用户删除重要数据后会对生产造成不可逆转的影响,使用soft-delete的效用才能显示,通过后台数据状态恢复即可。这样做利之处在于占用空间和数据量大时影响查询效率,可这些都是可通过设计手段进行优化的。