Go 框架三件套详解(Web/RPC/ORM) | 青训营笔记

43 阅读5分钟

这是我参与「第五届青训营 」笔记创作活动的第5天

重点内容

  • 应用前几节课的知识
  • 掌握三件套基本用法
  • 通过学习实战案例,可以完成Hertz/Kitex/Gorm后端开发任务

知识点介绍

Gorm的基础使用

type Product struct{
    Code  string
    Price uint
}
​
func (p Product)TableName() string {
    return "product"
}
​
func main() {
    db, err := gorm.Open(
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?参数"),
        &gorm.Config{})
    if err != nil{
        panic("failed to connect database")
    }
    // Create
    db.Create(&Product{Code: "D42", Price: 100})
    
    // Read
    var product Product
    db.First(&product, 1)                 // 根据整型主键查找
    db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录
    
    // Update
    db.Model(&product).Update("Price", 200)
    db.Model(&product).Updates(Product{Price: 200, Code: "F42"})
    db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
    
    // Delete
    db.Delete(&product, 1)
}
  • Gorm支持的数据库

    • MySql
    • SQLServer
    • PostgreSQL
    • SQLite

如何使用Upset

使用clause.OnConflict处理数据冲突

// 以不处理冲突为例,创建一条数据
p := &Product{Code: "D42", ID: 1}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)

如何使用默认值

通过使用default标签为字段定义默认值

type User struct {
    ID   int64  
    Name string `gorm:"default:galeone"`
    Age  int64  `gorm:"default:18"`
}

条件查询

db, err := gorm.Open(
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?参数"),
        &gorm.Config{})
if err != nil{
    panic("failed to connect database")
}
users := make([]*User, 0)
// IN SELECT * FROM users WHERE name IN ('qingxun', 'qingxun 2');
db.Where("name IN ?", []string{"qingxun", "qingxun 2"}).Find(&users)
// LIKE SELECT * FROM users WHERE name LIKE '%qing%';
db.Where("name LIKE ?", "%qing%").Find(&users)
// AND SELECT * FROM users WHERE name = 'qingxun' AND age > 22;
db.Where("name = ? AND age > ?", "qingxun", "22").Find(&users)
​
// SELECT * FROM users WHERE name = "qingxun";
db.Where(&User{Name: "qingxun", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "qingxun" AND age = 0;
db.Where(map[string]interface{}{"Name": "qinguxn", "age": 0}).Find(&users)

更新数据

// 条件更新单个列
// UPDATE users SET name='hello', updated_at="2013-11-17 21:34:16" WHERE age > 18;
db.Model(&User{}).Where("age > ?", 18).Update("name", "hello")
​
// 更新多个列
// 根据 ‘struct’ 更新属性,只会更新非零值
// UPDATE users SET name='hello', age=18, updated_at="2013-11-17 21:34:16" WHERE id = 111;
db.Model(&User{ID: 111}).Updates(User{Name: "hello", Age: 18})
​
// 根据 ‘map’ 更新属性
// UPDATE users SET name='hello',age=18,actived=false,updated_at="2013-11-17 21:34:16" WHERE id=111;
db.Model(&User{ID: 111}).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
​
// 更新选定字段
// UPDATE users SET name='hello' WHERE id=111;
db.Model(&User{ID: 111}).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
​
// SQL 表达式
// UPDATE "products" SET ”price"=price*2+100,updated_at="2013-11-17 21:34:16" WHERE id"= 111;
db.Model(&User{ID: 111}).Update("age", gorm.Expr("age * ? + ?", 2, 100))

删除数据

物理删除

db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;
​
db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;
​
db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);
​
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
// DELETE from emails where email LIKE "%jinzhu%";
​
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";
​

软删除

模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
​
// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
​
// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
​
查找被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

GORM事务

Gorm 提供了Begin、Commit、Rollback方法用于使用事务

db, err := gorm.Open(
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?参数"),
        &gorm.Config{})
if err != nil{
    panic("failed to connect database")
}
tx := db.Begin() // 开始事务
// 在事务中执行一些db操作(从这里开始,您应该使用‘tx’,而不是‘db')
if err = tx.Create(&User{Name: "name"}).Error;err != nil {
    tx.Rollback()
    // 遇到错误时回滚事务
    return
}
if err = tx.Create(&User{Name: "name1"}).Error; err != nil{
    tx.Rollback()
    return
}
// 提交事务
tx.Commit()

Gorm提供了Transaction方法用于自动提交事务,避免用户漏写Commit、Rollback

db, err := gorm.Open(
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?参数"),
        &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 
}

Gorm性能提高

db, err := gorm.Open(
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?参数"),
        &gorm.Config{
            SkipDefaultTransaction: true, // 关闭默认事务
            PrepareStmt:            true, // 缓存预编译语句
        })
if err != nil{
    panic("failed to connect database")
}
  • 对于写操作(创建、更新、删除),为了确保数据完整性,GORM会将他们封装在事务内部运行。但这会降低性能,你可以使用SkipDefaultTranscation关闭默认事务
  • 使用PrepareStmt缓存预编译语句可以提高后续调用速度

Kitex的基础使用

Kitex目前对Windows的支持不够完善

定义IDL

使用IDL定义服务与接口

namespace go api
​
struct Request {
    1: string message
}
​
struct Response {
    1: string message
}
​
service Echo {
    Response echo(1: Request req)
}

使用kitex -module example -service example echo.thrift命令生成代码

默认监听8888端口

Hertz的基础使用

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()
}

Hertz 提供了GET、POST、PUT、DELETE、ANY等方法用于注册路由

实践

github.com/cloudwego/k…

总结

了解Gorm/Kitex/Hertz是什么

  • Gorm:一个用于操作数据库的orm框架
  • Kitex:微服务框架
  • Hertz:对外提供api服务,提供接口聚合

熟悉了Gorm/Kitex/Hertz基础用法

  • 查阅文档

通过实战串联三个框架

  • 通过服务拆分拆分大作业

课后

  • 什么是IDL

接口描述语言(Interface description language,缩写IDL),是用来描述软件组件接口的一种计算机语言。IDL通过一种独立于编程语言的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信交流;比如,一个组件用C++写成,另一个组件用Java写成。

  • 什么是etcd

etcd(读作 et-see-dee)是一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调。etcd 有助于促进更加安全的自动更新,协调向主机调度的工作,并帮助设置容器的覆盖网络。

etcd 是许多其他项目的核心组件。最值得注意的是,它是 Kubernetes 的首要数据存储,也是容器编排的实际标准系统。使用 etcd, 云原生应用可以保持更为一致的运行时间,而且在个别服务器发生故障时也能正常工作。应用从 etcd 读取数据并写入到其中;通过分散配置数据,为节点配置提供冗余和弹性。