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

85 阅读4分钟

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

GORM

Gorm的约定(默认)

  • Gorm使用名为ID的字段作为主键
  • 使用结构体的蛇形复数作为表名
  • 字段名的蛇形作为列名
  • 使用CreatedAt和UpdateAt作为创建、更新时间

gorm例子

package main
​
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
  "gorm.io/gorm/schema"
)
​
type ProductInfo struct {
  gorm.Model
  Code  string `gorm:"size:256"`
  Price uint
}
​
func (p ProductInfo) TableName() string {
  return "product_info" //为model定义表名,这是一个约定,当然也可以由gorm的一些配置来自定义表名
}
​
func main() {
  dsn := "xys:232020ctt@@tcp(rm-uf6e4xr978w748b9w7o.mysql.rds.aliyuncs.com:3306)/sql_test?charset=utf8mb4&parseTime=true&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ //连接数据库
    NamingStrategy: schema.NamingStrategy{
      SingularTable: true,
    },
    PrepareStmt:       true,
    AllowGlobalUpdate: true,
  })
  if err != nil {
    panic("failed to connect database")
  }
  //迁移 schema
  err = db.AutoMigrate(&ProductInfo{})
  if err != nil {
    panic("autoMigrate failed")
  }
  // Create 创建一条传递一个对象,创建多条传递一个切片
  result := db.Create(&ProductInfo{Code: "D42", Price: 100})
  err = result.Error
​
  //Read
  var product ProductInfo
  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(ProductInfo{Price: 200, Code: "F42"})                //仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //可以将字段值更新为零值// Delete - 删除 product
  db.Delete(&product)
}

Grom支持的数据库

Gorm目前支持MySQL、SQLServer、PostgreSQL、SQLite

dsn := "root:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=true&loc=Local"

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ //连接数据库
   NamingStrategy: schema.NamingStrategy{
      SingularTable: true,
   },
   SkipDefaultTransaction: true,
   PrepareStmt:            true,
   AllowGlobalUpdate:      true,
})

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

GORM创建数据

infos := []*ProductInfo{{Code: "D45", Price: 100}, {Code: "D45", Price: 100}, {Code: "D45", Price: 100}}
result := db.Create(&ProductInfo{Code: "D42", Price: 100})
result = db.Create(infos)

GORM查询数据

//Read
var product ProductInfo
//First 查询不到数据会返回ErrRecordNotFound
db.First(&product, 1)                 // 根据整型主键查找
db.First(&product, "code = ?", "D42") //根据code字段值为 D42查找
productInfos := make([]*ProductInfo, 0)
result = db.Where("price > 100").Find(&productInfos)
result = db.Where("price = ?", 200).Find(&productInfos)
result = db.Where(&ProductInfo{Code: "D45"}).Find(&productInfos)
result = db.Where(map[string]interface{}{"Code": "D45"}).Find(&productInfos) //这种方式可以查询零值

GORM更新数据

//Update - 将 product 的price 更新为 200
db.Model(&product).Update("Price", 200)
// Update - 更新多个字段
db.Model(&product).Updates(ProductInfo{Price: 200, Code: "F42"})                //仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //可以将字段值更新为零值
db.Model(&ProductInfo{}).Where("id = ?", 1).Updates(ProductInfo{Code: "E56"})
db.Model(&ProductInfo{}).Where("id = ?", 1).Update("price", gorm.Expr("price * ?", 2))

GORM删除数据

GORM提供了gorm.DeletedAt来实现软删

拥有软删能力的Model在调用Delete时,记录不会从数据库中正真删除。但orm会置delete_at为当前时间,并且不能通过正常的查询方法找到该记录。

使用Unscoped可以查询到被软删的记录

// Delete - 删除 product
db.Delete(&product)
db.Delete(&ProductInfo{}, 1)
db.Delete(&ProductInfo{}, []int{1, 2, 3})
db.Where("code Like D%").Delete(&ProductInfo{})
db.Delete(ProductInfo{}, "code Like D%")

GORM 事务

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

// gorm 事务
tx := db.Begin()
product1 := ProductInfo{Code: "D42", Price: 100}
product2 := ProductInfo{Code: "D42", Price: 200}
err = tx.Create(&product1).Error
if err != nil {
   tx.Rollback()
   return
}
err = tx.Create(product2).Error
if err != nil {
   tx.Rollback()
   return
}
tx.Commit()

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

//gorm 事务2
err = db.Transaction(func(tx *gorm.DB) error {
   product1 := ProductInfo{Code: "D42", Price: 100}
   product2 := ProductInfo{Code: "D42", Price: 200}
   err = tx.Create(&product1).Error
   if err != nil {
      return err
   }
   err = tx.Create(product2).Error
   if err != nil {
      return err
   }
   return nil
})
if err != nil {
   return
}

GORM Hook

type User struct {
   gorm.Model
   Name string `gorm:"size:256"`
   Age  uint   `gorm:"default:18"`
}
type Email struct {
   gorm.Model
   Name  string `gorm:"size:256"`
   Email string `gorm:"size:256"`
}
​
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
   if u.Age < 0 {
      return errors.New("can't save invalid data")
   }
   return nil
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
   return tx.Create(&Email{Name: u.Name,Email: u.Name + "@**.com"}).Error
}

GORM 提供了CRUD的Hook能力

Hook在创建、查询、更新、删除等操作之前,之后自动调用的函数

如何任何的Hook返回错误,GORM将停止后续的操作并会滚事务

GORM 性能提高

对于写操作(创建、更新、删除),为了确保数据的完整性,GORM会将它们封装在事务里运行。但这会降低性能,你可以使用skipDefaultTransation关闭默认事务

使用PrepareStmt缓存预编译语句可以提高后续调用的速度,本机测试提高大约35%左右

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ //连接数据库
   SkipDefaultTransaction: true,
   PrepareStmt:       true,
})

GORM 生态

常用扩展:

image-20230120182545118