这是我参与「第五届青训营 」笔记创作活动的第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 生态
常用扩展: