这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
Go框架三件套(Web/RPC/ORM)
一、三件套介绍
Gorm 是 ORM框架
- 开发人员只需关系结构体,操作结构体,无需关注如何操作数据库
- 提高开发效率
- 使用反射牺牲性能,牺牲灵活性
- 保护数据库结构,操作结构体字段不会对数据库产生影响
Kitex 是 RPC框架
- 高性能,使用自研的高性能网络库Netpoll
- 强扩展,提供较多扩展接口,也可自行扩展
- 支持多协议,Thrift、Kitex Protobuf、gRPC、TTHeader、HTTP2
Hertz 是 HTTP框架
二、三件套使用
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( )
}