day8 Go框架三件套(Web/RPC/ORM) -- 介绍与GORM | 青训营笔记

346 阅读16分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天,本文介绍了三个 Go 开发框架 GORM,Kitex,Hertz之一GORM 的基本使用方法.

一.介绍

1. Gorm

Gorm是一个迭代了十年的功能强大的Go语言编写的ORM框架.

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,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.Kitex

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

  • 高性能

    使用自研的高性能网络库 Netpoll,性能相较 go net 具有显著优势。

  • 扩展性 提供了较多的扩展接口以及默认扩展实现,使用者也可以根据需要自行定制扩展,具体见下面的框架扩展。

  • 多消息协议

    RPC 消息协议默认支持 ThriftKitex ProtobufgRPC。Thrift 支持 Buffered 和 Framed 二进制协议;Kitex Protobuf 是 Kitex 自定义的 Protobuf 消息协议,协议格式类似 Thrift;gRPC 是对 gRPC 消息协议的支持,可以与 gRPC 互通。除此之外,使用者也可以扩展自己的消息协议。

  • 多传输协议

    传输协议封装消息协议进行 RPC 互通,传输协议可以额外透传元信息,用于服务治理,Kitex 支持的传输协议有 TTHeaderHTTP2。TTHeader 可以和 Thrift、Kitex Protobuf 结合使用;HTTP2 目前主要是结合 gRPC 协议使用,后续也会支持 Thrift。

  • 多种消息类型

    支持 PingPongOneway双向 Streaming。其中 Oneway 目前只对 Thrift 协议支持,双向 Streaming 只对 gRPC 支持,后续会考虑支持 Thrift 的双向 Streaming。

  • 服务治理

    支持服务注册/发现、负载均衡、熔断、限流、重试、监控、链路跟踪、日志、诊断等服务治理模块,大部分均已提供默认扩展,使用者可选择集成。

  • 代码生成

    Kitex 内置代码生成工具,可支持生成 ThriftProtobuf 以及脚手架代码。

3.Hertz

Hertz是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttpginecho 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点.

  • 高易用性

    容易上手,开箱即用,且不缺乏灵活性

  • 高性能

    Hertz 默认使用自研的高性能网络库 Netpoll,在一些特殊场景相较于 go net,Hertz 在 QPS、时延上均具有一定优势。关于性能数据,可参考下图 Echo 数据。

  • 高扩展性

    Hertz 采用了分层设计,提供了较多的接口以及默认的扩展实现,用户也可以自行扩展。

  • 多协议支持

    Hertz 框架原生提供 HTTP1.1、ALPN 协议支持。除此之外,由于分层设计,Hertz 甚至支持自定义构建协议解析逻辑,以满足协议层扩展的任意需求。

  • 网络层切换能力

    ertz 实现了 Netpoll 和 Golang 原生网络库 间按需切换能力,用户可以针对不同的场景选择合适的网络库,同时也支持以插件的方式为 Hertz 扩展网络库实现。

4.ORM

ORM全称是:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。一个对象就对应一张表,使用ORM,我们可以在不直接使用SQl语句的情况下,使用数据库.

5.RPC

RPC(Remote Procedure Call)远程过程调用协议,是调用另一个地址的存储过程或者函数,而不用显式编码这次远程调用的协议.RPC一般是网络从远程计算机上请求服务,它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。
它是可以像调用本地程序一样调用远程服务,隐藏了底层网络技术的协议,可以显著提高吞出量而代价不高.
在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

二. GORM

1.Gorm的基础操作

阅读 gorm.cn/docs/#Insta…
代码相关来源gorm.cn/zh_CN/docs/…

使用之前,因为Grom是第三方库,所以先安装 GORM 及需要连接对应数据库的驱动。
我们使用mysql连接作为展示.
mysql需要导入"gorm.io/driver/mysql"和"gorm.io/gorm"

image.png

安装

go get -u gorm.io/gorm   
go get -u gorm.io/driver/mysql

快速入门

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
)

type Product struct { //定义gorm model
   gorm.Model
   Code  string
   Price uint
}

func (p Product) TableName() string { //为model定义表名
   return "product"
}

func main() {
   log := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(log), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

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

   // Create(创建数据)
   db.Create(&Product{Code: "D42", Price: 100}) //创建多条数据传入切片

   // Read(查询数据)
   var product Product
   db.First(&product, 1) // 根据整型主键查找
   //传入指针,因为Grom要将查到的字段反写入结构体中(存在问题后面会介绍)
   db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

   //(更新数据)
   // Update - 将 product 的 price 更新为 200
   //或者使用db.Table()
   db.Model(&product).Update("Price", 200) //model()定义TableName()已经确定表名
   // 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)
   db.Delete(&product, "code = ?", "D42")
}

2.GORM支持的数据库

