这是我参与「第五届青训营」伴学笔记创作活动的第5天。
今天的内容主要包括 Go 框架的使用,主要包括 Gorm、Kitex 和 Hertz。
1 概述
Gorm 是一个 ORM 框架,有很丰富的开源扩展。
Kitex 是字节内部的 Golang 微服务 RPC 框架,高性能、强扩展、支持多协议,有丰富的开源扩展。
Hertz 是字节内部的 HTTP 框架。
2 Gorm
2.1 常见操作
Gorm Model 结构体对应数据库中的表,结构体中的变量对应表中的属性。表名通过结构体方法 TableName() 定义。
初始化数据库连接:Gorm.Open(Mysql.Open([dsn]), &Gorm.Config{}),表示使用 mysql 驱动打开对应数据库,采用 &Gorm.Config{} 中自定义的配置。
create 方法:db.Create(&结构体名{参数列表})
select 方法:db.First(结构体指针, 查找条件) 用于查询单条记录。Find()用于查询多条记录。
update 方法:db.Model(结构体指针).Update(字段名, 值) 用于更新1个属性。db.Model(结果体指针).Updates(传参) 用于更新多个属性,传参可以是结构体或 map,但结构体只更新非零字段,map则无限制。访问表名除了使用 .Model 还可以使用 .Table,但参数为字符串。
delete 方法:db.Delete(结构体指针, 查找条件)
2.2 Gorm 的基础约定
- 使用名为 ID 的字段作为主键
- 没有 TableName() 定义表名时,默认使用结构体名的蛇形复数(snake_name)作为表名
- 字段的蛇形作为列名
- 使用 CreateAt 和 UpdateAt 作为创建、更新时间
2.3 可用数据库
Gorm 通过驱动连接数据库,目前支持 Mysql、SQL Server、PostgreSQL 和 SQLite。每个数据库对应的 dsn 不同。其它数据库需要复用或自行编写驱动。
2.4 Gorm 创建数据
可以通过 tag 实现结构体内变量和数据库内属性名称的不同。
type Product struct {
ID uint `gorm:"primarykey"`
Code string `gorm:"column: code"`
Price uint `gorm:"column: user_id"`
}
Gorm 采用链式调用,因此可以在 Create 后查看err来确认,如果涉及自增主键,也会反写回结构体。
唯一索引冲突的解决方案:Upsert
使用 clause.OnConflict() 解决
// 不处理冲突
p := &Product{Code: "D42", ID: 1}
db.Clauses(clause.OnConflict{DoNothing: ture}).Create(&p)
Gorm 采用链式调用,因此可以在 Create 后查看err来确认,如果涉及自增主键,也会反写回结构体。
默认值的设置
在结构体定义时使用 default 标签定义默认值。
type Product struct {
ID int64
Name string `gorm:"default: galeone"`
Age int64 `gorm:"default: 18"`
}
2.5 Gorm 查询数据
使用 First 查找默认是主键升序的第一个结果,查询不到时会返回 ErrRecordNotFound 错误。Find 则不会返回数据。
查询多条数据时使用 Find。Where 参数除了常见的字符串,也接受结构体和 map 结构。但是结构体会忽略零值(同2.2中 update)。
// Find
users := make([]*User, 0)
res := db.Where("Age > 3").Find(&users) // select * from users where Age > 3
fmt.Println(res.RowsAffected()) //返回找到的记录数,等价于len(res)
fmt.Pringln(res.err) // 返回error
// IN
res := db.Where("name IN ?", []string{"zhangsan", "lisi"}).Find(&users)
// LIKE
res := db.Where("name LIKE ?", "%ang%"}).Find(&users)
// 多条件
res := db.Where("name = ? AND age >= ?", "zhangsan", 15}).Find(&users)
2.6 Gorm 更新数据
更新主要涉及到 Update 和 Updates 操作。下面例子中还包括 select 和表达式的应用方法。
// 更新单个列 Update:查找 ID 为11,年龄大于18的数据,把 name 字段对应的值修改为 hello
db.Model(&User{ID: 111}).Where("age > ?", 18).Update("name", "hello")
// 更新多列 Updates
db.Model(&User{ID: 111}).Updates(User{name: "hello", age: 18})
// 使用 map 更新
db.Model(&User{ID: 111}).Updates(map[string]interface(){name: "hello", age: 18})
// 更新选定字段
db.Model(&User{ID: 111}).Select("name").Updates(User{name: "hello"})
// 表达式
db.Model(&User{ID: 111}).Updates("age", gorm.Expr("age * ? + ?", 2, 5))
2.7 Gorm 删除数据
Gorm 中的删除操作主要分为物理删除和软删除。
物理删除即数据库中直接删除
db.Delete(&User{}, 11)
db.Delete(&User{}, "11")
db.Delete(&User{}, []int{1, 2, 3})
db.Where("name LIKE ?", "%ang%").Delete(&User{})
db.Delete(&User{}, "name LIKE ?", "%ang%")
软删除是在表中定义了一个字段 Deleted,标签为gorm.DeletedAt
,系统会将删除时间 DeletedAt 设置为当前时间。使用 Unscoped 可以看到被软删除的数据(db.Unscoped().Where().Find())。
2.8 Gorm 事务
Gorm 提供了 Begin、Commit、Rollback 方法。对于事务中对数据库的操作应当使用 tx (transaction) 而非 db。
db.Transaction() 可以在返回 error 或 panic 时自动实现 Commit 和 Rollback,避免忘记手动定义。(推荐用法)
2.9 Gorm Hock
Hock 可以在操作前后执行,用于进行参数校验、结果返回等功能。在启用 Hock 时 Gorm 会自动启动一个事务来保证数据一致性。
2.10 Gorm 的性能提高
对于写操作(Create, Update, Delete)的时候,系统会自动启动默认事务,降低性能。可以通过 SkipDefaultTransaction 来关闭默认事务。
使用 PrepareStmt 可以缓存预编译语句,也可以提高运行效率。
3 Kitex
Kitex 是一个 RPC 框架。
Kitex 对 Windows 支持不完善,目前代码可以运行但是代码生成工具有待完善。
使用 IDL 的目的是定义协议和接口。
4 Hertz
Hertz 是一个 Go 的 HTTP 框架。
4.1 Hertz 路由
Hertz 提供了 GET、POST、PUT、DELETE、ANY 等方法用于注册路由。Hertz 也提供了参数路由和通配路由,优先级为:静态路由 > 命名路由 > 通配路由。其中通配路由以"*"开头,可以匹配多段。参数路由以":"开头。
4.2 Hertz 的参数绑定
参数绑定即把请求内的参数转移到结构体里。Hertz 提供了 Band、Validate、BandAndValidate 函数用于参数绑定和校验。在结构体的定义中,需要通过 go tag 的方式将结构体内的变量和参数性进行绑定。
4.3 Hertz 的中间件
Hertz 的中间件分为客户端中间件和服务端中间件。在执行通用操作时常用中间件,例如打印日志、计算接口耗时等
4.4 其它常用功能
Hertz 提供了 Hertz Client 帮助用户发送 HTTP 请求。
Hertz 提供的代码生成工具 Hz,通过定义 IDL (interface description language) 即可自动生成代码。
5 总结
Go 虽然是这几年才被更多人知道的语言,但是在框架和生态方面都已经很完善了。除了查阅文档以外,很多其它语言框架的知识也可以进行迁移。而随着开源的发展,许多框架自带了新手教程和学习案例。在框架的学习路径上也许应该发展为“编程语言 —— 框架文档 —— 框架example —— 实际应用”。