三件套学习 | 青训营笔记

144 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

Go框架三件套(Web/RPC/ORM)

一、三件套介绍

Gorm 是 ORM框架

  • 开发人员只需关系结构体,操作结构体,无需关注如何操作数据库
  • 提高开发效率
  • 使用反射牺牲性能,牺牲灵活性
  • 保护数据库结构,操作结构体字段不会对数据库产生影响

Kitex 是 RPC框架

  • 高性能,使用自研的高性能网络库Netpoll
  • 强扩展,提供较多扩展接口,也可自行扩展
  • 支持多协议,Thrift、Kitex Protobuf、gRPC、TTHeader、HTTP2

Hertz 是 HTTP框架

  • 高易用性、高性能、高扩展性
  • 参考了其他开源框架 fasthttpginecho 的优势

二、三件套使用

1.Gorm

Gorrm约定

默认使用结构体中ID字段作为主键

如果没有为结构体定义表名,默认使用结构体的蛇形负数作为表名,默认使用结构体的字段名的蛇形作为列名

使用CreatAt、UpdateAt字段作为创建、更新时间

Gorm支持的数据库

MySQL、SQLServer、PostgreSQL、SQLite

Gorm通过驱动的方式连接数据库,如果需要连接其他数据库,可以自行开发驱动

Gorm天然防SQL注入

连接SQLServer

import{
    "gorm.io/driver/sqlserver"
    "gorm.io/gorm"
}
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})

Gorm创建数据

在数据库语句中,db.之后的where都是用来拼接sql语句的,但是find()、first()等,则是真正的执行sql语句,因此,在之后无法继续拼接sql语句!!!

    // 创建一条数据
    user := &User{Name : "xiaozhang"}
    res := db.Create(user) 
    fmt.Println(res.Error) // 返回错误信息
    fmt.Println(user.ID) // 返回插入数据的主键

Gorm查询数据

查询一条数据,使用First,查询不到会返回ErrRecordNotFound

user := &User{}
db.First(user) // select * from users order by id limit 1;

查询多条数据,使用Find不会返回错误,主要的查询方式

users := make([]*User, 0)
result := db.Where("age > ? And name IN ? And gender LIKE ?", "18", []string{"xiaozhang", "xiaoli"}, "%man%").Find(&users)
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)

使用结构体作为查询条件只会查询非零字段,即0、false、''都无法查询 如下查询为 select * from users where name = "xiaozhang"

db.Where(&User{Name : "xiaozhang", Age : 0}).Find(&users)
// 可以使用map查询
db.Where(map[string]interface{}{"Name" : "xiaozhang", "Age" : 0}).Find(&users)

Gorm更新数据

更新单个属性

根据条件更新

db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;

根据 model 的值更新

// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

根据条件和 model 的值进行更新

db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多个属性,和查询类似,结构体都有零值局限

根据 struct 更新属性,只会更新非零值的字段

db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

根据 map 更新属性

db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

Gorm删除数据

实际开发中要使用软删除, 拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录

删除一条数据

    user := &User{ID : 111}
    // user 的 ID 是 `111`
    db.Delete(&user)
    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

批量删除

    db.Where("age = ?", 20).Delete(&User{})
    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
    // 在查询时会忽略被软删除的记录
    db.Where("age = 20").Find(&user)
    // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

Gorm事务

Gorm提供了Begin、Commit、Rollback方法用于事务,在事务开启后,使用tx而不是db

Gorm提供了Transaction方法用于自动提交事务,避免用户漏写Commit、Rollback,推荐使用

// 连接数据库
tx := db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&User{Name:"name"}).Error; err != nil {
        return
    }
    return nil
}); err != nil {
    return 
}
// 提交事务
tx.Commit()

2.Kitex

安装Kitex代码生成工具(windows系统使用WSL2或虚拟机)

go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest

定义IDL

使用IDL定义服务与接口:如果我们使用RPC,就需要知道对方的接口以及需要传的参数,同时也需要知道返回值时什么样的,通过IDL可以有效约束双方的协议

namespace go api
struct Request {
    1: string message
}
struct Response {
    1: string message
}
service Echo {
    Response echo(1: Request req)
}

Client 发起请求

创建Client

import{
    "example/kitex_gen/api/echo"
    "github.com/cloudwego/kitex/client"
}
c, err := echo.NewClient("example", client.WithHostPorts("0.0.0.0.0:8888"))
if err != nil {
    log.Fatal(err)
}

3.Hertz

基本使用

服务监听8080端口并注册一个GET方法的路由函数

package main
import (
    "context"
    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main(){
    h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
    h.GET("/ping",func(c context.Context, ctx *app.RequestContext){
        ctx.JSON(consts.StatusOK, utils.H{"ping":"pong"})
    })
    h.Spin( )
}