5.15_Gorm初上手(一)| 青训营笔记

197 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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中的示例

  • -><-分别代表readwrite的权限控制
  • <: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})

批量插入

GORMCreateCreateInBatches都可以实现批量插入,注意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)

对于MysqlNot、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})

更新指定字段

指定字段的更新可以参考前面所说的SelectOmit函数指定字段。

批量更新

//方式一
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中提到了相当多的操作方式,远不止上述提到的增删改查,甚至于光是这部分也有很多东西没有提及,强烈建议大家去参考官方文档进行实践。