Gorm 官方支持的数据库类型有:MySQL(MariaDB), PostgreSQL, SQlite, SQL Server。

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

//连接SQLServer数据库
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
db,err := gorm.Open(sqlserver.Open(dsn),&gorm.Config{})

GORM 通过驱动来连接数据库,如果需要连接其他类型的数据库,可以复用/自行开发驱动.

什么是DSN

Data Source Name (DSN)的PDO命名惯例为:PDO驱动程序的名称,后面为一个冒号,再后面是可选的驱动程序连接数据库变量信息,如主机名、端口和数据库名。

下面以MySQL为例:mysql:host=localhost;dbname=testdb。

  • DSN 前缀 : 前缀是mysql0
  • host : 主机上的数据库服务器
  • port : 主机上数据库服务器监听的端口号。
  • dbname : 数据库的名称。
  • unix_socket : MySQL的UNIX套接字(不应该被用于主机或端口)。
  • charset : 字符集设置,请参考字符集设置.

3. GORM创建数据

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
)

type Product struct { //定义gorm model
   ID    uint   `gorm:"primarykey"`
   Code  string `gorm:"column:code"`
   Price uint   `gorm:"column:user_id"`
}

func main() {
   dsn := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

   p := &Product{
      Code: "D42",
   }
   //db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
   res := db.Create(p)
   fmt.Println(res.Error) //获取err
   fmt.Println(p.ID)      //返回插入数据的主键

   Products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}
   res = db.Create(Products)
   fmt.Println(res.Error) //获取err
   for _, p := range Products {
      fmt.Println(p)
   }
}

1.表的名字

  • 结构体的名字为表的名字,如果为单词,则结构体名字的复数为表的名字
  • 修改表名字,使用下面的函数,继承接口就能修改表名
func (p Product) TableName() string {
   return "user"
}

2.db.create(val interface{}) *gorm.DB

接收一个结构体指针,将结构体中的数据插入表中,并将表的主键反写回结构体中,返回一个gorm.
gorm常用两个成员:

  • gorm.DB.Error // 返回 error
  • gorm.DB.RowsAffected // 返回插入记录的条数

3.如何使用Upset

//以不冲突为例,创建一条数据
//使用clause.OnConflict处理数据冲突
p := &Product{
   Code: "D42",
   ID:   1,
}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
  • DoNothing : true : 遇到冲突时什么都不做
  • create()等执行sql的函数后面调用create()等函数,后面的函数不会生效,因为第一个create()时sql已经执行了

4, 如何使用默认值

type Product struct { 
   ID    uint   
   Code  string `gorm:"default:galeone"` // 不要添加多余的空格
   Price uint   `gorm:"default:18"`
}

5.选定字段的来创建

  • 选定字段创建
db.Select("Name", "Age", "CreatedAt").Create(&user)  
// INSERT INTO `users` (`name`,`age`) VALUES ("jinzhu", 18)
  • 排除选定字段
db.Omit("Age").Create(&user)  
// INSERT INTO `users` (`id`,`Name`) VALUES (1,"jinzhu")

4.GORM查询数据

import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
)

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

func main() {
   dsn := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

   //获取第一条记录(主键升序),查询不到数据则返回 ErrRecordNotFound
   u := &User{}
   db.First(u) // select * from users ORDER BY id LIMIT 1
   //查询多条数据
   users := make([]*User, 0)
   result := db.Where("age > 10").Find(&users) // select * from users where age > 10;
   fmt.Println(result.RowsAffected)            //返回找到的记录数,相当于len(users)
   fmt.Println(result.Error)                   //returns error
   //IN : SELECT * FROM user WHERE name IN ('jinzhu','jinzhu 2');
   db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
   //LIKE : SELECT * FROM users WHERE name LIKE '%jin%';
   db.Where("name LIKE ?", "%jin%").Find(&users)
   //AND : SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
   db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)

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

1.First(),Find(),Take(),Last()

  • First()获取第一条记录(主键升序)
  • Find()获取符合条件的记录(可以是多条)
  • Take()获取一条记录,没有指定排序字段
  • Last()获取最后一条记录(主键降序)
  • 使用First时,需要注意查询不到数据则返回 ErrRecordNotFound
  • 检查 ErrRecordNotFound 错误 : errors.Is(result.Error, gorm.ErrRecordNotFound)
  • 使用Find查询多条数据,查询不到数据不会返回错误
    u := &User{}
    db.First(u) 
    users := make([]*User, 0)
    result := db.Where("age > 10").Find(&users)
  • 使用Find可以使用db.Where()设置查询条件,将返回的结果保存起来,再Find(),或者直接链式调用db.Where().Find()
  • db.Find(u)效果等于db.Take(u)

2.Find()的返回值result

  • 类型为*gorm.DB
  • result.RowsAffected //返回找到的记录数,相当于len(users)
  • result.Error //返回错误

