这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记。
GORM
ORM(Object Relational Mapping)对象关系映射,是编程语言中的Object/Struct数据类型到关系型数据库中表的映射。GORM是Go语言中实现ORM的一个基本框架,官方文档非常具有参考价值。
Model声明
Go语言中的Models的概念对应于数据库表的概念,只完成纯粹的数据结构的定义。而GORM中倾向于约定,而不是配置,因此内置了一个约定的gorm.Model 结构体,约定主键、创建时间、更新时间、删除时间、索引等信息,默认上主键id是自增的。
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
另外对于GORM,其中也可以在结构体中,对字段进行权限控制,参考官方文档Models中的示例
->和<-分别代表read和write的权限控制<:create、<:update和<:false代表允许写创建、更新和拒绝3种模式,默认<-兼有创建更新。-代表忽略权限控制-:migration和-:all迁移忽略和读写迁移忽略,默认-读写忽略。
type User struct {
Name string `gorm:"<-:create"` // allow read and create
Name string `gorm:"<-:update"` // allow read and update
Name string `gorm:"<-"` // allow read and write (create and update)
Name string `gorm:"<-:false"` // allow read, disable write permission
Name string `gorm:"->"` // readonly (disable write permission unless it configured)
Name string `gorm:"->;<-:create"` // allow read and create
Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
Name string `gorm:"-"` // ignore this field when write and read with struct
Name string `gorm:"-:all"` // ignore this field when write, read and migrate with struct
Name string `gorm:"-:migration"` // ignore this field when migrate with struct
}
除此之外GORM还提供了很多字段标签使用,具体可以参考上述官方文档。
连接数据库
这里以Mysql数据库作为案例,默认使用Product类作为测试。
//Product类
type Product struct {
gorm.Model
Code string
Price uint
}
//数据库连接
dsn := "root:abc123@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect mysql database")
}
插入
单条记录插入
在GORM中使用Create可以完成数据的插入功能, 默认是Product的指针作为参数。也可以采用链式法则选择指定的字段插入记录,忽略其他的字段项,gorm.Model字段在数据库中不受影响。
db.Create(&model.Product{Code: "D42", Price: 111})
//只更新Code字段
db.Select("Code").Create(&model.Product{Code: "D42", Price: 111})
// 忽略Price字段
db.Omit("Price").Create(&model.Product{Code: "D42", Price: 111})
批量插入
GORM中Create和CreateInBatches都可以实现批量插入,注意slice的传参关系。
var products = []model.Product{{Code: "D1", Price: 111}, {Code: "D2", Price: 222}}
//方式一
db.Create(&products)
//方式二
db.CreateInBatches(products, 100)
参考Create的源码我们可以发现,在GORM config中有个CreateBatchSize参数,可以预设值批次大小,如果我们指定了参数的值,Create会优先调用CreateInBatches函数进行批量操作。
if db.CreateBatchSize > 0 {
return db.CreateInBatches(value, db.CreateBatchSize)
}
查询
查询单条记录
GORM提供了First、Take、Last三个方法检索单个对象,3种方法各有不同,具体以下面例子。
var product model.Product
db.First(&product) //按主键id升序的第一条数据
db.Take(&product) //第一条记录
db.Last(&product) //按主键id降序的第一条数据
同样3个函数可以按条件进行过滤查询。
db.First(&product, 1) //按照id=1条件查询
db.First(&product, "code = ?", "D42") //code="D42"条件查询
批量查询
使用Find可以实现批量查询
products := make([]model.Product, 5)
db.Find(&products)
过滤条件有多种写法
- 内联条件
db.Find(&products, "id in ?", []int{1, 2, 3})
- 使用Where过滤
Where作为链方法,最终交由Find最终执行结果,绑定到products上,Where接受String&Struct&Map多种条件类型。
//利用string
db.Where("id in ?", []int{1, 2, 3}).Find(&products)
//利用struct
db.Where(&model.Product{Price: 111}).Find(&products)
//利用Map
condMap := map[string]interface{}{"id": []int{1, 2, 3}}
db.Where(condMap).Find(&products)
对于Mysql的Not、Or、Order、Limit、Group By、Distinct、Join多种常用的操作,GORM也提供了函数支持,用法与Where类似作为链式的组成。
更新
更新所有字段
在GORM中使用Save会更新所有字段,即使字段没默认赋值,为零值。
product := model.Product{Code: "ddd", Price: 123}
product.ID = 2
db.Save(&product)
单列更新
Model函数指定了需要进行db操作的Model,然后用Update更新某列的值,另外Model后面还可以接Where过滤条件。如果Model没有指定主键id,会提示WHERE conditions required并报错,这时就成了批量更新,必须加上条件过滤。
var product model.Product
product.ID = 2
db.Model(&product).Update("Price", 250)
// 带Where条件
db.Model(&product).Where("Code = ?", "ddd"), Update("Price", 250)
多列更新
使用Updates方法可以实现多列更新,其中支持Struct&Map两种方式
var product model.Product
product.ID = 1
// struct
db.Model(&product).Updates(model.Product{Code: "dup",Price: 111})
// map[string]interface{}
db.Model(&product).Updates(map[string]interface{}{"Code":"dup", "Price":111})
更新指定字段
指定字段的更新可以参考前面所说的Select和Omit函数指定字段。
批量更新
//方式一
db.Model(&product).Where("price = ?", 111).Updates(model.Product{Price: 123})
//方式二:指定操作的表
db.Table("products").Where("price = ?", 111).Updates(model.Product{Price: 123})
删除
GORM删除采用Delete函数,同样具有Where和内联两种方式,这里不会返回删除的数据,如果需要返回删除数据,对支持返回数据的数据而言,借助Clauses函数可以得到删除值。就根据实践而言,mysql中好像没有能正确返回删除的数据。
//条件过滤
db.Where("code = ?", "dup").Delete(&model.Product{})
//删除id = 1
db.Delete(&product, 1)
//失败了,返回还是[]
db.Clauses(clause.Returning{}).Where("code = ?", "D42").Delete(products)
软删除
在gorm.Model中有DeletedAt字段,包含该字段时,数据库会进行软删除,DeletedAt字段会被置为当前时间,普通查询无法再找到该记录。使用Unscoped方法可以实现永久删除。
db.Unscoped().Where("code = ?", "D42").Delete(products)
总结
GORM中提到了相当多的操作方式,远不止上述提到的增删改查,甚至于光是这部分也有很多东西没有提及,强烈建议大家去参考官方文档进行实践。