这是我参加青训营的第 5 天
Gorm
基本使用
创建表
type Product struct {
ID uint `gorm:"primarykty"` // 设置表主键
Code string `gorm:"column: code"` // 别名
price Uint `gorm:"column: user_id"`// 别名
}
func main() {
db, err := gorm.Open(mysql.Open("username:password@tcp(localhost:9910)/database?charset=utf8"), &gorm.Config{}) // charset指的是编码格式
// 创建一条数据
p := &Product{Code: "D42"}
res := db.Create(p)
// 创建多条数据
products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}
res := db.Create(products)
}
如何使用默认值
type User struct {
ID int64
Name string `gorm:"default: galeone"` // 默认值
Age int64 `gorm:"default: 18"` // 默认值
}
查询数据
// 获取第一条数据(主键升序),若查询不到则返回 ErrRecorNotFound
u := &User{}
db.First(u) // select * from u order by id limit 1
// 查询多条数据
users := make([]*User, 0)
db.Where("Age > 10").Find(&users) // select * from users where Age > 10
fmt.Println(result.RowsAffected) // 返回找到的记录数,相当与`len(users)`
db.where("name IN ?", []string{"zhangsan", "lisi"}).Find(&users)
db.where("name LIKE ?", "%a%").Find(&users)
db.where("name = ? AND age = ?").Find(&users)
db.where(&User{Name: "zhangsan", Age: 0}).Find(&users)
db.where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
注意: 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果字段值为0、“”、false 或其他零值,该字段不会被用于构建查询条件,使用 Map 来构建查询条件
更新数据
注意:使用 Struct 更新时,只会更新非零值,如果需要更新零值可以使用 Map 更新或使用 Select 选择字段
删除数据
物理删除
软删除
注意:GORM 提供了 gorm.DeletedAt 用于帮助用户实现软删除
拥有软删除能力的 Model 调用 Delete 时,记录不会被从数据库中真正删除。 但 GORM 会将 DeletedAt 置为当前时间,并且你不能再通过正常的查询方法找到该记录。
使用 Unscoped 可以查询到被软删除的数据
事务
需要通过事务保证数据的一致性
Gorm 提供了 Begin、Commit、Rollback 方法用于使用事务
Begin():先固化连接,为了保证使用事务的时候是同一个连接,因为Golang对数据库操作使用的是连接池,然后执行开启事务的sql语句
Gorm 提供了 Tansaction 方法用于自动提交事务,防止用户因为漏写Commit、Rollback而引发的事故
db.Transaction(func(tx *gorm.DB) error {})
Hook
Gorm 在提供了 CURD 的 Hook 能力
Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数。
如果任何 Hook 返回错误,GORM 将停止后续的操作并回滚事务
性能提高
对于写操作(创建、更新、删除) ,为了确保数据的完整性, GORM 会将他们封装在事务内运行。但这会降低性能,可以使用 SkipDefaultTransaction 关闭默认事务
使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度
db, err := gorm.Open(mysql.Open("username:password@tcp(localhost:9910)/database?charset=utf8"), &gorm.Config{
SkipDefaultTransaction: true, // 关闭默认事务
PrepareStmt: true // 缓存预编译语句
})
Kitex
Kitex 目前对 Windows 的支持不完善,Windows 建议使用虚拟机
安装代码生成工具
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest
定义IDL
用途:如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样
简单定义一个IDL:
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Request req)
}
生成代码
build.sh:构建脚本
kitex_gen:内容相关的生成代码,主要是基础的 Server/Client 代码
main.go:程序入口
handler.go:用户在该文件里实现 IDL service 定义的方法
基本使用
服务默认监听8888端口
发起请求
-
创建 Client
-
发送请求
服务发现与注册
当使用微服务的时候通常使用服务发现与注册调用下游微服务
目前 Kitex 的服务注册与发现已经对接了主流的服务注册与发现中心,如ETCD, Nacos等
Kitex 底层实现会加一个缓存,并且后台会有一个更新对象逻辑和删除对象逻辑,防止对象失效。
Hertz
基本使用
使用 Hertz 实现,服务监听 8080 端口并注册了一个 GET 方法的路由函数
package main
import (...)
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()
}
server.Default: 会集成一个中间件Recover
路由
Hertx 提供了 GET、POST、PUT、DELETE、ANY 等方法用于注册路由
Hertz 提供了路由组(Group)的能力,用于支持路由分组的功能
v1 := h.Group("/v1"){
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
}
Hertz 提供了参数路由和通配路由,路由的优先级为:静态路由 > 命名路由 > 通配路由
Hertz 提供了 Bind、Validata、BindAndValidate 函数用于进行参数绑定和校验
中间件
Hertx 的中间件主要分为客户端中间件与服务端中间件
-
终止中间件调用链的执行函数
- c.Abort
- c.AbortWithMsg
- c.AbortWithStats
展示服务端中间件:
h.Use(MyMiddleware()) :注册全局中间件,每个路由前后都打印
Hertz 提供了 HTTP Client 用于帮助用户发送 HTTP 请求
代码生成工具
Hertz 提供了代码生成工具 Hz,通过定义 IDL 文件即可生成对应的基础服务代码
namespace go hello.example
struct HelloReq {
1: string Name (api.query="name");
}
struct HelloResp {
1: string RespBody;
}
service HelloService {
HelloResp HelloMethod(1: HelloReq request) (qpi.get="/hello")
}