3.Find()复杂查询条件

    db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
    db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)

    result := map[string]interface{}{}
    tx = db.Model(&User{}).First(&result)
  • where()的参数是可变长的
  • 将需要查询的值用?代替,并在后面按照顺序插入字符串替代?的位置
  • 可以使用map来传入查询条件
  • 也可以使用result(定义为 map[string]interface{}{})来接受结果,先使用model(&User{}),告诉Gorm result以User{}的形式接收数据,再调用First(&result)接收数据.
  • 如果model 类型没有定义主键,则按第一个字段排序
  • model()会在后面更新数据中讲

4.使用结构体为查询条件

当使用结构体作为条件查询时,GORM只会查询非零值字段.这意味着如果您的字段值为0,"",false或者其他零值,该字段不会被用于构建查询条件,建议使用Map来构建查询条件或者使用db.Select()的api.

  • GORM默认关闭全局更新与全局删除

数据库中存在的数据:
image.png

//错误:只有name条件生效,age不被作为查询的依据
tx = db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
fmt.Println(tx.RowsAffected)
for _, x := range users {
   fmt.Println(x)
}

//正确:
tx = db.Where("name = ? AND age = ?", "jinzhu", "0").Find(&users)
fmt.Println(tx.RowsAffected)
for _, x := range users {
   fmt.Println(x)
}

//正确:
tx = db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
fmt.Println(tx.RowsAffected)
for _, x := range users {
   fmt.Println(x)
}

查询结果:
image.png

当使用结构体时,也可以使用select()函数来挑选字段使用零值的查询条件.

select()
  • select()在查询、创建、更新时选择指定你想要的字段
  • 当您只需要字段的子集时使用选择。 默认情况下,GORM 将选择所有字段。 Select 接受字符串参数和数组。
// Select name and age of user using multiple arguments
db.Select("name", "age").Find(&users)
// Select name and age of user using an array
db.Select([]string{"name", "age"}).Find(&users)
  • 使用select()也可以挑选字段用来作为查询条件
    tx = db.Select(&User{Name: "jinzhu", Age: 0}).Find(&users)
    for _, x := range users {
       fmt.Println(x)
    }

查询结果: image.png

5.GORM更新数据

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

func main() {
   dsn := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

   //条件更新单个列
   //UPDATE users SET name='hello'  WHERE age > 18;
   db.Model(&User{ID: 111}).Where("age > ?", 18).Update("name", "hello")

   // 更新多个列
   // 根据'struct' 更新属性,只会更新非零值的字段
   // UPDATE users SET name='hello',age = 18 where id = 111;
   db.Model(&User{ID: 111}).Updates(User{Name: "hello", Age: 10})

   // 根据map更新属性
   // UPDATE users SET name='hello',age = 18,actived:false 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 users SET age = age * 2 + 100 where id = 111;
   db.Model(&User{ID: 111}).Update("age", gorm.Expr("age * ? + ?", 2, 100))
}
  • 如果User结构体实现了TableName接口,就选tableName实现的表名,否则就选蛇形复数
  • Model()指定您要运行数据库操作的模型
// 将所有用户的名字更新为 `hello`
db.Model(&User{}).Update("name", "hello")
// 将ID为111的名字更新为 `hello`
db.Model(&User{ID: 111}).Update("name", "hello")
// 如果用户的主键是非空的,将使用它作为条件,然后只会将该用户的名字更新为 `hello`
db.Model(&user).Update("name", "hello")
  • update()更新单个列,不能传递单个列,必须使用Model()或者TableName()设置表名,否则会报错

  • updates()可以传入结构体修改列,因此Model()不是必须的,但是没有过滤条件,可以使用Model(&user)或者whele()添加过滤条件

  • updates()也可以使用map或者select()规避零值更新的问题,使用结构体更新会忽略零值

    db.Model(&User{ID: 111}).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
    
  • SQL 表达式更新

    db.Model(&User{ID: 111}).Update("age", gorm.Expr("age * ? + ?", 2, 100))
    

    使用gorm.Expr()将参数传入clause.Expr{}结构体,用来以表达式的形式更新,等效于先查询要修改的值age再进行db.Model(&User{ID: 111}).Update("age", age * 2 + 100)修改

6.GORM更删除数据

Delete() 删除符合给定条件的值。如果值包含主键,则它包含在条件中。如果值包含 deleted_at 字段,则 Delete 执行软删除,而不是通过将 deleted_at 设置为 null(如果为 null)来执行软删除。

1.物理删除

  • 使用物理删除,user结构体需要注销字段:gorm.DeletedAt
  • 此外软删除必须使用结构体的指针或者slice,否则会报错,物理删除不必
