这是我参与「第五届青训营 」伴学笔记创作活动的第5天。主要描述了Go开发web后端的三个框架,包括ORM框架Gorm,Web框架Hertz、RPC框架Kitex。
1. Gorm
一个已经迭代了十多年的ORM框架,有着非常丰富的开源拓展。
1.1 一些特性
- Gorm默认使用名为ID的字段作为主键
- 默认使用结构体的蛇形复数作为表名,比如
XxxYyy默认对应Tag为xxx_yyys - 默认使用结构体字段名的蛇形作为列名
- 使用CreatedAt、UpdatedAt字段作为创建、更新时间 Gorm目前支持的数据库有MySQL、SQLServer、PostgreSQL、SQLite,其他类型的数据库可以复用或自行开发驱动。
1.2 基本使用
链接数据库
db, err := gorm.Open(mysql.Open(dsn string),
&gorm.Config{})
if err != nil {
panic(err)
}
定义表结构体
type TableName struct {
ID uint `gorm:"id"`
Code string `gorm:"code"`
Price uint `gorm:"price"`
}
增加数据
//创建一条
p := &TableName{...}
res := db.Create(p)
fmt.Println(res.Error)
fmt.Println(p.ID)
//创建多条
ps := []*TableName{{...},{...}...}
res := db.Create(ps)
fmt.Println(res.Error)
//使用clause.OnConflict处理数据冲突
p := &Product{...}
//不处理冲突
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
另外可在定义结构体时在tag中通过设置defult标签为字段定义默认值。
查询数据
// 获取第一条记录,查询不到时会返回error,不建议使用
u := &User{}
db.Frist(u)
// 查询多条数据
users := make([]*User, 0)
result := db.Where("age > 10").Find(&users)
results.RowAffected//找到的记录数
results.Error
// 复杂查询
db.Where("name IN ?",[]string{"str1","str2"}).Find(&users)
db.Where("name LIKE ?","%jin%").Find(&users)
db.Where("name = ? AND age >= ?","jinzhu","22").Find(&users)
下面为不常用的查询方式
//使用结构体
db.Where(&User{Name:"jinzhu", Age: 0}).Find(&users)
//使用map
db.Where(map[string]interface{}{"Name":"jinzhu","Age":0}).Find(&users)
使用结构体查询时查询条件只能用非零值字段。 更新数据
//更新单个列
db.Model(&User{ID: 111}).Where("age>?",18).Update("name","hello")
//更新多个列
db.Model(&User{ID: 111}).Updates(User{Name:"hello", Age:18})//只会更新非零值
db.Model(&User{ID: 111}).Updates(map[string]interface{"name":"hello","age":18})
//select选定更新字段
db.Model(&User{ID: 111}).Select("name").Updates(map[string]interface{"name":"hello","age":18})
//SQL表达式更新
db.Model(&User{ID: 111}).Update("age",gorm.Expr("age * ? + ?", 2, 100))
删除数据
Gorm提供了软删除的方式,在定义结构体时增加字段Deleted gorm.DeletedAt便可在调用Delete()时进行软删除,将Deleted字段更新成当前时间,软删除的数据不能通过Find()查询到,可以使用Unscoped()查询
db.Unscoped().Where("age = 20").Find(&users)
1.3 事务
一种基本的写法
tx := db.Begin()//开始事务
if err = tx.Create(&User{Name:"name"}).Error; err != nil {
tx.Rollback()//回滚
return
}
...
tx.Commit() //提交事务
为了防止用户忘记写Rollback和Commit,可以使用下面一种写法
if err = db.Transaction(func(tx *gorm.DB) error{
if err = tx.Create(&User{Name:"name"}).Error; err != nil {
return err //回滚
}
...
return nil //此处提交
}); err != nil {
return
}
2. Kitex
是字节内部的Golang微服务RPC框架,具有高性能、可拓展的主要特点。
2.1 安装代码生成工具
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thrifitgo@latest
2.2 IDL
要使用RPC,我们需要接口的参数以及返回的结果,此时需要通过IDL来定义服务与接口,下面是一个简单的示例
namespace go api
struct Request{
1: string message
}
struct Response{
1: string message
}
service Echo {
Response echo(1: Request req)
}
2.3 Kitex生成代码
使用kitex -module example -service example echo.thrift命令来生成代码,文件结构如下
|-- build.sh
|-- echo.thrift
|-- handler.go
|-- kitex_gen
| '-- api
| |-- echo
| | |-- client.go
| | |-- echo.go
| | |-- invoker.go
| | '-- server.go
| |-- echo.go
| '-- k-echo.go
|-- main.go
'-- script
|-- bootstrap.sh
'-- settings.py
build.sh是构建脚本;kitex_gen是IDL内容相关的生成代码,基础的Server/Client代码;main.go入口文件;handler.go有IDL service定义的方法,需要用户再此实现。
目前Kitex的注册服务与发现已经对接了主流的服务注册与发现中心,比如ETCD、Nacos等。
3. Hertz
是字节内部的HTTP框架,具有高易用性、高性能、高拓展的特点。
3.1 路由相关
GET、POST、PUT、DELETE等请求的注册路由
h.GET("/get",func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK,"get")
})
h.POST(...)
...
定义路由组
v1 := h.Group("/v1")
{
v1.POST("/login",loginEndpoint)
v1.POST("/submit",submitEndpoint)
...
}
参数路由和通配路由,优先级:静态路由>参数路由>通配路由。
参数路由: "/hertz/:paramName" /hertz不能被匹配
通配路由:"/hertz/*paramName" /hertz能被匹配
c.Param("paramName")获取参数值
3.2 数据绑定
定义结构体,在字段名后添加query、path、header、form、json、vd等tag,
在handler函数中通过c.BindAndValidate()进行数据绑定。
3.3 中间件
通过使用Use(f func(ctx context.Context, c *app.RequestContext))函数来注册中间件,调用Use函数的可以是server也可以是路由组。