gorm使用

215 阅读3分钟

默认约定

  • gorm使用名为ID的字段作为主键
  • 使用结构体的蛇形复数作为表名 这种情况自定义了表名
type Product struct {
	Code string
	Price uint
}

func (p Product) TableName() string {
	return "product"
}
  • 字段名的蛇形作为列名
  • 使用CreatedAt、UpdatedAt字段作为创建、更新时间

查询

First/Last

使用First时,查询不到数据会返回ErrRecordNotFound。First and Last 方法会按主键排序找到第一条记录和最后一条记录

result := db.First(&product)
fmt.Println("the number of result: ", result.RowsAffected)
fmt.Println("use first to serch, the error is : ", result.Error)

Find查询多条数据

查询不到不会返回错误

products := make([]*Product, 2)
result := db.Where("code = ?", "D42").Find(&products)
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)
db.Where("code IN ?", []string{"D42", "F42"}).Find(&products)
db.Where("code = ? OR code = ?", "D42", "F42").Find(&products)

使用结构体查询只会查询非零字段

db.Where(&Product{"F42", 0}).Find(&products)

上面这段查询只是查找code = F42的结果,并没有price = 0

如果要查询零值字段,使用map来构建查询条件

db.Where(map[string]interface{}{"Code": "F42", "Price": 0}).Find(&products)

更新

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

Model指定表名

db.Model(&Product{}).Where("code = ?", "F426").Update("code", "FF")

当时用Updates的参数为struct的时候可以将Model省略

db.Where("code = ?", "D42").Updates(&Product{Code: "D42", Price: 188})

如果更新零值使用map或select字段

db.Model(&Product{}).Where("code = ?", "F42").Updates(map[string]interface{}{"price": 0})
db.Model(&Product{}).Where("code = ?", "D42").Select("code").Update("code", "")

更新选定字段

db.Model(&Product{}).Where("code = ?", "D42").Select("code").Updates(&Product{Code: "D41", Price: 188})

price并不会修改

表达式更新

db.Model(&Product{}).Where("price != ?", 0).Update("price", gorm.Expr("price * ? + ?", 2, 100))

删除

物理删除

db.Where("price = ?", 0).Delete(&Product{})

软删除

如果包含了gorm.DeletedAt(也在gorm.Model中),那么该模型将会自动获得软删除能力

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。

查找被软删除的记录

使用Unscoped来查询被软删除的记录

db.Unscoped().Where("age = ?", 0).Find(&users)

永久删除

db.Unscoped().Where("age = ?", 0).Delete(&User{})

事务

gorm提供了Begin``Commit``Rollback方法用于使用事务

func main() {
	db, err := gorm.Open(
		mysql.Open("root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"),
		&gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

  tx := db.Begin()
  if err = tx.Create(&User{Name: "name"}).Error; err != nil {
    // when meet error to rollback
    tx.Rollback()
    return
  }
  if err = tx.Create(&User{Name: "name1"}).Error; err != nil {
    tx.Rollback()
    return
  }

  tx.Commit()
}

Transaction

Gorm提供了Transction方法用于自动提交事务,避免用户漏写CommitRollback

func main() {
	db, err := gorm.Open(
		mysql.Open("root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"),
		&gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}
  if err = db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&User{Name: "name"}).Error; err != nil {
      return err
    }
    if err = tx.Create(&User{Name: "name1"}).Error; err != nil {
      return err
    }
    return nil
  }); err != nil {
    return
  }
}

禁用默认事务

gorm.ConfigSkipDefaultTransaction设为true

Hook

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

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

创建

  • BeforeSave
  • BeforeCreate
  • AfterCreate
  • AfterSave
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  if u.Age < 0 {
    return errors.New("can't save invalid age")
  }
  return nil
}
type Email struct {
  ID int64
  Email string
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  return tx.Create(&Email{ID: u.ID, Email: u.Name + "@xxx.com"}).Error
}

更新

  • BeforeSave
  • AfterSave
  • BeforeUpdate
  • AfterUpdate

删除

  • BeforeDelete
  • AfterDelete