func delete1(db *gorm.DB) {
   db.Delete(User{}, 10)                              // DELETE FROM users Where id = 10;
   db.Delete(User{}, "10")                            // DELETE FROM users Where id = 10;
   db.Delete(User{}, []int{1, 2, 3})                  // DELETE FROM users WHERE id IN (1,2,3)
   db.Where("name LIKE ?", "%jinzhu%").Delete(User{}) // DELETE FROM users WHERE name LIKE "%jinzhu%";
   db.Delete(User{}, "name LIKE ?", "%jinzhu%")       // DELETE FROM users WHERE name LIKE "%jinzhu%";
}

直接删除数据库中的内容,一般情况下应使用软删除.

2.软删除

  • GORM提供了gorm.DeleteAt用于帮助用户实现软删除
  • 拥有软删除能力的Model调用Delete时,记录不会从被数据库中真正的删除.但GORM会将DeleteAt置为当前时间,并且你不能再通过正常的查询方法找到该记录.
  • 使用Unscoped可以查询到被软删的数据
import (
   "fmt"
   "gorm.io/driver/mysql"
   "gorm.io/gorm"
)

type User struct {
   ID      int64
   Name    string `gorm:"default:galeone"`
   Age     int64  `gorm:"default:18"`
   actived bool
   Deleted gorm.DeletedAt `gorm:"column:deleted_at"` //软删除必须
}

func main() {
   dsn := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

   //软删除
   //delete1(db)
   u := &User{ID: 111} // u的id是111
   db.Delete(&u)       // DELETE FROM users Where id = 111;

   //批量删除
   tx := db.Where("age = ?", 20).Delete(&User{}) // DELETE FROM users Where age = 20;
   fmt.Println(tx.RowsAffected)
   users := make([]*User, 0)
   //在查询时会忽略被软删除的记录
   tx = db.Where("age = 20").Find(&users) // select * from users WHERE age = 20 and deleted_at IS NULL;
   for _, x := range users {
      fmt.Println(x)  //不会输出
   }
   fmt.Println()
   // 在查询时不会忽略被软删除的记录
   tx = db.Unscoped().Where("age = 20").Find(&users) // select * from users WHERE age = 20;
   for _, x := range users {
      fmt.Println(x)  //输出信息
   }
}

软删除前:
image.png 软删除后:
image.png

6. GORM事务

  • 事务 : 由一次或者多次基本操作构成,或者说,事务由一条或者多条 SQL 语句构成。 事务包含的所有 SQL 语句作为一个整体向数据库提交,只有所有的 SQL 语句都执行完成,整个事务才算成功,一旦某个 SQL 语句执行失败,整个事务就失败了。 事务失败后需要回滚所有的 SQL 语句。
  • Gorm提供了Begin,Commit,Rollback方法用于使用事务
func main() {
   dsn := User + ":" + Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
   db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
   if err != nil {
      panic("failed to connect database" + fmt.Sprintf("%s", err))
   }

   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()
}
  • db.begin()执行了两步操作:

    • 固化连接,保证使用同一个连接,底层数据库操作使用的是连接池
    • 执行开启sql的sql语句
  • Gorm提供了Tansaction方法用于自动提交事务,避免用户漏写Commit,Rollback

func TransactionTest(db gorm.DB) {
   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
   }
}
  • 返回err或者panic的时候会自动进行tx.Rollback(),返回nil的时候自动Commit().
  • Tansaction方法的实现就是使用defer进行err和panic的拦截

7. GORM Hook

type User struct {
   ID   int64
   Name string `gorm:"defer:goleone"`
   Age  int64  `gorm:"defer:18"`
}

type Email struct {
   ID    int64
   Name  string
   Email string
}

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
   if u.Age < 0 {
      return errors.New("can't save invalid data")
   }
   return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
   return tx.Create(&Email{
      ID:    u.ID,
      Name:  u.Name,
      Email: u.Name + "@***,com",
   }).Error
}
  • GORM默认开了一个事务,Hook操作也在默认操作里,可以保证一致性,因此性能会有影响
  • GORM在提供了CURD的Hook能力.
  • Hook是在创建,查询,更新,删除等操作之前.之后自动调用的函数.
  • 如果任何Hook返回错误,GORM将停止后续的操作并回滚事务

8. GORM性能提高

dsn := Const.User + ":" + Const.Pass + "@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
   SkipDefaultTransaction: true, //关闭默认事务
   PrepareStmt:            true, //缓存预编译语句
}) //连接数据库
if err != nil {
   panic("failed to connect database" + fmt.Sprintf("%s", err))
}
  • 对于写操作(创建,更新,删除),为了确保数据的完整性,GORM会将它们封装在事务内运行.但这会降低性能,你可以用SkipDefaultTransaction关闭默认事务.
  • 使用PrepareStmt缓存预编译语句可以提高后续调用的速度.

9.GORM生态

更多GROM用法请查看GORM的中文文档