Gorm总结

118 阅读4分钟

声明模型

GORM 通过将 Go 结构体(Go structs) 映射到数据库表来简化数据库交互。

  • CreatedAt UPdatedAt 两个字段是特殊字段,当记录被创建或更新时,GORM 会自动向内填充当前时间。

  • 主键:GORM 使用一个名为ID 的字段作为每个模型的默认主键。

  • 表名:默认情况下,GORM 将结构体名称转换为 snake_case 并为表名加上复数形式。 例如,一个 User 结构体在数据库中的表名变为 users 。

  • 列名:GORM 自动将结构体字段名称转换为 snake_case 作为数据库中的列名。


查询

单个查询(First Last)

First and Last 方法会按主键排序找到第一条记录和最后一条记录 (分别)。 只有在目标struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。且没有找到记录时,它会返回 ErrRecordNotFound 错误

根据主键查询

单个查询时,可以传入第二个参数:主键值。

var user User
db.First(&user, 1) // 查询主键为 1 的记录

当目标对象有一个主键值时,将使用主键构建查询条件,例如:

var user = User{ID: 10}
db.First(&user)
// SELECT * FROM users WHERE id = 10;

var result User
db.Model(User{ID: 10}).First(&result)

条件查询

GORM 的查询构建器会收集所有的查询条件,并在你调用 Find、First、Take、Scan 等方法时才会真正执行查询Where。

如果对象设置了主键,条件查询将不会覆盖主键的值,而是用 And 连接条件。 写gorm就规范一些,结构体就只指定主键值,剩余的条件都用Where声明

var user = User{ID: 10}
db.Where("id = ?", 20).First(&user)
// SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1

struct && map 条件查询(Where方法)

GORM 只会将结构体对象中的非零值作为查询条件。 要使零值也作为查询条件,需要使用 map 作为查询条件。

内联查询

不使用Where Model,直接使用First Find 等方法。

db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";

Joins 添加关联表

db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

更新

Save

Save 会保存所有的字段,即使字段是零值 如果保存值没有主键,它将执行Create,否则它将执行Update。

    user := User{ID:1,Name: "jinzhu", Age: 18}
    db.Save(&user)

不要将 Save 和 Model一同使用, 这是 未定义的行为。

Updates 批量更新

如果你执行一个没有任何条件的批量更新,GORM 默认不会运行,并且会返回 ErrMissingWhereClause 错误

db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClause

删除

Delete的方法使用和First一样

  • 前面model指定表并且查询完成后 调用delete批量删除
  • delete 传入 struct,指定表的同时指定要删除的记录的主键
  • delte第一个参数指定表,第二个参数(未声明的情况)是目标记录的主键
  • 内联查询 第二个参数是查询条件。 删除一条记录时,删除对象需要指定主键,否则会触发 批量删除

当你试图执行不带任何条件的批量删除时,GORM将不会运行并返回ErrMissingWhereClause 错误。 如果硬是要这么做,用原生sql

像First, Find, Delte这些可以自己指定表的方法,都可以不需要Model

Belongs to 定义外键,子表

belongs to 会与另一个模型建立了一对一的连接(子表逻辑上来看)。

// 子表的model中要包含外键,和父表model
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company
}

type Company struct {
  ID   int
  Name string
}

Blong to 和 Has one, Has three查询主体不同。 例如一个用户有一张信用卡。 以信用卡为查询主体,就要使用Belong to模式 卡属于用户。 以用户为查询主体,就要使用Has one模式,用户拥有一张信用卡。 两者在实现上正好是互补的 连贯的。

Has Many 一对多

// User 有多张 CreditCard,UserID 是外键
type User struct {
  gorm.Model
  CreditCards []CreditCard  `gorm:"foreignKey:UserID"`  
}

type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}

gorm:"foreignKey:UserID" 作用是定义子表中的外键,即哪一个字段和我们的主键关联。

ManytoMany

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  gorm.Model
  Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
  Users []*User `gorm:"many2many:user_languages;"`
}

Scopes

作用域允许你复用通用的逻辑,这种共享逻辑需要定义为类型func(*gorm.DB) *gorm.DB

func Paginate(page, size int) func(db *gorm.DB) *gorm.DB{
	return func (db *gorm.DB) *gorm.DB{
		if page <=0{
			page = 1
		}
		switch {
		case  size >= 100:
			size = 100	
		case  size <= 10 : 
			size = 10
		}
		offset := (page - 1) *size
		return db.Offset(offset).Limit(size)
	}
}

不可寻指错误


在 Go 语言中,只有可寻址的值(addressable value)才能被赋值。不可寻址的值包括:

  • 字面量(如 123、"hello")

  • 常量

  • 函数的返回值(除非返回的是指针)

  • 映射(map)中的元素

  • 切片(slice)中的元素(除非切片本身是可寻址的)

我们需要确保在gorm操作中,Find,First,Scan等方法的参数是可寻址的,这样gorm反射操作才不会出问题。 而指针是可寻址的,因此Find,First,Scan等方法的参数是指针,而不是变量本身。