Go框架三件套(Web/RPC/GORM)一 | 青训营

169 阅读6分钟

Go框架三件套(Web/RPC/GORM)一 | 青训营

1.前言

1.1 Gorm

ORM(Object Relational Mapping, 对象关系映射),主要作用是在编程中,把面向对象的概念和数据库中表的概念对应起来;Gorm是一个已经迭代了10年+的功能强大的ORM框架,在字节内部被广泛使用并且拥有非常丰富的开源扩展。

1.2 Kitex

Kitex是字节内部的Golang微服务RPC框架,具有高性能、强可扩展的主要特点,支持多协议并且拥有丰富的开源扩展。

1.3 Hertz

Hertz是字节内部分HTTP框架,参考了其他开源框架的优势,结合字节跳动内部的需求,具有高易用性、高性能、高扩展性特点

2.GROM

2.1 GROM特性

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

2.2 驱动方式链接

GORM 是通过驱动的方式来连接数据库的,目前支持 Mysql、SQLSever、PostgreSQL、SQLite。如果需要连接其他类型的数据库,可以 复用/自行 开发驱动。

  • 驱动,是指软件驱动程序。是一种中间件,它能够将应用程序与硬件或其他系统之间的信息进行转换。在数据库领域中,驱动程序就是一种软件,它能够将应用程序与数据库之间的信息进行转换。
  • 驱动程序通常提供一组标准的数据库连接接口。这些接口包括连接数据库、执行 SQL 语句、获取结果集等。应用程序可以使用这些接口与数据库进行交互。

在 GORM 中,驱动程序是将 Go 语言与数据库之间的信息进行转换的软件。 GORM 库使用驱动程序来连接数据库,并使用驱动程序提供的接口来进行数据库操作。这样,GORM 库就不用关心数据库是哪种数据库,只要有驱动程序就可以连接数据库。驱动是一种软件,它能够将 Go 语言与数据库之间的信息进行转换。驱动通过一组标准的数据库连接接口来提供连接数据库的功能。

GORM 的设计原则是对数据库访问进行封装,而不是对 SQL 语句进行封装。这样可以让开发人员专注于业务逻辑,而不用关心数据库的细节。以驱动的方式链接数据库的好处是:

  • 使用驱动可以支持多种数据库,而不用对 GORM 进行修改。
  • 使用驱动可以更好地支持数据库的新特性。
  • 使用驱动可以更好地进行数据库性能优化。

2.3 DSN

DSN(Data Source Name)的缩写,是一种数据库连接字符串,它提供了连接数据库所需的所有信息,通常包括数据库类型、数据库名称、用户名、密码和连接地址等信息。例如对于 mysql 数据库来说,DSN 格式如user:password@tcp(host:port)/dbname

参数名含义
user数据库用户名
password数据库密码
host数据库地址
port数据库端口
dbname数据库名称

DSN 字符串的格式可能因数据库类型而异。使用DSN字符串连接数据库是一种常用的方式,因为它简化了连接数据库的过程,并且使用DSN字符串连接的数据库驱动程序通常支持多种数据库类型,这样就不用写多个连接数据库的函数了。

2.4 安装和简单实用

2.4.1 GORM安装

使用下面的命令进行安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
Quick Start
package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

// 创建数据模型
type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // 迁移 schema
  db.AutoMigrate(&Product{})

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

2.4.2 Mysql 案例

案例说明
  1. 在 main 函数中,我们通过调用 gorm.Open("mysql", "root:password@/dbname?charset=utf8&parseTime=True&loc=Local") 来建立数据库连接。
  2. 我们使用 db.AutoMigrate(&Product{}) 来自动迁移数据库结构,即自动根据 Product 结构体创建数据表。
  3. 我们使用 db.Create(&Product{Name: "面包", Price: 3}) 来创建一条数据。
  4. 使用 db.Find(&products) 来查询所有数据并将其存入 products 变量中。
  5. 我们使用 db.Model(&Product{}).Where("name = ?", "面包").Update("price", 4) 来更新名称为 "面包" 的产品的价格。
  6. 我们使用 db.Where("name = ?", "面包").Delete(&Product{}) 来删除名称为 "面包" 的产品。
  7. 最后,通过 defer db.Close() 关闭数据库连接。
案例程序
package main

import (
    "fmt"

    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

// 定义数据模型
type Product struct {
    ID    uint   `gorm:"primary_key"`
    Name  string `gorm:"size:255"`
    Price uint
}

func main() {
    // 建立数据库连接
    db, err := gorm.Open("mysql", "root:password@/dbname?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        panic("连接数据库失败")
    }
    defer db.Close()

    // 自动迁移数据库结构
    db.AutoMigrate(&Product{})

    // 创建一条数据
    db.Create(&Product{Name: "面包", Price: 3})

    // 查询所有数据
    var products []Product
    db.Find(&products)
    fmt.Println("所有产品:", products)

    // 更新一条数据
    db.Model(&Product{}).Where("name = ?", "面包").Update("price", 4)

    // 删除一条数据
    db.Where("name = ?", "面包").Delete(&Product{})
}
可以使用gorm.Open()函数打开数据库连接db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")

2.4.3 GORM用法

    // 创建数据库记录
    db.Create(&Product{Code: "L1212", Price: 1000})

    // 读取数据库记录
    var product Product
    db.First(&product, 1) // find product with id 1

    // 更新数据库记录
    db.Model(&product).Update("Price", 2000)

    // 删除数据库记录
    db.Delete(&product)

2.4.4 解决数据冲突

在 GORM 中,使用 OnConflict 方法可以解决数据冲突问题,指定在冲突时应该采取的操作。一般来说,在执行插入操作时可能会因为主键重复而导致数据冲突。使用 OnConflict 方法可以解决这个问题。db.Save(&Product{Name: "面包", Price: 3}).OnConflict("(name) DO UPDATE SET price = excluded.price").这行代码表示如果 name 列重复时,将执行更新操作来更新 price 列。还可以使用 OnConflict("(name) DO NOTHING") 来让重复的数据不做任何操作。

2.4.5 使用默认值

1) GORM 支持在数据库中设置默认值。这可以通过在结构体中使用 "default" 标签来实现。
    type Product struct {
        gorm.Model
        Name  string
        Price int `gorm:"default:0"`
    }
在创建或更新数据时,如果没有指定 Price 列的值,那么会自动将其设置为 0。
2) 默认值还可以使用SQL表达式和函数来进行设置。如果你想让 created_at 列默认值为当前时间,你可以这样定义:
    type Product struct {
        gorm.Model
        Name  string
        Price int `gorm:"default:0"`
        CreatedAt  time.Time `gorm:"default:now()"`
    }
设置默认值是非常有用的,它可以帮助你简化代码并减少错误。