Go框架三件套 | 青训营笔记

57 阅读3分钟

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

Gorm

  • 基本操作
package main
​
import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)
​
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?charset=utf8mb4&parseTime=True&loc=Local"),  
      &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 - 将 product 的 price 更新为 200
  db.Model(&product).Update("Price", 200)
  // Update - 更新多个字段
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
​
  // Delete - 删除 product
  db.Delete(&product, 1)
}
  • 可以用clause.OnConflict 处理数据冲突情况 如:

    p := &Product{Code: ''D42", ID:1}

    db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)

    此为不处理冲突,直接插入

  • 查询:用First 和 Where + Find 进行查询,业务开发中一般用后者,前者在没有数据的时候会返回Error,而后者只是返回空数组

  • 在用struct进行查询时,零值将不会被列入查询条件中;用map可以解决这个问题

    // SELECT* FROM users WHERE name ="jinzhu";
    db.Where(&User{Name:"jinzhu",Age: 0).Find(&users)
    // SELECT * FROM users WHERE name ="jinzhu" AND age = 0;
    db.Where(map[string]interface{}{"Name":"jinzhu""Age": 0)).Find(&users)
    
  • 更新:除了能用Select()选择只更新的字段,其他同上,这样看起来平常使用map能防止一些不必要的错误

  • gorm提供了一种表达式更新

    // SQL 表达式更新
    // UPDATE "products" SET "price" = price * 2 + 100"updated at : 2013-11-17 21:34:10' WHERE "id" = 3
    db.Model(&User{ID: 111)).Update( column: "age", gorm,Expr( expr: "age * ? + ?", args... 2, 100))
    
  • 删除:使用 Where + delete 即可,但除了这种物理删除,GORM也提供了 gorm.DeleteAt 来进行软删除,可通过Unscoped 来进行恢复.

type User struct {
    ID      int64
    Name    string `gorm:"default:galeone"`
    Age     int64  `gorm:"default:18"`
    Deleted gorm.DeletedAt
}
  • 事务:使用Begin开启一个连接,此时使用Begin返回的变量tx,而非调用的db;使用Rollback回滚,Commit提交;当然Gorm也提供了Transaction来开启自动提交事务;包括Hook
  • SkipDefaultTransaction false 关闭默认事务 PrepareStmt true开启预编译
  • GORM生态非常丰富,包括且不限于锁,读写分离,索引等等. 这些去官方文档可以查到很多

Kitex

  • 基于IDL:在 RPC 框架中,我们知道,服务端与客户端通信的前提是远程通信,但这种通信又存在一种关联,那就是通过一套相关的协议(消息、通信、传输等)来规范,但客户端又不用关心底层的技术实现,只要定义好了这种通信方式即可。
package main
​
import (
    "context"
    'example/kitex_gen/api"
)
// EchoImpl implements the last service interface defined in the IDL
type EchoImpl structf{}
// Echo implements the EchoImpl interface .
func (s *EchoImpl) Echo(ctx context.(ontext, reg *api.Request) (resp *api.Response, err error) {
    // TODO: Your code here ...
    return
}
  • 同样,Kitex也拥有非常多的扩展,ETCD,Nacos等服务注册,具体可以去官网查看文档

Hertz

package main
​
import (
    "context"
​
    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)
​
func main() {
    h := server.Default()
​
    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
            ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
    })
​
    h.Spin()
}
  • 路由
    • Hertz提供了GET,POST,DELETE,PUT,PATCH等路由注册,也拥有路由组(Group)的功能,用于支持路由分组的功能,同时中间件也可以注册到路由组上
    • 三种路由类型优先级: 静态 > 命名 > 通配
  • 函数 :Hertz 提供了 Bind、Validate、BindAndValidate 函数用于进行参数绑定和校验

个人总结

今天课程讲了开发三大件GORM 数据库映射,Kitex RPC框架,Hertz http框架,虽然我们项目用的是go-zero,但也能从类似框架上看到一样的模式,课程中结合实战的例子讲了这几个框架的串联,将整个项目的技术栈串联起来,给人更有实际开发的流程感觉.