GIN-GORM基础学习|青训营笔记

158 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第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简化)

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的效用才能显示,通过后台数据状态恢复即可。这样做利之处在于占用空间和数据量大时影响查询效率,可这些都是可通过设计手段进行优化的。