这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
一.GKH框架三件套
1.GORM框架
对数据的增删改茶
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"fmt"
"time"
"errors"
)
// 定义 User 数据模型,绑定数据库中 users 表
// Gorm 操作数据库,需要定义一个 struct 类型和 Mysql 表进行绑定或者叫映射
// struct 中的字段 和 Mysql 表的字段一一对应
// 在这里 User 类型可以代表 Mysql user表
type User struct {
ID int64 // 主键
// 通过在字段后面的标签说明,定义golang字段和表字段的关系
// 例如 `gorm:"column:username"` 标签说明含义是: Mysql 表的列名为 username
// 这里 golang 定义的 Username 变量和 Mysql 表字段 username 一样,他们的名字可以不一样。
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
CreateTime int64 `gorm:"column:createtime"`
}
// 设置表名,可以通过给 struct 类型定义 TableName 函数,返回当前 struct 绑定的 Mysql 表名是什么
func (u User) TableName() string {
// 绑定 Mysql 表名为 users
return "users"
}
func main() {
// 配置 MySQL 连接参数
username := "root" //账号
password := "123456" //密码
host := "127.0.0.1" //数据库地址,可以是Ip或者域名
port := 3306 //数据库端口
Dbname := "codebaoku" //数据库名
// 通过前面的数据库参数,拼接 Mysql DSN,其实就是数据库连接串(数据源名称)
// Mysql dsn格式: {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local
// 类似{username}使用花括号包着的名字都是需要替换的参数
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
username, password, host, port, Dbname)
// 连接 Mysql
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("连接数据库失败, error=" + err.Error())
}
// 定义一个用户,并初始化数据
u := User{
Username:"codebaoku",
Password:"123456",
CreateTime:time.Now().Unix(),
}
// 插入一条用户数据
// 下面代码会自动生成SQL语句:INSERT INTO `users` (`username`,`password`,`createtime`) VALUES ('codebaoku','123456','1540824823')
if err := db.Create(&u).Error; err != nil {
fmt.Println("插入失败", err)
return
}
// 查询并返回第一条数据
// 定义需要保存数据的struct变量
u = User{}
// 自动生成sql: SELECT * FROM `users` WHERE (username = 'codebaoku') LIMIT 1
result := db.Where("username = ?", "codebaoku").First(&u)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println("找不到记录")
return
}
// 打印查询到的数据
fmt.Println(u.Username,u.Password)
// 更新数据库记录
// 自动生成Sql语句: UPDATE `users` SET `password` = '654321' WHERE (username = 'codebaoku')
db.Model(&User{}).Where("username = ?", "codebaoku").Update("password", "654321")
// 删除数据库记录
// 自动生成Sql: DELETE FROM `users` WHERE (username = 'codebaoku')
db.Where("username = ?", "codebaoku").Delete(&User{})
}
注意事项:
- First查询会出现ErrREcordNotFound,可以使用FInd查询多条数据,返回不到不会返回错误。
- 如果要设置0值,使用map集合
物理删除和软删除:
使用gorm.DeletedAt实现软删除,并记录删除时间为当前时间
使用Unsecoped可以查询被软删的数据
GORM事务处理
- 自动事务处理
通过 db.Transaction 函数实现事务,如果闭包函数返回错误,则回滚事务。 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') 返回任何错误都会回滚事务
db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
- 手动事务处理
HOOK
在CRUD之前后处理一些事情,如果任何Hook出现问题,则会停止操作并回滚
GORM性能提高
因为在写操作的时候默认开启了事务,所以在一些简单的场景时,可以关闭事务
在连接数据库时,设置参数SkipDefaultTransac为true
2.Hertz框架
HTTP调用
基本使用
h := server.Default(server.WithHostPorts("127.0.0.1:8848))
h.GET("/PING", func(){})
h.Spin()
使用流程
- 使用IDL,这里使用 Thrift 作为 IDL 的定义(也支持使用 Protobuf 定义的 IDL)这个服务有一个 API: Hello,它的请求参数是一个 query,响应是一个包含一个 RespBody 字段的 Json。
- 直接生成代码,在hello_service.go中去完成代码实现
3.Kitex框架
RPC(Remote Procedure Call Protocol)远程过程调用协议。一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。 比较正式的描述是:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
序列化
Kitex 框架及命令行工具,默认支持 thrift 和 proto3 两种 IDL,对应的 Kitex 支持 thrift 和 protobuf 两种序列化协议。传输上 Kitex 使用扩展的 thrift 作为底层的传输协议(注:thrift 既是 IDL 格式,同时也是序列化协议和传输协议)。IDL 全称是 Interface Definition Language,接口定义语言。
生成代码
客户端Client
创建
上述代码中,echo.NewClient 用于创建 client,其第一个参数为调用的 服务名,第二个参数为 options,用于传入参数,此处的 client.WithHostPorts 用于指定服务端的地址。
调用
创建请求
通过获取的Clinet对象的Echo方法调用
client, err := hello.NewClient("hello", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
for {
req := &api.Request{Message: "my request"}
resp, err := client.Echo(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Println(resp)
time.Sleep(time.Second)
addReq := &api.AddRequest{First: 512, Second: 512}
addResp, err := client.Add(context.Background(), addReq)
if err != nil {
log.Fatal(err)
}
log.Println(addResp)
time.Sleep(time.Second)
}