这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天,本次是针对Gorm、Kitex、Hertz三件套的学习。
三件套:Gorm、Kitex、Hertz
Gorm是一个功能强大的ORM框架
Kitex是Golang微服务RPC框架
Hertz是HTTP框架
一、Gorm的基本使用 (gorm.cn/zh_CN/docs)
go get "gorm.io/driver/mysql"
go get "gorm.io/gorm"
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 定义gorm model
type Product struct {
Code string
Price uint
}
// 为model定义表名
func (p Product) Tablename() string {
return "product"
}
func main() {
//初始化数据库连接
db, err := gorm.Open( //使用gorm.Open去传递一个mysql.Open,gorm根据不同的驱动支持不同的数据库
mysql.Open("root:123456@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
//通过&gorm.Config{}去传递一些自定义配置
&gorm.Config{})
if err != nil { //判断数据库是否连接成功
panic("failed to connect database") //panic里面可以加一些错误信息
}
//支持创建一条和多条数据
//创建一条数据时,传递的是一个对象
//创建多条数据时,传递的是一个切片(数组)
db.Create(&Product{Code: "D42", Price: 100})
var product Product
db.First(&product, 1) //支持查一条数据(根据整形主键查找),传入的是product指针(gorm把查询到的字段反省回到指针,如果不是指针,写不回去)
db.First(&product, "code=?", "D42") //后面是传递条件(查找code字段为D42的值)(First方法查询一条,Find方法可以查询多条)
//更新数据(在Model上面已经定义了tableName),用 .Model去传递这个结构体;另外一种是使用 .Table,传递的是一个字符串
db.Model(&product).Update("price", 200) //更新一列数据
//更新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) //支持传递一个结构体,结构体本身有一个默认零值(gorm下使用结构体,仅更新非零值)
//更新零值,传递一个map
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //支持传递一个map
//删除(先一个结构体,再条件)
db.Delete(product, 1)
}
Gorm官网:gorm.cn/zh_CN/docs/
Gorm的约定(默认)
- gorm使用名为ID的字段作为主键,
- 使用结构体的蛇形复数(生成的表是复数形式)作为表名,字段名的蛇形作为列名,
- 使用CreateAt、UpdateAt字段作为创建、更新时间
支持的数据库:MySQL、SQLServer、PostgreSQL、SQLite
Gorm通过驱动连接数据库,如果需要连接其他类型的数据库,可以复用/自行开发驱动
四个数据库驱动程序,如果要连接到这些数据库,则需要导入不同的驱动程序包并定义不同的格式
MySQL
- 导入:import " github.com/jinzhu/gorm/dialects/mysql" 或者导入" github.com/go-sql-driver/mysql"
- DSN:"用户:密码@/dbname?charset = utf8 \ parseTime = True \ loc = Local"(用户是指用户名,密码是指密码,dbname是数据库名)
SQLServer
- 导入:import _" github.com/jinzhu/gorm/dialects/postgres"
- DSN:" sqlserver://用户名:密码@主机:端口?数据库= dbname"(用户名是指用户名,密码是指密码,主机是指主机地址,端口是指端口号,数据库是指数据库名称)
Sqlite3
导入:import _" github.com/jinzhu/gorm/dialects/sqlite"
DSN:连接到Sqlite3数据库的DSN只需要指定Sqlite3数据库文件的路径,例如:/tmp/gorm.db
PostgreSQL
导入:import _" github.com/jinzhu/gorm/dialects/mssql"
DSN:主机= myhost端口= myport用户= gorm dbname = gorm密码= mypassword(//host指主机地址,port指端口号,user指用户名,dbname指数据库名,password指密码)
type Good struct {
ID uint `gorm:"primarykey"` //指定主键
Code string `gorm:"column:code"`
Price uint `gorm:"column:price"`
}
使用clause.OnConflict处理数据冲突
//以不处理冲突为例,创建一条数据
p :=&Good{Code:"GSSS",ID:1}
db.Clauses(clause.OnConflict{DoNothing:true}).Create(&p) //调用Create、Delete、Update,这些都是finish的api,前面都是组合api,当调用这些动词api时(finish api),是真正地执行sql语句,如果.Create()后面再加上.Where()条件的话,是不生效的,因为调用到Create时就已经执行完了
使用default标签字段定义默认值
type User struct{
ID int64
Name string 'gorm:"default:galeone"'
Age int64 '"gorm:default:18"'
}
查询数据:gorm.cn/zh_CN/docs/…
First:默认查一条数据,返回第一条查询到的数据,按照主键的升序,如果查询不到数据,就会返回ErrRecordNotFound
Find:查询多条数据,查询不到数据,返回一个空数组,不会返回err
结构体:当使用结构体作为条件查询时,gorm只会查询非零字段,即如果字段值为0,"",false或其他零值,该字段不会被用于构建查询条件,(更新字段类似)使用Map来构建查询条件(有零值时,也可以使用gorm提供的select的api(执行字段))
//select * from users where name=”xxx";
db.Where(&User{Name:"xxx",Age:0}).Find(&user) //传递结构体,查询时,因为结构体的零值问题,不会把零值的条件加进去
//select *from users where name="xxx" and age=0;
db.Where(map[string]interface{}{Name:"xxx",Age:0}).Find(&user) //传递map,会处理零值的问题
更新数据:gorm.cn/zh_CN/docs/…
更新单个列:
//update users set name="hello" where age>10;
db.Model(&User{ID:11}).Where("age>?",10).Update("name","hello") //使用Update时需要使用Model或者Table指定表名,否则报错
更新多个列(结构体+Updates):
//update users set name="hello",age=10,where id=11
db.Model(&User{ID:11}).Updates(User{Name:"hello",Age:10})
更新多个列(map+Updates):
//update users set name="hello",age=10,where id=11
db.Model(&User{ID:11}).Updates(map[string]interface{}{Name:"hello",Age:10})
更新选定字段
//update users set name="hello",where id=11
db.Model(&User{ID:11}).Select("name").Updates(map[string]interface{}{Name:"hello",Age:10})
SQL表达式更新
//update "users" set "age=age*2+100",where "id=1";
db.Model(&User{ID:1}).Update("age",gorm.Expr("age*?+?",2,100))
使用struct更新时,只会更新非零值,如果需要更新零值,可以使用Map更新或使用Select选择字段
删除数据: gorm.cn/zh_CN/docs/…
物理删除:(从数据库中真正删除)
db.Delete(&User{},10) //delete from users where id=10
db.Delete(&User{},"10") //delete from users where id=10 (默认识别id为主键)
db.Delete(&User{},[]int{1,2,3}) //delete from users where id in (1,2,3)
db.Where("name like ?","xx").Delete(User{}) //传入的是结构体,结构体已经实现了tablename,不需要调用.Model或者.tableName
db.Delete(User{},"name like ?","xx")
软删除:
gorm提供了gorm.DeleteAt实现软删
拥有软删能力的Model调用Delete时,记录不会从数据库中真正删除,但gorm会将DeleteAt置为当前时间,之后不能通过正常的查询方法找到该条记录,可以使用Unscoped查询到被软删的数据。
赋予软删功能,在结构体定义中加入:Deleted gorm.DeleteAt
事务:
Begin、Commit、Rollback
tx :=db.Begin() //开启事务,返回一个gorm对象,一定要返回这个对象,使用tx,而不是db,db.Begin()执行两个操作,一是固化一个连接,要保证使用事务的时使用的是同一个链接,golang底层对数据库操作使用的是连接池。
tx.Rollback() //回滚事务
tx.Commit() //提交事务
gorm提供Transaction方法用于自动提交事务,避免用户漏写Commit、Rollback
db, err := gorm.Open(
mysql.Open("root:123456@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err != nil {
panic("failed to connect database")
}
if err :=db.Transaction(func(tx *gorm.DB) error{
if err=tx.Create(&User{Name:"name"}).Error;err!=nil{
return err
}
if err=tx.Create(&User{Name:"name1"}).Error;err!=nil{
tx.Rollback()
return err
}
return nil
});err!=nil{
return
}
hook
使用hook时,会添加默认事务,如果任何hook返回错误,gorm将停止后续的操作并回滚事务
生态:
| gorm代码生成工具 | github.com/go-gorm/gen |
|---|---|
| gorm分片库方案 | github.com/go-gorm/sha… |
| gorm手动索引 | github.com/go-gorm/hin… |
| gorm乐观锁 | github.com/go-gorm/opt… |
| gorm读写分离 | github.com/go-gorm/dbr… |
| gorm OpenTelemetry扩展 | github.com/go-gorm/ope… |
二、Kitex(www.cloudwego.io/zh/docs/kit…)
安装:
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest
kitex服务默认监听8888端口
IDL:
使用IDL定义服务与接口如果要进行RPC,需要知道对方接口、参数、返回值等,这就需要通过IDL来约定双方的协议。
生成代码:kitex -module example -service example echo.thrift
build.sh----构建脚本
kitex_gen-----IDL内容相关的生成代码,主要是基础的Server/Client代码
main.go-----程序入口
handler.go------用户在该文件里实现IDL Service定义的方法
三、Hertz(www.cloudwego.io/zh/docs/her…)
Hertz提供了参数路由和通配路由,路由的优先级为:静态路由>命名路由>通配路由